mirror of https://github.com/CGAL/cgal
Update code/doc after pre-review
This commit is contained in:
parent
4f56fdff8e
commit
0ae811604a
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue