Update code/doc after pre-review

This commit is contained in:
Mael Rouxel-Labbé 2019-12-10 13:32:55 +01:00
parent 4f56fdff8e
commit 0ae811604a
10 changed files with 477 additions and 376 deletions

View File

@ -1,37 +0,0 @@
namespace CGAL {
/*!
\ingroup PkgOptimalBoundingBoxConcepts
\cgalConcept
The concept `OptimalBoundingBoxTraits` describes the requirements of the traits class
used in the function `CGAL::optimal_bounding_box()`, and in particular the need for
a 3x3 matrix type with a number of supporting functions (determinant and transposed matrix
computations, etc.).
\cgalRefines `Kernel`
\cgalHasModel `CGAL::Optimal_bounding_box::Optimal_bounding_box_traits`
*/
class OptimalBoundingBoxTraits
{
public:
/// The field number type; must be a model of the concept `FieldNumberType`.
typedef unspecified_type FT;
/// A 3x3 matrix type; must be a model of the concept `SvdTraits::Matrix` and support
/// matrix-matrix and scalar-matrix multiplication, as well as matrix-matrix addition.
typedef unspecified_type Matrix;
/// Returns the transpose of a matrix.
Matrix transpose(const Matrix& mat) const;
/// Returns the determinant of a matrix.
FT compute_determinant(const Matrix& matrix) const;
/// Returns the unary matrix Q obtained in the QR decompoisiton of the matrix `A`.
Matrix get_Q(const Matrix& A) const;
};
} // namespace CGAL

View File

@ -0,0 +1,45 @@
namespace CGAL {
/*!
\ingroup PkgOrientedBoundingBoxConcepts
\cgalConcept
The concept `OrientedBoundingBoxTraits` describes the requirements of the traits class
used in the function `CGAL::oriented_bounding_box()`, and in particular the need for
a 3x3 matrix type with a number of supporting functions (determinant and transposed matrix
computations, etc.).
\cgalRefines `Kernel`
\cgalHasModel `CGAL::Oriented_bounding_box_traits`
*/
class OrientedBoundingBoxTraits
{
public:
/// The field number type; must be a model of the concept `FieldNumberType`.
typedef unspecified_type FT;
/// The 3D point type; must be model of `Point_3`
typedef unspecified_type Point_3;
/// A construction object that must provide the function operator:
/// `CGAL::Bbox_3 operator()(const Point_3&)`,
/// which returns an axis-aligned bounding that contains the point
typedef unspecified_type Construct_bbox_3;
/// A 3x3 matrix type; model of the concept `SvdTraits::Matrix` and which supports
/// matrix-matrix and scalar-matrix multiplication, as well as matrix-matrix addition.
typedef unspecified_type Matrix;
/// Returns the transpose of a matrix.
Matrix transpose(const Matrix& mat) const;
/// Returns the determinant of a matrix.
FT compute_determinant(const Matrix& matrix) const;
/// Returns the unary matrix Q obtained in the QR decomposition of the matrix `A`.
Matrix get_Q(const Matrix& A) const;
};
} // namespace CGAL

View File

@ -45,7 +45,7 @@ is the geometric traits instance in which the mesh processing operation should b
\cgalNPEnd
\cgalNPBegin{use_convex_hull} \anchor OBB_use_convex_hull
Parameter used in optimal bounding box construction to indicate whether the algorithm should
Parameter used in the construction of oriented bounding box to indicate whether the algorithm should
first extract the extreme points (points that are on the 3D convex hull) of the input data range
to accelerate the computation of the bounding box.
\n

View File

@ -9,6 +9,9 @@
/// \defgroup PkgOptimalBoundingBoxFunctions Optimal Bounding Box Methods
/// \ingroup PkgOptimalBoundingBoxRef
/// \defgroup PkgOptimalBoundingBox_Oriented_bounding_box Oriented Bounding Box Methods
/// \ingroup PkgOptimalBoundingBoxFunctions
/*!
\addtogroup PkgOptimalBoundingBoxRef
\cgalPkgDescriptionBegin{Optimal Bounding Box,PkgOptimalBoundingBox}
@ -37,14 +40,14 @@ that are used in this package.
\cgalCRPSection{Concepts}
- `CGAL::OptimalBoundingBoxTraits`
- `CGAL::OrientedBoundingBoxTraits`
\cgalCRPSection{Classes}
- `CGAL::Optimal_bounding_box::Optimal_bounding_box_traits`
- `CGAL::Oriented_bounding_box_traits`
\cgalCRPSection{Methods}
- `CGAL::optimal_bounding_box()`
- \link PkgOptimalBoundingBox_Oriented_bounding_box `CGAL::oriented_bounding_box()` \endlink
*/

View File

@ -4,12 +4,14 @@
#include <CGAL/Surface_mesh.h>
#include <CGAL/optimal_bounding_box.h>
#include <CGAL/Real_timer.h>
#include <fstream>
#include <iostream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_3 Point;
typedef K::Aff_transformation_3 Aff_transformation;
typedef CGAL::Surface_mesh<Point> Surface_mesh;
int main(int argc, char** argv)
@ -22,9 +24,21 @@ int main(int argc, char** argv)
return EXIT_FAILURE;
}
// test one
Aff_transformation aff;
// API test
CGAL::Real_timer timer;
timer.start();
std::array<Point, 8> obb_points;
CGAL::oriented_bounding_box(sm, obb_points);
std::cout << "Elapsed time: " << timer.time() << std::endl;
// Make a mesh out of the oriented bounding box
Surface_mesh obb_sm;
CGAL::optimal_bounding_box(sm, obb_sm);
CGAL::make_hexahedron(obb_points[0], obb_points[1], obb_points[2], obb_points[3],
obb_points[4], obb_points[5], obb_points[6], obb_points[7], obb_sm);
std::ofstream("obb.off") << obb_sm;

View File

@ -21,29 +21,36 @@
#endif
namespace CGAL {
namespace Optimal_bounding_box {
#if defined(CGAL_EIGEN3_ENABLED) || defined(DOXYGEN_RUNNING)
/// \ingroup PkgOptimalBoundingBoxClasses
///
/// The class `CGAL::Optimal_bounding_box::Optimal_bounding_box_traits` is a traits type
/// to be used with the functions `CGAL::optimal_bounding_box()`.
/// The class `CGAL::Oriented_bounding_box_traits` is a traits type
/// to be used with the functions `CGAL::oriented_bounding_box()`.
/// It uses the third party library \ref thirdpartyEigen "Eigen", which must therefore
/// be available on the system for this class to be used.
///
/// \tparam K must be a model of `Kernel`
///
/// \cgalModels `OptimalBoundingBoxTraits`
/// \cgalModels `OrientedBoundingBoxTraits`
///
template <typename K>
class Optimal_bounding_box_traits
: public K
class Oriented_bounding_box_traits
{
public:
/// The field number type
typedef typename K::FT FT;
/// The point type
typedef typename K::Point_3 Point_3;
/// The affine transformation type
typedef typename K::Aff_transformation_3 Aff_transformation_3;
/// The axis-aligned bounding box construction object
typedef typename K::Construct_bbox_3 Construct_bbox_3;
/// The matrix type
typedef CGAL::Eigen_matrix<FT, 3, 3> Matrix;
@ -52,15 +59,18 @@ private:
public:
/// Constructor from the base kernel
explicit Optimal_bounding_box_traits(const K& k = K()) : K(k) { }
explicit Oriented_bounding_box_traits() { }
/// Get the transpose of a matrix
/// Offers `construct_bbox_3_object()(const Point_3&)`
Construct_bbox_3 construct_bbox_3_object() const { return Construct_bbox_3(); }
/// Returns the transpose of a matrix
Matrix transpose(const Matrix& mat) const
{
return Matrix(mat.eigen_object().transpose());
}
/// Get the determinant of a matrix
/// Returns the determinant of a matrix
FT compute_determinant(const Matrix& matrix) const
{
return matrix.eigen_object().determinant();
@ -78,7 +88,6 @@ public:
};
#endif // defined(CGAL_EIGEN3_ENABLED) || defined(DOXYGEN_RUNNING)
} // namespace Optimal_bounding_box
} // namespace CGAL
#endif // CGAL_OPTIMAL_BOUNDING_BOX_BOUNDING_BOX_TRAITS_H

View File

@ -49,7 +49,7 @@ public:
const Traits& traits)
:
m_population(traits),
m_rng(rng), // @todo just a parameter of genetic_algorithm() ?
m_rng(rng),
m_points(points),
m_traits(traits)
{ }
@ -144,12 +144,12 @@ public:
const std::size_t population_size = 50;
// hardcoded nelder_mead_iterations
const std::size_t nelder_mead_iterations = 20;
const std::size_t nelder_mead_iterations = 150;
// stopping criteria prameters
double prev_fit_value = 0.;
double new_fit_value = 0.;
const double tolerance = 1e-2;
const double tolerance = 1e-9;
int stale = 0;
for(std::size_t t=0; t<generations; ++t)
@ -176,14 +176,19 @@ public:
//std::cout << "pop after nelder mead: " << std::endl;
//pop.show_population();
//std::cout << std::endl;
const Matrix& R_now = fitness_map.get_best();
std::cout << "det = " << m_traits.compute_determinant(R_now) << std::endl;
#endif
new_fit_value = fitness_map.get_best_fitness_value();
const double difference = new_fit_value - prev_fit_value;
if(CGAL::abs(difference) < tolerance * new_fit_value)
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_DEBUG
const Matrix& R_now = fitness_map.get_best();
std::cout << "new best: " << R_now << std::endl;
std::cout << "det = " << m_traits.compute_determinant(R_now) << std::endl;
std::cout << "value difference with previous: " << difference << std::endl;
#endif
if(CGAL::abs(difference) < tolerance * new_fit_value) // @todo should depend on input bbox diag
++stale;
if(stale == 5)

