Merge pull request #3973 from sgiraudot/Kernel_23-Add_hash_functions-GF

[Small Feature] Add hash functions for kernel objects
This commit is contained in:
Laurent Rineau 2019-06-20 14:21:09 +02:00
commit 8fd3f2d8fd
20 changed files with 380 additions and 4 deletions

View File

@ -63,6 +63,14 @@ Release date: September 2019
the approximate dihedral angle between 2 vectors. Corresponding functors
in the model (`Compute_approximate_angle_3`) and free function (`approximate_angle`)
are also added.
- The following objects are now hashable and thus trivially usable
with `std::unordered_set` and `std::unordered_map`:
`CGAL::Aff_transformation_2`, `CGAL::Aff_transformation_3`,
`CGAL::Bbox_2`, `CGAL::Bbox_3`, `CGAL::Circle_2`,
`CGAL::Iso_cuboid_3`, `CGAL::Iso_rectangle_2`, `CGAL::Point_2`,
`CGAL::Point_3`, `CGAL::Segment_2`, `CGAL::Segment_3`,
`CGAL::Sphere_3`, `CGAL::Vector_2`, `CGAL::Vector_3`,
`CGAL::Weighted_point_2` and `CGAL::Weighted_point_3`.
### IO Streams
- Added new functions to support some parts of the WKT file format:

View File

@ -34,6 +34,8 @@ translation vector \f$ (v_0,\,v_1,\,1)\f$ appears in the last column of the
matrix. The entries \f$ m_{20}\f$ and \f$ m_{21}\f$ are always zero and
therefore do not appear in the constructors.
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
\sa `Identity_transformation`
\sa `Rotation`
\sa `Scaling`

View File

@ -29,6 +29,8 @@ In three-dimensional space we have a \f$ 4\times 4\f$ matrix
\f$ m_{32}\f$ are always zero and therefore do not appear in the
constructors.
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
\sa `CGAL::Aff_transformation_2<Kernel>`
\sa `CGAL::Identity_transformation`
\sa `CGAL::Reflection`

View File

@ -7,6 +7,8 @@ namespace CGAL {
An object `b` of the class `Bbox_2` is a bounding
box in the two-dimensional Euclidean plane \f$ \E^2\f$. This class is not templated.
\cgalModels `Hashable`
\sa `CGAL::Bbox_3`
*/

View File

@ -7,6 +7,8 @@ namespace CGAL {
An object `b` of the class `Bbox_3` is a bounding
box in the three-dimensional Euclidean space \f$ \E^3\f$.
\cgalModels `Hashable`
\sa `CGAL::Bbox_2`
*/

View File

@ -12,6 +12,7 @@ splits \f$ \E^2\f$ into a bounded and an unbounded side. Note that the
circle can be degenerated, i.e.\ the squared radius may be zero.
\cgalModels `Kernel::Circle_2`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -17,6 +17,7 @@ whereas the coordinate type of an iso-oriented cuboid is chosen by
the user.
\cgalModels `Kernel::IsoCuboid_3`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -18,6 +18,7 @@ whereas the coordinate type of an iso-oriented rectangle is chosen by
the user.
\cgalModels `Kernel::IsoRectangle_2`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -12,10 +12,6 @@ model `Cartesian<NT>`, the two types are the same. For the
kernel model `Homogeneous<NT>`, `Kernel::RT` is equal
to `NT`, and `Kernel::FT` is equal to `Quotient<NT>`.
\cgalHeading{Operators}
The following operations can be applied on points:
\cgalHeading{Example}
The following declaration creates two points with
@ -35,6 +31,7 @@ std::cout << p.x() << " " << p.y() << std::endl;
\endcode
\cgalModels `Kernel::Point_2`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -17,6 +17,7 @@ to `NT`, and `Kernel::FT` is equal to `Quotient<NT>`.
The following operations can be applied on points:
\cgalModels `Kernel::Point_3`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -15,6 +15,7 @@ perform a square root operation which is not defined for all
number types, which is expensive, and may not be exact.
\cgalModels `Kernel::Segment_2`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -15,6 +15,7 @@ perform a square root operation which is not defined for all
number types, which is expensive, and may not be exact.
\cgalModels `Kernel::Segment_3`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -12,6 +12,7 @@ splits \f$ \E^3\f$ into a bounded and an unbounded side. Note that the
sphere can be degenerated, i.e.\ the squared radius may be zero.
\cgalModels `Kernel::Sphere_3`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -13,6 +13,7 @@ will explicitly state where you can pass this constant as an argument
instead of a vector initialized with zeros.
\cgalModels `Kernel::Vector_2`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -13,6 +13,7 @@ will explicitly state where you can pass this constant as an argument
instead of a vector initialized with zeros.
\cgalModels `Kernel::Vector_3`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
\sa `cross_product_grp`
\sa `determinant_grp`

View File

@ -14,6 +14,7 @@ to `NT`, and `Kernel::FT` is equal to `Quotient<NT>`.
\sa `Point_2<Kernel>`
\cgalModels `Kernel::WeightedPoint_2`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -14,6 +14,7 @@ to `NT`, and `Kernel::FT` is equal to `Quotient<NT>`.
\sa `Point_3<Kernel>`
\cgalModels `Kernel::WeightedPoint_3`
\cgalModels `Hashable` if `Kernel` is a cartesian kernel and if `Kernel::FT` is `Hashable`
*/
template< typename Kernel >

View File

@ -31,6 +31,7 @@
#include <CGAL/Kernel/global_functions_2.h>
#include <CGAL/Kernel/global_functions_3.h>
#include <CGAL/Kernel/hash_functions.h>
namespace CGAL {

View File

@ -0,0 +1,280 @@
// Copyright (c) 2019
// GeometryFactory (France)
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
// SPDX-License-Identifier: LGPL-3.0+
//
//
// Author(s) : Simon Giraudot
#ifndef CGAL_KERNEL_HASH_FUNCTIONS_H
#define CGAL_KERNEL_HASH_FUNCTIONS_H
#include <boost/functional/hash.hpp>
#include <type_traits>
namespace CGAL
{
using boost::hash_value;
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Aff_transformation_2<K>& transform)
{
std::size_t result = hash_value(transform.cartesian(0,0));
for(int i=0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
if (!(i == 0 && j == 0))
boost::hash_combine(result, hash_value(transform.cartesian(i,j)));
return result;
}
inline std::size_t
hash_value (const Bbox_2& bbox)
{
std::size_t result = hash_value(bbox.xmin());
boost::hash_combine(result, hash_value(bbox.xmax()));
boost::hash_combine(result, hash_value(bbox.ymin()));
boost::hash_combine(result, hash_value(bbox.ymax()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Circle_2<K>& circle)
{
std::size_t result = hash_value(circle.center());
boost::hash_combine(result, hash_value(circle.squared_radius()));
boost::hash_combine(result, hash_value(circle.orientation()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Iso_rectangle_2<K>& iso_rectangle)
{
std::size_t result = hash_value(iso_rectangle.min());
boost::hash_combine(result, hash_value(iso_rectangle.max()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Point_2<K>& point)
{
std::size_t result = hash_value(point.x());
boost::hash_combine(result, hash_value(point.y()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Segment_2<K>& segment)
{
std::size_t result = hash_value(segment.source());
boost::hash_combine(result, hash_value(segment.target()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Vector_2<K>& vector)
{
std::size_t result = hash_value(vector.x());
boost::hash_combine(result, hash_value(vector.y()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Weighted_point_2<K>& weighed_point)
{
std::size_t result = hash_value(weighed_point.point());
boost::hash_combine(result, hash_value(weighed_point.weight()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Aff_transformation_3<K>& transform)
{
std::size_t result = hash_value(transform.cartesian(0,0));
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 4; ++j)
if (!(i == 0 && j == 0))
boost::hash_combine(result, hash_value(transform.cartesian(i,j)));
return result;
}
inline std::size_t
hash_value (const Bbox_3& bbox)
{
std::size_t result = hash_value(bbox.xmin());
boost::hash_combine(result, hash_value(bbox.xmax()));
boost::hash_combine(result, hash_value(bbox.ymin()));
boost::hash_combine(result, hash_value(bbox.ymax()));
boost::hash_combine(result, hash_value(bbox.zmin()));
boost::hash_combine(result, hash_value(bbox.zmax()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Iso_cuboid_3<K>& iso_cuboid)
{
std::size_t result = hash_value(iso_cuboid.min());
boost::hash_combine(result, hash_value(iso_cuboid.max()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Point_3<K>& point)
{
std::size_t result = hash_value(point.x());
boost::hash_combine(result, hash_value(point.y()));
boost::hash_combine(result, hash_value(point.z()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Segment_3<K>& segment)
{
std::size_t result = hash_value(segment.source());
boost::hash_combine(result, hash_value(segment.target()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Sphere_3<K>& sphere)
{
std::size_t result = hash_value(sphere.center());
boost::hash_combine(result, hash_value(sphere.squared_radius()));
boost::hash_combine(result, hash_value(sphere.orientation()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Vector_3<K>& vector)
{
std::size_t result = hash_value(vector.x());
boost::hash_combine(result, hash_value(vector.y()));
boost::hash_combine(result, hash_value(vector.z()));
return result;
}
template <typename K>
inline std::enable_if_t<std::is_same<typename K::Rep_tag, Cartesian_tag>::value, std::size_t>
hash_value (const Weighted_point_3<K>& weighed_point)
{
std::size_t result = hash_value(weighed_point.point());
boost::hash_combine(result, hash_value(weighed_point.weight()));
return result;
}
} //namespace CGAL
// overloads of std::hash used for using std::unordered_[set/map] on CGAL Kernel objects
namespace std
{
template <typename K> struct hash<CGAL::Aff_transformation_2<K> > {
std::size_t operator() (const CGAL::Aff_transformation_2<K>& transform) const {
return CGAL::hash_value<K> (transform);
}
};
template <> struct hash<CGAL::Bbox_2> {
std::size_t operator() (const CGAL::Bbox_2& bbox) const {
return CGAL::hash_value (bbox);
}
};
template <typename K> struct hash<CGAL::Circle_2<K> > {
std::size_t operator() (const CGAL::Circle_2<K>& circle) const {
return CGAL::hash_value<K> (circle);
}
};
template <typename K> struct hash<CGAL::Iso_rectangle_2<K> > {
std::size_t operator() (const CGAL::Iso_rectangle_2<K>& iso_rectangle) const {
return CGAL::hash_value<K> (iso_rectangle);
}
};
template <typename K> struct hash<CGAL::Point_2<K> > {
std::size_t operator() (const CGAL::Point_2<K>& point) const {
return CGAL::hash_value<K> (point);
}
};
template <typename K> struct hash<CGAL::Segment_2<K> > {
std::size_t operator() (const CGAL::Segment_2<K>& segment) const {
return CGAL::hash_value<K> (segment);
}
};
template <typename K> struct hash<CGAL::Vector_2<K> > {
std::size_t operator() (const CGAL::Vector_2<K>& vector) const {
return CGAL::hash_value<K> (vector);
}
};
template <typename K> struct hash<CGAL::Weighted_point_2<K> > {
std::size_t operator() (const CGAL::Weighted_point_2<K>& weighted_point) const {
return CGAL::hash_value<K> (weighted_point);
}
};
template <typename K> struct hash<CGAL::Aff_transformation_3<K> > {
std::size_t operator() (const CGAL::Aff_transformation_3<K>& transform) const {
return CGAL::hash_value<K> (transform);
}
};
template <> struct hash<CGAL::Bbox_3> {
std::size_t operator() (const CGAL::Bbox_3& bbox) const {
return CGAL::hash_value (bbox);
}
};
template <typename K> struct hash<CGAL::Iso_cuboid_3<K> > {
std::size_t operator() (const CGAL::Iso_cuboid_3<K>& iso_cuboid) const {
return CGAL::hash_value<K> (iso_cuboid);
}
};
template <typename K> struct hash<CGAL::Point_3<K> > {
std::size_t operator() (const CGAL::Point_3<K>& point) const {
return CGAL::hash_value<K> (point);
}
};
template <typename K> struct hash<CGAL::Segment_3<K> > {
std::size_t operator() (const CGAL::Segment_3<K>& segment) const {
return CGAL::hash_value<K> (segment);
}
};
template <typename K> struct hash<CGAL::Sphere_3<K> > {
std::size_t operator() (const CGAL::Sphere_3<K>& sphere) const {
return CGAL::hash_value<K> (sphere);
}
};
template <typename K> struct hash<CGAL::Vector_3<K> > {
std::size_t operator() (const CGAL::Vector_3<K>& vector) const {
return CGAL::hash_value<K> (vector);
}
};
template <typename K> struct hash<CGAL::Weighted_point_3<K> > {
std::size_t operator() (const CGAL::Weighted_point_3<K>& weighted_point) const {
return CGAL::hash_value<K> (weighted_point);
}
};
}
#endif // CGAL_KERNEL_HASH_FUNCTIONS_H

View File

@ -0,0 +1,71 @@
#include <unordered_set>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Bbox_2.h>
typedef CGAL::Simple_cartesian<double> SC;
typedef CGAL::Exact_predicates_inexact_constructions_kernel Epick;
template <typename Object>
void test (const Object& obj)
{
std::unordered_set<Object> unordered_set;
unordered_set.insert (obj);
unordered_set.insert (obj);
assert (unordered_set.size() == 1);
}
int main()
{
test (SC::Aff_transformation_2 (1, 2, 3, 4));
test (Epick::Aff_transformation_2 (1, 2, 3, 4));
test (CGAL::Bbox_2 ());
test (SC::Circle_2 (CGAL::ORIGIN, 1));
test (Epick::Circle_2 (CGAL::ORIGIN, 1));
test (SC::Iso_rectangle_2 (1, 2, 3, 4));
test (Epick::Iso_rectangle_2 (1, 2, 3, 4));
test (SC::Point_2 (1, 2));
test (Epick::Point_2 (1, 2));
test (SC::Segment_2 (SC::Point_2 (1, 2), SC::Point_2 (3, 4)));
test (Epick::Segment_2 (Epick::Point_2 (1, 2), Epick::Point_2 (3, 4)));
test (SC::Vector_2 (1, 2));
test (Epick::Vector_2 (1, 2));
test (SC::Weighted_point_2 (SC::Point_2 (1, 2), 3));
test (Epick::Weighted_point_2 (Epick::Point_2 (1, 2), 3));
test (SC::Aff_transformation_3 (1, 2, 3, 4, 5, 6, 7, 8, 9));
test (Epick::Aff_transformation_3 (1, 2, 3, 4, 5, 6, 7, 8, 9));
test (CGAL::Bbox_3 ());
test (SC::Iso_cuboid_3 (1, 2, 3, 4, 5, 6));
test (Epick::Iso_cuboid_3 (1, 2, 3, 4, 5, 6));
test (SC::Point_3 (1, 2, 3));
test (Epick::Point_3 (1, 2, 3));
test (SC::Segment_3 (SC::Point_3 (1, 2, 3), SC::Point_3 (4, 5, 6)));
test (Epick::Segment_3 (Epick::Point_3 (1, 2, 3), Epick::Point_3 (4, 5, 6)));
test (SC::Sphere_3 (CGAL::ORIGIN, 1));
test (Epick::Sphere_3 (CGAL::ORIGIN, 1));
test (SC::Vector_3 (1, 2, 3));
test (Epick::Vector_3 (1, 2, 3));
test (SC::Weighted_point_3 (SC::Point_3 (1, 2, 3), 4));
test (Epick::Weighted_point_3 (Epick::Point_3 (1, 2, 3), 4));
return 0;
}