Merge pull request #4260 from maxGimeno/SMS-Bounded_input_distance_placement-maxGimeno

SMS: Bounded_input_distance_placement
This commit is contained in:
Laurent Rineau 2020-02-07 16:25:14 +01:00
commit b0afd77dab
6 changed files with 332 additions and 3 deletions

View File

@ -0,0 +1,55 @@
namespace CGAL {
namespace Surface_mesh_simplification {
/*
\ingroup PkgSurfaceMeshSimplificationRef
The class `Bounded_distance_placement` is a model for the concept `GetPlacement`.
This placement class is a wrapper around another (so-called <em>base</em>) placement class.
The position of a vertex resulting from the contraction of an edge is obtained by first querying
the base placement class, and checking whether this tentative position is not too far
(according to a user-provided distance bound) from the input mesh.
If it is too far, the position is rejected and no position is returned; otherwise,
the position is returned.
\tparam BasePlacement must be a model of `GetPlacement`.
\tparam GeomTraits must be a model of `Kernel` and be identical to the traits specified
in the named parameters of the function `edge_collapse()` (if specified).
The distance check is performed using an AABB tree and this class thus depends on the package \ref PkgAABBTree.
\cgalModels `GetPlacement`
*/
template<class BasePlacement, class GeomTraits>
class Bounded_distance_placement
: public BasePlacement
{
public:
//
typedef typename GeomTraits::FT FT;
// \name Creation
//
// @{
// The distance bound `d` is used to control that during simplification,
// no vertex has a distance to the input that would be greater than `d`.
Bounded_distance_placement(const FT d, const BasePlacement& base_placement = BasePlacement());
// @}
// \name Operations
// @{
// Returns the placement computed by `base_placement`, provided the distance between the input
// and this placement is smaller than `d`. Otherwise, nothing is returned.
boost::optional<typename Edge_profile::Point> operator()(const Edge_profile& profile) const;
// @}
};
} // namespace Surface_Mesh_Simplification
} // namespace CGAL

View File

@ -12,13 +12,13 @@ associating quadrics to vertices.
Note however, that they may still be wrapped with slight behavior modifying classes
such as `Constrained_placement` or `Bounded_normal_change_placement`.
Note that these policies depend on the third party \ref thirdpartyEigen library.
\tparam TriangleMesh is the type of surface mesh being simplified, and must be a model
of the `MutableFaceGraph` and `HalfedgeListGraph` concepts.
\tparam GeomTraits must be a model of `Kernel`. If you have passed a traits class in the optional
named parameters in the call to `edge_collapse()`, the types must be identical.
These policies depend on the third party \ref thirdpartyEigen library.
*/
template <typename TriangleMesh, typename GeomTraits>
class GarlandHeckbert_policies

View File