View File

@ -37,8 +37,8 @@ compute_fitness(const typename Traits::Matrix& R, // rotation matrix
CGAL_assertion(points.size() >= 3);
FT xmin, ymin, zmin, xmax, ymax, zmax;
xmin = ymin = zmin = std::numeric_limits<double>::max();
xmax = ymax = zmax = std::numeric_limits<double>::lowest();
xmin = ymin = zmin = FT(std::numeric_limits<double>::max());
xmax = ymax = zmax = FT(std::numeric_limits<double>::lowest());
for(const Point& pt : points)
{

View File

@ -0,0 +1,370 @@
// Copyright (c) 2018-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) : Mael Rouxel-Labbé
//
#ifndef CGAL_OPTIMAL_BOUNDING_BOX_ORIENTED_BOUNDING_BOX_H
#define CGAL_OPTIMAL_BOUNDING_BOX_ORIENTED_BOUNDING_BOX_H
#include <CGAL/license/Optimal_bounding_box.h>
#include <CGAL/Optimal_bounding_box/internal/population.h>
#include <CGAL/Optimal_bounding_box/internal/evolution.h>
#include <CGAL/Optimal_bounding_box/Oriented_bounding_box_traits.h>
#include <CGAL/boost/graph/Named_function_parameters.h>
#include <CGAL/boost/graph/named_params_helper.h>
#include <CGAL/assertions.h>
#include <CGAL/Bbox_3.h>
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/convex_hull_3.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Iso_cuboid_3.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/Random.h>
#include <CGAL/Simple_cartesian.h>
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
#include <CGAL/Real_timer.h>
#endif
#include <array>
#include <iostream>
#include <iterator>
#include <type_traits>
#include <vector>
#ifdef DOXYGEN_RUNNING
#define CGAL_BGL_NP_TEMPLATE_PARAMETERS NamedParameters
#define CGAL_BGL_NP_CLASS NamedParameters
#endif
namespace CGAL {
namespace Optimal_bounding_box {
namespace internal {
template <typename PointRange, typename Traits>
void construct_oriented_bounding_box(std::array<typename Traits::Point_3, 8>& obb_points,
const typename Traits::Aff_transformation_3& transformation,
const typename Traits::Aff_transformation_3& inverse_transformation,
const PointRange& points,
const Traits& traits)
{
typedef typename Traits::FT FT;
typedef typename Traits::Point_3 Point;
// Construct the bbox of the transformed point set
CGAL::Bbox_3 bbox;
for(const Point& pt : points)
{
const Point rotated_pt = transformation.transform(pt);
bbox += traits.construct_bbox_3_object()(rotated_pt);
}
obb_points[0] = Point(bbox.xmin(), bbox.ymin(), bbox.zmin());
obb_points[1] = Point(bbox.xmax(), bbox.ymin(), bbox.zmin());
obb_points[2] = Point(bbox.xmax(), bbox.ymax(), bbox.zmin());
obb_points[3] = Point(bbox.xmin(), bbox.ymax(), bbox.zmin());
obb_points[4] = Point(bbox.xmin(), bbox.ymax(), bbox.zmax()); // see order in make_hexahedron()...
obb_points[5] = Point(bbox.xmin(), bbox.ymin(), bbox.zmax());
obb_points[6] = Point(bbox.xmax(), bbox.ymin(), bbox.zmax());
obb_points[7] = Point(bbox.xmax(), bbox.ymax(), bbox.zmax());
// Apply the inverse rotation to the rotated axis aligned bounding box
for(std::size_t i=0; i<8; ++i)
obb_points[i] = inverse_transformation.transform(obb_points[i]);
}
template <typename PointRange, typename Traits>
void compute_best_transformation(typename Traits::Aff_transformation_3& transformation,
typename Traits::Aff_transformation_3& inverse_transformation,
const PointRange& points,
CGAL::Random& rng,
const Traits& traits)
{
typedef typename Traits::Matrix Matrix;
typedef typename Traits::Aff_transformation_3 Aff_transformation_3;
const std::size_t max_generations = 50; // @todo hidden NP
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
CGAL::Real_timer timer;
timer.start();
#endif
Evolution<PointRange, Traits> search_solution(points, rng, traits);
search_solution.evolve(max_generations);
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
std::cout << "evolve: " << timer.time() << std::endl;
timer.reset();
#endif
const Matrix& rot = search_solution.get_best();
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
std::cout << "get best: " << timer.time() << std::endl;
#endif
transformation = Aff_transformation_3(rot(0, 0), rot(0, 1), rot(0, 2),
rot(1, 0), rot(1, 1), rot(1, 2),
rot(2, 0), rot(2, 1), rot(2, 2));
// inverse transformation is simply the transposed since the matrix is unary
inverse_transformation = Aff_transformation_3(rot(0, 0), rot(1, 0), rot(2, 0),
rot(0, 1), rot(1, 1), rot(2, 1),
rot(0, 2), rot(1, 2), rot(2, 2));
}
// Following two functions are overload to dispatch depending on return type
template <typename PointRange, typename Traits>
void construct_oriented_bounding_box(typename Traits::Aff_transformation_3& transformation,
const PointRange& points,
CGAL::Random& rng,
const Traits& traits)
{
typename Traits::Aff_transformation_3 inverse_transformation;
compute_best_transformation(transformation, inverse_transformation, points, rng, traits);
}
template <typename PointRange, typename Traits>
void construct_oriented_bounding_box(std::array<typename Traits::Point_3, 8>& obb_points,
const PointRange& points,
CGAL::Random& rng,
const Traits& traits)
{
typename Traits::Aff_transformation_3 transformation, inverse_transformation;
compute_best_transformation(transformation, inverse_transformation, points, rng, traits);
construct_oriented_bounding_box(obb_points, transformation, inverse_transformation, points, traits);
}
// Entry point, decide whether to compute the CH_3 or not
template <typename Output, typename PointRange, typename Traits>
void construct_oriented_bounding_box(Output& output,
const bool use_ch,
const PointRange& points,
CGAL::Random& rng,
const Traits& traits)
{
typedef typename Traits::Matrix Matrix;
typedef typename Traits::Point_3 Point;
CGAL_static_assertion((std::is_same<typename boost::range_value<PointRange>::type, Point>::value));
if(use_ch) // construct the convex hull to reduce the number of points
{
std::vector<Point> ch_points;
extreme_points_3(points, std::back_inserter(ch_points));
std::cout << "points on CH: " << ch_points.size() << std::endl;
return construct_oriented_bounding_box(output, ch_points, rng, traits);
}
else
{
return construct_oriented_bounding_box(output, points, rng, traits);
}
}
} // namespace Optimal_bounding_box
} // namespace internal
/// \addtogroup PkgOptimalBoundingBox_Oriented_bounding_box
///
/// The function `oriented_bounding_box` computes an approximation of the <i>optimal bounding box</i>,
/// which is defined as the rectangular box with smallest volume of all the rectangular boxes containing
/// the input points.
///
/// Internally, the algorithm uses an optimization process to find a transformation (rotation)
/// \f$ {\mathcal R}_b\f$ such that the axis-aligned box of the rotated input point set
/// has a volume that is as small as possible.
///
/// \cgalHeading{Input}
///
/// The input can be either a range of points, or a polygon mesh.
///
/// \cgalHeading{Output}
///
/// The result of the algorithm can be obtained as either:
/// - the best affine transformation (\f$ {\mathcal R}_b\f$) that the algorithm has found;
/// - an array of eight points, representing the best oriented bounding box (\f$ {\mathcal B}_b\f$)
/// that the algorithm has constructed, which is related to (\f$ {\mathcal R}_b\f$) as it is
/// the inverse transformation of the axis-aligned bounding box of the transformed point set.
/// The order of the points in the array is the same as in the function
/// \link PkgBGLHelperFct `CGAL::make_hexahedron()` \endlink,
/// which is a useful function to construct a mesh from these points.
///
/// Note that when returning an array of points, these points are constructed from the axis-aligned
/// bounding box and some precision loss should be expected if a kernel not providing exact constructions
/// is used.
/// \ingroup PkgOptimalBoundingBox_Oriented_bounding_box
///
/// See above.
///
/// \tparam PointRange a model of `Range`
/// \tparam Output `std::array<Point, 8>` with `Point` being equivalent to the traits' `Point_3` type,
/// or `CGAL::Affine_transformation_3<K>` with `K` being a kernel compatible with the point type.
/// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
///
/// \param points the input points
/// \param out the resulting array of points or affine transformation
/// \param np an optional sequence of \ref obb_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `pmesh`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` must be available in `PolygonMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits}
/// a geometric traits class instance, model of the concept `OrientedBoundingBoxTraits`.
/// %Default is `CGAL::Oriented_bounding_box_traits<K>` where `K` is deduced from the point type.
/// \cgalParamEnd
/// \cgalParamBegin{use_convex_hull}
/// a Boolean value to indicate whether the algorithm should first extract the so-called extreme
/// points of the data range (i.e. construct the convex hull) to reduce the input data range
/// and accelerate the algorithm. %Default is `true`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename PointRange,
typename Output,
typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
void oriented_bounding_box(const PointRange& points,
Output& out,
const CGAL_BGL_NP_CLASS& np,
#ifndef DOXYGEN_RUNNING
typename boost::enable_if<
typename boost::has_range_iterator<PointRange>
>::type* = 0
#endif
)
{
using CGAL::parameters::choose_parameter;
using CGAL::parameters::get_parameter;
#if defined(CGAL_EIGEN3_ENABLED)
typedef typename boost::range_value<PointRange>::type Point;
typedef typename CGAL::Kernel_traits<Point>::type K;
typedef Oriented_bounding_box_traits<K> Default_traits;
#else
typedef void Default_traits;
#endif
typedef typename internal_np::Lookup_named_param_def<internal_np::geom_traits_t,
CGAL_BGL_NP_CLASS,
Default_traits>::type Geom_traits;
CGAL_static_assertion_msg(!(std::is_same<Geom_traits, void>::value),
"You must provide a traits class or have Eigen enabled!");
Geom_traits traits = choose_parameter(get_parameter(np, internal_np::geom_traits), Geom_traits());
const bool use_ch = choose_parameter(get_parameter(np, internal_np::use_convex_hull), true);
const unsigned int seed = choose_parameter(get_parameter(np, internal_np::random_seed), 0); // undocumented
CGAL::Random rng(seed);
// @todo handle those cases instead
CGAL_assertion(points.size() >= 3);
if(points.size() <= 3)
{
std::cerr << "The oriented bounding box cannot YET be computed for a mesh with fewer than 4 vertices!\n";
return;
}
return Optimal_bounding_box::internal::construct_oriented_bounding_box(out, use_ch, points, rng, traits);
}
/// \ingroup PkgOptimalBoundingBox_Oriented_bounding_box
///
/// Extracts the vertices of the mesh as a point range and calls the other overload.
///
/// \tparam PolygonMesh a model of `VertexListGraph`
/// \tparam Output `std::array<Point, 8>` with `Point` being equivalent to the traits' `Point_3` type,
/// or `CGAL::Affine_transformation_3<K>` with `K` being a kernel compatible with the point type.
/// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
///
/// \param pmesh the input mesh
/// \param out the resulting array of points or affine transformation
/// \param np an optional sequence of \ref obb_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `pmesh`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` must be available in `PolygonMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits}
/// a geometric traits class instance, model of the concept `OrientedBoundingBoxTraits`.
/// %Default is `CGAL::Oriented_bounding_box_traits<K>` where `K` is deduced from the point type.
/// \cgalParamEnd
/// \cgalParamBegin{use_convex_hull}
/// a Boolean value to indicate whether the algorithm should first extract the so-called extreme
/// points of the data range (i.e. construct the convex hull) to reduce the input data range
/// and accelerate the algorithm. %Default is `true`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename PolygonMesh,
typename Output,
typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
void oriented_bounding_box(const PolygonMesh& pmesh,
Output& out,
const CGAL_BGL_NP_CLASS& np,
#ifndef DOXYGEN_RUNNING
typename boost::disable_if<
typename boost::has_range_iterator<PolygonMesh>
>::type* = 0
#endif
)
{
namespace PMP = CGAL::Polygon_mesh_processing;
using CGAL::parameters::choose_parameter;
using CGAL::parameters::get_parameter;
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
typedef typename PMP::GetVertexPointMap<PolygonMesh, CGAL_BGL_NP_CLASS>::const_type VPM;
typedef typename boost::property_traits<VPM>::value_type Point;
VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_const_property_map(vertex_point, pmesh));
std::vector<Point> points;
points.reserve(num_vertices(pmesh));
for(vertex_descriptor v : vertices(pmesh))
points.push_back(get(vpm, v));
oriented_bounding_box(points, out, np);
}
/// \cond SKIP_IN_MANUAL
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Convenience overloads
/////////////////////////////////////////////////////////////////////////////////////////////////
template <typename InputData /*range or mesh*/, typename OutputType /*array or transformation*/>
void oriented_bounding_box(const InputData& data,
OutputType& out)
{
return oriented_bounding_box(data, out, CGAL::parameters::all_default());
}
/// \endcond
} // end namespace CGAL
#endif // CGAL_OPTIMAL_BOUNDING_BOX_ORIENTED_BOUNDING_BOX_H

View File

@ -14,321 +14,13 @@
#ifndef CGAL_OPTIMAL_BOUNDING_BOX_OBB_H
#define CGAL_OPTIMAL_BOUNDING_BOX_OBB_H
#include <CGAL/license/Optimal_bounding_box.h>
/**
* \ingroup PkgOptimalBoundingBoxRef
* \file CGAL/optimal_bounding_box.h
* Convenience header file including the headers for all the free functions of this package.
*/
#include <CGAL/Optimal_bounding_box/internal/population.h>
#include <CGAL/Optimal_bounding_box/internal/evolution.h>
#include <CGAL/Optimal_bounding_box/Optimal_bounding_box_traits.h>
#include <CGAL/boost/graph/Named_function_parameters.h>
#include <CGAL/boost/graph/named_params_helper.h>
#include <CGAL/assertions.h>
#include <CGAL/Bbox_3.h>
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/convex_hull_3.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Iso_cuboid_3.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/Random.h>
#include <CGAL/Simple_cartesian.h>
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
#include <CGAL/Real_timer.h>
#endif
#include <array>
#include <iostream>
#include <iterator>
#include <type_traits>
#include <vector>
#ifdef DOXYGEN_RUNNING
#define CGAL_BGL_NP_TEMPLATE_PARAMETERS NamedParameters
#define CGAL_BGL_NP_CLASS NamedParameters
#endif
namespace CGAL {
namespace Optimal_bounding_box {
namespace internal {
// works on matrices only
template <typename PointRange, typename Traits>
void post_processing(const typename Traits::Matrix& R,
std::array<typename Traits::Point_3, 8>& obb,
const PointRange& points,
const Traits& traits)
{
typedef typename Traits::FT FT;
typedef typename Traits::Point_3 Point;
typedef typename Traits::Matrix Matrix;
CGAL_assertion(R.number_of_rows() == 3 && R.number_of_columns() == 3);
const Matrix Rt = traits.transpose(R);
CGAL::Bbox_3 bbox;
for(const Point& pt : points)
{
// @fixme should it be R here Rt at the other one... ?
const FT x = pt.x(), y = pt.y(), z = pt.z();
Point rotated_pt(x*R(0, 0) + y*R(0, 1) + z*R(0, 2),
x*R(1, 0) + y*R(1, 1) + z*R(1, 2),
x*R(2, 0) + y*R(2, 1) + z*R(2, 2));
bbox += traits.construct_bbox_3_object()(rotated_pt);
}
// @todo could avoid building a cuboid
typename Traits::Iso_cuboid_3 ic(bbox);
// 3) apply inverse rotation to rotated AABB
for(std::size_t i = 0; i<8; ++i)
{
const FT x = ic[i].x(), y = ic[i].y(), z = ic[i].z();
obb[i] = Point(x*Rt(0, 0) + y*Rt(0, 1) + z*Rt(0, 2),
x*Rt(1, 0) + y*Rt(1, 1) + z*Rt(1, 2),
x*Rt(2, 0) + y*Rt(2, 1) + z*Rt(2, 2));
}
}
template <typename PointRange, typename Traits>
void construct_optimal_bounding_box(std::array<typename Traits::Point_3, 8>& obb_points,
const PointRange& points,
CGAL::Random& rng,
const Traits& traits)
{
typedef typename Traits::Matrix Matrix;
const std::size_t max_generations = 100;
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
CGAL::Real_timer timer;
timer.start();
#endif
Evolution<PointRange, Traits> search_solution(points, rng, traits);
search_solution.evolve(max_generations);
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
std::cout << "evolve: " << timer.time() << std::endl;
timer.reset();
#endif
const Matrix& rotation = search_solution.get_best();
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
std::cout << "get best: " << timer.time() << std::endl;
timer.reset();
#endif
post_processing(rotation, obb_points, points, traits);
#ifdef CGAL_OPTIMAL_BOUNDING_BOX_BENCHMARKS
std::cout << "post-processing: " << timer.time() << std::endl;
#endif
}
template <typename PointRange, typename Traits>
void construct_optimal_bounding_box(std::array<typename Traits::Point_3, 8>& obb_points,
const bool use_ch,
const PointRange& points,
CGAL::Random& rng,
const Traits& traits)
{
typedef typename Traits::Matrix Matrix;
typedef typename Traits::Point_3 Point;
CGAL_static_assertion((std::is_same<typename boost::range_value<PointRange>::type, Point>::value));
if(use_ch) // construct the convex hull to reduce the number of points
{
std::vector<Point> ch_points;
extreme_points_3(points, std::back_inserter(ch_points));
return construct_optimal_bounding_box(obb_points, ch_points, rng, traits);
}
else
{
return construct_optimal_bounding_box(obb_points, points, rng, traits);
}
}
} // namespace Optimal_bounding_box
} // namespace internal
/// \ingroup PkgOptimalBoundingBoxFunctions
///
/// constructs a rectangular box that contains all the input points. This bounding box
/// is obtained via an optimization process aiming to get a close approximation of the
/// optimal bounding box, which is defined as the smallest (in terms of volume)
/// of all the rectangular boxes containing the input points.
///
/// \tparam PointRange a model of `Range` with value type `Point`
/// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
///
/// \param points the input points
/// \param obb_points the eight points of the resulting englobing box.
/// The order of points is the same as in the function `CGAL::make_hexahedron()`
/// \param np an optional sequence of \ref obb_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `pmesh`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` must be available in `PolygonMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits}
/// a geometric traits class instance, model of the concept `OptimalBoundingBoxTraits`. %Default is
/// `CGAL::Optimal_bounding_box::Optimal_bounding_box_traits<K>` where `K` is deduced
/// from the point type, which must then be compatible with `CGAL::Kernel_traits`.
/// \cgalParamEnd
/// \cgalParamBegin{use_convex_hull}
/// a Boolean value to indicate whether the algorithm should first extract the so-called extreme
/// points of the data range (i.e. construct the convex hull) to reduce the input data range
/// and accelerate the algorithm. The optimal value of this parameter will depend on the data
/// as it is a balance between two costs. %Default is `true`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \pre the value type of `PointRange` is `Point`
///
template <typename PointRange,
typename Point,
typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
void optimal_bounding_box(const PointRange& points,
std::array<Point, 8>& obb_points,
const CGAL_BGL_NP_CLASS& np)
{
using CGAL::parameters::choose_parameter;
using CGAL::parameters::get_parameter;
#if defined(CGAL_EIGEN3_ENABLED)
typedef typename CGAL::Kernel_traits<Point>::type K;
typedef Optimal_bounding_box::Optimal_bounding_box_traits<K> Default_traits;
#else
typedef void Default_traits;
#endif
typedef typename internal_np::Lookup_named_param_def<internal_np::face_size_map_t,
CGAL_BGL_NP_CLASS,
Default_traits>::type Geom_traits;
CGAL_static_assertion_msg(!(std::is_same<Geom_traits, void>::value),
"You must provide a traits class or have Eigen enabled!");
Geom_traits traits = choose_parameter(get_parameter(np, internal_np::geom_traits), Geom_traits());
const bool use_ch = choose_parameter(get_parameter(np, internal_np::use_convex_hull), false);
const unsigned int seed = choose_parameter(get_parameter(np, internal_np::random_seed), 0); // undocumented
CGAL::Random rng(seed);
// @todo handle those cases instead
CGAL_assertion(points.size() >= 3);
if(points.size() <= 3)
{
std::cerr << "The optimal bounding box cannot YET be computed for a mesh with fewer than 4 vertices!\n";
return;
}
return Optimal_bounding_box::internal::construct_optimal_bounding_box(obb_points, use_ch, points, rng, traits);
}
/// \cond SKIP_IN_MANUAL
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Convenience overloads for point ranges
/////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PointRange, typename Point>
void optimal_bounding_box(const PointRange& points,
std::array<Point, 8>& obb_points)
{
return optimal_bounding_box(points, obb_points, CGAL::parameters::all_default());
}
/// \endcond
/// \ingroup PkgOptimalBoundingBoxFunctions
///
/// constructs a rectangular box that contains the input mesh. This bounding box
/// is obtained via an optimization process aiming to get a close approximation of the
/// optimal bounding box, which is defined as the smallest (in terms of volume)
/// of all the rectangular boxes containing the input mesh.
///
/// \tparam PolygonMesh a model of `FaceListGraph`
/// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
///
/// \param pmesh the input mesh
/// \param obb_mesh the resulting enclosing bounding box (an hexahedron)
/// \param np an optional sequence of \ref obb_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `pmesh`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` must be available in `PolygonMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits}
/// a geometric traits class instance, model of the concept `OptimalBoundingBoxTraits`. %Default is
/// `CGAL::Optimal_bounding_box::Optimal_bounding_box_traits<K>` where `K` is deduced
/// from the point type, which must be compatible with `CGAL::Kernel_traits`.
/// \cgalParamEnd
/// \cgalParamBegin{use_convex_hull}
/// a Boolean value to indicate whether the algorithm should first extract the so-called extreme
/// points of the data range (i.e. construct the convex hull) to reduce the input data range
/// and accelerate the algorithm. The optimal value of this parameter will depend on the data
/// as it is a balance between two costs. %Default is `true`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename PolygonMesh,
typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
void optimal_bounding_box(const PolygonMesh& pmesh,
PolygonMesh& obb_mesh,
const CGAL_BGL_NP_CLASS& np)
{
namespace PMP = CGAL::Polygon_mesh_processing;
using CGAL::parameters::choose_parameter;
using CGAL::parameters::get_parameter;
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
typedef typename PMP::GetVertexPointMap<PolygonMesh, CGAL_BGL_NP_CLASS>::const_type VPM;
typedef typename boost::property_traits<VPM>::value_type Point;
VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_const_property_map(vertex_point, pmesh));
std::vector<Point> points;
points.reserve(num_vertices(pmesh));
for(vertex_descriptor v : vertices(pmesh))
points.push_back(get(vpm, v));
std::array<Point, 8> obb_points;
optimal_bounding_box(points, obb_points, np);
CGAL::make_hexahedron(obb_points[0], obb_points[1], obb_points[2], obb_points[3],
obb_points[4], obb_points[5], obb_points[6], obb_points[7], obb_mesh);
}
/// \cond SKIP_IN_MANUAL
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Convenience overloads for polygon meshes
/////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PolygonMesh>
void optimal_bounding_box(const PolygonMesh& pmesh,
PolygonMesh& obb_mesh)
{
return optimal_bounding_box(pmesh, obb_mesh, CGAL::parameters::all_default());
}
/// \endcond
} // end namespace CGAL
#include <CGAL/Optimal_bounding_box/Oriented_bounding_box_traits.h>
#include <CGAL/Optimal_bounding_box/oriented_bounding_box.h>
#endif // CGAL_OPTIMAL_BOUNDING_BOX_OBB_H