Clean code

This commit is contained in:
Mael Rouxel-Labbé 2021-12-21 10:46:59 +01:00
parent bacdc08865
commit fb59f96f8d
7 changed files with 624 additions and 671 deletions

View File

@ -90,9 +90,9 @@ void time_all_vector(const std::vector<Surface_mesh>& meshes, const fs::path& ou
// copying of the meshes // copying of the meshes
std::vector<Surface_mesh> temp_meshes = meshes; std::vector<Surface_mesh> temp_meshes = meshes;
for (int i = 0; i < n_burns; ++i) for(int i = 0; i < n_burns; ++i)
{ {
for (Surface_mesh& mesh : temp_meshes) for(Surface_mesh& mesh : temp_meshes)
{ {
edge_collapse<Policy>(mesh); edge_collapse<Policy>(mesh);
} }
@ -100,12 +100,12 @@ void time_all_vector(const std::vector<Surface_mesh>& meshes, const fs::path& ou
temp_meshes = meshes; temp_meshes = meshes;
// measure each run // measure each run
for (int i = 0; i < n_samples; ++i) for(int i = 0; i < n_samples; ++i)
{ {
// measure time taken by the edge_collapse function over each mesh individually // measure time taken by the edge_collapse function over each mesh individually
time::time_point start_time = time::steady_clock::now(); time::time_point start_time = time::steady_clock::now();
for (Surface_mesh& mesh : temp_meshes) for(Surface_mesh& mesh : temp_meshes)
{ {
edge_collapse<Policy>(mesh); edge_collapse<Policy>(mesh);
} }
@ -150,7 +150,7 @@ std::vector<std::pair<Surface_mesh, std::string>> get_all_meshes(const fs::path&
Surface_mesh mesh; Surface_mesh mesh;
// iterate through all files in the given directory // iterate through all files in the given directory
for (fs::directory_iterator dir_iter(dir); dir_iter != end_iter; ++dir_iter) for(fs::directory_iterator dir_iter(dir); dir_iter != end_iter; ++dir_iter)
{ {
// look for .off files // look for .off files
if (fs::is_regular_file(dir_iter->status()) if (fs::is_regular_file(dir_iter->status())
@ -188,7 +188,7 @@ void time_policy(const fs::path& dir, const fs::path& output_file)
auto vec = get_all_meshes(dir); auto vec = get_all_meshes(dir);
std::vector<Surface_mesh> meshes { }; std::vector<Surface_mesh> meshes { };
for (const auto& p : vec) { for(const auto& p : vec) {
std::cout << p.second << '\n'; std::cout << p.second << '\n';
} }
@ -233,13 +233,13 @@ void hausdorff_errors_mesh(const Surface_mesh& mesh, const fs::path& out, InputI
{ {
fs::ofstream out_stream {out}; fs::ofstream out_stream {out};
for (InputIt it = begin; it != end; ++it) for(InputIt it = begin; it != end; ++it)
{ {
std::array<double, 4> errs = hausdorff_errors(mesh, *it); std::array<double, 4> errs = hausdorff_errors(mesh, *it);
out_stream << "ratio: " << *it << '\n'; out_stream << "ratio: " << *it << '\n';
for (double e : errs) for(double e : errs)
{ {
out_stream << std::to_string(e) << '\n'; out_stream << std::to_string(e) << '\n';
} }
@ -269,13 +269,13 @@ void hausdorff_errors_dir(const fs::path& dir, const fs::path& out, double ratio
fs::ofstream out_stream {out}; fs::ofstream out_stream {out};
for (auto& p : meshes) for(auto& p : meshes)
{ {
// calculate the errors and output to file // calculate the errors and output to file
std::array<double, 4> errors = hausdorff_errors(p.first, ratio); std::array<double, 4> errors = hausdorff_errors(p.first, ratio);
out_stream << p.second << '\n'; out_stream << p.second << '\n';
for (double err : errors) for(double err : errors)
{ {
out_stream << std::to_string(err) << '\n'; out_stream << std::to_string(err) << '\n';
} }
@ -284,7 +284,7 @@ void hausdorff_errors_dir(const fs::path& dir, const fs::path& out, double ratio
void write_aspect_ratios(fs::ofstream& out, const Surface_mesh& mesh) void write_aspect_ratios(fs::ofstream& out, const Surface_mesh& mesh)
{ {
for (auto face : mesh.faces()) for(auto face : faces(mesh))
{ {
out << std::to_string(PMP::face_aspect_ratio(face, mesh)) << '\n'; out << std::to_string(PMP::face_aspect_ratio(face, mesh)) << '\n';
} }

View File

@ -24,97 +24,93 @@
namespace CGAL { namespace CGAL {
namespace Surface_mesh_simplification { namespace Surface_mesh_simplification {
// derived class implements functions used in the base class that
// takes the derived class as template argument - see "CRTP"
//
// implements classic plane quadrics
template<typename TriangleMesh, typename GeomTraits> template<typename TriangleMesh, typename GeomTraits>
class GarlandHeckbert_policies : class GarlandHeckbert_policies :
public internal::GarlandHeckbert_placement_base< public internal::GarlandHeckbert_placement_base<
typename boost::property_map< typename boost::property_map<
TriangleMesh, TriangleMesh,
CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>> CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign> >
>::type, >::type,
GeomTraits, GeomTraits,
GarlandHeckbert_policies<TriangleMesh, GeomTraits> GarlandHeckbert_policies<TriangleMesh, GeomTraits>
>, >,
public internal::GarlandHeckbert_cost_base< public internal::GarlandHeckbert_cost_base<
typename boost::property_map< typename boost::property_map<
TriangleMesh, TriangleMesh,
CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>> CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign> >
>::type, >::type,
GeomTraits, GeomTraits,
GarlandHeckbert_policies<TriangleMesh, GeomTraits> GarlandHeckbert_policies<TriangleMesh, GeomTraits> >
>
{ {
public:
typedef typename GeomTraits::FT FT;
public: // This is ugly, we later only use the Mat_4 from the Cost_base, but we want to define
typedef typename GeomTraits::FT FT; // the matrix here already so it's nicer to define Cost_base in the first place
typedef typename Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> GH_matrix;
typedef CGAL::dynamic_vertex_property_t<GH_matrix> Cost_property;
// This is ugly, we later only use the Mat_4 from the typedef typename boost::property_map<TriangleMesh, Cost_property>::type Vertex_cost_map;
// Cost_base, but we want to define the matrix here already so it's nicer to define
// Cost_base in the first place
typedef typename Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> GH_matrix;
typedef CGAL::dynamic_vertex_property_t<GH_matrix> Cost_property;
typedef typename boost::property_map<TriangleMesh, Cost_property>::type Vertex_cost_map;
typedef internal::GarlandHeckbert_placement_base< typedef internal::GarlandHeckbert_placement_base<
Vertex_cost_map, GeomTraits, GarlandHeckbert_policies<TriangleMesh, GeomTraits> Vertex_cost_map, GeomTraits, GarlandHeckbert_policies<TriangleMesh, GeomTraits>
> Placement_base; > Placement_base;
typedef internal::GarlandHeckbert_cost_base< typedef internal::GarlandHeckbert_cost_base<
Vertex_cost_map, GeomTraits, GarlandHeckbert_policies<TriangleMesh, GeomTraits> Vertex_cost_map, GeomTraits, GarlandHeckbert_policies<TriangleMesh, GeomTraits>
> Cost_base; > Cost_base;
// both types are the same, this is so we avoid casting back to the base class in // both types are the same, this is so we avoid casting back to the base class in get_cost() or get_placement()
// get_cost() or get_placement() typedef GarlandHeckbert_policies Get_cost;
typedef GarlandHeckbert_policies Get_cost; typedef GarlandHeckbert_policies Get_placement;
typedef GarlandHeckbert_policies Get_placement;
// introduce both operators into scope so we get an overload operator() // these 'using' directives are needed to choose between the definitions of these types
// this is needed since Get_cost and Get_placement are the same type
using Cost_base::operator();
using Placement_base::operator();
// these using directives are needed to choose between the definitions of these types
// in Cost_base and Placement_base (even though they are the same) // in Cost_base and Placement_base (even though they are the same)
using typename Cost_base::Mat_4; using typename Cost_base::Mat_4;
using typename Cost_base::Col_4; using typename Cost_base::Col_4;
using typename Cost_base::Point_3; using typename Cost_base::Point_3;
using typename Cost_base::Vector_3; using typename Cost_base::Vector_3;
GarlandHeckbert_policies(TriangleMesh& tmesh, FT dm = FT(100)) private:
Vertex_cost_map vcm_;
public:
GarlandHeckbert_policies(TriangleMesh& tmesh,
FT dm = FT(100)) // unused @tmp
{ {
vcm_ = get(Cost_property(), tmesh); vcm_ = get(Cost_property(), tmesh);
/** // initialize the two base class cost matrices (protected members)
* initialize the two base class cost matrices (protected members)
*/
Cost_base::init_vcm(vcm_); Cost_base::init_vcm(vcm_);
Placement_base::init_vcm(vcm_); Placement_base::init_vcm(vcm_);
} }
const Get_cost& get_cost() const { return *this; }
const Get_placement& get_placement() const { return *this; }
Vertex_cost_map get_vcm() const { return vcm_; }
// introduce both operators into scope so we get an overload operator()
// this is needed since Get_cost and Get_placement are the same type
using Cost_base::operator();
using Placement_base::operator();
public:
template<typename VertexPointMap> template<typename VertexPointMap>
Mat_4 construct_quadric_from_face( Mat_4 construct_quadric_from_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const VertexPointMap& point_map, const TriangleMesh& tmesh,
const TriangleMesh& tmesh, const VertexPointMap point_map,
typename boost::graph_traits<TriangleMesh>::face_descriptor f, const GeomTraits& gt) const
const GeomTraits& gt) const
{ {
return internal::construct_classic_plane_quadric_from_face( return internal::construct_classic_plane_quadric_from_face(f, tmesh, point_map, gt);
point_map, tmesh, f, gt);
} }
template<typename VertexPointMap> template<typename VertexPointMap>
Mat_4 construct_quadric_from_edge( Mat_4 construct_quadric_from_edge(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he,
const VertexPointMap& point_map, const TriangleMesh& tmesh,
const TriangleMesh& tmesh, const VertexPointMap point_map,
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he, const GeomTraits& gt) const
const GeomTraits& gt) const
{ {
return internal::construct_classic_plane_quadric_from_edge( return internal::construct_classic_plane_quadric_from_edge(he, tmesh, point_map, gt);
point_map, tmesh, he, gt);
} }
Col_4 construct_optimal_point(const Mat_4& quadric, const Col_4& p0, const Col_4& p1) const Col_4 construct_optimal_point(const Mat_4& quadric, const Col_4& p0, const Col_4& p1) const
@ -122,21 +118,10 @@ class GarlandHeckbert_policies :
return internal::construct_optimal_point_singular<GeomTraits>(quadric, p0, p1); return internal::construct_optimal_point_singular<GeomTraits>(quadric, p0, p1);
} }
const Get_cost& get_cost() const { return *this; } Mat_4 construct_quadric_from_normal(const Vector_3& normal,
const Get_placement& get_placement() const { return *this; } const Point_3& point,
const GeomTraits& gt) const
// return a copy of the vertex cost map to inspect quadrics
Vertex_cost_map get_vcm() const { return vcm_; }
private:
Vertex_cost_map vcm_;
// helper function to construct quadrics
Mat_4 construct_quadric_from_normal(const Vector_3& normal, const Point_3& point,
const GeomTraits& gt) const
{ {
auto dot_product = gt.compute_scalar_product_3_object(); auto dot_product = gt.compute_scalar_product_3_object();
auto construct_vector = gt.construct_vector_3_object(); auto construct_vector = gt.construct_vector_3_object();
@ -151,7 +136,7 @@ class GarlandHeckbert_policies :
} }
}; };
} //namespace Surface_mesh_simplification } // namespace Surface_mesh_simplification
} //namespace CGAL } // namespace CGAL
#endif //CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_POLICIES_H #endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_POLICIES_H

View File

@ -14,74 +14,63 @@
#define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PROBABILISTIC_POLICIES_H #define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PROBABILISTIC_POLICIES_H
#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/GarlandHeckbert_policy_base.h> #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_policy_base.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_functions.h> #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_functions.h>
#include <Eigen/Dense> #include <Eigen/Dense>
namespace CGAL { namespace CGAL {
namespace Surface_mesh_simplification { namespace Surface_mesh_simplification {
// derived class implements functions used in the base class that // Implements probabilistic plane quadrics,
// takes the derived class as template argument - see "CRTP" // optionally takes a face variance map giving a per-face variance
// template<typename TriangleMesh,
// implements probabilistic plane quadrics, optionally takes a face variance map typename GeomTraits,
// giving a per-face variance typename FaceVarianceMap = Constant_property_map<
template<typename TriangleMesh, typename GeomTraits, typename typename boost::graph_traits<TriangleMesh>::face_descriptor,
FaceVarianceMap = Constant_property_map< std::pair<typename GeomTraits::FT, typename GeomTraits::FT> > >
typename boost::graph_traits<TriangleMesh>::face_descriptor, class GarlandHeckbert_probabilistic_policies
std::pair<typename GeomTraits::FT, typename GeomTraits::FT> : public internal::GarlandHeckbert_placement_base<
>> typename boost::property_map<
class GarlandHeckbert_probabilistic_policies : TriangleMesh,
public internal::GarlandHeckbert_placement_base< CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign> >
typename boost::property_map< >::type,
TriangleMesh, GeomTraits,
CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>> GarlandHeckbert_probabilistic_policies<TriangleMesh, GeomTraits, FaceVarianceMap> >,
>::type, public internal::GarlandHeckbert_cost_base<
GeomTraits, typename boost::property_map<
GarlandHeckbert_probabilistic_policies<TriangleMesh, GeomTraits, FaceVarianceMap> TriangleMesh,
>, CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>>
public internal::GarlandHeckbert_cost_base< >::type,
typename boost::property_map< GeomTraits,
TriangleMesh, GarlandHeckbert_probabilistic_policies<TriangleMesh, GeomTraits, FaceVarianceMap> >
CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>>
>::type,
GeomTraits,
GarlandHeckbert_probabilistic_policies<TriangleMesh, GeomTraits, FaceVarianceMap>
>
{ {
typedef typename GeomTraits::FT FT;
typedef typename GeomTraits::FT FT;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::property_traits<FaceVarianceMap>::value_type Face_variance;
public: typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> GH_matrix; typedef typename boost::property_traits<FaceVarianceMap>::value_type Face_variance;
typedef CGAL::dynamic_vertex_property_t<GH_matrix> Cost_property;
typedef typename boost::property_map<TriangleMesh, Cost_property>::type Vertex_cost_map; public:
typedef typename Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> GH_matrix;
typedef CGAL::dynamic_vertex_property_t<GH_matrix> Cost_property;
typedef typename boost::property_map<TriangleMesh, Cost_property>::type Vertex_cost_map;
typedef internal::GarlandHeckbert_placement_base< typedef internal::GarlandHeckbert_placement_base<
Vertex_cost_map, GeomTraits, GarlandHeckbert_probabilistic_policies<TriangleMesh, GeomTraits> Vertex_cost_map, GeomTraits, GarlandHeckbert_probabilistic_policies<TriangleMesh, GeomTraits>
> Placement_base; > Placement_base;
typedef internal::GarlandHeckbert_cost_base< typedef internal::GarlandHeckbert_cost_base<
Vertex_cost_map, GeomTraits, GarlandHeckbert_probabilistic_policies<TriangleMesh, GeomTraits> Vertex_cost_map, GeomTraits, GarlandHeckbert_probabilistic_policies<TriangleMesh, GeomTraits>
> Cost_base; > Cost_base;
// both types are the same, this is so we avoid casting back to the base class in // both types are the same, this is so we avoid casting back to the base class in
// get_cost() or get_placement() // get_cost() or get_placement()
typedef GarlandHeckbert_probabilistic_policies Get_cost; typedef GarlandHeckbert_probabilistic_policies Get_cost;
typedef GarlandHeckbert_probabilistic_policies Get_placement; typedef GarlandHeckbert_probabilistic_policies Get_placement;
// so that operator() gets overloaded, this is needed because now Get_cost and Get_placement
// are the same
using Cost_base::operator();
using Placement_base::operator();
// these using directives are needed to choose between the definitions of these types // these using directives are needed to choose between the definitions of these types
// in Cost_base and Placement_base (even though they are the same) // in Cost_base and Placement_base (even though they are the same)
@ -90,31 +79,45 @@ class GarlandHeckbert_probabilistic_policies :
using typename Cost_base::Point_3; using typename Cost_base::Point_3;
using typename Cost_base::Vector_3; using typename Cost_base::Vector_3;
private:
Vertex_cost_map vcm_;
// magic number determined by some testing
static constexpr FT good_default_variance_unit = 0.05;
// magic number - for most use cases, there is no input variance, so it makes sense to
// set the positional variance to a smaller value than the normal variance
static constexpr FT position_variance_factor = 0.1;
FaceVarianceMap face_variance_map;
public:
// default discontinuity multiplier is 100 // default discontinuity multiplier is 100
GarlandHeckbert_probabilistic_policies(TriangleMesh& tmesh) GarlandHeckbert_probabilistic_policies(TriangleMesh& tmesh)
: GarlandHeckbert_probabilistic_policies(tmesh, 100) : GarlandHeckbert_probabilistic_policies(tmesh, 100)
{ } { }
GarlandHeckbert_probabilistic_policies(TriangleMesh& tmesh, FT dm) GarlandHeckbert_probabilistic_policies(TriangleMesh& tmesh, FT dm)
: Cost_base(dm) : Cost_base(dm)
{ {
// initialize the private variable vcm so it's lifetime is bound to that of the policy's // initialize the private variable vcm so it's lifetime is bound to that of the policy's
vcm_ = get(Cost_property(), tmesh); vcm_ = get(Cost_property(), tmesh);
// initialize both vcms // initialize both vcms
Cost_base::init_vcm(vcm_); Cost_base::init_vcm(vcm_);
Placement_base::init_vcm(vcm_); Placement_base::init_vcm(vcm_);
// try to initialize the face variance map using the estimated variance // try to initialize the face variance map using the estimated variance
// parameters are constants defined for this class // parameters are constants defined for this class
face_variance_map = FaceVarianceMap { face_variance_map =
internal::estimate_variances(tmesh, GeomTraits(), good_default_variance_unit, FaceVarianceMap { internal::estimate_variances(tmesh, GeomTraits(),
position_variance_factor) }; good_default_variance_unit,
position_variance_factor) };
} }
GarlandHeckbert_probabilistic_policies(TriangleMesh& tmesh, FT dm, const FaceVarianceMap& fvm) GarlandHeckbert_probabilistic_policies(TriangleMesh& tmesh, FT dm, const FaceVarianceMap& fvm)
: Cost_base(dm), face_variance_map(fvm) : Cost_base(dm), face_variance_map(fvm)
{ {
// initialize the private variable vcm so it's lifetime is bound to that of the policy's // initialize the private variable vcm so it's lifetime is bound to that of the policy's
vcm_ = get(Cost_property(), tmesh); vcm_ = get(Cost_property(), tmesh);
@ -122,70 +125,56 @@ class GarlandHeckbert_probabilistic_policies :
Cost_base::init_vcm(vcm_); Cost_base::init_vcm(vcm_);
Placement_base::init_vcm(vcm_); Placement_base::init_vcm(vcm_);
} }
const Get_cost& get_cost() const { return *this; }
const Get_placement& get_placement() const { return *this; }
// so that operator() gets overloaded, this is needed because now Get_cost and Get_placement are the same
using Cost_base::operator();
using Placement_base::operator();
public:
template<typename VPM> template<typename VPM>
Mat_4 construct_quadric_from_face( Mat_4 construct_quadric_from_face(face_descriptor f,
const VPM& point_map, const TriangleMesh& tmesh,
const TriangleMesh& tmesh, const VPM point_map,
face_descriptor f, const GeomTraits& gt) const
const GeomTraits& gt) const
{ {
const Vector_3 normal = internal::construct_unit_normal_from_face< const Vector_3 normal = internal::construct_unit_normal_from_face(f, tmesh, point_map, gt);
VPM, TriangleMesh, GeomTraits>(point_map, tmesh, f, gt);
const Point_3 p = get(point_map, source(halfedge(f, tmesh), tmesh)); const Point_3 p = get(point_map, source(halfedge(f, tmesh), tmesh));
FT n_variance; FT n_variance;
FT p_variance; FT p_variance;
std::tie(n_variance, p_variance) = get(face_variance_map, f); std::tie(n_variance, p_variance) = get(face_variance_map, f);
return internal::construct_prob_plane_quadric_from_normal(normal, p, gt, n_variance, p_variance); return internal::construct_prob_plane_quadric_from_normal(normal, p, gt, n_variance, p_variance);
} }
template<typename VPM>
Mat_4 construct_quadric_from_edge(
const VPM& point_map,
const TriangleMesh& tmesh,
halfedge_descriptor he,
const GeomTraits& gt) const
{
const Vector_3 normal = internal::construct_edge_normal(point_map, tmesh, he, gt);
template<typename VPM>
Mat_4 construct_quadric_from_edge(halfedge_descriptor he,
const TriangleMesh& tmesh,
const VPM point_map,
const GeomTraits& gt) const
{
const Vector_3 normal = internal::construct_edge_normal(he, tmesh, point_map, gt);
const Point_3 p = get(point_map, source(he, tmesh)); const Point_3 p = get(point_map, source(he, tmesh));
FT n_variance; FT n_variance;
FT p_variance; FT p_variance;
std::tie(n_variance, p_variance) = get(face_variance_map, face(he, tmesh)); std::tie(n_variance, p_variance) = get(face_variance_map, face(he, tmesh));
return internal::construct_prob_plane_quadric_from_normal(normal, p, gt, n_variance, p_variance); return internal::construct_prob_plane_quadric_from_normal(normal, p, gt, n_variance, p_variance);
} }
Col_4 construct_optimal_point(const Mat_4& quadric, const Col_4& p0, Col_4 construct_optimal_point(const Mat_4& quadric,
const Col_4& p1) const const Col_4& p0,
const Col_4& p1) const
{ {
return internal::construct_optimal_point_invertible<GeomTraits>(quadric); return internal::construct_optimal_point_invertible<GeomTraits>(quadric);
} }
const Get_cost& get_cost() const { return *this; }
const Get_placement& get_placement() const { return *this; }
private:
Vertex_cost_map vcm_;
// magic number determined by some testing
static constexpr FT good_default_variance_unit = 0.05;
// magic number - for most use cases, there is no input variance, so it makes sense to
// set the positional variance to a smaller value than the normal variance
static constexpr FT position_variance_factor = 0.1;
FaceVarianceMap face_variance_map;
}; };
} // namespace Surface_mesh_simplification } // namespace Surface_mesh_simplification
} // namespace CGAL } // namespace CGAL
#endif //CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PROBABILISTIC_POLICIES_H #endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PROBABILISTIC_POLICIES_H

View File

@ -13,8 +13,10 @@
#define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PROBABILISTIC_TRI_POLICIES_H #define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PROBABILISTIC_TRI_POLICIES_H
#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/GarlandHeckbert_policy_base.h> #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_policy_base.h>
#include <Eigen/Dense> #include <Eigen/Dense>
#include <CGAL/boost/graph/Named_function_parameters.h> #include <CGAL/boost/graph/Named_function_parameters.h>
@ -28,57 +30,50 @@ namespace Surface_mesh_simplification {
// derives from cost_base and placement_base // derives from cost_base and placement_base
// implements probabilistic triangle faces and optionally takes a face variance map // implements probabilistic triangle faces and optionally takes a face variance map
// analogously to probabilistic plane quadrics // analogously to probabilistic plane quadrics
template<typename TriangleMesh, typename GeomTraits, typename template<typename TriangleMesh,
FaceVarianceMap = Constant_property_map< typename GeomTraits,
typename boost::graph_traits<TriangleMesh>::face_descriptor, typename GeomTraits::FT typename FaceVarianceMap =
>> Constant_property_map<typename boost::graph_traits<TriangleMesh>::face_descriptor,
class GarlandHeckbert_probabilistic_tri_policies : typename GeomTraits::FT> >
public internal::GarlandHeckbert_placement_base< class GarlandHeckbert_probabilistic_tri_policies
typename boost::property_map< : public internal::GarlandHeckbert_placement_base<
TriangleMesh, typename boost::property_map<
CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>> TriangleMesh,
>::type, CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>>
GeomTraits, >::type,
GarlandHeckbert_probabilistic_tri_policies<TriangleMesh, GeomTraits, FaceVarianceMap> GeomTraits,
>, GarlandHeckbert_probabilistic_tri_policies<TriangleMesh, GeomTraits, FaceVarianceMap>
>,
public internal::GarlandHeckbert_cost_base< public internal::GarlandHeckbert_cost_base<
typename boost::property_map< typename boost::property_map<
TriangleMesh, TriangleMesh,
CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>> CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>>
>::type, >::type,
GeomTraits, GeomTraits,
GarlandHeckbert_probabilistic_tri_policies<TriangleMesh, GeomTraits, FaceVarianceMap> GarlandHeckbert_probabilistic_tri_policies<TriangleMesh, GeomTraits, FaceVarianceMap> >
>
{ {
typedef typename boost::property_traits<FaceVarianceMap>::value_type Face_variance;
typedef typename boost::property_traits<FaceVarianceMap>::value_type Face_variance; public:
typedef typename GeomTraits::FT FT;
public: typedef typename Eigen::Matrix<FT, 3, 3, Eigen::DontAlign> Mat_3;
typedef typename GeomTraits::FT FT; typedef typename Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> GH_matrix;
typedef CGAL::dynamic_vertex_property_t<GH_matrix> Cost_property;
typedef typename Eigen::Matrix<FT, 3, 3, Eigen::DontAlign> Mat_3; typedef typename boost::property_map<TriangleMesh, Cost_property>::type Vertex_cost_map;
typedef typename Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> GH_matrix;
typedef CGAL::dynamic_vertex_property_t<GH_matrix> Cost_property;
typedef typename boost::property_map<TriangleMesh, Cost_property>::type Vertex_cost_map;
typedef internal::GarlandHeckbert_placement_base< typedef internal::GarlandHeckbert_placement_base<
Vertex_cost_map, GeomTraits, GarlandHeckbert_probabilistic_tri_policies<TriangleMesh, GeomTraits> Vertex_cost_map, GeomTraits, GarlandHeckbert_probabilistic_tri_policies<TriangleMesh, GeomTraits>
> Placement_base; > Placement_base;
typedef internal::GarlandHeckbert_cost_base< typedef internal::GarlandHeckbert_cost_base<
Vertex_cost_map, GeomTraits, GarlandHeckbert_probabilistic_tri_policies<TriangleMesh, GeomTraits> Vertex_cost_map, GeomTraits, GarlandHeckbert_probabilistic_tri_policies<TriangleMesh, GeomTraits>
> Cost_base; > Cost_base;
// both types are the same, this is so we avoid casting back to the base class in // both types are the same, this is so we avoid casting back to the base class in
// get_cost() or get_placement() // get_cost() or get_placement()
typedef GarlandHeckbert_probabilistic_tri_policies Get_cost; typedef GarlandHeckbert_probabilistic_tri_policies Get_cost;
typedef GarlandHeckbert_probabilistic_tri_policies Get_placement; typedef GarlandHeckbert_probabilistic_tri_policies Get_placement;
// so that operator() gets overloaded, this is needed because now Get_cost and Get_placement
// are the same
using Cost_base::operator();
using Placement_base::operator();
// these using directives are needed to choose between the definitions of these types // these using directives are needed to choose between the definitions of these types
// in Cost_base and Placement_base (even though they are the same) // in Cost_base and Placement_base (even though they are the same)
@ -86,15 +81,27 @@ class GarlandHeckbert_probabilistic_tri_policies :
using typename Cost_base::Col_4; using typename Cost_base::Col_4;
using typename Cost_base::Point_3; using typename Cost_base::Point_3;
using typename Cost_base::Vector_3; using typename Cost_base::Vector_3;
private:
Vertex_cost_map vcm_;
FaceVarianceMap face_variance_map;
// same meaning as for probabilistic plane quadrics
static constexpr FT good_default_variance_unit = 0.05;
// this is only used when we fall back to probabilistic planes for the discontinuous edges,
// the actual triangle quadric calculation only uses the normal variance
static constexpr FT position_variance_factor = 1;
public:
// default discontinuity multiplier is 100 // default discontinuity multiplier is 100
GarlandHeckbert_probabilistic_tri_policies(TriangleMesh& tmesh) GarlandHeckbert_probabilistic_tri_policies(TriangleMesh& tmesh)
: GarlandHeckbert_probabilistic_tri_policies(tmesh, 100) : GarlandHeckbert_probabilistic_tri_policies(tmesh, 100)
{ } { }
GarlandHeckbert_probabilistic_tri_policies(TriangleMesh& tmesh, FT dm) GarlandHeckbert_probabilistic_tri_policies(TriangleMesh& tmesh, FT dm)
: Cost_base(dm) : Cost_base(dm)
{ {
// initialize the private variable vcm so it's lifetime is bound to that of the policy's // initialize the private variable vcm so it's lifetime is bound to that of the policy's
vcm_ = get(Cost_property(), tmesh); vcm_ = get(Cost_property(), tmesh);
@ -107,19 +114,19 @@ class GarlandHeckbert_probabilistic_tri_policies :
std::tie(variance, discard_position) = internal::estimate_variances(tmesh, GeomTraits(), std::tie(variance, discard_position) = internal::estimate_variances(tmesh, GeomTraits(),
good_default_variance_unit, position_variance_factor); good_default_variance_unit, position_variance_factor);
// see probabilistic plane quadrics // see probabilistic plane quadrics
face_variance_map = FaceVarianceMap { variance }; face_variance_map = FaceVarianceMap { variance };
} }
GarlandHeckbert_probabilistic_tri_policies(TriangleMesh& tmesh, GarlandHeckbert_probabilistic_tri_policies(TriangleMesh& tmesh,
FT dm, FT dm,
const FaceVarianceMap* fvm) const FaceVarianceMap* fvm)
: Cost_base(dm), face_variance_map(fvm) : Cost_base(dm), face_variance_map(fvm)
{ {
// we need positive variances so that we always get an invertible matrix // we need positive variances so that we always get an invertible matrix
CGAL_precondition(sdn > 0.0 && sdp > 0.0); // CGAL_precondition(sdn > 0. && sdp > 0.); // @fixme what was that, check history
// initialize the private variable vcm so it's lifetime is bound to that of the policy's // initialize the private variable vcm so it's lifetime is bound to that of the policy's
vcm_ = get(Cost_property(), tmesh); vcm_ = get(Cost_property(), tmesh);
@ -127,61 +134,51 @@ class GarlandHeckbert_probabilistic_tri_policies :
Cost_base::init_vcm(vcm_); Cost_base::init_vcm(vcm_);
Placement_base::init_vcm(vcm_); Placement_base::init_vcm(vcm_);
} }
const Get_cost& get_cost() const { return *this; }
const Get_placement& get_placement() const { return *this; }
// so that operator() gets overloaded, this is needed because now Get_cost and Get_placement are the same
using Cost_base::operator();
using Placement_base::operator();
public:
template<typename VPM, typename TM> template<typename VPM, typename TM>
Mat_4 construct_quadric_from_face( Mat_4 construct_quadric_from_face(typename boost::graph_traits<TM>::face_descriptor f,
const VPM& point_map, const TM& tmesh,
const TM& tmesh, const VPM point_map,
typename boost::graph_traits<TM>::face_descriptor f, const GeomTraits& gt) const
const GeomTraits& gt) const
{ {
FT variance = get(face_variance_map, f); FT variance = get(face_variance_map, f);
return internal::construct_prob_triangle_quadric_from_face( return internal::construct_prob_triangle_quadric_from_face(f, variance, tmesh, point_map, gt);
point_map, tmesh, f, variance, gt);
} }
// we don't have a sensible way to construct a triangle quadric // we don't have a sensible way to construct a triangle quadric
// from an edge, so we fall back to probabilistic plane quadrics here // from an edge, so we fall back to probabilistic plane quadrics here
template<typename VPM, typename TM> template<typename VPM, typename TM>
Mat_4 construct_quadric_from_edge( Mat_4 construct_quadric_from_edge(typename boost::graph_traits<TM>::halfedge_descriptor he,
const VPM& point_map, const TM& tmesh,
const TM& tmesh, const VPM point_map,
typename boost::graph_traits<TM>::halfedge_descriptor he, const GeomTraits& gt) const
const GeomTraits& gt) const
{ {
// same as in probabilistic plane policy // same as in probabilistic plane policy
const Vector_3 normal = internal::construct_edge_normal(point_map, tmesh, he, gt); const Vector_3 normal = internal::construct_edge_normal(he, tmesh, point_map, gt);
const Point_3 p = get(point_map, source(he, tmesh)); const Point_3 p = get(point_map, source(he, tmesh));
FT variance = get(face_variance_map, face(he, tmesh)); FT variance = get(face_variance_map, face(he, tmesh));
return internal::construct_prob_plane_quadric_from_normal(normal, p, gt, variance, return internal::construct_prob_plane_quadric_from_normal(normal, p, gt, variance,
position_variance_factor * variance); position_variance_factor * variance);
} }
Col_4 construct_optimal_point(const Mat_4 quadric, const Col_4& p0, const Col_4& p1) const Col_4 construct_optimal_point(const Mat_4 quadric, const Col_4& p0, const Col_4& p1) const
{ {
return internal::construct_optimal_point_invertible<GeomTraits>(quadric); return internal::construct_optimal_point_invertible<GeomTraits>(quadric);
} }
const Get_cost& get_cost() const { return *this; }
const Get_placement& get_placement() const { return *this; }
private:
Vertex_cost_map vcm_;
FaceVarianceMap face_variance_map;
// same meaning as for probabilistic plane quadrics
static constexpr FT good_default_variance_unit = 0.05;
// this is only used when we fall back to probabilistic planes for the discontinuous edges,
// the actual triangle quadric calculation only uses the normal variance
static constexpr FT position_variance_factor = 1;
}; };
} // namespace Surface_mesh_simplification } // namespace Surface_mesh_simplification
} // namespace CGAL } // namespace CGAL
#endif //CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PROBABILISTIC_TRI_POLICIES_H #endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PROBABILISTIC_TRI_POLICIES_H

View File

@ -18,55 +18,60 @@
#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_policy_base.h> #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_policy_base.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_functions.h> #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_functions.h>
#include <Eigen/Dense> #include <Eigen/Dense>
#include <iostream> #include <iostream>
namespace CGAL { namespace CGAL {
namespace Surface_mesh_simplification { namespace Surface_mesh_simplification {
// use triangle quadrics for the faces, classical plane quadrics for the edges // use triangle quadrics for the faces, classical plane quadrics for the edges
// and optimize with a check for singular matrices // and optimize with a check for singular matrices
// //
// implements classic triangle policies // implements classic triangle policies
template<typename TriangleMesh, typename GeomTraits> template<typename TriangleMesh, typename GeomTraits>
class GarlandHeckbert_triangle_policies : class GarlandHeckbert_triangle_policies
public internal::GarlandHeckbert_cost_base< : public internal::GarlandHeckbert_cost_base<
typename boost::property_map< typename boost::property_map<
TriangleMesh, TriangleMesh,
CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>> CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>>
>::type, >::type,
GeomTraits, GeomTraits,
GarlandHeckbert_triangle_policies<TriangleMesh, GeomTraits> GarlandHeckbert_triangle_policies<TriangleMesh, GeomTraits>
>, >,
public internal::GarlandHeckbert_placement_base< public internal::GarlandHeckbert_placement_base<
typename boost::property_map< typename boost::property_map<
TriangleMesh, TriangleMesh,
CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>> CGAL::dynamic_vertex_property_t<Eigen::Matrix<typename GeomTraits::FT, 4, 4, Eigen::DontAlign>>
>::type, >::type,
GeomTraits, GeomTraits,
GarlandHeckbert_triangle_policies<TriangleMesh, GeomTraits> GarlandHeckbert_triangle_policies<TriangleMesh, GeomTraits> >
>
{ {
public: public:
typedef typename GeomTraits::FT FT; typedef typename GeomTraits::FT FT;
typedef Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> Mat_4; typedef Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> Mat_4;
typedef Eigen::Matrix<FT, 1, 4> Col_4; typedef Eigen::Matrix<FT, 1, 4> Col_4;
typedef CGAL::dynamic_vertex_property_t<Mat_4> Cost_property; typedef CGAL::dynamic_vertex_property_t<Mat_4> Cost_property;
typedef typename boost::property_map<TriangleMesh, Cost_property>::type Vertex_cost_map; typedef typename boost::property_map<TriangleMesh, Cost_property>::type Vertex_cost_map;
typedef internal::GarlandHeckbert_placement_base< typedef internal::GarlandHeckbert_placement_base<
Vertex_cost_map, GeomTraits, GarlandHeckbert_triangle_policies<TriangleMesh, GeomTraits> Vertex_cost_map, GeomTraits, GarlandHeckbert_triangle_policies<TriangleMesh, GeomTraits>
> Placement_base; > Placement_base;
typedef internal::GarlandHeckbert_cost_base< typedef internal::GarlandHeckbert_cost_base<
Vertex_cost_map, GeomTraits, GarlandHeckbert_triangle_policies<TriangleMesh, GeomTraits> Vertex_cost_map, GeomTraits, GarlandHeckbert_triangle_policies<TriangleMesh, GeomTraits>
> Cost_base; > Cost_base;
using Cost_base::operator(); typedef GarlandHeckbert_triangle_policies Get_cost;
using Placement_base::operator(); typedef GarlandHeckbert_triangle_policies Get_placement;
private:
Vertex_cost_map vcm_;
public:
GarlandHeckbert_triangle_policies(TriangleMesh& tmesh, FT dm = FT(100)) GarlandHeckbert_triangle_policies(TriangleMesh& tmesh, FT dm = FT(100))
{ {
vcm_ = get(Cost_property(), tmesh); vcm_ = get(Cost_property(), tmesh);
@ -75,45 +80,39 @@ class GarlandHeckbert_triangle_policies :
Placement_base::init_vcm(vcm_); Placement_base::init_vcm(vcm_);
} }
public:
const Get_cost& get_cost() const { return *this; }
const Get_placement& get_placement() const { return *this; }
using Cost_base::operator();
using Placement_base::operator();
public:
template<typename VertexPointMap> template<typename VertexPointMap>
Mat_4 construct_quadric_from_face( Mat_4 construct_quadric_from_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const VertexPointMap& point_map, const TriangleMesh& tmesh,
const TriangleMesh& tmesh, const VertexPointMap point_map,
typename boost::graph_traits<TriangleMesh>::face_descriptor f, const GeomTraits& gt) const
const GeomTraits& gt) const
{ {
return internal::construct_classic_triangle_quadric_from_face( return internal::construct_classic_triangle_quadric_from_face(f, tmesh, point_map, gt);
point_map, tmesh, f, gt);
} }
template<typename VertexPointMap> template<typename VertexPointMap>
Mat_4 construct_quadric_from_edge( Mat_4 construct_quadric_from_edge(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he,
const VertexPointMap& point_map, const TriangleMesh& tmesh,
const TriangleMesh& tmesh, const VertexPointMap point_map,
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he, const GeomTraits& gt) const
const GeomTraits& gt) const
{ {
return internal::construct_classic_plane_quadric_from_edge( return internal::construct_classic_plane_quadric_from_edge(he, tmesh, point_map, gt);
point_map, tmesh, he, gt);
} }
Col_4 construct_optimal_point(const Mat_4& quadric, const Col_4& p0, const Col_4& p1) const Col_4 construct_optimal_point(const Mat_4& quadric, const Col_4& p0, const Col_4& p1) const
{ {
return internal::construct_optimal_point_singular<GeomTraits>(quadric, p0, p1); return internal::construct_optimal_point_singular<GeomTraits>(quadric, p0, p1);
} }
typedef GarlandHeckbert_triangle_policies Get_cost;
typedef GarlandHeckbert_triangle_policies Get_placement;
const Get_cost& get_cost() const { return *this; }
const Get_placement& get_placement() const { return *this; }
private:
Vertex_cost_map vcm_;
}; };
} //namespace Surface_mesh_simplification } // namespace Surface_mesh_simplification
} //namespace CGAL } // namespace CGAL
#endif //CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_POLICIES_H #endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_POLICIES_H

View File

@ -17,6 +17,7 @@
#include <CGAL/Surface_mesh_simplification/internal/Common.h> #include <CGAL/Surface_mesh_simplification/internal/Common.h>
#include <Eigen/Dense> #include <Eigen/Dense>
#include <iostream> #include <iostream>
namespace CGAL { namespace CGAL {
@ -28,7 +29,7 @@ template<typename Matrix>
bool invert_matrix_4(const Matrix& m, Matrix& im) bool invert_matrix_4(const Matrix& m, Matrix& im)
{ {
double det; double det;
double A2323 = m(2, 2) * m(3, 3) - m(2, 3) * m(3, 2); double A2323 = m(2, 2) * m(3, 3) - m(2, 3) * m(3, 2);
double A1323 = m(2, 1) * m(3, 3) - m(2, 3) * m(3, 1); double A1323 = m(2, 1) * m(3, 3) - m(2, 3) * m(3, 1);
double A1223 = m(2, 1) * m(3, 2) - m(2, 2) * m(3, 1); double A1223 = m(2, 1) * m(3, 2) - m(2, 2) * m(3, 1);
@ -54,10 +55,8 @@ bool invert_matrix_4(const Matrix& m, Matrix& im)
- m(0, 3) * ( m(1, 0) * A1223 - m(1, 1) * A0223 + m(1, 2) * A0123 ); - m(0, 3) * ( m(1, 0) * A1223 - m(1, 1) * A0223 + m(1, 2) * A0123 );
det = 1 / det; det = 1 / det;
if (det == 0.0) if(det == 0.)
{
return false; return false;
}
// we never actually use values other than those in the third column, // we never actually use values other than those in the third column,
// so might as well not calculate them // so might as well not calculate them
@ -82,8 +81,8 @@ bool invert_matrix_4(const Matrix& m, Matrix& im)
} }
template<typename GeomTraits> template<typename GeomTraits>
Eigen::Matrix<typename GeomTraits::FT, 3, 1> vector_to_col_vec( Eigen::Matrix<typename GeomTraits::FT, 3, 1>
const typename GeomTraits::Vector_3& v) vector_to_col_vec(const typename GeomTraits::Vector_3& v)
{ {
Eigen::Matrix<typename GeomTraits::FT, 3, 1> col {v.x(), v.y(), v.z()}; Eigen::Matrix<typename GeomTraits::FT, 3, 1> col {v.x(), v.y(), v.z()};
return col; return col;
@ -97,22 +96,19 @@ template<typename GeomTraits>
using Col_4 = Eigen::Matrix<typename GeomTraits::FT, 4, 1>; using Col_4 = Eigen::Matrix<typename GeomTraits::FT, 4, 1>;
template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits> template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits>
typename GeomTraits::Vector_3 construct_unit_normal_from_face( typename GeomTraits::Vector_3
const VertexPointMap& point_map, construct_unit_normal_from_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tmesh, const TriangleMesh& tmesh,
typename boost::graph_traits<TriangleMesh>::face_descriptor f, const VertexPointMap point_map,
const GeomTraits& gt) const GeomTraits& gt)
{ {
// initialize all necessary kernel functions typedef typename boost::property_traits<VertexPointMap>::reference point_reference;
auto unit_normal = gt.construct_unit_normal_3_object(); typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
// reference and descriptor types auto unit_normal = gt.construct_unit_normal_3_object();
typedef typename boost::property_traits<VertexPointMap>::reference point_reference;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
const halfedge_descriptor h = halfedge(f, tmesh); const halfedge_descriptor h = halfedge(f, tmesh);
// get the three points of the face and calculate their unit normal
const point_reference p = get(point_map, source(h, tmesh)); const point_reference p = get(point_map, source(h, tmesh));
const point_reference q = get(point_map, target(h, tmesh)); const point_reference q = get(point_map, target(h, tmesh));
const point_reference r = get(point_map, target(next(h, tmesh), tmesh)); const point_reference r = get(point_map, target(next(h, tmesh), tmesh));
@ -120,17 +116,17 @@ typename GeomTraits::Vector_3 construct_unit_normal_from_face(
return unit_normal(p, q, r); return unit_normal(p, q, r);
} }
template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits> template<typename TriangleMesh, typename VertexPointMap, typename GeomTraits>
typename GeomTraits::Vector_3 construct_edge_normal( typename GeomTraits::Vector_3
const VertexPointMap& point_map, construct_edge_normal(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he,
const TriangleMesh& tmesh, const TriangleMesh& tmesh,
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he, const VertexPointMap point_map,
const GeomTraits& gt) const GeomTraits& gt)
{ {
typedef typename GeomTraits::Vector_3 Vector_3; typedef typename GeomTraits::Vector_3 Vector_3;
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
const Vector_3 face_normal = construct_unit_normal_from_face(point_map, tmesh, face(he, tmesh), gt); const Vector_3 face_normal = construct_unit_normal_from_face(face(he, tmesh), tmesh, point_map, gt);
const vertex_descriptor vs = source(he, tmesh); const vertex_descriptor vs = source(he, tmesh);
const vertex_descriptor vt = target(he, tmesh); const vertex_descriptor vt = target(he, tmesh);
@ -139,17 +135,16 @@ typename GeomTraits::Vector_3 construct_edge_normal(
const Vector_3 discontinuity_normal = cross_product(edge_vector, face_normal); const Vector_3 discontinuity_normal = cross_product(edge_vector, face_normal);
// normalize // normalize
const Vector_3 normal = discontinuity_normal const Vector_3 normal = discontinuity_normal / sqrt(discontinuity_normal.squared_length());
/ sqrt(discontinuity_normal.squared_length());
return normal; return normal;
} }
template<typename GeomTraits> template<typename GeomTraits>
Mat_4<GeomTraits> construct_classic_plane_quadric_from_normal( Mat_4<GeomTraits>
const typename GeomTraits::Vector_3& normal, construct_classic_plane_quadric_from_normal(const typename GeomTraits::Vector_3& normal,
const typename GeomTraits::Point_3& point, const typename GeomTraits::Point_3& point,
const GeomTraits& gt) const GeomTraits& gt)
{ {
typedef typename GeomTraits::FT FT; typedef typename GeomTraits::FT FT;
@ -157,70 +152,68 @@ Mat_4<GeomTraits> construct_classic_plane_quadric_from_normal(
auto construct_vector = gt.construct_vector_3_object(); auto construct_vector = gt.construct_vector_3_object();
// negative dot product between the normal and the position vector // negative dot product between the normal and the position vector
const FT d = -dot_product(normal, construct_vector(ORIGIN, point)); const FT d = - dot_product(normal, construct_vector(ORIGIN, point));
// row vector given by d appended to the normal // row vector given by d appended to the normal
const Eigen::Matrix<FT, 1, 4> row (normal.x(), normal.y(), normal.z(), d); const Eigen::Matrix<FT, 1, 4> row { normal.x(), normal.y(), normal.z(), d };
// outer product // outer product
return row.transpose() * row; return row.transpose() * row;
} }
template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits> template<typename TriangleMesh, typename VertexPointMap, typename GeomTraits>
Mat_4<GeomTraits> construct_classic_plane_quadric_from_face( Mat_4<GeomTraits>
const VertexPointMap& point_map, construct_classic_plane_quadric_from_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& mesh, const TriangleMesh& mesh,
typename boost::graph_traits<TriangleMesh>::face_descriptor f, const VertexPointMap point_map,
const GeomTraits& gt) const GeomTraits& gt)
{ {
const typename GeomTraits::Vector_3 normal auto normal = construct_unit_normal_from_face(f, mesh, point_map, gt);
= construct_unit_normal_from_face(point_map, mesh, f, gt);
// get any point of the face // get any point of the face
const auto p = get(point_map, source(halfedge(f, mesh), mesh)); const auto p = get(point_map, source(halfedge(f, mesh), mesh));
return construct_classic_plane_quadric_from_normal(normal, p, gt); return construct_classic_plane_quadric_from_normal(normal, p, gt);
} }
template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits> template<typename TriangleMesh, typename VertexPointMap, typename GeomTraits>
Mat_4<GeomTraits> construct_classic_plane_quadric_from_edge( Mat_4<GeomTraits>
const VertexPointMap& point_map, construct_classic_plane_quadric_from_edge(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he,
const TriangleMesh& mesh, const TriangleMesh& mesh,
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he, const VertexPointMap point_map,
const GeomTraits& gt) const GeomTraits& gt)
{ {
typedef typename GeomTraits::Vector_3 Vector_3;
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename GeomTraits::Vector_3 Vector_3; const Vector_3 normal = construct_edge_normal(he, mesh, point_map, gt);
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
Vector_3 normal = construct_edge_normal(point_map, mesh, he, gt);
// use this normal to construct the quadric analogously to constructing quadric // use this normal to construct the quadric analogously to constructing quadric
// from the normal of the face // from the normal of the face
return construct_classic_plane_quadric_from_normal(normal, get(point_map, source(he, mesh)), gt); return construct_classic_plane_quadric_from_normal(normal, get(point_map, source(he, mesh)), gt);
} }
template <typename GeomTraits> template <typename GeomTraits>
Mat_4<GeomTraits> construct_prob_plane_quadric_from_normal( Mat_4<GeomTraits>
const typename GeomTraits::Vector_3& mean_normal, construct_prob_plane_quadric_from_normal(const typename GeomTraits::Vector_3& mean_normal,
const typename GeomTraits::Point_3& point, const typename GeomTraits::Point_3& point,
const GeomTraits& gt, const GeomTraits& gt,
typename GeomTraits::FT face_nv, typename GeomTraits::FT face_nv,
typename GeomTraits::FT face_mv) typename GeomTraits::FT face_mv)
{ {
typedef typename GeomTraits::FT FT;
typedef typename GeomTraits::Vector_3 Vector_3;
typedef Eigen::Matrix<FT, 4, 4> Mat_4;
auto squared_length = gt.compute_squared_length_3_object(); auto squared_length = gt.compute_squared_length_3_object();
auto dot_product = gt.compute_scalar_product_3_object(); auto dot_product = gt.compute_scalar_product_3_object();
auto construct_vec_3 = gt.construct_vector_3_object(); auto construct_vec_3 = gt.construct_vector_3_object();
typedef typename GeomTraits::FT FT;
typedef typename GeomTraits::Vector_3 Vector_3;
typedef Eigen::Matrix<FT, 4, 4> Mat_4;
const Vector_3 mean_vec = construct_vec_3(ORIGIN, point); const Vector_3 mean_vec = construct_vec_3(ORIGIN, point);
const FT dot_mnmv = dot_product(mean_normal, mean_vec); const FT dot_mnmv = dot_product(mean_normal, mean_vec);
// Eigen column vector of length 3 // Eigen column vector of length 3
const Eigen::Matrix<FT, 3, 1> mean_n_col {mean_normal.x(), mean_normal.y(), mean_normal.z()}; const Eigen::Matrix<FT, 3, 1> mean_n_col { mean_normal.x(), mean_normal.y(), mean_normal.z() };
// start by setting values along the diagonal // start by setting values along the diagonal
Mat_4 mat = face_nv * Mat_4::Identity(); Mat_4 mat = face_nv * Mat_4::Identity();
@ -235,7 +228,7 @@ Mat_4<GeomTraits> construct_prob_plane_quadric_from_normal(
// the b column and row appear with a negative sign // the b column and row appear with a negative sign
const auto b1 = -(dot_mnmv * mean_normal + face_nv * mean_vec); const auto b1 = -(dot_mnmv * mean_normal + face_nv * mean_vec);
const Eigen::Matrix<FT, 3, 1> b {b1.x(), b1.y(), b1.z()}; const Eigen::Matrix<FT, 3, 1> b { b1.x(), b1.y(), b1.z() };
mat.col(3).head(3) = b; mat.col(3).head(3) = b;
mat.row(3).head(3) = b.transpose(); mat.row(3).head(3) = b.transpose();
@ -243,27 +236,25 @@ Mat_4<GeomTraits> construct_prob_plane_quadric_from_normal(
// set the value in the bottom right corner, we get this by considering // set the value in the bottom right corner, we get this by considering
// that we only have single variances given instead of covariance matrices // that we only have single variances given instead of covariance matrices
mat(3, 3) = CGAL::square(dot_mnmv) mat(3, 3) = CGAL::square(dot_mnmv)
+ face_nv * squared_length(mean_vec) + face_nv * squared_length(mean_vec)
+ face_mv * squared_length(mean_normal) + face_mv * squared_length(mean_normal)
+ 3 * face_nv * face_mv; + 3 * face_nv * face_mv;
return mat; return mat;
} }
template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits> template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits>
std::array<typename GeomTraits::Vector_3, 3> vectors_from_face( std::array<typename GeomTraits::Vector_3, 3>
const VertexPointMap& point_map, vectors_from_face(const VertexPointMap point_map,
const TriangleMesh& tmesh, const TriangleMesh& tmesh,
typename boost::graph_traits<TriangleMesh>::face_descriptor f, typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const GeomTraits& gt) const GeomTraits& gt)
{ {
typedef typename boost::property_traits<VertexPointMap>::reference Point_reference;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
auto construct_vector = gt.construct_vector_3_object(); auto construct_vector = gt.construct_vector_3_object();
typedef typename boost::property_traits<VertexPointMap>::reference Point_reference;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
std::array<typename GeomTraits::Vector_3, 3> arr { };
const halfedge_descriptor h = halfedge(f, tmesh); const halfedge_descriptor h = halfedge(f, tmesh);
// get all points and turn them into location vectors so we can use cross product on them // get all points and turn them into location vectors so we can use cross product on them
@ -271,28 +262,28 @@ std::array<typename GeomTraits::Vector_3, 3> vectors_from_face(
const Point_reference q = get(point_map, target(h, tmesh)); const Point_reference q = get(point_map, target(h, tmesh));
const Point_reference r = get(point_map, target(next(h, tmesh), tmesh)); const Point_reference r = get(point_map, target(next(h, tmesh), tmesh));
arr[0] = construct_vector(ORIGIN, p); std::array<typename GeomTraits::Vector_3, 3> arr { construct_vector(ORIGIN, p),
arr[1] = construct_vector(ORIGIN, q); construct_vector(ORIGIN, q),
arr[2] = construct_vector(ORIGIN, r); construct_vector(ORIGIN, r) };
return arr; return arr;
} }
template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits> template<typename TriangleMesh, typename VertexPointMap, typename GeomTraits>
Mat_4<GeomTraits> construct_classic_triangle_quadric_from_face( Mat_4<GeomTraits>
const VertexPointMap& point_map, construct_classic_triangle_quadric_from_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tmesh, const TriangleMesh& tmesh,
typename boost::graph_traits<TriangleMesh>::face_descriptor f, const VertexPointMap point_map,
const GeomTraits& gt) const GeomTraits& gt)
{ {
typedef typename GeomTraits::FT FT;
auto cross_product = gt.construct_cross_product_vector_3_object(); auto cross_product = gt.construct_cross_product_vector_3_object();
auto sum_vectors = gt.construct_sum_of_vectors_3_object(); auto sum_vectors = gt.construct_sum_of_vectors_3_object();
auto dot_product = gt.compute_scalar_product_3_object(); auto dot_product = gt.compute_scalar_product_3_object();
typedef typename GeomTraits::FT FT;
auto vectors = vectors_from_face(point_map, tmesh, f, gt); auto vectors = vectors_from_face(point_map, tmesh, f, gt);
Vector_3 a = vectors[0]; Vector_3 a = vectors[0];
Vector_3 b = vectors[1]; Vector_3 b = vectors[1];
Vector_3 c = vectors[2]; Vector_3 c = vectors[2];
@ -305,52 +296,52 @@ Mat_4<GeomTraits> construct_classic_triangle_quadric_from_face(
const FT scalar_triple_product = dot_product(ab, c); const FT scalar_triple_product = dot_product(ab, c);
Eigen::Matrix<FT, 1, 4> row; Eigen::Matrix<FT, 1, 4> row;
row << sum_of_cross_product.x(), sum_of_cross_product.y(),
row << sum_of_cross_product.x(), sum_of_cross_product.y(), sum_of_cross_product.z(), - scalar_triple_product;
sum_of_cross_product.z(), -scalar_triple_product;
// calculate the outer product of row^t*row // calculate the outer product of row^t*row
return row.transpose() * row; return row.transpose() * row;
} }
template<typename GeomTraits> template<typename GeomTraits>
Eigen::Matrix<typename GeomTraits::FT, 3, 3> skew_sym_mat_cross_product( Eigen::Matrix<typename GeomTraits::FT, 3, 3>
const typename GeomTraits::Vector_3& v) skew_sym_mat_cross_product(const typename GeomTraits::Vector_3& v)
{ {
Eigen::Matrix<typename GeomTraits::FT, 3, 3> mat; Eigen::Matrix<typename GeomTraits::FT, 3, 3> mat;
mat << 0, -v.z(), v.y(), mat << 0, - v.z(), v.y(),
v.z(), 0, -v.x(), v.z(), 0, - v.x(),
-v.y(), v.x(), 0; - v.y(), v.x(), 0;
return mat; return mat;
} }
template<typename VertexPointMap, typename TriangleMesh, typename GeomTraits> template<typename TriangleMesh, typename VertexPointMap, typename GeomTraits>
Mat_4<GeomTraits> construct_prob_triangle_quadric_from_face( Mat_4<GeomTraits>
const VertexPointMap& point_map, construct_prob_triangle_quadric_from_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tmesh, typename GeomTraits::FT var,
typename boost::graph_traits<TriangleMesh>::face_descriptor f, const TriangleMesh& tmesh,
typename GeomTraits::FT var, const VertexPointMap point_map,
const GeomTraits& gt) const GeomTraits& gt)
{ {
typedef typename GeomTraits::FT FT;
typedef typename GeomTraits::Vector_3 Vector_3;
typedef Eigen::Matrix<FT, 3, 3> Mat_3;
typedef Mat_4<GeomTraits> Mat_4;
auto construct_vector = gt.construct_vector_3_object(); auto construct_vector = gt.construct_vector_3_object();
auto cross_product = gt.construct_cross_product_vector_3_object(); auto cross_product = gt.construct_cross_product_vector_3_object();
auto sum_vectors = gt.construct_sum_of_vectors_3_object(); auto sum_vectors = gt.construct_sum_of_vectors_3_object();
auto dot_product = gt.compute_scalar_product_3_object(); auto dot_product = gt.compute_scalar_product_3_object();
// array containing the position vectors corresponding to // array containing the position vectors corresponding to
// the vertices of the given face // the vertices of the given face
auto vectors = vectors_from_face(point_map, tmesh, f, gt); auto vectors = vectors_from_face(point_map, tmesh, f, gt);
typedef typename GeomTraits::FT FT; const Vector_3& a = vectors[0];
typedef typename GeomTraits::Vector_3 Vector_3; const Vector_3& b = vectors[1];
typedef Eigen::Matrix<FT, 3, 3> Mat_3; const Vector_3& c = vectors[2];
typedef Mat_4<GeomTraits> Mat_4;
Vector_3 a = vectors[0];
Vector_3 b = vectors[1];
Vector_3 c = vectors[2];
// calculate certain vectors used later // calculate certain vectors used later
const Vector_3 ab = cross_product(a, b); const Vector_3 ab = cross_product(a, b);
@ -368,44 +359,41 @@ Mat_4<GeomTraits> construct_prob_triangle_quadric_from_face(
const Vector_3 sum_of_cross_product = sum_vectors(sum_vectors(ab, bc), ca); const Vector_3 sum_of_cross_product = sum_vectors(sum_vectors(ab, bc), ca);
const Eigen::Matrix<FT, 3, 1, Eigen::DontAlign> const Eigen::Matrix<FT, 3, 1, Eigen::DontAlign>
sum_cp_col{ sum_of_cross_product.x(), sum_of_cross_product.y(), sum_of_cross_product.z() }; sum_cp_col { sum_of_cross_product.x(), sum_of_cross_product.y(), sum_of_cross_product.z() };
Mat_3 A = sum_cp_col * sum_cp_col.transpose(); Mat_3 A = sum_cp_col * sum_cp_col.transpose();
A += var * (cp_ab * cp_ab.transpose() + cp_bc * cp_bc.transpose() + cp_ca * cp_ca.transpose()); A += var * (cp_ab * cp_ab.transpose() + cp_bc * cp_bc.transpose() + cp_ca * cp_ca.transpose());
// add the 3 simple cross inference matrix - components (we only have one // add the 3 simple cross inference matrix - components (we only have one variance here)
// variance here) A += 6 * square(var) * Mat_3::Identity();
A += 6 * var * var * Mat_3::Identity();
// we need the determinant of matrix with columns a, b, c - we use the scalar triple product // we need the determinant of matrix with columns a, b, c - we use the scalar triple product
const FT det = dot_product(ab, c); const FT det = dot_product(ab, c);
// compute the b vector, this follows the formula directly - but we can factor // compute the b vector, this follows the formula directly - but we can factor
// out the diagonal covariance matrices // out the diagonal covariance matrices
const Eigen::Matrix<FT, 3, 1> res_b = det * sum_cp_col const Eigen::Matrix<FT, 3, 1> res_b =
- var * ( det * sum_cp_col - var * (vector_to_col_vec<GeomTraits>(cross_product(a_minus_b, ab))
vector_to_col_vec<GeomTraits>(cross_product(a_minus_b, ab)) + vector_to_col_vec<GeomTraits>(cross_product(b_minus_c, bc))
+ vector_to_col_vec<GeomTraits>(cross_product(b_minus_c, bc)) + vector_to_col_vec<GeomTraits>(cross_product(c_minus_a, ca)))
+ vector_to_col_vec<GeomTraits>(cross_product(c_minus_a, ca))) + 2 * square(var) * vector_to_col_vec<GeomTraits>(sum_vectors(sum_vectors(a, b), c));
+ 2 * vector_to_col_vec<GeomTraits>(sum_vectors(sum_vectors(a, b), c)) * var * var;
const FT res_c = det * det const FT ab2 = dot_product(ab, ab);
+ var * ( const FT bc2 = dot_product(bc, bc);
dot_product(ab, ab) const FT ca2 = dot_product(ca, ca);
+ dot_product(bc, bc) const FT a2 = dot_product(a, a);
+ dot_product(ca, ca)) const FT b2 = dot_product(b, b);
+ var * var * ( const FT c2 = dot_product(c, c);
2 * (
dot_product(a, a) const FT res_c = square(det)
+ dot_product(b, b) + var * (ab2 + bc2 + ca2)
+ dot_product(c, c)) + square(var) * (2 * (a2 + b2 + c2) + 6 * var);
+ 6 * var);
Mat_4 ret = Mat_4::Zero(); Mat_4 ret = Mat_4::Zero();
ret.block(0, 0, 3, 3) = A; ret.block(0, 0, 3, 3) = A;
ret.block(3, 0, 1, 3) = -res_b.transpose(); ret.block(3, 0, 1, 3) = - res_b.transpose();
ret.block(0, 3, 3, 1) = -res_b; ret.block(0, 3, 3, 1) = - res_b;
ret(3, 3) = res_c; ret(3, 3) = res_c;
return ret; return ret;
@ -424,13 +412,12 @@ Col_4<GeomTraits> construct_optimal_point_invertible(const Mat_4<GeomTraits>& qu
} }
template <typename GeomTraits> template <typename GeomTraits>
Col_4<GeomTraits> construct_optimal_point_singular( Col_4<GeomTraits> construct_optimal_point_singular(const Mat_4<GeomTraits>& quadric,
const Mat_4<GeomTraits>& quadric, const Col_4<GeomTraits>& p0,
const Col_4<GeomTraits>& p0, const Col_4<GeomTraits>& p1)
const Col_4<GeomTraits>& p1)
{ {
typedef typename GeomTraits::FT FT; typedef typename GeomTraits::FT FT;
// in this case, the matrix mat may no be invertible, // in this case, the matrix mat may no be invertible,
// so we save the result to check // so we save the result to check
Mat_4<GeomTraits> mat; Mat_4<GeomTraits> mat;
@ -438,12 +425,12 @@ Col_4<GeomTraits> construct_optimal_point_singular(
Mat_4<GeomTraits> inverse; Mat_4<GeomTraits> inverse;
bool invertible = invert_matrix_4(mat, inverse); bool invertible = invert_matrix_4(mat, inverse);
if (invertible) if(invertible)
{ {
return inverse.col(3); return inverse.col(3);
} }
else else
{ {
Col_4<GeomTraits> opt_pt; Col_4<GeomTraits> opt_pt;
@ -451,11 +438,11 @@ Col_4<GeomTraits> construct_optimal_point_singular(
const FT a = (p1mp0.transpose() * quadric * p1mp0)(0, 0); const FT a = (p1mp0.transpose() * quadric * p1mp0)(0, 0);
const FT b = 2 * (p0.transpose() * quadric * p1mp0)(0, 0); const FT b = 2 * (p0.transpose() * quadric * p1mp0)(0, 0);
if (a == 0) if(a == 0)
{ {
if (b < 0) if(b < 0)
opt_pt = p1; opt_pt = p1;
else if (b == 0) else if(b == 0)
opt_pt = 0.5 * (p0 + p1); opt_pt = 0.5 * (p0 + p1);
else else
opt_pt = p0; opt_pt = p0;
@ -486,27 +473,28 @@ Col_4<GeomTraits> construct_optimal_point_singular(
} }
template<typename TriangleMesh, typename GeomTraits> template<typename TriangleMesh, typename GeomTraits>
std::pair<typename GeomTraits::FT, typename GeomTraits::FT> estimate_variances( std::pair<typename GeomTraits::FT, typename GeomTraits::FT>
const TriangleMesh& mesh, const GeomTraits& gt, estimate_variances(const TriangleMesh& mesh,
typename GeomTraits::FT variance, const GeomTraits& gt,
typename GeomTraits::FT p_factor) typename GeomTraits::FT variance,
typename GeomTraits::FT p_factor)
{ {
typedef typename TriangleMesh::Vertex_index vertex_descriptor; typedef typename TriangleMesh::Vertex_index vertex_descriptor;
typedef typename TriangleMesh::Edge_index edge_descriptor; typedef typename TriangleMesh::Edge_index edge_descriptor;
typedef typename GeomTraits::FT FT; typedef typename GeomTraits::FT FT;
typedef typename GeomTraits::Point_3 Point_3; typedef typename GeomTraits::Point_3 Point_3;
FT average_edge_length = 0; FT average_edge_length = 0;
auto construct_vector = gt.construct_vector_3_object(); auto construct_vector = gt.construct_vector_3_object();
for (edge_descriptor e : edges(mesh)) for(edge_descriptor e : edges(mesh))
{ {
vertex_descriptor v1 = mesh.vertex(e, 0); vertex_descriptor v1 = mesh.vertex(e, 0);
vertex_descriptor v2 = mesh.vertex(e, 1); vertex_descriptor v2 = mesh.vertex(e, 1);
const Point_3& p1 = mesh.point(v1); const Point_3& p1 = mesh.point(v1);
const Point_3& p2 = mesh.point(v2); const Point_3& p2 = mesh.point(v2);
const Vector_3 vec = construct_vector(p1, p2); const Vector_3 vec = construct_vector(p1, p2);
average_edge_length += sqrt(vec.squared_length()); average_edge_length += sqrt(vec.squared_length());

View File

@ -17,7 +17,7 @@
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Edge_profile.h> #include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Edge_profile.h>
#include <CGAL/Surface_mesh_simplification/internal/Common.h> #include <CGAL/Surface_mesh_simplification/internal/Common.h>
#include <iostream>
#include <CGAL/tags.h> #include <CGAL/tags.h>
#include <Eigen/Dense> #include <Eigen/Dense>
@ -28,76 +28,155 @@ namespace CGAL {
namespace Surface_mesh_simplification { namespace Surface_mesh_simplification {
namespace internal { namespace internal {
template<typename Mat_4> template <typename Mat_4>
Mat_4 combine_matrices(const Mat_4& a, const Mat_4& b) { Mat_4 combine_matrices(const Mat_4& a, const Mat_4& b)
{
return a + b; return a + b;
} }
template<typename VCM, template <typename VertexCostMap,
typename GeomTraits, typename GeomTraits,
typename QuadricImpl> typename QuadricImpl>
class GarlandHeckbert_cost_base class GarlandHeckbert_cost_base
{ {
public: public:
// Tells the main function of 'Edge_collapse' that these // Tells the main function of 'Edge_collapse' that these
// policies must call "initialize" and "update" functions. // policies must call "initialize" and "update" functions.
typedef CGAL::Tag_true Update_tag; typedef CGAL::Tag_true Update_tag;
typedef VCM Vertex_cost_map; typedef VertexCostMap Vertex_cost_map;
typedef typename GeomTraits::FT FT; typedef typename GeomTraits::FT FT;
typedef Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> Mat_4; typedef Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> Mat_4;
typedef Eigen::Matrix<FT, 4, 1> Col_4; typedef Eigen::Matrix<FT, 4, 1> Col_4;
typedef typename GeomTraits::Point_3 Point_3; typedef typename GeomTraits::Point_3 Point_3;
typedef typename GeomTraits::Vector_3 Vector_3; typedef typename GeomTraits::Vector_3 Vector_3;
GarlandHeckbert_cost_base() : m_cost_matrices() { } private:
Vertex_cost_map m_cost_matrices;
FT discontinuity_multiplier;
public:
GarlandHeckbert_cost_base()
: m_cost_matrices()
{ }
GarlandHeckbert_cost_base(FT dm) GarlandHeckbert_cost_base(FT dm)
: m_cost_matrices(), discontinuity_multiplier(dm) { } : m_cost_matrices(), discontinuity_multiplier(dm)
{ }
GarlandHeckbert_cost_base(Vertex_cost_map vcm, FT dm) GarlandHeckbert_cost_base(Vertex_cost_map vcm, FT dm)
: m_cost_matrices(vcm), discontinuity_multiplier(dm) { } : m_cost_matrices(vcm), discontinuity_multiplier(dm)
{ }
template<typename TM> protected:
static bool is_discontinuity_edge(const typename boost::graph_traits<TM>::halfedge_descriptor& h, void init_vcm(const Vertex_cost_map vcm)
{
m_cost_matrices = vcm;
}
Col_4 point_to_homogenous_column(const Point_3& point) const
{
return Col_4(point.x(), point.y(), point.z(), FT(1));
}
template <typename TM, typename VPM>
Mat_4 construct_quadric(typename boost::graph_traits<TM>::face_descriptor f,
const TM& tmesh,
const VPM point_map,
const GeomTraits& gt) const
{
return static_cast<const QuadricImpl*>(this)->construct_quadric_from_face(f, tmesh, point_map, gt);
}
template <typename TM, typename VPM>
Mat_4 construct_quadric(typename boost::graph_traits<TM>::halfedge_descriptor he,
const TM& tmesh,
const VPM point_map,
const GeomTraits& gt) const
{
return static_cast<const QuadricImpl*>(this)->construct_quadric_from_edge(he, tmesh, point_map, gt);
}
template <typename TM, typename VPM>
Vector_3 construct_edge_normal(typename boost::graph_traits<TM>::halfedge_descriptor he,
const TM& tmesh,
const VPM point_map,
const GeomTraits& gt) const
{
typedef typename boost::graph_traits<TM>::vertex_descriptor vertex_descriptor;
const Vector_3 face_normal = construct_unit_normal_from_face(face(he, tmesh), tmesh, point_map, gt);
const vertex_descriptor vs = source(he, tmesh);
const vertex_descriptor vt = target(he, tmesh);
const Vector_3 edge_vector { get(point_map, vs), get(point_map, vt) };
const Vector_3 discontinuity_normal = cross_product(edge_vector, face_normal);
// normalize
const Vector_3 normal = discontinuity_normal / sqrt(discontinuity_normal.squared_length());
return normal;
}
template <typename VPM, typename TM>
Vector_3 construct_unit_normal_from_face(typename boost::graph_traits<TM>::face_descriptor f,
const TM& tmesh,
const VPM point_map,
const GeomTraits& gt) const
{
typedef typename boost::graph_traits<TM>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::property_traits<VPM>::reference Point_reference;
auto unit_normal = gt.construct_unit_normal_3_object();
const halfedge_descriptor h = halfedge(f, tmesh);
// get the three points of the face and calculate their unit normal
const Point_reference p = get(point_map, source(h, tmesh));
const Point_reference q = get(point_map, target(h, tmesh));
const Point_reference r = get(point_map, target(next(h, tmesh), tmesh));
return unit_normal(p, q, r);
}
template <typename TM>
static bool is_discontinuity_edge(const typename boost::graph_traits<TM>::halfedge_descriptor h,
const TM& tmesh) const TM& tmesh)
{ {
return is_border_edge(h, tmesh); return is_border_edge(h, tmesh);
} }
public:
// initialize all quadrics // initialize all quadrics
template<typename TM, template <typename TM, typename VPM>
typename VPM> void initialize(const TM& tmesh,
void initialize(const TM& tmesh, const VPM& vpm, const GeomTraits& gt) const const VPM vpm,
const GeomTraits& gt) const
{ {
// the graph library types used here typedef boost::graph_traits<TM> GraphTraits;
typedef boost::graph_traits<TM> GraphTraits; typedef typename GraphTraits::vertex_descriptor vertex_descriptor;
typedef typename GraphTraits::vertex_descriptor vertex_descriptor; typedef typename GraphTraits::halfedge_descriptor halfedge_descriptor;
typedef typename GraphTraits::halfedge_descriptor halfedge_descriptor; typedef typename GraphTraits::face_descriptor face_descriptor;
typedef typename GraphTraits::face_descriptor face_descriptor; typedef typename boost::property_traits<VPM>::reference Point_reference;
typedef typename boost::property_traits<VPM>::reference Point_reference;
Mat_4 zero_mat = Mat_4::Zero(); Mat_4 zero_mat = Mat_4::Zero();
for(vertex_descriptor v : vertices(tmesh)) for(vertex_descriptor v : vertices(tmesh))
{
put(m_cost_matrices, v, zero_mat); put(m_cost_matrices, v, zero_mat);
}
for(face_descriptor f : faces(tmesh)) for(face_descriptor f : faces(tmesh))
{ {
if (f == GraphTraits::null_face()) if(f == GraphTraits::null_face())
{
continue; continue;
}
const halfedge_descriptor h = halfedge(f, tmesh); const halfedge_descriptor h = halfedge(f, tmesh);
// construtct the (4 x 4) matrix representing the plane quadric // construtct the (4 x 4) matrix representing the plane quadric
const Mat_4 quadric = construct_quadric<VPM, TM>(vpm, tmesh, f, gt); const Mat_4 quadric = construct_quadric(f, tmesh, vpm, gt);
for(halfedge_descriptor shd : halfedges_around_face(h, tmesh)) for(halfedge_descriptor shd : halfedges_around_face(h, tmesh))
{ {
@ -107,12 +186,10 @@ class GarlandHeckbert_cost_base
put(m_cost_matrices, vs, combine_matrices(get(m_cost_matrices, vs), quadric)); put(m_cost_matrices, vs, combine_matrices(get(m_cost_matrices, vs), quadric));
if(!is_discontinuity_edge(shd, tmesh)) if(!is_discontinuity_edge(shd, tmesh))
{
continue; continue;
}
const Mat_4 discontinuous_quadric const Mat_4 discontinuous_quadric =
= discontinuity_multiplier * construct_quadric<VPM, TM>(vpm, tmesh, shd, gt); discontinuity_multiplier * construct_quadric(shd, tmesh, vpm, gt);
put(m_cost_matrices, vs, combine_matrices(get(m_cost_matrices, vs), discontinuous_quadric)); put(m_cost_matrices, vs, combine_matrices(get(m_cost_matrices, vs), discontinuous_quadric));
put(m_cost_matrices, vt, combine_matrices(get(m_cost_matrices, vt), discontinuous_quadric)); put(m_cost_matrices, vt, combine_matrices(get(m_cost_matrices, vt), discontinuous_quadric));
@ -120,13 +197,15 @@ class GarlandHeckbert_cost_base
} }
} }
template<typename Profile> template <typename Profile>
boost::optional<typename Profile::FT> operator()(const Profile& profile, boost::optional<typename Profile::FT>
const boost::optional<typename Profile::Point>& placement) const operator()(const Profile& profile,
const boost::optional<typename Profile::Point>& placement) const
{ {
typedef boost::optional<typename Profile::FT> Optional_FT; typedef boost::optional<typename Profile::FT> Optional_FT;
if(!placement) { if(!placement)
{
// return empty value // return empty value
return boost::optional<typename Profile::FT>(); return boost::optional<typename Profile::FT>();
} }
@ -142,163 +221,79 @@ class GarlandHeckbert_cost_base
return cost; return cost;
} }
template<typename Profile, typename VertexDescriptor> template <typename Profile, typename VertexDescriptor>
void update_after_collapse(const Profile& profile, void update_after_collapse(const Profile& profile,
const VertexDescriptor new_v) const const VertexDescriptor new_v) const
{ {
put(m_cost_matrices, new_v, put(m_cost_matrices, new_v,
combine_matrices(get(m_cost_matrices, profile.v0()), combine_matrices(get(m_cost_matrices, profile.v0()),
get(m_cost_matrices, profile.v1()))); get(m_cost_matrices, profile.v1())));
} }
protected:
void init_vcm(const Vertex_cost_map& vcm) {
m_cost_matrices = vcm;
}
template<typename VPM, typename TM>
Vector_3 construct_edge_normal(
const VPM& point_map,
const TM& tmesh,
typename boost::graph_traits<TM>::halfedge_descriptor he,
const GeomTraits& gt) const
{
typedef typename boost::graph_traits<TM>::vertex_descriptor vertex_descriptor;
const Vector_3 face_normal = this->construct_unit_normal_from_face(
point_map, tmesh, face(he, tmesh), gt);
const vertex_descriptor vs = source(he, tmesh);
const vertex_descriptor vt = target(he, tmesh);
const Vector_3 edge_vector = Vector_3(get(point_map, vs), get(point_map, vt));
const Vector_3 discontinuity_normal = cross_product(edge_vector, face_normal);
// normalize
const Vector_3 normal = discontinuity_normal
/ sqrt(discontinuity_normal.squared_length());
return normal;
}
template<typename VPM, typename TM>
Vector_3 construct_unit_normal_from_face(
const VPM& point_map,
const TM& tmesh,
typename boost::graph_traits<TM>::face_descriptor f,
const GeomTraits& gt) const
{
// initialize all necessary kernel functions
auto unit_normal = gt.construct_unit_normal_3_object();
// reference and descriptor types
typedef typename boost::property_traits<VPM>::reference Point_reference;
typedef typename boost::graph_traits<TM>::halfedge_descriptor halfedge_descriptor;
const halfedge_descriptor h = halfedge(f, tmesh);
// get the three points of the face and calculate their unit normal
const Point_reference p = get(point_map, source(h, tmesh));
const Point_reference q = get(point_map, target(h, tmesh));
const Point_reference r = get(point_map, target(next(h, tmesh), tmesh));
return unit_normal(p, q, r);
}
private:
Vertex_cost_map m_cost_matrices;
FT discontinuity_multiplier;
Col_4 point_to_homogenous_column(const Point_3& point) const
{
return Col_4(point.x(), point.y(), point.z(), FT(1));
}
template<typename VPM, typename TM>
Mat_4 construct_quadric(
const VPM& point_map,
const TM& tmesh,
typename boost::graph_traits<TM>::face_descriptor f, const GeomTraits& gt) const
{
return static_cast<const QuadricImpl*>(this)
->construct_quadric_from_face(point_map, tmesh, f, gt);
}
template<typename VPM, typename TM>
Mat_4 construct_quadric(
const VPM& point_map,
const TM& tmesh,
typename boost::graph_traits<TM>::halfedge_descriptor he,
const GeomTraits& gt) const
{
return static_cast<const QuadricImpl*>(this)
->construct_quadric_from_edge(point_map, tmesh, he, gt);
}
}; };
template<typename VCM, template <typename VertexCostMap,
typename GeomTraits, typename GeomTraits,
typename QuadricImpl> typename QuadricImpl>
class GarlandHeckbert_placement_base class GarlandHeckbert_placement_base
{ {
public: public:
// define type required by the Get_cost concept // define type required by the Get_cost concept
typedef VCM Vertex_cost_map; typedef VertexCostMap Vertex_cost_map;
// matrix and column vector types // matrix and column vector types
typedef typename GeomTraits::FT FT; typedef typename GeomTraits::FT FT;
typedef typename GeomTraits::Point_3 Point_3; typedef typename GeomTraits::Point_3 Point_3;
typedef Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> Mat_4; typedef Eigen::Matrix<FT, 4, 4, Eigen::DontAlign> Mat_4;
typedef Eigen::Matrix<FT, 4, 1> Col_4; typedef Eigen::Matrix<FT, 4, 1> Col_4;
GarlandHeckbert_placement_base() { } private:
GarlandHeckbert_placement_base(Vertex_cost_map cost_matrices) : m_cost_matrices(cost_matrices) { } Vertex_cost_map m_cost_matrices;
template<typename Profile> public:
boost::optional<typename Profile::Point> operator()(const Profile& profile) const GarlandHeckbert_placement_base() { }
{ GarlandHeckbert_placement_base(Vertex_cost_map cost_matrices)
CGAL_precondition(!get(m_cost_matrices, profile.v0()).isZero(0)); : m_cost_matrices(cost_matrices)
CGAL_precondition(!get(m_cost_matrices, profile.v1()).isZero(0)); { }
// the combined matrix has already been computed in the evaluation of the cost... protected:
const Mat_4 combinedMatrix = combine_matrices( void init_vcm(const Vertex_cost_map& vcm)
get(m_cost_matrices, profile.v0()),
get(m_cost_matrices, profile.v1()));
const Col_4 p0 = point_to_homogenous_column(profile.p0());
const Col_4 p1 = point_to_homogenous_column(profile.p1());
const Col_4 opt = construct_optimum(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;
}
protected:
void init_vcm(const Vertex_cost_map& vcm)
{ {
m_cost_matrices = vcm; m_cost_matrices = vcm;
} }
private:
Vertex_cost_map m_cost_matrices;
// use CRTP to call the quadric implementation // use CRTP to call the quadric implementation
Col_4 construct_optimum(const Mat_4& mat, const Col_4& p0, const Col_4& p1) const Col_4 construct_optimum(const Mat_4& mat, const Col_4& p0, const Col_4& p1) const
{ {
return static_cast<const QuadricImpl*>(this)->construct_optimal_point(mat, p0, p1); return static_cast<const QuadricImpl*>(this)->construct_optimal_point(mat, p0, p1);
} }
Col_4 point_to_homogenous_column(const Point_3& point) const Col_4 point_to_homogenous_column(const Point_3& point) const
{ {
return Col_4(point.x(), point.y(), point.z(), FT(1)); return Col_4(point.x(), point.y(), point.z(), FT(1));
} }
public:
template <typename Profile>
boost::optional<typename Profile::Point> operator()(const Profile& profile) const
{
CGAL_precondition(!get(m_cost_matrices, profile.v0()).isZero(0));
CGAL_precondition(!get(m_cost_matrices, profile.v1()).isZero(0));
// the combined matrix has already been computed in the evaluation of the cost...
const Mat_4 combinedMatrix = combine_matrices(get(m_cost_matrices, profile.v0()),
get(m_cost_matrices, profile.v1()));
const Col_4 p0 = point_to_homogenous_column(profile.p0());
const Col_4 p1 = point_to_homogenous_column(profile.p1());
const Col_4 opt = construct_optimum(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;
}
}; };
} // namespace internal } // namespace internal