mirror of https://github.com/CGAL/cgal
Adress Mael's review and partially the one of Pierre
This commit is contained in:
parent
e34868cea3
commit
295ed81e91
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
The concept `FrechetDistanceTraits` defines the requirements of the
|
The concept `FrechetDistanceTraits` defines the requirements of the
|
||||||
first template parameter of the functions `CGAL::is_Frechet_distance_larger()`
|
first template parameter of the functions `CGAL::is_Frechet_distance_larger()`
|
||||||
and `CGAL::approximate_Frechet_distance()`.
|
and `CGAL::bounded_error_Frechet_distance()`.
|
||||||
|
|
||||||
|
|
||||||
\cgalHasModelsBegin
|
\cgalHasModelsBegin
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@ namespace CGAL {
|
||||||
|
|
||||||
This package provides functions for computing the Fréchet distance of polylines in any dimension under the Euclidean metric.
|
This package provides functions for computing the Fréchet distance of polylines in any dimension under the Euclidean metric.
|
||||||
|
|
||||||
\section secFrechetDistanceIntroduction Introdution
|
\section secFrechetDistanceIntroduction Introduction
|
||||||
|
|
||||||
The Fréchet distance is a classical dissimilarity measure between polylines.
|
The Fréchet distance is a classical dissimilarity measure between polylines.
|
||||||
Its advantages over other measures is that it both considers the polylines as continuous objects and takes into account the ordering of the points.
|
Its advantages over other measures are that it both considers the polylines as continuous objects and takes into account the ordering of the points.
|
||||||
Intuitively, the Fréchet distance is commonly explained as follows: Imagine a human walking on one polyline while a dog walks on the other polyline,
|
Intuitively, the Fréchet distance is commonly explained as follows: Imagine a human walking on one polyline while a dog walks on the other polyline,
|
||||||
they are connected by a leash, and they are only allowed to walk forward. The Fréchet distance is the shortest leash length that allows the human
|
they are connected by a leash, and they are only allowed to walk forward. The Fréchet distance is the shortest leash length that allows the human
|
||||||
and the dog to jointly walk from start to end on their respective trajectories.
|
and the dog to jointly walk from start to end on their respective trajectories.
|
||||||
|
|
@ -45,11 +45,10 @@ The traits classes have as template parameter a kernel, model of the concept `Ke
|
||||||
In case the static constant `Has_filtered_predicates` of the kernel is `true`, the functions
|
In case the static constant `Has_filtered_predicates` of the kernel is `true`, the functions
|
||||||
combine interval arithmetic with a fallback to exact computing in case comparisons of intervals
|
combine interval arithmetic with a fallback to exact computing in case comparisons of intervals
|
||||||
are uncertain. This means for `Simple_cartesian<double>` that there are no guarantees, for
|
are uncertain. This means for `Simple_cartesian<double>` that there are no guarantees, for
|
||||||
`Simple_cartesian<Exact_rational>` that all computation is done with exact rationals,
|
`Simple_cartesian<Exact_rational>` that all computations are performed with exact rationals,
|
||||||
and for kernels such as `Exact_predicates_inexact_constructions_kernel`, `Exact_predicates_exact_constructions_kernel`,
|
and for kernels such as `Exact_predicates_inexact_constructions_kernel`, `Exact_predicates_exact_constructions_kernel`,
|
||||||
`Exact_predicates_exact_constructions_kernel_with_sqrt`,
|
`Exact_predicates_exact_constructions_kernel_with_sqrt`,
|
||||||
as well as `Epick_d` and `Epeck_d`, the computation is filtered and hence fast and at
|
as well as `Epick_d` and `Epeck_d`, the computation is filtered and hence both fast and guaranteed to be correct.
|
||||||
the same time guaranteed to be correct.
|
|
||||||
|
|
||||||
|
|
||||||
\section secFrechetDistanceImplementation Implementation
|
\section secFrechetDistanceImplementation Implementation
|
||||||
|
|
@ -92,6 +91,14 @@ polylines in 4-dimensional Euclidean space using `bounded_error_Frechet_distance
|
||||||
\cgalExample{Frechet_distance/Frechet_distance_d.cpp}
|
\cgalExample{Frechet_distance/Frechet_distance_d.cpp}
|
||||||
|
|
||||||
|
|
||||||
|
\subsection subsecFrechetDistanceDSExample Searching Close Curves
|
||||||
|
|
||||||
|
The following example shows how to store many curves in a data structure and to find all curves
|
||||||
|
closed than a distance bound for a query curve.
|
||||||
|
|
||||||
|
\cgalExample{Frechet_distance/Frechet_DS_3.cpp}
|
||||||
|
|
||||||
|
|
||||||
\subsection subsecFrechetDistanceImageCredits Image Credits
|
\subsection subsecFrechetDistanceImageCredits Image Credits
|
||||||
|
|
||||||
The teaser image is a visualization of two data points from the <a href="https://archive.ics.uci.edu/dataset/175/character+trajectories">Character Trajectories</a> data set.
|
The teaser image is a visualization of two data points from the <a href="https://archive.ics.uci.edu/dataset/175/character+trajectories">Character Trajectories</a> data set.
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@
|
||||||
\example Frechet_distance/Frechet_distance_2.cpp
|
\example Frechet_distance/Frechet_distance_2.cpp
|
||||||
\example Frechet_distance/Frechet_distance_3.cpp
|
\example Frechet_distance/Frechet_distance_3.cpp
|
||||||
\example Frechet_distance/Frechet_distance_d.cpp
|
\example Frechet_distance/Frechet_distance_d.cpp
|
||||||
|
\example Frechet_distance/Frechet_DS_3.cpp
|
||||||
*/
|
*/
|
||||||
|
|
@ -87,20 +87,21 @@ namespace internal
|
||||||
* \ingroup PkgFrechetDistanceFunctions
|
* \ingroup PkgFrechetDistanceFunctions
|
||||||
* determines if the Frechet distance between two polylines is larger than a given distance bound.
|
* determines if the Frechet distance between two polylines is larger than a given distance bound.
|
||||||
*
|
*
|
||||||
* \tparam Traits a model of `FrechetDistanceTraits`
|
* \tparam PointRange a model of the concept `RandomAccessContainer` whose value type is a `Point_d`
|
||||||
* \tparam PointRange a model of the concept `RandomAccessContainer` with `Traits::Point_d` as value type
|
|
||||||
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||||
*
|
*
|
||||||
* \param polyline1 the first polyline defined by a sequence of consecutive points
|
* \param polyline1 the first polyline, defined by a sequence of consecutive points
|
||||||
* \param polyline2 the second polyline defined by a sequence of consecutive points
|
* \param polyline2 the second polyline, defined by a sequence of consecutive points
|
||||||
* \param distance_bound the distance to compare against
|
* \param distance_bound the distance to compare against
|
||||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below:
|
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" containing the one listed below:
|
||||||
*
|
*
|
||||||
* \cgalNamedParamsBegin
|
* \cgalNamedParamsBegin
|
||||||
* \cgalParamNBegin{geom_traits}
|
* \cgalParamNBegin{geom_traits}
|
||||||
* \cgalParamDescription{an instance of a geometric traits class}
|
* \cgalParamDescription{an instance of a geometric traits class}
|
||||||
* \cgalParamType{a model of `FrechetDistanceTraits`}
|
* \cgalParamType{a model of `FrechetDistanceTraits`}
|
||||||
* \cgalParamDefault{`Frechet_distance_traits_2`, `Frechet_distance_traits_3`, or`Frechet_distance_traits_d`, depending on the dimension of the point type.}
|
* \cgalParamDefault{`Frechet_distance_traits_2`, `Frechet_distance_traits_3`, or `Frechet_distance_traits_d`, depending on the dimension of the point type
|
||||||
|
* deduced from the point type, using `Kernel_traits`.}
|
||||||
|
* \cgalParamExtra{The input point type (the range's value type) must be equal to the traits' `Point_d` type.
|
||||||
* \cgalParamNEnd
|
* \cgalParamNEnd
|
||||||
* \cgalNamedParamsEnd
|
* \cgalNamedParamsEnd
|
||||||
*
|
*
|
||||||
|
|
@ -144,27 +145,28 @@ bool is_Frechet_distance_larger(const PointRange& polyline1,
|
||||||
* returns an estimate of the Fréchet distance between the two polylines that is at most `error_bound`
|
* returns an estimate of the Fréchet distance between the two polylines that is at most `error_bound`
|
||||||
* away from the actual Fréchet distance between the two polylines.
|
* away from the actual Fréchet distance between the two polylines.
|
||||||
*
|
*
|
||||||
* \tparam Traits a model of `FrechetDistanceTraits`
|
* \tparam PointRange a model of the concept `RandomAccessContainer` whose value type is a `Point_d`.
|
||||||
* \tparam PointRange a model of the concept `RandomAccessContainer` with `Traits::Point_d` as value type.
|
|
||||||
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||||
*
|
*
|
||||||
* \param polyline1 the first polyline defined by a sequence of consecutive points
|
* \param polyline1 the first polyline, defined by a sequence of consecutive points
|
||||||
* \param polyline2 the second polyline defined by a sequence of consecutive points
|
* \param polyline2 the second polyline, defined by a sequence of consecutive points
|
||||||
* \param error_bound a maximum bound by which the Fréchet distance estimate is allowed to deviate from the actual Fréchet distance
|
* \param error_bound a maximum bound by which the Fréchet distance estimate is allowed to deviate from the actual Fréchet distance
|
||||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below:
|
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" containing the one listed below:
|
||||||
*
|
*
|
||||||
* \cgalNamedParamsBegin
|
* \cgalNamedParamsBegin
|
||||||
* \cgalParamNBegin{geom_traits}
|
* \cgalParamNBegin{geom_traits}
|
||||||
* \cgalParamDescription{an instance of a geometric traits class}
|
* \cgalParamDescription{an instance of a geometric traits class}
|
||||||
* \cgalParamType{a model of `FrechetDistanceTraits`}
|
* \cgalParamType{a model of `FrechetDistanceTraits`}
|
||||||
* \cgalParamDefault{`Frechet_distance_traits_2`, `Frechet_distance_traits_3`, or`Frechet_distance_traits_d`, depending on the dimension of the point type.}
|
* \cgalParamDefault{`Frechet_distance_traits_2`, `Frechet_distance_traits_3`, or `Frechet_distance_traits_d`, depending on the dimension of the point type
|
||||||
|
* deduced from the point type, using `Kernel_traits`.}
|
||||||
|
* \cgalParamExtra{The input point type (the range's value type) must be equal to the traits' `Point_d` type.
|
||||||
* \cgalParamNEnd
|
* \cgalParamNEnd
|
||||||
* \cgalNamedParamsEnd
|
* \cgalNamedParamsEnd
|
||||||
*
|
*
|
||||||
* \pre the polylines must not be empty
|
* \pre the polylines must not be empty
|
||||||
*
|
*
|
||||||
* @return an interval enclosing the exact result, the difference between the upper and
|
* @return an interval enclosing the exact Fréchet distance, the difference between the upper and
|
||||||
* the lower bound being less than `error_bound`.
|
* the lower bound being smaller than `error_bound`.
|
||||||
*/
|
*/
|
||||||
template <class PointRange, class NamedParameters = parameters::Default_named_parameters>
|
template <class PointRange, class NamedParameters = parameters::Default_named_parameters>
|
||||||
std::pair<double,double> bounded_error_Frechet_distance(const PointRange& polyline1,
|
std::pair<double,double> bounded_error_Frechet_distance(const PointRange& polyline1,
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace CGAL {
|
||||||
namespace Frechet_distance {
|
namespace Frechet_distance {
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* A data structure to store curves with a function that enables to find those curves which are close to a query curve.
|
* A data structure to store curves with a function that enables to find those curves which are closer than a distance bound to a query curve.
|
||||||
*
|
*
|
||||||
* \tparam Traits a model of `FrechetDistanceTraits`
|
* \tparam Traits a model of `FrechetDistanceTraits`
|
||||||
* \tparam PointRange a model of the concept `RandomAccessContainer` with `Traits::Point_d` as value type.
|
* \tparam PointRange a model of the concept `RandomAccessContainer` with `Traits::Point_d` as value type.
|
||||||
|
|
@ -45,7 +45,13 @@ class Neighbor_search
|
||||||
using PolylineIDs = std::vector<PolylineID>;
|
using PolylineIDs = std::vector<PolylineID>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#ifndef DOXYGEN_RUNNING
|
||||||
Neighbor_search() = default;
|
Neighbor_search() = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DOXYGEN_RUNNING
|
||||||
|
using Point = Traits::Point_d;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*! inserts curves
|
/*! inserts curves
|
||||||
*/
|
*/
|
||||||
|
|
@ -72,7 +78,12 @@ void Neighbor_search<PointRange, Traits>::insert(
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class PointRange, class Traits>
|
template <class PointRange, class Traits>
|
||||||
auto Neighbor_search<PointRange, Traits>::get_close_curves(
|
#ifdef DOXYGEN_RUNNING
|
||||||
|
std::vector<std::size_t>
|
||||||
|
#else
|
||||||
|
auto
|
||||||
|
#endif
|
||||||
|
Neighbor_search<PointRange, Traits>::get_close_curves(
|
||||||
const Polyline& curve, double distance) -> PolylineIDs
|
const Polyline& curve, double distance) -> PolylineIDs
|
||||||
{
|
{
|
||||||
auto result = kd_tree.search(curve, distance);
|
auto result = kd_tree.search(curve, distance);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,10 @@ public:
|
||||||
Construct_cartesian_const_iterator_d construct_cartesian_const_iterator_d_object() const {
|
Construct_cartesian_const_iterator_d construct_cartesian_const_iterator_d_object() const {
|
||||||
return Construct_cartesian_const_iterator_d();
|
return Construct_cartesian_const_iterator_d();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Compare_squared_distance_d construct_compare_squared_distance_d_object() const {
|
||||||
|
return Compare_squared_distance_d();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace CGAL
|
} // end of namespace CGAL
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,10 @@ public:
|
||||||
Construct_cartesian_const_iterator_d construct_cartesian_const_iterator_d_object() const {
|
Construct_cartesian_const_iterator_d construct_cartesian_const_iterator_d_object() const {
|
||||||
return Construct_cartesian_const_iterator_d();
|
return Construct_cartesian_const_iterator_d();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Compare_squared_distance_d construct_compare_squared_distance_d_object() const {
|
||||||
|
return Compare_squared_distance_d();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace CGAL
|
} // end of namespace CGAL
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public:
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
introduces an \em empty bounding box with lower left
|
constructs an \em empty bounding box with lower left
|
||||||
corner point at \f$ (\infty, \infty) \f$
|
corner point at \f$ (\infty, \infty) \f$
|
||||||
and with upper right corner point at
|
and with upper right corner point at
|
||||||
\f$ (-\infty, -\infty) \f$, \f$ \infty \f$ being
|
\f$ (-\infty, -\infty) \f$, \f$ \infty \f$ being
|
||||||
|
|
@ -28,7 +28,7 @@ and with upper right corner point at
|
||||||
*/
|
*/
|
||||||
Bbox_2();
|
Bbox_2();
|
||||||
/*!
|
/*!
|
||||||
introduces a bounding box `b` with lower left corner at
|
constructs a bounding box `b` with lower left corner at
|
||||||
`(xmin, ymin)` and with upper right corner at
|
`(xmin, ymin)` and with upper right corner at
|
||||||
`(xmax, ymax)`.
|
`(xmax, ymax)`.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public:
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
introduces an \em empty bounding box with lower left
|
constructs an \em empty bounding box with lower left
|
||||||
corner point at \f$ (\infty, \infty, \infty) \f$
|
corner point at \f$ (\infty, \infty, \infty) \f$
|
||||||
and with upper right corner point at
|
and with upper right corner point at
|
||||||
\f$ (-\infty, -\infty, -\infty) \f$, \f$ \infty \f$ being
|
\f$ (-\infty, -\infty, -\infty) \f$, \f$ \infty \f$ being
|
||||||
|
|
@ -29,7 +29,7 @@ and with upper right corner point at
|
||||||
Bbox_3();
|
Bbox_3();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
introduces a bounding box `b` with lexicographically
|
constructs a bounding box `b` with lexicographically
|
||||||
smallest corner point at `(xmin, ymin, zmin)`
|
smallest corner point at `(xmin, ymin, zmin)`
|
||||||
and lexicographically largest corner point at
|
and lexicographically largest corner point at
|
||||||
`(xmax, ymax, zmax)`.
|
`(xmax, ymax, zmax)`.
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ namespace CGAL {
|
||||||
/*!
|
/*!
|
||||||
\ingroup PkgKernelDKernelObjs
|
\ingroup PkgKernelDKernelObjs
|
||||||
|
|
||||||
An object `b` of the class `Bbox_d` is a bounding
|
An object `b` of the class `Bbox_d` is a bounding box in the d-dimensional Euclidean space \f$ \E^d\f$.
|
||||||
box in the d-dimensional Euclidean plane \f$ \E^d\f$. This class is templated with a dimension tag.
|
|
||||||
|
\tparam DimensionTag must be an instance of `CGAL::Dimension_tag`.
|
||||||
|
|
||||||
\cgalModels{Hashable}
|
\cgalModels{Hashable}
|
||||||
|
|
||||||
|
|
@ -18,11 +19,19 @@ template <typename DimensionTag>
|
||||||
class Bbox_d {
|
class Bbox_d {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
/// \name Types
|
||||||
|
/// @{
|
||||||
|
/// An iterator for enumerating the %Cartesian coordinates of the point with the lexicographically
|
||||||
|
//// smallest coordinates followed by those of the point with the lexicographically largest coordinates.
|
||||||
|
typedef unspecified_type Cartesian_const_iterator;
|
||||||
|
/// @}
|
||||||
|
|
||||||
/// \name Creation
|
/// \name Creation
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
introduces an \em empty bounding box with lower left
|
constructs an \em empty bounding box with lower left
|
||||||
corner coordinates at \f$ \infty \f$
|
corner coordinates at \f$ \infty \f$
|
||||||
and with upper right corner coordinates at
|
and with upper right corner coordinates at
|
||||||
\f$ -\infty \f$, \f$ \infty \f$ being
|
\f$ -\infty \f$, \f$ \infty \f$ being
|
||||||
|
|
@ -31,21 +40,21 @@ and with upper right corner coordinates at
|
||||||
Bbox_d();
|
Bbox_d();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
introduces a d-dimensional bounding box from a 2d bounding box.
|
constructs a d-dimensional bounding box from a 2D bounding box.
|
||||||
\pre the dimension must be 2D
|
\pre the dimension must be 2D
|
||||||
*/
|
*/
|
||||||
Bbox_d(const Bbox_2& b);
|
Bbox_d(const Bbox_2& b);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
introduces a d-dimensional bounding box from a range.
|
constructs a d-dimensional bounding box from a range of coordinates
|
||||||
\pre the range must have the size of the dimension.
|
\pre the range must have the size of the dimension.
|
||||||
\tparam I an iterator model of `InputIterator` with value type double
|
\tparam InputIterator an input iterator with value type `double`
|
||||||
*/
|
*/
|
||||||
template <typename I>
|
template <typename InputIterator>
|
||||||
Bbox_d(int d, I b, I e);
|
Bbox_d(int d, InputIterator b, InputIterator e);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
introduces a d-dimensional bounding box from a 3d bounding box.
|
constructs a d-dimensional bounding box from a 3D bounding box.
|
||||||
\pre the dimension must be 3D
|
\pre the dimension must be 3D
|
||||||
*/
|
*/
|
||||||
Bbox_d(const Bbox_3& b);
|
Bbox_d(const Bbox_3& b);
|
||||||
|
|
@ -56,7 +65,7 @@ Bbox_d(const Bbox_3& b);
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
ests for equality.
|
tests for equality.
|
||||||
*/
|
*/
|
||||||
bool operator==(const Bbox_d &c) const;
|
bool operator==(const Bbox_d &c) const;
|
||||||
|
|
||||||
|
|
@ -72,15 +81,26 @@ int dimension() const;
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
returns an iterator for the %Cartesian coordinates of the lower left and the upper right corner.
|
returns the `i`-th %Cartesian coordinate of the "lower left" corner.
|
||||||
|
*/
|
||||||
|
double min(int i) const;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns the `i`-th %Cartesian coordinate of the "upper right" corner.
|
||||||
|
*/
|
||||||
|
double max(int i) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
returns an iterator for the %Cartesian coordinates of the "lower left" and the "upper right" corner.
|
||||||
*/
|
*/
|
||||||
Cartesian_const_iterator cartesian_begin() const;
|
Cartesian_const_iterator cartesian_begin() const;
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
returns the past-the-end iterator for the %Cartesian coordinates of the lower left and the upper right corner.
|
returns the past-the-end iterator for the %Cartesian coordinates of the "lower left" and the "upper right" corner.
|
||||||
*/
|
*/
|
||||||
Cartesian_const_iterator cartesian_begin() const;
|
Cartesian_const_iterator cartesian_end() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
returns a bounding box of `b` and `c`.
|
returns a bounding box of `b` and `c`.
|
||||||
|
|
@ -117,7 +137,7 @@ intersection is non-empty.
|
||||||
\relates Bbox_d
|
\relates Bbox_d
|
||||||
*/
|
*/
|
||||||
template <typename DimensionTag>
|
template <typename DimensionTag>
|
||||||
bool do_overlap(const Bbox_d &bb1, const Bbox_d &bb2);
|
bool do_overlap(const Bbox_d<DimensionTag> &bb1, const Bbox_d<DimensionTag> &bb2);
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue