mirror of https://github.com/CGAL/cgal
Merge pull request #4260 from maxGimeno/SMS-Bounded_input_distance_placement-maxGimeno
SMS: Bounded_input_distance_placement
This commit is contained in:
commit
b0afd77dab
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue