Rework the storing of cost marices for GH simplification

This commit is contained in:
Mael Rouxel-Labbé 2019-09-19 14:29:54 +02:00
parent 4a0f4536d6
commit cb3c68eca2
5 changed files with 124 additions and 48 deletions

View File

@ -26,6 +26,8 @@
#include <CGAL/Surface_mesh_simplification/internal/Common.h> #include <CGAL/Surface_mesh_simplification/internal/Common.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_core.h> #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_core.h>
#include <CGAL/tags.h>
#include <boost/optional/optional.hpp> #include <boost/optional/optional.hpp>
#include <utility> #include <utility>
@ -33,25 +35,51 @@
namespace CGAL { namespace CGAL {
namespace Surface_mesh_simplification { namespace Surface_mesh_simplification {
template<class TM_> template <typename TM_>
struct GarlandHeckbert_cost_matrix
{
typedef internal::GarlandHeckbert_core<TM_> GH_Core;
typedef typename GH_Core::Matrix4x4 type;
};
template <typename TM_, typename VCM_>
class GarlandHeckbert_cost class GarlandHeckbert_cost
{ {
public:
typedef TM_ TM; typedef TM_ TM;
typedef VCM_ Vertex_cost_map;
typedef typename internal::GarlandHeckbertCore<TM> GHC; public:
typedef typename GHC::garland_heckbert_state_type garland_heckbert_state_type; typedef typename boost::graph_traits<TM>::vertex_descriptor vertex_descriptor;
typedef typename GHC::Matrix4x4 Matrix4x4; typedef typename boost::graph_traits<TM>::halfedge_descriptor halfedge_descriptor;
typedef typename GHC::Row4 Row4;
typedef typename GHC::Col4 Col4;
typedef typename GHC::FT FT; typedef internal::GarlandHeckbert_core<TM> GH_Core;
typedef typename GH_Core::Matrix4x4 Matrix4x4;
typedef typename GH_Core::Col4 Col4;
typedef typename GH_Core::FT FT;
typedef typename boost::optional<FT> Optional_FT; typedef typename boost::optional<FT> Optional_FT;
GarlandHeckbert_cost(const garland_heckbert_state_type& aCostMatrices) // Tells the edge collapse main function that we need to call "initialize"
: mCostMatrices(aCostMatrices) // and "update" functions. A bit awkward, but still better than abusing visitors.
typedef CGAL::Tag_true Update_tag;
GarlandHeckbert_cost(Vertex_cost_map& vcm,
const FT discontinuity_multiplier = 100)
:
m_cost_matrices(vcm),
m_discontinuity_multiplier(discontinuity_multiplier)
{ } { }
void initialize(const TM& tmesh) const
{
for(const halfedge_descriptor h : halfedges(tmesh))
{
const vertex_descriptor v = target(h, tmesh);
put(m_cost_matrices, v, GH_Core::fundamental_error_quadric(h, tmesh, m_discontinuity_multiplier));
}
}
template <typename Profile> template <typename Profile>
boost::optional<typename Profile::FT> boost::optional<typename Profile::FT>
operator()(const Profile& aProfile, operator()(const Profile& aProfile,
@ -60,19 +88,29 @@ public:
if(!aPlacement) if(!aPlacement)
return boost::optional<typename Profile::FT>(); return boost::optional<typename Profile::FT>();
Matrix4x4 combinedMatrix = std::move(GHC::combine_matrices( CGAL_precondition(get(m_cost_matrices, aProfile.v0()) != Matrix4x4());
mCostMatrices.at(aProfile.v0()), CGAL_precondition(get(m_cost_matrices, aProfile.v1()) != Matrix4x4());
mCostMatrices.at(aProfile.v1())));
Col4 pt = std::move(GHC::point_to_homogenous_column(*aPlacement)); const Matrix4x4 combined_matrix = GH_Core::combine_matrices(get(m_cost_matrices, aProfile.v0()),
get(m_cost_matrices, aProfile.v1()));
Optional_FT cost = (pt.transpose() * combinedMatrix * pt)(0, 0); const Col4 pt = GH_Core::point_to_homogenous_column(*aPlacement);
const Optional_FT cost = (pt.transpose() * combined_matrix * pt)(0, 0);
return cost; return cost;
} }
template <typename Profile>
void update_after_collapse(const Profile& aProfile,
const vertex_descriptor new_v) const
{
put(m_cost_matrices, new_v,
GH_Core::combine_matrices(get(m_cost_matrices, aProfile.v0()),
get(m_cost_matrices, aProfile.v1())));
}
private: private:
const garland_heckbert_state_type& mCostMatrices; Vertex_cost_map m_cost_matrices;
const FT m_discontinuity_multiplier;
}; };
} // namespace Surface_mesh_simplification } // namespace Surface_mesh_simplification

View File

@ -26,10 +26,10 @@
namespace CGAL { namespace CGAL {
namespace Surface_mesh_simplification { namespace Surface_mesh_simplification {
template <class FT> template <class FT_>
class GarlandHeckbert_cost_stop_predicate class GarlandHeckbert_cost_stop_predicate
{ {
const FT m_gh_cost_threshold; typedef FT_ FT;
public: public:
GarlandHeckbert_cost_stop_predicate(const FT gh_cost_threshold) GarlandHeckbert_cost_stop_predicate(const FT gh_cost_threshold)
@ -37,14 +37,17 @@ public:
m_gh_cost_threshold(gh_cost_threshold) m_gh_cost_threshold(gh_cost_threshold)
{ } { }
template <typename F, typename Profile> template <typename Profile>
bool operator()(const F& aCurrentCost, bool operator()(const FT& current_cost,
const Profile&, const Profile&,
std::size_t, std::size_t,
std::size_t) const std::size_t) const
{ {
return aCurrentCost >= m_gh_cost_threshold; return current_cost >= m_gh_cost_threshold;
} }
private:
const FT m_gh_cost_threshold;
}; };
} // namespace Surface_mesh_simplification } // namespace Surface_mesh_simplification

View File

@ -24,50 +24,52 @@
#include <CGAL/license/Surface_mesh_simplification.h> #include <CGAL/license/Surface_mesh_simplification.h>
#include <CGAL/Surface_mesh_simplification/internal/Common.h> #include <CGAL/Surface_mesh_simplification/internal/Common.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/Lindstrom_Turk_core.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_core.h> #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_core.h>
#include <boost/optional/optional.hpp>
namespace CGAL { namespace CGAL {
namespace Surface_mesh_simplification { namespace Surface_mesh_simplification {
template<class TM_> template<class TM_, typename VCM_>
class GarlandHeckbert_placement class GarlandHeckbert_placement
{ {
public: public:
typedef TM_ TM; typedef TM_ TM;
typedef typename internal::GarlandHeckbertCore<TM> GHC; typedef VCM_ Vertex_cost_map;
typedef typename GHC::garland_heckbert_state_type garland_heckbert_state_type;
typedef typename GHC::Matrix4x4 Matrix4x4;
typedef typename GHC::Row4 Row4;
typedef typename GHC::Col4 Col4;
typedef typename GHC::FT FT; typedef typename internal::GarlandHeckbert_core<TM> GH_core;
typedef typename boost::optional<FT> Optional_FT; typedef typename GH_core::Matrix4x4 Matrix4x4;
typedef typename GH_core::Col4 Col4;
GarlandHeckbert_placement(const garland_heckbert_state_type& aCostMatrices) GarlandHeckbert_placement(const Vertex_cost_map& cost_matrices)
: mCostMatrices(aCostMatrices) : m_cost_matrices(cost_matrices)
{ } { }
template <typename Profile> template <typename Profile>
boost::optional<typename Profile::Point> operator()(const Profile& aProfile) const boost::optional<typename Profile::Point> operator()(const Profile& aProfile) const
{ {
const Matrix4x4 combinedMatrix = std::move(GHC::combine_matrices( CGAL_precondition(get(m_cost_matrices, aProfile.v0()) != Matrix4x4());
mCostMatrices.at(aProfile.v0()), CGAL_precondition(get(m_cost_matrices, aProfile.v1()) != Matrix4x4());
mCostMatrices.at(aProfile.v1())));
const Col4 p0 = std::move(GHC::point_to_homogenous_column(aProfile.p0())); // the combined matrix has already been computed in the evaluation of the cost...
const Col4 p1 = std::move(GHC::point_to_homogenous_column(aProfile.p1())); const Matrix4x4 combinedMatrix = GH_core::combine_matrices(
const Col4 opt = std::move(GHC::optimal_point(combinedMatrix, p0, p1)); get(m_cost_matrices, aProfile.v0()),
get(m_cost_matrices, aProfile.v1()));
boost::optional<typename Profile::Point> pt const Col4 p0 = GH_core::point_to_homogenous_column(aProfile.p0());
= typename Profile::Point(opt(0) / opt(3), opt(1) / opt(3), opt(2) / opt(3)); const Col4 p1 = GH_core::point_to_homogenous_column(aProfile.p1());
const Col4 opt = GH_core::optimal_point(combinedMatrix, p0, p1);
boost::optional<typename Profile::Point> pt = typename Profile::Point(opt(0) / opt(3),
opt(1) / opt(3),
opt(2) / opt(3));
return pt; return pt;
} }
private: private:
const garland_heckbert_state_type& mCostMatrices; const Vertex_cost_map m_cost_matrices;
}; };
} // namespace Surface_mesh_simplification } // namespace Surface_mesh_simplification

View File

@ -39,7 +39,7 @@ namespace Surface_mesh_simplification {
namespace internal { namespace internal {
template<class TM_> template<class TM_>
struct GarlandHeckbertCore struct GarlandHeckbert_core
{ {
typedef TM_ TM; typedef TM_ TM;
typedef boost::graph_traits<TM> GraphTraits; typedef boost::graph_traits<TM> GraphTraits;
@ -61,8 +61,6 @@ struct GarlandHeckbertCore
typedef typename Eigen::Matrix<FT, 1, 4> Row4; typedef typename Eigen::Matrix<FT, 1, 4> Row4;
typedef typename Eigen::Matrix<FT, 4, 1> Col4; typedef typename Eigen::Matrix<FT, 4, 1> Col4;
typedef std::unordered_map<vertex_descriptor, Matrix4x4> garland_heckbert_state_type;
static Col4 point_to_homogenous_column(const Point& pt) static Col4 point_to_homogenous_column(const Point& pt)
{ {
return Col4(pt.x(), pt.y(), pt.z(), FT(1)); return Col4(pt.x(), pt.y(), pt.z(), FT(1));

View File

@ -33,6 +33,21 @@
namespace CGAL { namespace CGAL {
namespace Surface_mesh_simplification { namespace Surface_mesh_simplification {
namespace internal {
BOOST_MPL_HAS_XXX_TRAIT_DEF(Update_tag)
template <typename Cost_oracle,
bool has_Update_tag = has_Update_tag<Cost_oracle>::value>
struct Oracles_require_updates :
public CGAL::Boolean_tag<Cost_oracle::Update_tag::value>
// when Mesh_domain has the nested type Has_features
{ };
template <typename Cost_oracle>
struct Oracles_require_updates<Cost_oracle, false> : public CGAL::Tag_false { };
} // namespace internal
// Implementation of the vertex-pair collapse triangulated surface mesh simplification algorithm // Implementation of the vertex-pair collapse triangulated surface mesh simplification algorithm
template<class TM_, template<class TM_,
@ -171,8 +186,22 @@ public:
int run(); int run();
private: private:
template <bool Tag = internal::Oracles_require_updates<GetCost>::value>
void initialize_oracles() const { };
template <>
void initialize_oracles<true>() const { Get_cost.initialize(mSurface); }
template <bool Tag = internal::Oracles_require_updates<GetCost>::value>
void update_oracles_after_collapse(const Profile& /*aProfile*/, const vertex_descriptor /*aKeptV*/) const { };
template <>
void update_oracles_after_collapse<true>(const Profile& aProfile, const vertex_descriptor aKeptV) const
{
Get_cost.update_after_collapse(aProfile, aKeptV);
}
void collect(); void collect();
void loop(); void loop();
bool is_collapse_topologically_valid(const Profile& aProfile); bool is_collapse_topologically_valid(const Profile& aProfile);
bool is_tetrahedron(const halfedge_descriptor h1); bool is_tetrahedron(const halfedge_descriptor h1);
bool is_open_triangle(const halfedge_descriptor h1); bool is_open_triangle(const halfedge_descriptor h1);
@ -426,6 +455,9 @@ run()
Visitor.OnStarted(mSurface); Visitor.OnStarted(mSurface);
// this is similar to the visitor, but for the cost/stop/placement oracles
initialize_oracles();
// First collect all candidate edges in a PQ // First collect all candidate edges in a PQ
collect(); collect();
@ -550,7 +582,9 @@ collect()
Placement_type lPlacement = lProfile.p0(); Placement_type lPlacement = lProfile.p0();
vertex_descriptor rResult = halfedge_collapse_bk_compatibility(lProfile.v0_v1(), Edge_is_constrained_map); vertex_descriptor rResult = halfedge_collapse_bk_compatibility(lProfile.v0_v1(), Edge_is_constrained_map);
put(Vertex_point_map, rResult, *lPlacement); put(Vertex_point_map, rResult, *lPlacement);
Visitor.OnCollapsed(lProfile, rResult); Visitor.OnCollapsed(lProfile, rResult);
update_oracles_after_collapse(lProfile, rResult);
} }
CGAL_SMS_TRACE(0, "Initial edge count: " << mInitialEdgeCount); CGAL_SMS_TRACE(0, "Initial edge count: " << mInitialEdgeCount);
@ -1197,6 +1231,7 @@ collapse(const Profile& aProfile,
} }
Visitor.OnCollapsed(aProfile, rResult); Visitor.OnCollapsed(aProfile, rResult);
update_oracles_after_collapse(aProfile, rResult);
update_neighbors(rResult); update_neighbors(rResult);