@ -20,8 +20,9 @@ or can be intentionally returned to prevent the edge from being collapsed.
\cgalHasModel `CGAL::Surface_mesh_simplification::GarlandHeckbert_policies<TriangleMesh, GeomTraits>`
\cgalHasModel `CGAL::Surface_mesh_simplification::Bounded_normal_change_placement<Placement>`
\cgalHasModel `CGAL::Surface_mesh_simplification::Constrained_placement<Placement>`
*/
class GetPlacement
{
public:

View File

@ -0,0 +1,192 @@
// Copyright (c) 2019 GeometryFactory (France). All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Maxime Gimeno,
// Mael Rouxel-Labbé
//
#ifndef CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_EDGE_COLLAPSE_BOUNDED_DISTANCE_PLACEMENT_H
#define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_EDGE_COLLAPSE_BOUNDED_DISTANCE_PLACEMENT_H
#include <CGAL/license/Surface_mesh_simplification.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/AABB_triangle_primitive.h>
#include <CGAL/assertions.h>
#include <CGAL/Default.h>
#include <CGAL/intersections.h>
#include <CGAL/boost/graph/named_params_helper.h>
#include <boost/optional.hpp>
#include <vector>
#include <type_traits>
namespace CGAL {
namespace Surface_mesh_simplification {
// An AABB tree can also be passed to the placement instead, see undocumented specialization below
template<typename BasePlacement, typename GeomTraits>
class Bounded_distance_placement
{
typedef GeomTraits Geom_traits;
typedef typename Geom_traits::FT FT;
typedef typename GeomTraits::Triangle_3 Triangle;
typedef std::vector<Triangle> Triangle_container;
typedef typename Triangle_container::iterator TC_iterator;
typedef CGAL::AABB_triangle_primitive<GeomTraits, TC_iterator> Primitive;
typedef CGAL::AABB_traits<GeomTraits, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> AABB_tree;
private:
template <typename Profile>
void initialize_tree(const Profile& profile) const
{
CGAL_static_assertion((std::is_same<GeomTraits, typename Profile::Geom_traits>::value));
typedef typename Profile::Triangle_mesh Triangle_mesh;
typedef typename boost::graph_traits<Triangle_mesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<Triangle_mesh>::face_descriptor face_descriptor;
CGAL_precondition(m_tree_ptr == nullptr);
const Triangle_mesh& tm = profile.surface_mesh();
const Geom_traits& gt = profile.geom_traits();
std::vector<Triangle> input_triangles;
for(face_descriptor f : faces(profile.surface_mesh()))
{
halfedge_descriptor h = halfedge(f, tm);
CGAL_assertion(!is_border(h, tm));
input_triangles.push_back(gt.construct_triangle_3_object()(
get(profile.vertex_point_map(), source(h, tm)),
get(profile.vertex_point_map(), target(h, tm)),
get(profile.vertex_point_map(), target(next(h, tm), tm))));
}
m_tree_ptr = new AABB_tree(input_triangles.begin(), input_triangles.end());
const_cast<AABB_tree*>(m_tree_ptr)->build();
}
public:
Bounded_distance_placement(const FT dist,
const BasePlacement& placement = BasePlacement())
:
m_sq_threshold_dist(CGAL::square(dist)),
m_tree_ptr(nullptr),
m_base_placement(placement)
{ }
~Bounded_distance_placement()
{
if(m_tree_ptr != nullptr)
delete m_tree_ptr;
}
template <typename Profile>
boost::optional<typename Profile::Point>
operator()(const Profile& profile) const
{
typedef typename Profile::Point Point;
boost::optional<typename Profile::Point> op = m_base_placement(profile);
if(op)
{
if(m_tree_ptr == nullptr)
initialize_tree(profile);
CGAL_assertion(m_tree_ptr != nullptr);
CGAL_assertion(!m_tree_ptr->empty());
const Point& p = *op;
m_tree_ptr->accelerate_distance_queries();
const Point& cp = m_tree_ptr->best_hint(p).first; // requires accelerate distance query to be called.
// We could do better by having access to the internal kd-tree
// and call search_any_point with a fuzzy_sphere.
// if no input vertex is closer than the threshold, then
// any face closer than the threshold is intersected by
// the sphere (avoid the inclusion of the mesh into the threshold sphere)
if(CGAL::compare_squared_distance(p, cp, m_sq_threshold_dist) != LARGER ||
m_tree_ptr->do_intersect(CGAL::Sphere_3<Geom_traits>(p, m_sq_threshold_dist)))
return op;
return boost::optional<Point>();
}
return op;
}
private:
const FT m_sq_threshold_dist;
mutable const AABB_tree* m_tree_ptr;
const BasePlacement m_base_placement;
};
// Undocumented specizalization where an _already built_ AABB tree is passed
template<typename BasePlacement, typename AABBTraits>
class Bounded_distance_placement<BasePlacement, CGAL::AABB_tree<AABBTraits> >
{
typedef CGAL::AABB_tree<AABBTraits> AABB_tree;
typedef typename AABB_tree::AABB_traits::FT FT;
public:
Bounded_distance_placement(const FT dist,
const AABB_tree& tree,
const BasePlacement& placement = BasePlacement())
:
m_sq_threshold_dist(CGAL::square(dist)),
m_tree_ptr(&tree),
m_base_placement(placement)
{ }
template <typename Profile>
boost::optional<typename Profile::Point>
operator()(const Profile& profile) const
{
typedef typename Profile::Geom_traits Geom_traits;
typedef typename Profile::Point Point;
boost::optional<typename Profile::Point> op = m_base_placement(profile);
if(op)
{
CGAL_assertion(m_tree_ptr != nullptr);
CGAL_assertion(!m_tree_ptr->empty());
const Point& p = *op;
m_tree_ptr->accelerate_distance_queries();
const Point& cp = m_tree_ptr->best_hint(p).first; // requires accelerate distance query to be called.
if(CGAL::compare_squared_distance(p, cp, m_sq_threshold_dist) != LARGER ||
m_tree_ptr->do_intersect(CGAL::Sphere_3<Geom_traits>(p, m_sq_threshold_dist)))
return op;
return boost::optional<Point>();
}
return op;
}
private:
const FT m_sq_threshold_dist;
mutable const AABB_tree* m_tree_ptr;
const BasePlacement m_base_placement;
};
} // namespace Surface_mesh_simplification
} // namespace CGAL
#endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_EDGE_COLLAPSE_BOUNDED_DISTANCE_PLACEMENT_H

View File

@ -1,9 +1,13 @@
AABB_tree
Algebraic_foundations
BGL
Cartesian_kernel
Circulator
Distance_2
Distance_3
Installation
Intersections_2
Intersections_3
Interval_support
Kernel_23
Modular_arithmetic
@ -12,5 +16,6 @@ Profiling_tools
Property_map
Random_numbers
STL_Extension
Spatial_searching
Stream_support
Surface_mesh_simplification

View File

@ -0,0 +1,76 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
// Simplification function
#include <CGAL/Surface_mesh_simplification/edge_collapse.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Bounded_distance_placement.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/Count_stop_predicate.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/LindstromTurk_cost.h>
#include <CGAL/Surface_mesh_simplification/Policies/Edge_collapse/LindstromTurk_placement.h>
//AABB_tree
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
//bbox
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <iostream>
#include <fstream>
namespace SMS = CGAL::Surface_mesh_simplification;
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Surface_mesh<Point_3> Surface;
typedef SMS::LindstromTurk_cost<Surface> Cost;
typedef SMS::LindstromTurk_placement<Surface> Placement;
typedef CGAL::AABB_face_graph_triangle_primitive<Surface> Primitive;
typedef CGAL::AABB_traits<Kernel, Primitive> Traits;
typedef CGAL::AABB_tree<Traits> Tree;
typedef SMS::Bounded_distance_placement<Placement, Kernel> Filtered_placement;
typedef SMS::Bounded_distance_placement<Placement, Tree> Filtered_placement_with_tree;
int main(int argc, char** argv)
{
Surface ref_mesh;
std::ifstream is(argc > 1 ? argv[1] : "data/helmet.off");
is >> ref_mesh;
SMS::Count_stop_predicate<Surface> stop(num_halfedges(ref_mesh)/10);
std::cout << "input has " << num_vertices(ref_mesh) << " vertices." << std::endl;
CGAL::Iso_cuboid_3<Kernel> bbox(CGAL::Polygon_mesh_processing::bbox(ref_mesh));
Point_3 cmin = (bbox.min)();
Point_3 cmax = (bbox.max)();
const double diag = CGAL::approximate_sqrt(CGAL::squared_distance(cmin, cmax));
Surface mesh_cpy = ref_mesh; // need a copy to keep the AABB tree valid
Surface small_mesh = ref_mesh;
Surface big_mesh = ref_mesh;
Tree tree(faces(mesh_cpy).first, faces(mesh_cpy).second, mesh_cpy);
Placement placement_ref;
SMS::edge_collapse(ref_mesh, stop, CGAL::parameters::get_cost(Cost()).get_placement(placement_ref));
Filtered_placement_with_tree placement_small(0.00005*diag, tree, placement_ref);
SMS::edge_collapse(small_mesh, stop, CGAL::parameters::get_cost(Cost()).get_placement(placement_small));
Filtered_placement placement_big(50*diag, placement_ref); // lazily builds the AABB tree
SMS::edge_collapse(big_mesh, stop, CGAL::parameters::get_cost(Cost()).get_placement(placement_big));
std::cout << "no filtering: " << vertices(ref_mesh).size() << " vertices left" << std::endl;
std::cout << "large filtering distance: " << vertices(big_mesh).size() << " vertices left" << std::endl;
std::cout << "small filtering distance: " << vertices(small_mesh).size() << " vertices left" << std::endl;
assert(vertices(small_mesh).size() != vertices(ref_mesh).size());
assert(vertices(big_mesh).size() == vertices(ref_mesh).size());
return EXIT_SUCCESS;
}