Merge 0e66dd52a7 into dbcace69e6
|
|
@ -26,6 +26,9 @@
|
|||
#include <CGAL/AABB_tree/internal/Is_ray_intersection_geomtraits.h>
|
||||
#include <CGAL/AABB_tree/internal/Primitive_helper.h>
|
||||
#include <CGAL/AABB_tree/internal/Remove_optional.h>
|
||||
#include <CGAL/Filtered_predicate.h>
|
||||
#include <CGAL/Filtered_kernel/internal/Static_filters/Static_filter_error.h>
|
||||
#include <CGAL/Filtered_kernel/internal/Static_filters/tools.h>
|
||||
#include <CGAL/Kernel_23/internal/Has_boolean_tags.h>
|
||||
#include <CGAL/Search_traits_2.h>
|
||||
#include <optional>
|
||||
|
|
@ -120,6 +123,258 @@ public:
|
|||
Intersection_distance intersection_distance_object() const { return Intersection_distance(); }
|
||||
};
|
||||
|
||||
template<typename GeomTraits>
|
||||
class Compare_distance_2 {
|
||||
typedef typename GeomTraits::Point_2 Point;
|
||||
typedef typename GeomTraits::FT FT;
|
||||
|
||||
/// Bounding box type.
|
||||
typedef typename CGAL::Bbox_2 Bounding_box;
|
||||
public:
|
||||
CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound) const
|
||||
{
|
||||
return do_intersect_circle_iso_rectangle_2
|
||||
(GeomTraits().construct_circle_2_object()
|
||||
(p, GeomTraits().compute_squared_distance_2_object()(p, bound)), bb) ?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const
|
||||
{
|
||||
return GeomTraits().do_intersect_2_object()
|
||||
(GeomTraits().construct_circle_2_object()
|
||||
(p, GeomTraits().compute_squared_distance_2_object()(p, bound)), pr) ?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const
|
||||
{
|
||||
return GeomTraits().do_intersect_2_object()
|
||||
(GeomTraits().construct_circle_2_object()(p, sq_distance),
|
||||
pr) ?
|
||||
CGAL::SMALLER :
|
||||
CGAL::LARGER;
|
||||
}
|
||||
|
||||
typename GeomTraits::Boolean do_intersect_circle_iso_rectangle_2(const typename GeomTraits::Circle_2& circle,
|
||||
const typename GeomTraits::Iso_rectangle_2& rec) const
|
||||
{
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Point_2 Point;
|
||||
|
||||
Point center = circle.center();
|
||||
|
||||
// Check that the minimum distance to the box is smaller than the radius, otherwise there is
|
||||
// no intersection. `distance` stays at 0 if the center is inside or on `rec`.
|
||||
FT distance = FT(0);
|
||||
if (center.x() < rec.xmin())
|
||||
{
|
||||
FT d = rec.xmin() - center.x();
|
||||
distance += d * d;
|
||||
}
|
||||
else if (center.x() > rec.xmax())
|
||||
{
|
||||
FT d = center.x() - rec.xmax();
|
||||
distance += d * d;
|
||||
}
|
||||
|
||||
if (center.y() < rec.ymin())
|
||||
{
|
||||
FT d = rec.ymin() - center.y();
|
||||
distance += d * d;
|
||||
}
|
||||
else if (center.y() > rec.ymax())
|
||||
{
|
||||
FT d = center.y() - rec.ymax();
|
||||
distance += d * d;
|
||||
}
|
||||
|
||||
if (distance <= circle.squared_radius())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename GeomTraits,
|
||||
bool Has_filtered_predicates = internal::Has_filtered_predicates<GeomTraits>::value,
|
||||
bool Has_static_filters = internal::Has_static_filters<GeomTraits>::value>
|
||||
class Compare_distance_getter_2 {};
|
||||
|
||||
template <typename GeomTraits>
|
||||
class Compare_distance_getter_2<GeomTraits, false, false> {
|
||||
// this class is in charge of checking what K provides (i.e., can we use filtered predicates, can we use statically filtered predicates, etc.)
|
||||
// depending on that it defines
|
||||
public:
|
||||
typedef Compare_distance_2<GeomTraits> type;
|
||||
static Compare_distance_2<GeomTraits> compare_distance_object() {
|
||||
return Compare_distance_2<GeomTraits>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename GeomTraits>
|
||||
class Compare_distance_getter_2<GeomTraits, true, false> {
|
||||
// this class is in charge of checking what K provides (i.e., can we use filtered predicates, can we use statically filtered predicates, etc.)
|
||||
// depending on that it defines
|
||||
|
||||
typedef GeomTraits Kernel;
|
||||
|
||||
typedef typename Kernel::Exact_kernel EKernel;
|
||||
typedef typename Kernel::Approximate_kernel AKernel;
|
||||
typedef typename Kernel::C2E C2E;
|
||||
typedef typename Kernel::C2F C2F;
|
||||
|
||||
typedef Compare_distance_2<EKernel> Exact_functor;
|
||||
typedef Compare_distance_2<AKernel> Filtered_functor;
|
||||
|
||||
public:
|
||||
typedef Filtered_predicate<Exact_functor, Filtered_functor,
|
||||
C2E, C2F> Compare_distance_pred;
|
||||
typedef Compare_distance_pred type;
|
||||
|
||||
static Compare_distance_pred compare_distance_object() {
|
||||
return Compare_distance_pred(Exact_functor(), Filtered_functor());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename GeomTraits>
|
||||
class Compare_distance_getter_2<GeomTraits, true, true> {
|
||||
// this class is in charge of checking what K provides (i.e., can we use filtered predicates, can we use statically filtered predicates, etc.)
|
||||
// depending on that it defines
|
||||
class Statically_filtered_compare_distance {
|
||||
public:
|
||||
typedef typename GeomTraits::Point_2 Point;
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Circle_2 Circle_2;
|
||||
|
||||
/// Bounding box type.
|
||||
typedef CGAL::Bbox_2 Bounding_box;
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const {
|
||||
return Compare_distance_getter_2<GeomTraits, true, false>::compare_distance_object()(p, pr, bound);
|
||||
}
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const {
|
||||
return Compare_distance_getter_2<GeomTraits, true, false>::compare_distance_object()(p, pr, sq_distance);
|
||||
}
|
||||
|
||||
Comparison_result operator()(const Point& p, const Bounding_box& b, const Point& bound) const {
|
||||
Circle_2 s = GeomTraits().construct_circle_2_object()(p, GeomTraits().compute_squared_distance_2_object()(p, bound));
|
||||
|
||||
CGAL_BRANCH_PROFILER_3(std::string("semi-static failures/attempts/calls to : ") +
|
||||
std::string(CGAL_PRETTY_FUNCTION), tmp);
|
||||
|
||||
internal::Static_filters_predicates::Get_approx<Point> get_approx; // Identity functor for all points
|
||||
const Point& c = s.center();
|
||||
|
||||
double scx, scy, ssr;
|
||||
double bxmin = b.xmin(), bymin = b.ymin(),
|
||||
bxmax = b.xmax(), bymax = b.ymax();
|
||||
|
||||
if (internal::fit_in_double(get_approx(c).x(), scx) &&
|
||||
internal::fit_in_double(get_approx(c).y(), scy) &&
|
||||
internal::fit_in_double(s.squared_radius(), ssr))
|
||||
{
|
||||
CGAL_BRANCH_PROFILER_BRANCH_1(tmp);
|
||||
|
||||
if ((ssr < 1.11261183279326254436e-293) || (ssr > 2.80889552322236673473e+306)) {
|
||||
CGAL_BRANCH_PROFILER_BRANCH_2(tmp);
|
||||
return Compare_distance_getter_2<GeomTraits, true, false>::compare_distance_object()(p, b, bound);
|
||||
}
|
||||
double distance = 0;
|
||||
double max1 = 0;
|
||||
double double_tmp_result = 0;
|
||||
double eps = 0;
|
||||
if (scx < bxmin)
|
||||
{
|
||||
double bxmin_scx = bxmin - scx;
|
||||
max1 = bxmin_scx;
|
||||
|
||||
distance = square(bxmin_scx);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if ((max1 < 3.33558365626356687717e-147) || (max1 > 1.67597599124282407923e+153))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
else if (scx > bxmax)
|
||||
{
|
||||
double scx_bxmax = scx - bxmax;
|
||||
max1 = scx_bxmax;
|
||||
|
||||
distance = square(scx_bxmax);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if ((max1 < 3.33558365626356687717e-147) || (max1 > 1.67597599124282407923e+153))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
|
||||
if (scy < bymin)
|
||||
{
|
||||
double bymin_scy = bymin - scy;
|
||||
if (max1 < bymin_scy) {
|
||||
max1 = bymin_scy;
|
||||
}
|
||||
|
||||
distance += square(bymin_scy);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if ((max1 < 3.33558365626356687717e-147) || ((max1 > 1.67597599124282407923e+153)))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps) {
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
}
|
||||
else if (scy > bymax)
|
||||
{
|
||||
double scy_bymax = scy - bymax;
|
||||
if (max1 < scy_bymax) {
|
||||
max1 = scy_bymax;
|
||||
}
|
||||
distance += square(scy_bymax);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if (((max1 < 3.33558365626356687717e-147)) || ((max1 > 1.67597599124282407923e+153)))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
|
||||
// double_tmp_result and eps were growing all the time
|
||||
// no need to test for > eps as done earlier in at least one case
|
||||
|
||||
return CGAL::SMALLER;
|
||||
}
|
||||
return Compare_distance_getter_2<GeomTraits, true, false>::compare_distance_object()(p, b, bound);
|
||||
}
|
||||
};
|
||||
public:
|
||||
typedef Statically_filtered_compare_distance type;
|
||||
|
||||
static Statically_filtered_compare_distance compare_distance_object() {
|
||||
return Statically_filtered_compare_distance();
|
||||
}
|
||||
};
|
||||
|
||||
} } //end of namespace internal::AABB_tree
|
||||
|
||||
/// \addtogroup PkgAABBTreeRef
|
||||
|
|
@ -129,35 +384,6 @@ public:
|
|||
template< typename AABBTraits>
|
||||
class AABB_tree;
|
||||
|
||||
|
||||
/// This traits class handles any type of 2D geometric
|
||||
/// primitives provided that the proper intersection tests and
|
||||
/// constructions are implemented. It handles points, rays, lines and
|
||||
/// segments as query types for intersection detection and
|
||||
/// computations, and it handles points as query type for distance
|
||||
/// queries.
|
||||
///
|
||||
/// \cgalModels{AABBTraits,AABBRayIntersectionTraits}
|
||||
///
|
||||
/// \tparam GeomTraits must be a model of the concept \ref AABBGeomTraits_2,
|
||||
/// and provide the geometric types as well as the intersection tests and computations.
|
||||
/// \tparam Primitive provide the type of primitives stored in the AABB_tree.
|
||||
/// It is a model of the concept `AABBPrimitive` or `AABBPrimitiveWithSharedData`.
|
||||
///
|
||||
/// \tparam BboxMap must be a model of `ReadablePropertyMap` that has as key type a primitive id,
|
||||
/// and as value type a `Bounding_box`.
|
||||
/// If the type is `Default` the `Datum` must have the
|
||||
/// member function `bbox()` that returns the bounding box of the primitive.
|
||||
///
|
||||
/// If the argument `GeomTraits` is a model of the concept \ref
|
||||
/// AABBRayIntersectionGeomTraits_2, this class is also a model of \ref
|
||||
/// AABBRayIntersectionTraits.
|
||||
///
|
||||
/// \sa `AABBTraits`
|
||||
/// \sa `AABB_tree`
|
||||
/// \sa `AABBPrimitive`
|
||||
/// \sa `AABBPrimitiveWithSharedData`
|
||||
|
||||
template<typename GeomTraits, typename AABBPrimitive, typename BboxMap = Default>
|
||||
class AABB_traits_2
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
|
|
@ -167,8 +393,9 @@ class AABB_traits_2
|
|||
#endif
|
||||
{
|
||||
typedef typename CGAL::Object Object;
|
||||
typedef GeomTraits Geom_traits;
|
||||
|
||||
public:
|
||||
typedef GeomTraits Geom_traits;
|
||||
|
||||
typedef AABB_traits_2<GeomTraits, AABBPrimitive, BboxMap> AT;
|
||||
// AABBTraits concept types
|
||||
|
|
@ -384,98 +611,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// This should go down to the GeomTraits, i.e. the kernel
|
||||
// and the internal implementation should change its name from
|
||||
// do_intersect to something like does_contain (this is what we compute,
|
||||
// this is not the same do_intersect as the spherical kernel)
|
||||
class Compare_distance {
|
||||
typedef typename AT::Point Point;
|
||||
typedef typename AT::FT FT;
|
||||
typedef typename AT::Primitive Primitive;
|
||||
public:
|
||||
CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_true) const
|
||||
{
|
||||
return do_intersect_circle_iso_rectangle_2
|
||||
(GeomTraits().construct_circle_2_object()
|
||||
(p, GeomTraits().compute_squared_distance_2_object()(p, bound)), bb)?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
typedef typename internal::AABB_tree::Compare_distance_getter_2<GeomTraits>::type Compare_distance;
|
||||
|
||||
CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_false) const
|
||||
{
|
||||
return do_intersect_circle_iso_rectangle_2
|
||||
(GeomTraits().construct_circle_2_object()
|
||||
(p, GeomTraits().compute_squared_distance_2_object()(p, bound)), bb)?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound) const
|
||||
{
|
||||
return (*this)(p, bb, bound, Boolean_tag<internal::Has_static_filters<GeomTraits>::value>());
|
||||
}
|
||||
|
||||
// The following functions seem unused...?
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const
|
||||
{
|
||||
return GeomTraits().do_intersect_2_object()
|
||||
(GeomTraits().construct_circle_2_object()
|
||||
(p, GeomTraits().compute_squared_distance_2_object()(p, bound)), pr)?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const
|
||||
{
|
||||
return GeomTraits().do_intersect_2_object()
|
||||
(GeomTraits().construct_circle_2_object()(p, sq_distance),
|
||||
pr) ?
|
||||
CGAL::SMALLER :
|
||||
CGAL::LARGER;
|
||||
}
|
||||
|
||||
typename GeomTraits::Boolean do_intersect_circle_iso_rectangle_2(const typename GeomTraits::Circle_2& circle,
|
||||
const typename GeomTraits::Iso_rectangle_2& rec) const
|
||||
{
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Point_2 Point;
|
||||
|
||||
Point center = circle.center();
|
||||
|
||||
// Check that the minimum distance to the box is smaller than the radius, otherwise there is
|
||||
// no intersection. `distance` stays at 0 if the center is inside or on `rec`.
|
||||
FT distance = FT(0);
|
||||
if (center.x() < rec.xmin())
|
||||
{
|
||||
FT d = rec.xmin() - center.x();
|
||||
distance += d * d;
|
||||
}
|
||||
else if (center.x() > rec.xmax())
|
||||
{
|
||||
FT d = center.x() - rec.xmax();
|
||||
distance += d * d;
|
||||
}
|
||||
|
||||
if (center.y() < rec.ymin())
|
||||
{
|
||||
FT d = rec.ymin() - center.y();
|
||||
distance += d * d;
|
||||
}
|
||||
else if (center.y() > rec.ymax())
|
||||
{
|
||||
FT d = center.y() - rec.ymax();
|
||||
distance += d * d;
|
||||
}
|
||||
|
||||
if (distance <= circle.squared_radius())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Closest_point closest_point_object() const {return Closest_point(*this);}
|
||||
Compare_distance compare_distance_object() const {return Compare_distance();}
|
||||
Closest_point closest_point_object() const { return Closest_point(*this); }
|
||||
Compare_distance compare_distance_object() const { return internal::AABB_tree::Compare_distance_getter_2<GeomTraits>::compare_distance_object(); }
|
||||
|
||||
typedef enum { CGAL_AXIS_X = 0,
|
||||
CGAL_AXIS_Y = 1} Axis;
|
||||
|
|
@ -519,7 +658,7 @@ private:
|
|||
//-------------------------------------------------------
|
||||
template<typename GT, typename P, typename B>
|
||||
typename AABB_traits_2<GT,P,B>::Axis
|
||||
AABB_traits_2<GT,P,B>::longest_axis(const Bounding_box& bbox)
|
||||
AABB_traits_2<GT,P,B>::longest_axis(const Bounding_box& bbox)
|
||||
{
|
||||
const double dx = bbox.xmax() - bbox.xmin();
|
||||
const double dy = bbox.ymax() - bbox.ymin();
|
||||
|
|
@ -534,9 +673,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
} // end namespace CGAL
|
||||
} // end namespace CGAL
|
||||
|
||||
#include <CGAL/enable_warnings.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@
|
|||
#include <CGAL/AABB_tree/internal/Is_ray_intersection_geomtraits.h>
|
||||
#include <CGAL/AABB_tree/internal/Primitive_helper.h>
|
||||
#include <CGAL/AABB_tree/internal/Remove_optional.h>
|
||||
#include <CGAL/Filtered_predicate.h>
|
||||
#include <CGAL/Filtered_kernel/internal/Static_filters/Static_filter_error.h>
|
||||
#include <CGAL/Filtered_kernel/internal/Static_filters/tools.h>
|
||||
#include <CGAL/Kernel_23/internal/Has_boolean_tags.h>
|
||||
#include <CGAL/Search_traits_3.h>
|
||||
|
||||
|
|
@ -124,6 +127,346 @@ public:
|
|||
Intersection_distance intersection_distance_object() const { return Intersection_distance(); }
|
||||
};
|
||||
|
||||
template<typename GeomTraits>
|
||||
class Compare_distance_3 {
|
||||
typedef typename GeomTraits::Point_3 Point;
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Boolean Boolean;
|
||||
|
||||
/// Bounding box type.
|
||||
typedef typename CGAL::Bbox_3 Bounding_box;
|
||||
public:
|
||||
CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound) const
|
||||
{
|
||||
return do_intersect_sphere_iso_cuboid_3
|
||||
(GeomTraits().construct_sphere_3_object()
|
||||
(p, GeomTraits().compute_squared_distance_3_object()(p, bound)), bb) ?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const
|
||||
{
|
||||
return GeomTraits().do_intersect_3_object()
|
||||
(GeomTraits().construct_sphere_3_object()
|
||||
(p, GeomTraits().compute_squared_distance_3_object()(p, bound)), pr) ?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const
|
||||
{
|
||||
return GeomTraits().do_intersect_3_object()
|
||||
(GeomTraits().construct_sphere_3_object()(p, sq_distance),
|
||||
pr) ?
|
||||
CGAL::SMALLER :
|
||||
CGAL::LARGER;
|
||||
}
|
||||
|
||||
Boolean do_intersect_sphere_iso_cuboid_3(const typename GeomTraits::Sphere_3& sphere,
|
||||
const Bounding_box& box) const
|
||||
{
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Point_3 Point;
|
||||
|
||||
const FT bxmin = box.xmin();
|
||||
const FT bymin = box.ymin();
|
||||
const FT bzmin = box.zmin();
|
||||
const FT bxmax = box.xmax();
|
||||
const FT bymax = box.ymax();
|
||||
const FT bzmax = box.zmax();
|
||||
|
||||
// Check that the minimum distance to the box is smaller than the radius, otherwise there is
|
||||
// no intersection. `distance` stays at 0 if the center is inside or on `rec`.
|
||||
|
||||
FT d = FT(0);
|
||||
FT distance = FT(0);
|
||||
const FT sr = typename GeomTraits::Compute_squared_radius_3()(sphere);
|
||||
|
||||
const Point center = typename GeomTraits::Construct_center_3()(sphere);
|
||||
typename GeomTraits::Cartesian_const_iterator_3 cci = typename GeomTraits::Construct_cartesian_const_iterator_3()(center);
|
||||
|
||||
if (*cci < bxmin)
|
||||
{
|
||||
d = bxmin - *cci;
|
||||
d = square(d);
|
||||
if (d > sr)
|
||||
return false;
|
||||
|
||||
distance = d;
|
||||
}
|
||||
else if (*cci > bxmax)
|
||||
{
|
||||
d = *cci - bxmax;
|
||||
d = square(d);
|
||||
if (d > sr)
|
||||
return false;
|
||||
|
||||
distance = d;
|
||||
}
|
||||
|
||||
cci++;
|
||||
if (*cci < bymin)
|
||||
{
|
||||
d = bymin - *cci;
|
||||
d = square(d);
|
||||
if (d > sr)
|
||||
return false;
|
||||
|
||||
distance += d;
|
||||
}
|
||||
else if (*cci > bymax)
|
||||
{
|
||||
d = *cci - bymax;
|
||||
d = square(d);
|
||||
if (d > sr)
|
||||
return false;
|
||||
|
||||
distance += d;
|
||||
}
|
||||
|
||||
cci++;
|
||||
if (*cci < bzmin)
|
||||
{
|
||||
d = bzmin - *cci;
|
||||
d = square(d);
|
||||
if (d > sr)
|
||||
return false;
|
||||
|
||||
distance += d;
|
||||
}
|
||||
else if (*cci > bzmax)
|
||||
{
|
||||
d = *cci - bzmax;
|
||||
d = square(d);
|
||||
if (d > sr)
|
||||
return false;
|
||||
|
||||
distance += d;
|
||||
}
|
||||
// Note that with the way the distance above is computed, the distance is '0' if the box strictly
|
||||
// contains the sphere. But since we use '>', we don't exit
|
||||
return (distance <= sr);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename GeomTraits, bool Has_filtered_predicates = internal::Has_filtered_predicates<GeomTraits>::value, bool Has_static_filters = internal::Has_static_filters<GeomTraits>::value>
|
||||
class Compare_distance_getter_3 {};
|
||||
|
||||
template <typename GeomTraits>
|
||||
class Compare_distance_getter_3<GeomTraits, false, false> {
|
||||
// this class is in charge of checking what K provides (i.e., can we use filtered predicates, can we use statically filtered predicates, etc.)
|
||||
// depending on that it defines
|
||||
public:
|
||||
typedef Compare_distance_3<GeomTraits> type;
|
||||
static Compare_distance_3<GeomTraits> compare_distance_object() {
|
||||
return Compare_distance_3<GeomTraits>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename GeomTraits>
|
||||
class Compare_distance_getter_3<GeomTraits, true, false> {
|
||||
// this class is in charge of checking what K provides (i.e., can we use filtered predicates, can we use statically filtered predicates, etc.)
|
||||
// depending on that it defines
|
||||
|
||||
typedef GeomTraits Kernel;
|
||||
|
||||
typedef typename Kernel::Exact_kernel EKernel;
|
||||
typedef typename Kernel::Approximate_kernel AKernel;
|
||||
typedef typename Kernel::C2E C2E;
|
||||
typedef typename Kernel::C2F C2F;
|
||||
|
||||
typedef Compare_distance_3<EKernel> Exact_functor;
|
||||
typedef Compare_distance_3<AKernel> Filtered_functor;
|
||||
|
||||
public:
|
||||
typedef Filtered_predicate<Exact_functor, Filtered_functor,
|
||||
C2E, C2F> Compare_distance_pred;
|
||||
typedef Compare_distance_pred type;
|
||||
|
||||
static Compare_distance_pred compare_distance_object() {
|
||||
return Compare_distance_pred(Exact_functor(), Filtered_functor());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename GeomTraits>
|
||||
class Compare_distance_getter_3<GeomTraits, true, true> {
|
||||
// this class is in charge of checking what K provides (i.e., can we use filtered predicates, can we use statically filtered predicates, etc.)
|
||||
// depending on that it defines
|
||||
class Statically_filtered_compare_distance {
|
||||
public:
|
||||
typedef typename GeomTraits::Point_3 Point;
|
||||
typedef typename GeomTraits::FT FT;
|
||||
typedef typename GeomTraits::Boolean Boolean;
|
||||
|
||||
/// Bounding box type.
|
||||
typedef CGAL::Bbox_3 Bounding_box;
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const {
|
||||
return Compare_distance_getter_3<GeomTraits, true, false>::compare_distance_object()(p, pr, bound);
|
||||
}
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const {
|
||||
return Compare_distance_getter_3<GeomTraits, true, false>::compare_distance_object()(p, pr, sq_distance);
|
||||
}
|
||||
|
||||
// This static filter was introduced by https://github.com/CGAL/cgal/pull/5507 .
|
||||
// In case of uncertainty, it avoids exact arithmetic and indicates an intersection.
|
||||
// This is acceptable for search data structures like AABB trees as the actual elements
|
||||
// will be tested later.
|
||||
Comparison_result operator()(const Point& p, const Bounding_box& b, const Point& bound) const {
|
||||
Sphere_3 s = GeomTraits().construct_sphere_3_object()(p, GeomTraits().compute_squared_distance_3_object()(p, bound));
|
||||
CGAL_BRANCH_PROFILER_3(std::string("semi-static failures/attempts/calls to : ") +
|
||||
std::string(CGAL_PRETTY_FUNCTION), tmp);
|
||||
|
||||
internal::Static_filters_predicates::Get_approx<Point> get_approx; // Identity functor for all points
|
||||
const Point& c = s.center();
|
||||
|
||||
double scx, scy, scz, ssr;
|
||||
double bxmin = b.xmin(), bymin = b.ymin(), bzmin = b.zmin(),
|
||||
bxmax = b.xmax(), bymax = b.ymax(), bzmax = b.zmax();
|
||||
|
||||
if (internal::fit_in_double(get_approx(c).x(), scx) &&
|
||||
internal::fit_in_double(get_approx(c).y(), scy) &&
|
||||
internal::fit_in_double(get_approx(c).z(), scz) &&
|
||||
internal::fit_in_double(s.squared_radius(), ssr))
|
||||
{
|
||||
CGAL_BRANCH_PROFILER_BRANCH_1(tmp);
|
||||
|
||||
if ((ssr < 1.11261183279326254436e-293) || (ssr > 2.80889552322236673473e+306)) {
|
||||
CGAL_BRANCH_PROFILER_BRANCH_2(tmp);
|
||||
return Compare_distance_getter_3<GeomTraits, true, false>::compare_distance_object()(p, b, bound);
|
||||
}
|
||||
double distance = 0;
|
||||
double max1 = 0;
|
||||
double double_tmp_result = 0;
|
||||
double eps = 0;
|
||||
if (scx < bxmin)
|
||||
{
|
||||
double bxmin_scx = bxmin - scx;
|
||||
max1 = bxmin_scx;
|
||||
|
||||
distance = square(bxmin_scx);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if ((max1 < 3.33558365626356687717e-147) || (max1 > 1.67597599124282407923e+153))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
else if (scx > bxmax)
|
||||
{
|
||||
double scx_bxmax = scx - bxmax;
|
||||
max1 = scx_bxmax;
|
||||
|
||||
distance = square(scx_bxmax);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if ((max1 < 3.33558365626356687717e-147) || (max1 > 1.67597599124282407923e+153))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
|
||||
|
||||
if (scy < bymin)
|
||||
{
|
||||
double bymin_scy = bymin - scy;
|
||||
if (max1 < bymin_scy) {
|
||||
max1 = bymin_scy;
|
||||
}
|
||||
|
||||
distance += square(bymin_scy);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if ((max1 < 3.33558365626356687717e-147) || ((max1 > 1.67597599124282407923e+153)))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
else if (scy > bymax)
|
||||
{
|
||||
double scy_bymax = scy - bymax;
|
||||
if (max1 < scy_bymax) {
|
||||
max1 = scy_bymax;
|
||||
}
|
||||
distance += square(scy_bymax);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if (((max1 < 3.33558365626356687717e-147)) || ((max1 > 1.67597599124282407923e+153)))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
|
||||
|
||||
if (scz < bzmin)
|
||||
{
|
||||
double bzmin_scz = bzmin - scz;
|
||||
if (max1 < bzmin_scz) {
|
||||
max1 = bzmin_scz;
|
||||
}
|
||||
distance += square(bzmin_scz);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if (((max1 < 3.33558365626356687717e-147)) || ((max1 > 1.67597599124282407923e+153)))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
else if (scz > bzmax)
|
||||
{
|
||||
double scz_bzmax = scz - bzmax;
|
||||
if (max1 < scz_bzmax) {
|
||||
max1 = scz_bzmax;
|
||||
}
|
||||
|
||||
distance += square(scz_bzmax);
|
||||
double_tmp_result = (distance - ssr);
|
||||
|
||||
if (((max1 < 3.33558365626356687717e-147)) || ((max1 > 1.67597599124282407923e+153)))
|
||||
return CGAL::SMALLER;
|
||||
|
||||
eps = 1.99986535548615598560e-15 * (std::max)(ssr, square(max1));
|
||||
|
||||
if (double_tmp_result > eps)
|
||||
return CGAL::LARGER;
|
||||
}
|
||||
|
||||
// double_tmp_result and eps were growing all the time
|
||||
// no need to test for > eps as done earlier in at least one case
|
||||
return CGAL::SMALLER;
|
||||
|
||||
CGAL_BRANCH_PROFILER_BRANCH_2(tmp);
|
||||
}
|
||||
return Compare_distance_getter_3<GeomTraits, true, false>::compare_distance_object()(p, b, bound);
|
||||
}
|
||||
};
|
||||
public:
|
||||
typedef Statically_filtered_compare_distance type;
|
||||
|
||||
static Statically_filtered_compare_distance compare_distance_object() {
|
||||
return Statically_filtered_compare_distance();
|
||||
}
|
||||
};
|
||||
|
||||
} } //end of namespace internal::AABB_tree
|
||||
|
||||
/// \addtogroup PkgAABBTreeRef
|
||||
|
|
@ -390,59 +733,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// This should go down to the GeomTraits, i.e. the kernel
|
||||
// and the internal implementation should change its name from
|
||||
// do_intersect to something like does_contain (this is what we compute,
|
||||
// this is not the same do_intersect as the spherical kernel)
|
||||
class Compare_distance {
|
||||
typedef typename AT::Point Point;
|
||||
typedef typename AT::FT FT;
|
||||
typedef typename AT::Primitive Primitive;
|
||||
public:
|
||||
CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_true) const
|
||||
{
|
||||
return GeomTraits().do_intersect_3_object()
|
||||
(GeomTraits().construct_sphere_3_object()
|
||||
(p, GeomTraits().compute_squared_distance_3_object()(p, bound)), bb,true)?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound, Tag_false) const
|
||||
{
|
||||
return GeomTraits().do_intersect_3_object()
|
||||
(GeomTraits().construct_sphere_3_object()
|
||||
(p, GeomTraits().compute_squared_distance_3_object()(p, bound)), bb)?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
CGAL::Comparison_result operator()(const Point& p, const Bounding_box& bb, const Point& bound) const
|
||||
{
|
||||
return (*this)(p, bb, bound, Boolean_tag<internal::Has_static_filters<GeomTraits>::value>());
|
||||
}
|
||||
|
||||
// The following functions seem unused...?
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const Point& bound) const
|
||||
{
|
||||
return GeomTraits().do_intersect_3_object()
|
||||
(GeomTraits().construct_sphere_3_object()
|
||||
(p, GeomTraits().compute_squared_distance_3_object()(p, bound)), pr)?
|
||||
CGAL::SMALLER : CGAL::LARGER;
|
||||
}
|
||||
|
||||
template <class Solid>
|
||||
CGAL::Comparison_result operator()(const Point& p, const Solid& pr, const FT& sq_distance) const
|
||||
{
|
||||
return GeomTraits().do_intersect_3_object()
|
||||
(GeomTraits().construct_sphere_3_object()(p, sq_distance),
|
||||
pr) ?
|
||||
CGAL::SMALLER :
|
||||
CGAL::LARGER;
|
||||
}
|
||||
};
|
||||
typedef typename internal::AABB_tree::Compare_distance_getter_3<GeomTraits>::type Compare_distance;
|
||||
|
||||
Closest_point closest_point_object() const {return Closest_point(*this);}
|
||||
Compare_distance compare_distance_object() const {return Compare_distance();}
|
||||
Compare_distance compare_distance_object() const {return internal::AABB_tree::Compare_distance_getter_3<GeomTraits>::compare_distance_object();}
|
||||
|
||||
typedef enum { CGAL_AXIS_X = 0,
|
||||
CGAL_AXIS_Y = 1,
|
||||
|
|
@ -492,7 +786,7 @@ private:
|
|||
//-------------------------------------------------------
|
||||
template<typename GT, typename P, typename B>
|
||||
typename AABB_traits_3<GT,P,B>::Axis
|
||||
AABB_traits_3<GT,P,B>::longest_axis(const Bounding_box& bbox)
|
||||
AABB_traits_3<GT,P,B>::longest_axis(const Bounding_box& bbox)
|
||||
{
|
||||
const double dx = bbox.xmax() - bbox.xmin();
|
||||
const double dy = bbox.ymax() - bbox.ymin();
|
||||
|
|
@ -522,8 +816,6 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
} // end namespace CGAL
|
||||
|
||||
#include <CGAL/enable_warnings.h>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Cartesian_kernel
|
|||
Circulator
|
||||
Distance_2
|
||||
Distance_3
|
||||
Filtered_kernel
|
||||
Installation
|
||||
Intersections_2
|
||||
Intersections_3
|
||||
|
|
|
|||
|
|
@ -0,0 +1,497 @@
|
|||
// #define CGAL_AW2_DEBUG_PP
|
||||
|
||||
#include "Alpha_wrap_2_options.h"
|
||||
#include "Screenshot_options.h"
|
||||
#include "scene.h"
|
||||
#include "ui_Alpha_wrap_2.h"
|
||||
|
||||
#include <CGAL/Real_timer.h>
|
||||
|
||||
#include <CGAL/Qt/utility.h>
|
||||
#include <CGAL/Qt/DemosMainWindow.h>
|
||||
|
||||
#include <QtGui>
|
||||
#include <QString>
|
||||
#include <QActionGroup>
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
#include <QWidget>
|
||||
|
||||
#include <string>
|
||||
|
||||
class MainWindow
|
||||
: public CGAL::Qt::DemosMainWindow,
|
||||
public Ui_MainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Scene* m_scene;
|
||||
|
||||
unsigned int maxNumRecentFiles;
|
||||
QAction* recentFilesSeparator;
|
||||
QVector<QAction*> recentFileActs;
|
||||
|
||||
QString currentScreenshotDir = "./";
|
||||
QString currentScreenshotFilename = "screenshot";
|
||||
int currentScreenshotNumber = 0;
|
||||
|
||||
CGAL::Real_timer timer;
|
||||
|
||||
public:
|
||||
MainWindow();
|
||||
~MainWindow();
|
||||
|
||||
void update();
|
||||
void update_viewer_camera();
|
||||
|
||||
protected:
|
||||
void addRecentFiles(QMenu* menu, QAction* insertBefore = 0);
|
||||
unsigned int maxNumberOfRecentFiles() const;
|
||||
|
||||
protected Q_SLOTS:
|
||||
// drag & drop
|
||||
void dropEvent(QDropEvent *event);
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
|
||||
// recent files
|
||||
void openRecentFile_aux();
|
||||
void updateRecentFileActions();
|
||||
void addToRecentFiles(QString fileName);
|
||||
|
||||
// io
|
||||
void open(QString file);
|
||||
|
||||
void setStepByStepAction(bool b);
|
||||
void update_stats(const Alpha_wrapper& wrapper);
|
||||
void resetAction();
|
||||
|
||||
public Q_SLOTS:
|
||||
void on_actionLoad_triggered();
|
||||
void on_actionSaveInput_triggered();
|
||||
void on_actionClear_triggered();
|
||||
void on_actionScreenshot_triggered();
|
||||
void on_actionScreenshotSettings_triggered();
|
||||
|
||||
void on_actionExplodeInput_triggered();
|
||||
void on_actionSmoothInput_triggered();
|
||||
void on_actionAlphaWrap_triggered();
|
||||
void on_actionNextStep_triggered();
|
||||
void on_actionNext10Steps_triggered();
|
||||
void on_actionTerminate_triggered();
|
||||
|
||||
void on_actionViewInput_triggered();
|
||||
void on_actionViewAlphaWrap_triggered();
|
||||
void on_actionViewInsideOutside_triggered();
|
||||
void on_actionViewTriangulationEdge_triggered();
|
||||
void on_actionViewVoronoiDiagram_triggered();
|
||||
void on_actionViewEmptyCircles_triggered();
|
||||
void on_actionViewSteinerPoint_triggered();
|
||||
void on_actionViewNextGate_triggered();
|
||||
void on_actionViewNextGateEmptyCircle_triggered();
|
||||
void on_actionViewStats_triggered();
|
||||
|
||||
Q_SIGNALS:
|
||||
void openRecentFile(QString filename);
|
||||
};
|
||||
|
||||
MainWindow::MainWindow()
|
||||
: DemosMainWindow(),
|
||||
Ui_MainWindow(),
|
||||
maxNumRecentFiles(15),
|
||||
recentFileActs(15)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
// init scene
|
||||
m_scene = new Scene;
|
||||
viewer->set_scene(m_scene);
|
||||
|
||||
// accepts drop events
|
||||
setAcceptDrops(true);
|
||||
connect(actionQuit, SIGNAL(triggered()), this, SLOT(close()));
|
||||
addRecentFiles(menuInput, actionQuit);
|
||||
connect(this, SIGNAL(openRecentFile(QString)), this, SLOT(open(QString)));
|
||||
|
||||
stats->setVisible(false);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete m_scene;
|
||||
}
|
||||
|
||||
void MainWindow::update()
|
||||
{
|
||||
viewer->repaint();
|
||||
}
|
||||
|
||||
void MainWindow::update_viewer_camera()
|
||||
{
|
||||
CGAL::Bbox_2 bbox = m_scene->get_bbox();
|
||||
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||
CGAL::square(bbox.ymax() - bbox.ymin()));
|
||||
viewer->set_camera(0.5 * (bbox.xmin() + bbox.xmax()),
|
||||
0.5 * (bbox.ymin() + bbox.ymax()),
|
||||
1.5 / diag_length);
|
||||
}
|
||||
|
||||
void MainWindow::addRecentFiles(QMenu* menu, QAction* insertBeforeAction)
|
||||
{
|
||||
if(insertBeforeAction) {
|
||||
recentFilesSeparator = menu->insertSeparator(insertBeforeAction);
|
||||
} else {
|
||||
recentFilesSeparator = menu->addSeparator();
|
||||
}
|
||||
recentFilesSeparator->setVisible(false);
|
||||
|
||||
for(unsigned int i=0; i<maxNumberOfRecentFiles(); ++i) {
|
||||
recentFileActs[i] = new QAction(this);
|
||||
recentFileActs[i]->setVisible(false);
|
||||
connect(recentFileActs[i], SIGNAL(triggered()), this, SLOT(openRecentFile_aux()));
|
||||
if(insertBeforeAction)
|
||||
menu->insertAction(insertBeforeAction, recentFileActs[i]);
|
||||
else
|
||||
menu->addAction(recentFileActs[i]);
|
||||
}
|
||||
updateRecentFileActions();
|
||||
}
|
||||
|
||||
unsigned int MainWindow::maxNumberOfRecentFiles() const { return maxNumRecentFiles; }
|
||||
|
||||
// drag & drop
|
||||
void MainWindow::dropEvent(QDropEvent *event)
|
||||
{
|
||||
Q_FOREACH(QUrl url, event->mimeData()->urls())
|
||||
{
|
||||
QString filename = url.toLocalFile();
|
||||
if(!filename.isEmpty())
|
||||
{
|
||||
QTextStream(stderr) << QString("dropEvent(\"%1\")\n").arg(filename);
|
||||
open(filename);
|
||||
}
|
||||
}
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if(event->mimeData()->hasFormat("text/uri-list"))
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
// recent files
|
||||
void MainWindow::openRecentFile_aux()
|
||||
{
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
if(action)
|
||||
Q_EMIT openRecentFile(action->data().toString());
|
||||
}
|
||||
|
||||
void MainWindow::updateRecentFileActions()
|
||||
{
|
||||
QSettings settings;
|
||||
QStringList files = settings.value("recentFileList").toStringList();
|
||||
|
||||
int numRecentFiles = qMin(files.size(), static_cast<int>(maxNumberOfRecentFiles()));
|
||||
for(int i=0; i<numRecentFiles; ++i) {
|
||||
QString strippedName = QFileInfo(files[i]).fileName();
|
||||
QString text = tr("&%1 %2").arg(i).arg(strippedName);
|
||||
recentFileActs[i]->setText(text);
|
||||
recentFileActs[i]->setData(files[i]);
|
||||
recentFileActs[i]->setVisible(true);
|
||||
}
|
||||
for(unsigned int j = numRecentFiles; j < maxNumberOfRecentFiles(); ++j)
|
||||
recentFileActs[j]->setVisible(false);
|
||||
|
||||
recentFilesSeparator->setVisible(numRecentFiles > 0);
|
||||
}
|
||||
|
||||
void MainWindow::addToRecentFiles(QString fileName)
|
||||
{
|
||||
QSettings settings;
|
||||
QStringList files = settings.value("recentFileList").toStringList();
|
||||
files.removeAll(fileName);
|
||||
files.prepend(fileName);
|
||||
while (files.size() > static_cast<int>(maxNumRecentFiles))
|
||||
files.removeLast();
|
||||
settings.setValue("recentFileList", files);
|
||||
updateRecentFileActions();
|
||||
}
|
||||
|
||||
// io
|
||||
void MainWindow::open(QString filename)
|
||||
{
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
bool success = m_scene->load(filename);
|
||||
QApplication::restoreOverrideCursor();
|
||||
if(!success)
|
||||
return;
|
||||
this->actionExplodeInput->setEnabled(true);
|
||||
this->actionSmoothInput->setEnabled(true);
|
||||
this->actionAlphaWrap->setEnabled(true);
|
||||
addToRecentFiles(filename);
|
||||
update_viewer_camera();
|
||||
update();
|
||||
resetAction();
|
||||
}
|
||||
|
||||
void MainWindow::setStepByStepAction(bool b)
|
||||
{
|
||||
this->actionNextStep->setEnabled(b);
|
||||
this->actionNext10Steps->setEnabled(b);
|
||||
this->actionTerminate->setEnabled(b);
|
||||
}
|
||||
|
||||
void MainWindow::update_stats(const Alpha_wrapper& wrapper)
|
||||
{
|
||||
if(!wrapper.queue().empty())
|
||||
return;
|
||||
|
||||
actionViewStats->setEnabled(true);
|
||||
stats->setVisible(actionViewStats->isChecked());
|
||||
// output # of cmp is # of edges for now
|
||||
complexity->setText(QString::fromStdString("Alpha Wrap complexity : "+ std::to_string(m_scene->get_alpha_wrap().size()) +" edges"));
|
||||
time->setText(QString::fromStdString("Total execution time : "+ std::to_string(timer.time()) +" s"));
|
||||
double percentage_nb_proj = (m_scene->get_nb_proj()*100.)/m_scene->get_nb_steiner();
|
||||
double percentage_nb_inter = 100. - percentage_nb_proj;
|
||||
steiner->setText(QString::fromStdString("Steiner points : "+ std::to_string(percentage_nb_proj) +"% projection - "+ std::to_string(percentage_nb_inter) + "% intersection"));
|
||||
gate->setText(QString::fromStdString("Number of gates traversed : "+ std::to_string(m_scene->get_nb_gate_traversed())));
|
||||
}
|
||||
|
||||
void MainWindow::resetAction()
|
||||
{
|
||||
timer.reset();
|
||||
setStepByStepAction(false);
|
||||
actionViewStats->setEnabled(false);
|
||||
stats->setVisible(false);
|
||||
m_scene->reset_stats();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionLoad_triggered()
|
||||
{
|
||||
QSettings settings;
|
||||
QString directory =
|
||||
settings.value("Open PSLG", QDir::current().dirName())
|
||||
.toString();
|
||||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open PSLG"), directory, tr("DAT files (*.dat)"));
|
||||
if(!fileName.isEmpty()) {
|
||||
open(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionSaveInput_triggered()
|
||||
{
|
||||
QSettings settings;
|
||||
QString directory =
|
||||
settings.value("Save PSLG", QDir::current().dirName())
|
||||
.toString();
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save PSLG"), directory, tr("DAT files (*.dat)"));
|
||||
if(!fileName.isEmpty()) {
|
||||
AW2::IO::write_output_polylines_file(fileName.toStdString(), m_scene->get_input());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionClear_triggered()
|
||||
{
|
||||
m_scene->clear();
|
||||
this->actionExplodeInput->setEnabled(false);
|
||||
this->actionSmoothInput->setEnabled(false);
|
||||
this->actionAlphaWrap->setEnabled(false);
|
||||
resetAction();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionScreenshot_triggered()
|
||||
{
|
||||
QPixmap pixmap(viewer->size());
|
||||
viewer->render(&pixmap, QPoint(), QRegion(viewer->rect()));
|
||||
QString filepath = currentScreenshotDir + QDir::separator() + currentScreenshotFilename + "_" + QString::number(currentScreenshotNumber) + ".png";
|
||||
pixmap.save(filepath);
|
||||
currentScreenshotNumber++;
|
||||
}
|
||||
|
||||
void MainWindow::on_actionScreenshotSettings_triggered()
|
||||
{
|
||||
ScreenshotOptions dialog_box_screenshot(this, m_scene->get_screenshot_folder(),
|
||||
m_scene->get_screenshot_filename(),
|
||||
m_scene->get_screenshot_number());
|
||||
if(dialog_box_screenshot.exec() != QDialog::Accepted) return;
|
||||
currentScreenshotDir = dialog_box_screenshot.get_current_screenshot_dir();
|
||||
currentScreenshotFilename = dialog_box_screenshot.get_current_screenshot_filename();
|
||||
currentScreenshotNumber = dialog_box_screenshot.get_current_screenshot_numbering_start();
|
||||
m_scene->set_screenshot_folder(currentScreenshotDir);
|
||||
m_scene->set_screenshot_filename(currentScreenshotFilename);
|
||||
m_scene->set_screenshot_number(currentScreenshotNumber);
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::on_actionExplodeInput_triggered()
|
||||
{
|
||||
m_scene->get_alpha_wrap().clear();
|
||||
m_scene->get_wrapper().clear();
|
||||
m_scene->explode_input();
|
||||
resetAction();
|
||||
update_viewer_camera();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionSmoothInput_triggered()
|
||||
{
|
||||
m_scene->get_alpha_wrap().clear();
|
||||
m_scene->get_wrapper().clear();
|
||||
m_scene->smooth_input();
|
||||
resetAction();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionAlphaWrap_triggered()
|
||||
{
|
||||
AlphaWrapOptions dialog_box_alpha_wrap(this, m_scene->get_alpha(), m_scene->get_offset(),
|
||||
m_scene->get_is_alpha_relative(), m_scene->get_is_offset_relative(),
|
||||
m_scene->get_is_step_by_step());
|
||||
if(dialog_box_alpha_wrap.exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
resetAction();
|
||||
m_scene->set_alpha(dialog_box_alpha_wrap.get_alpha_value());
|
||||
m_scene->set_offset(dialog_box_alpha_wrap.get_offset_value());
|
||||
m_scene->set_is_alpha_relative(dialog_box_alpha_wrap.is_alpha_relative());
|
||||
m_scene->set_is_offset_relative(dialog_box_alpha_wrap.is_offset_relative());
|
||||
m_scene->set_is_step_by_step(dialog_box_alpha_wrap.is_step_by_step());
|
||||
|
||||
// Get the alpha wrap options
|
||||
double alpha_value = dialog_box_alpha_wrap.get_alpha_value();
|
||||
double offset_value = dialog_box_alpha_wrap.get_offset_value();
|
||||
if(dialog_box_alpha_wrap.is_alpha_relative() ||
|
||||
dialog_box_alpha_wrap.is_offset_relative()) {
|
||||
double longest_diag_length = m_scene->get_diagonal_bbox();
|
||||
if(dialog_box_alpha_wrap.is_alpha_relative())
|
||||
alpha_value = longest_diag_length / alpha_value;
|
||||
if(dialog_box_alpha_wrap.is_offset_relative())
|
||||
offset_value = longest_diag_length / offset_value;
|
||||
}
|
||||
|
||||
timer.start();
|
||||
m_scene->init_alpha_data_structure(alpha_value, offset_value);
|
||||
if(!dialog_box_alpha_wrap.is_step_by_step()) {
|
||||
m_scene->alpha_flood_fill();
|
||||
}
|
||||
timer.stop();
|
||||
m_scene->extract_pslg_2_soup();
|
||||
setStepByStepAction(dialog_box_alpha_wrap.is_step_by_step());
|
||||
update_viewer_camera();
|
||||
update();
|
||||
update_stats(m_scene->get_wrapper());
|
||||
}
|
||||
|
||||
void MainWindow::on_actionNextStep_triggered()
|
||||
{
|
||||
Alpha_wrapper& wrapper = m_scene->get_wrapper();
|
||||
if(wrapper.queue().empty())
|
||||
return;
|
||||
timer.start();
|
||||
m_scene->alpha_flood_fill(1);
|
||||
timer.stop();
|
||||
m_scene->extract_pslg_2_soup();
|
||||
update();
|
||||
update_stats(wrapper);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionNext10Steps_triggered()
|
||||
{
|
||||
Alpha_wrapper& wrapper = m_scene->get_wrapper();
|
||||
if(wrapper.queue().empty())
|
||||
return;
|
||||
timer.start();
|
||||
m_scene->alpha_flood_fill(10);
|
||||
timer.stop();
|
||||
m_scene->extract_pslg_2_soup();
|
||||
update();
|
||||
update_stats(wrapper);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionTerminate_triggered()
|
||||
{
|
||||
Alpha_wrapper& wrapper = m_scene->get_wrapper();
|
||||
if(wrapper.queue().empty())
|
||||
return;
|
||||
timer.start();
|
||||
m_scene->alpha_flood_fill();
|
||||
timer.stop();
|
||||
m_scene->extract_pslg_2_soup();
|
||||
update();
|
||||
update_stats(wrapper);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewInput_triggered() {
|
||||
m_scene->toggle_view_input();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewAlphaWrap_triggered() {
|
||||
m_scene->toggle_view_alpha_wrap();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewInsideOutside_triggered() {
|
||||
m_scene->toggle_view_dt2_inside_outside();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewTriangulationEdge_triggered() {
|
||||
m_scene->toggle_view_dt2_edge();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewVoronoiDiagram_triggered() {
|
||||
m_scene->toggle_view_voronoi();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewEmptyCircles_triggered() {
|
||||
m_scene->toggle_view_empty_alpha_pencils();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewSteinerPoint_triggered() {
|
||||
m_scene->toggle_view_steiner_point();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewNextGate_triggered() {
|
||||
m_scene->toggle_view_next_gate();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewNextGateEmptyCircle_triggered() {
|
||||
m_scene->toggle_view_next_gate_pencil();
|
||||
update();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewStats_triggered() {
|
||||
stats->setVisible(!stats->isVisible());
|
||||
}
|
||||
|
||||
#include "Alpha_wrap_2.moc"
|
||||
#include <CGAL/Qt/resources.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
app.setApplicationName("2D Alpha Wrapping");
|
||||
|
||||
// Import resources from libCGAL (Qt6).
|
||||
CGAL_QT_INIT_RESOURCES;
|
||||
|
||||
MainWindow window;
|
||||
window.show();
|
||||
return app.exec();
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/cgal/help" lang="en" >
|
||||
<file alias="about_CGAL.html" >about_CGAL.html</file>
|
||||
<file>about_Alpha_wrap_2.html</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<author>Cédric Portaneri, Pierre Alliez</author>
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>859</width>
|
||||
<height>673</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>CGAL - 2D Alpha Wrap</string>
|
||||
</property>
|
||||
<widget class="Viewer" name="centralwidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetNoConstraint</enum>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="Viewer" name="viewer" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>20</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="locale">
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="stats">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="complexity">
|
||||
<property name="text">
|
||||
<string>Alpha wrap complexity: # edges</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="steiner">
|
||||
<property name="text">
|
||||
<string>Steiner points: # % projection - # % intersection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="gate">
|
||||
<property name="text">
|
||||
<string>Number of gates traversed: #</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="time">
|
||||
<property name="text">
|
||||
<string>Total execution time: # s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>859</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuInput">
|
||||
<property name="title">
|
||||
<string>Input</string>
|
||||
</property>
|
||||
<addaction name="actionLoad"/>
|
||||
<addaction name="actionSaveInput"/>
|
||||
<addaction name="actionClear"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionScreenshot"/>
|
||||
<addaction name="actionScreenshotSettings"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuAlgo">
|
||||
<property name="title">
|
||||
<string>Algo</string>
|
||||
</property>
|
||||
<addaction name="actionExplodeInput"/>
|
||||
<addaction name="actionSmoothInput"/>
|
||||
<addaction name="actionAlphaWrap"/>
|
||||
<addaction name="actionNextStep"/>
|
||||
<addaction name="actionNext10Steps"/>
|
||||
<addaction name="actionTerminate"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<addaction name="actionViewInsideOutside"/>
|
||||
<addaction name="actionViewTriangulationEdge"/>
|
||||
<addaction name="actionViewVoronoiDiagram"/>
|
||||
<addaction name="actionViewEmptyCircles"/>
|
||||
<addaction name="actionViewSteinerPoint"/>
|
||||
<addaction name="actionViewNextGate"/>
|
||||
<addaction name="actionViewNextGateEmptyCircle"/>
|
||||
<addaction name="actionViewInput"/>
|
||||
<addaction name="actionViewAlphaWrap"/>
|
||||
<addaction name="actionViewStats"/>
|
||||
</widget>
|
||||
<addaction name="menuInput"/>
|
||||
<addaction name="menuAlgo"/>
|
||||
<addaction name="menuView"/>
|
||||
</widget>
|
||||
<action name="actionQuit">
|
||||
<property name="text">
|
||||
<string>Quit</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClear">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Space</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLoad">
|
||||
<property name="text">
|
||||
<string>Load...</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Load PSLG</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAlphaWrap">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Alpha Wrap</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Return</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNextStep">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Next Step (J)</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>J</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNext10Steps">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Next 10 Steps (K)</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>K</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTerminate">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Terminate (L)</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>L</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExplodeInput">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Explode Input</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>E</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewInsideOutside">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Inside/Outside</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>T</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewVoronoiDiagram">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Voronoi Diagram</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Y</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewInput">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Input</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewAlphaWrap">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Alpha Wrap</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionScreenshotSettings">
|
||||
<property name="text">
|
||||
<string>Screenshot Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewSteinerPoint">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Steiner Point</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>P</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewNextGate">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Next Gate</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>U</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewStats">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Stats</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>M</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSmoothInput">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Smooth Input</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Z</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewEmptyCircles">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Empty Circles</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>H</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewNextGateEmptyCircle">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Next Gate Empty Circle</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>G</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewTriangulationEdge">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Triangulation Edge</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSaveInput">
|
||||
<property name="text">
|
||||
<string>Save Input...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Viewer</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Viewer.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) 2019-2020 X, The Moonshot Factory (USA).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Author(s) : Pierre Alliez pierre.alliez@inria.fr
|
||||
// : Michael Hemmer mhsaar@gmail.com
|
||||
// : Cedric Portaneri cportaneri@gmail.com
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_DEMO_OPTIONS_H
|
||||
#define CGAL_ALPHA_WRAP_2_DEMO_OPTIONS_H
|
||||
|
||||
#include "ui_Alpha_wrap_2_options.h"
|
||||
|
||||
/**
|
||||
* @class AlphaWrapOptions
|
||||
* @brief Interface to tune the alpha wrapping options
|
||||
*/
|
||||
class AlphaWrapOptions
|
||||
: public QDialog, private Ui::AlphaWrapOptions
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AlphaWrapOptions(QWidget*, double alpha, double offset,
|
||||
bool is_alpha_relative,
|
||||
bool is_offset_relative,
|
||||
bool step_by_step)
|
||||
{
|
||||
setupUi(this);
|
||||
alphaValue->setValue(alpha);
|
||||
offsetValue->setValue(offset);
|
||||
radioButtonAlphaAbs->setChecked(!is_alpha_relative);
|
||||
radioButtonAlphaRel->setChecked(is_alpha_relative);
|
||||
radioButtonOffsetAbs->setChecked(!is_offset_relative);
|
||||
radioButtonOffsetRel->setChecked(is_offset_relative);
|
||||
stepByStepCheckBox->setChecked(step_by_step);
|
||||
}
|
||||
|
||||
bool is_alpha_relative() {
|
||||
return radioButtonAlphaRel->isChecked();
|
||||
}
|
||||
|
||||
double get_alpha_value() {
|
||||
return alphaValue->value();
|
||||
}
|
||||
|
||||
bool is_offset_relative() {
|
||||
return radioButtonOffsetRel->isChecked();
|
||||
}
|
||||
|
||||
double get_offset_value() {
|
||||
return offsetValue->value();
|
||||
}
|
||||
|
||||
bool is_step_by_step() {
|
||||
return stepByStepCheckBox->isChecked();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_DEMO_OPTIONS_H
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AlphaWrapOptions</class>
|
||||
<widget class="QDialog" name="AlphaWrapOptions">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModality::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>698</width>
|
||||
<height>177</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Alpha Wrap Options</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QFrame" name="Alpha">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QDoubleSpinBox" name="alphaValue">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>9999.989999999999782</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>20.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="titleAlpha">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Alpha</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButtonAlphaRel">
|
||||
<property name="text">
|
||||
<string>Relative</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButtonAlphaAbs">
|
||||
<property name="text">
|
||||
<string>Absolute</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QFrame" name="Offset">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="offsetValue">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>9999.989999999999782</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>600.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="2" colspan="2">
|
||||
<widget class="QLabel" name="titleOffset">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Offset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButtonOffsetRel">
|
||||
<property name="text">
|
||||
<string>Relative</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioButtonOffsetAbs">
|
||||
<property name="text">
|
||||
<string>Absolute</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="stepByStepCheckBox">
|
||||
<property name="text">
|
||||
<string>Step by step</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>AlphaWrapOptions</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>AlphaWrapOptions</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# Created by the script cgal_create_cmake_script
|
||||
# This is the CMake script for compiling a CGAL application.
|
||||
|
||||
cmake_minimum_required(VERSION 3.12...3.31)
|
||||
project(Alpha_wrap_2_Demo)
|
||||
|
||||
find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt6)
|
||||
|
||||
find_package(Qt6 QUIET COMPONENTS Widgets)
|
||||
|
||||
if(CGAL_Qt6_FOUND AND Qt6_FOUND)
|
||||
add_compile_definitions(QT_NO_KEYWORDS)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
# The executable itself.
|
||||
qt6_wrap_ui(Alpha_wrap_2_uis Alpha_wrap_2.ui Alpha_wrap_2_options.ui Screenshot_options.ui)
|
||||
qt6_wrap_cpp(Alpha_wrap_2_mocs Alpha_wrap_2_options.h Screenshot_options.h)
|
||||
qt6_add_resources(Alpha_wrap_2_qrc Alpha_wrap_2.qrc)
|
||||
|
||||
qt_add_executable(Alpha_wrap_2 Alpha_wrap_2.cpp Viewer.cpp ${Alpha_wrap_2_uis} ${Alpha_wrap_2_mocs} ${Alpha_wrap_2_qrc})
|
||||
|
||||
add_to_cached_list(CGAL_EXECUTABLE_TARGETS Alpha_wrap_2)
|
||||
|
||||
target_link_libraries(Alpha_wrap_2 PRIVATE CGAL::CGAL CGAL::CGAL_Qt6 Qt6::Widgets)
|
||||
|
||||
include(${CGAL_MODULES_DIR}/CGAL_add_test.cmake)
|
||||
cgal_add_compilation_test(Alpha_wrap_2)
|
||||
|
||||
include(${CGAL_MODULES_DIR}/CGAL_add_test.cmake)
|
||||
cgal_add_compilation_test(Alpha_wrap_2)
|
||||
else()
|
||||
message("NOTICE: This demo requires CGAL and Qt6, and will not be compiled.")
|
||||
endif()
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2019-2020 X, The Moonshot Factory (USA).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Author(s) : Pierre Alliez pierre.alliez@inria.fr
|
||||
// : Michael Hemmer mhsaar@gmail.com
|
||||
// : Cedric Portaneri cportaneri@gmail.com
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_SCREENSHOT_OPTIONS_H
|
||||
#define CGAL_ALPHA_WRAP_2_SCREENSHOT_OPTIONS_H
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "ui_Screenshot_options.h"
|
||||
|
||||
/**
|
||||
* @class ScreenshotOptions
|
||||
* @brief Interface to tune the screenshot options
|
||||
*/
|
||||
class ScreenshotOptions
|
||||
: public QDialog, private Ui::ScreenshotOptions
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QString screenshot_dir_path = QDir::currentPath();
|
||||
|
||||
public:
|
||||
ScreenshotOptions(QWidget*,
|
||||
QString folder,
|
||||
QString filename,
|
||||
int num)
|
||||
{
|
||||
setupUi(this);
|
||||
connect(pushButtonFolder, SIGNAL(clicked()), this,
|
||||
SLOT(on_pushButtonFolderTriggered()));
|
||||
connect(buttonBox, SIGNAL(accepted()), this,
|
||||
SLOT(accept()));
|
||||
connect(buttonBox, SIGNAL(rejected()), this,
|
||||
SLOT(close()));
|
||||
textEditFolder->setPlainText(folder);
|
||||
textEditFilename->setPlainText(filename);
|
||||
spinBoxNumbering->setValue(num);
|
||||
}
|
||||
|
||||
QString get_current_screenshot_dir() {
|
||||
return screenshot_dir_path;
|
||||
}
|
||||
|
||||
QString get_current_screenshot_filename() {
|
||||
return textEditFilename->toPlainText();
|
||||
}
|
||||
|
||||
int get_current_screenshot_numbering_start() {
|
||||
return spinBoxNumbering->value();
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void on_pushButtonFolderTriggered()
|
||||
{
|
||||
screenshot_dir_path = QFileDialog::getExistingDirectory(0, ("Select screenshot output folder"), QDir::currentPath());
|
||||
QStringList all_dir = screenshot_dir_path.split(QDir::separator());
|
||||
textEditFolder->setPlainText(QDir::separator()+all_dir[1]+QDir::separator()+".."+QDir::separator()+all_dir[all_dir.size()-1]);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_SCREENSHOT_OPTIONS_H
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScreenshotOptions</class>
|
||||
<widget class="QDialog" name="ScreenshotOptions">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModality::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>457</width>
|
||||
<height>257</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Screenshot Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="Folder">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="titleFolder">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEditFolder">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LayoutDirection::RightToLeft</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonFolder">
|
||||
<property name="text">
|
||||
<string>Open...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="Filename">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="titleFilename">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Filename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEditFilename">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="titleFilename_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>_#.png</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="Numbering">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="titleNumbering">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string># start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxNumbering">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
#include <QtGui>
|
||||
|
||||
#include "scene.h"
|
||||
#include "Viewer.h"
|
||||
|
||||
Viewer::Viewer(QWidget *pParent)
|
||||
: CGAL::QGLViewer(pParent)
|
||||
{
|
||||
m_scene = NULL;
|
||||
m_center_x = m_center_y = 0.5;
|
||||
m_scale = 1.0;
|
||||
|
||||
setAutoFillBackground(false);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
}
|
||||
|
||||
void Viewer::resizeGL(int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
double aspect_ratio = double(height) / double(width);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(-1.0, 1.0, -aspect_ratio, aspect_ratio, -1.0, 1.0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
}
|
||||
|
||||
void Viewer::initializeGL()
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
|
||||
glClearColor(1., 1., 1., 0.);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_SMOOTH);
|
||||
}
|
||||
|
||||
void Viewer::paintGL()
|
||||
{
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
if(!m_scene) return;
|
||||
|
||||
glPushMatrix();
|
||||
glScaled(m_scale, m_scale, m_scale);
|
||||
glTranslated(-m_center_x, -m_center_y, 0.0);
|
||||
m_scene->render();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void Viewer::wheelEvent(QWheelEvent *event) {
|
||||
if(!m_scene) return;
|
||||
m_scale += (m_scale/100) * (event->angleDelta().y() / 120);
|
||||
if(m_scale < 0.0) m_scale = 0.0;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void Viewer::mousePressEvent(QMouseEvent *event) {
|
||||
if(!m_scene) return;
|
||||
m_mouse_click = event->pos();
|
||||
|
||||
if(event->button() == Qt::LeftButton)
|
||||
{
|
||||
is_drawing = true;
|
||||
setCursor(QCursor(Qt::PointingHandCursor));
|
||||
sample_mouse_path(m_mouse_click, true,
|
||||
Qt::ShiftModifier == QApplication::keyboardModifiers());
|
||||
}
|
||||
else
|
||||
{
|
||||
setCursor(QCursor(Qt::ClosedHandCursor));
|
||||
}
|
||||
}
|
||||
|
||||
void Viewer::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if(!m_scene) return;
|
||||
m_mouse_move = event->pos();
|
||||
|
||||
if(event->buttons() == Qt::LeftButton)
|
||||
{
|
||||
if(m_mouse_move != m_mouse_click)
|
||||
sample_mouse_path(m_mouse_move, false,
|
||||
Qt::ShiftModifier == QApplication::keyboardModifiers());
|
||||
}
|
||||
else
|
||||
{
|
||||
move_camera(m_mouse_click, m_mouse_move);
|
||||
}
|
||||
|
||||
m_mouse_click = m_mouse_move;
|
||||
update();
|
||||
}
|
||||
|
||||
void Viewer::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if(!m_scene) return;
|
||||
m_mouse_move = event->pos();
|
||||
|
||||
if(event->button() == Qt::LeftButton)
|
||||
{
|
||||
is_drawing = false;
|
||||
if(m_mouse_move != m_mouse_click)
|
||||
sample_mouse_path(m_mouse_move, true,
|
||||
Qt::ShiftModifier == QApplication::keyboardModifiers());
|
||||
}
|
||||
else
|
||||
{
|
||||
move_camera(m_mouse_click, m_mouse_move);
|
||||
}
|
||||
|
||||
m_mouse_click = m_mouse_move;
|
||||
setCursor(QCursor(Qt::ArrowCursor));
|
||||
update();
|
||||
}
|
||||
|
||||
void Viewer::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if((event->key() == Qt::Key_Shift) && is_drawing) {
|
||||
m_scene->close_input();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void Viewer::keyReleaseEvent(QKeyEvent *event)
|
||||
{
|
||||
if((event->key() == Qt::Key_Shift) && is_drawing ) {
|
||||
m_scene->open_input();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void Viewer::sample_mouse_path(const QPoint& point, bool new_cmp, bool is_closed)
|
||||
{
|
||||
double x, y;
|
||||
convert_to_world_space(point, x, y);
|
||||
|
||||
m_scene->add_vertex(Point_2(x, y), new_cmp, is_closed);
|
||||
m_scene->set_mouse_pos(Point_2(x, y));
|
||||
}
|
||||
|
||||
void Viewer::move_camera(const QPoint& p0, const QPoint& p1)
|
||||
{
|
||||
m_center_x -= double(p1.x() - p0.x()) / double(width());
|
||||
m_center_y += double(p1.y() - p0.y()) / double(height());
|
||||
}
|
||||
|
||||
void Viewer::convert_to_world_space(const QPoint& point, double &x, double &y)
|
||||
{
|
||||
double aspect_ratio = double(height()) / double(width());
|
||||
|
||||
x = double(point.x()) / double(width());
|
||||
x = (2.0*x - 1.0) / m_scale;
|
||||
x += m_center_x;
|
||||
|
||||
y = 1.0 - double(point.y()) / double(height());
|
||||
y = (2.0*y - 1.0) * aspect_ratio / m_scale;
|
||||
y += m_center_y;
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// 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) : Pierre Alliez
|
||||
// Cedric Portaneri,
|
||||
// Mael Rouxel-Labbé
|
||||
// Andreas Fabri
|
||||
// Michael Hemmer
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_DEMO_VIEWER_H
|
||||
#define CGAL_ALPHA_WRAP_2_DEMO_VIEWER_H
|
||||
|
||||
#include "scene.h"
|
||||
|
||||
#include <CGAL/Qt/qglviewer.h>
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QPaintEvent>
|
||||
|
||||
class Viewer
|
||||
: public CGAL::QGLViewer
|
||||
{
|
||||
private:
|
||||
Scene* m_scene;
|
||||
|
||||
// camera
|
||||
double m_scale;
|
||||
double m_center_x, m_center_y;
|
||||
|
||||
// mouse
|
||||
QPoint m_mouse_click, m_mouse_move;
|
||||
|
||||
bool is_drawing = false;
|
||||
|
||||
public:
|
||||
Viewer(QWidget *parent);
|
||||
|
||||
void set_scene(Scene* pScene) { m_scene = pScene; }
|
||||
|
||||
void set_camera(const double x, const double y, const double s)
|
||||
{
|
||||
m_center_x = x;
|
||||
m_center_y = y;
|
||||
m_scale = s;
|
||||
}
|
||||
|
||||
protected:
|
||||
// GL
|
||||
void paintGL();
|
||||
void initializeGL();
|
||||
void resizeGL(int width, int height);
|
||||
|
||||
// mouse
|
||||
void wheelEvent(QWheelEvent *event);
|
||||
void mouseMoveEvent(QMouseEvent *event);
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
void keyReleaseEvent(QKeyEvent *event);
|
||||
|
||||
void sample_mouse_path(const QPoint& point, bool new_cmp, bool is_closed);
|
||||
void move_camera(const QPoint& p0, const QPoint& p1);
|
||||
void convert_to_world_space(const QPoint& point, double &x, double &y);
|
||||
};
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_DEMO_VIEWER_H
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
<h2>2D Alpha Wrapping</h2>
|
||||
<p>Copyright © 2022 Google LLC (USA).</p>
|
||||
<p>This application illustrates the 2D Alpha Wrapping.</p>
|
||||
<p>See also <a href="https://www.cgal.org/Pkg/AlphaWrap2">the online manual</a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
<p><img src=":/cgal/logos/CGAL.png"></p>
|
||||
<h2>Computational Geometry Algorithms Library<!--CGAL_VERSION--></h2>
|
||||
<p>CGAL provides efficient and reliable geometric algorithms in the form of a C++ library.</p>
|
||||
<p>For more information visit <a href="https://www.cgal.org/">www.cgal.org</a></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2019-2020 X, The Moonshot Factory (USA).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Author(s) : Pierre Alliez pierre.alliez@inria.fr
|
||||
// : Michael Hemmer mhsaar@gmail.com
|
||||
// : Cedric Portaneri cportaneri@gmail.com
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_DEMO_CONVERSION_UTILS_H
|
||||
#define CGAL_ALPHA_WRAP_2_DEMO_CONVERSION_UTILS_H
|
||||
|
||||
#include <CGAL/Point_2.h>
|
||||
#include <CGAL/Segment_2.h>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
template <typename Edge>
|
||||
std::array<decltype(Edge().first->vertex(0)), 2>
|
||||
delaunay_edge_to_delaunay_vertex_array(const Edge& edge)
|
||||
{
|
||||
return { edge.first->vertex((edge.second + 1)%3),
|
||||
edge.first->vertex((edge.second + 2)%3) };
|
||||
}
|
||||
|
||||
template <typename Kernel>
|
||||
CGAL::Segment_2<Kernel> make_segment_2(const CGAL::Point_2<Kernel>& p0,
|
||||
const CGAL::Point_2<Kernel>& p1) {
|
||||
CGAL_precondition(p0 != p1);
|
||||
return CGAL::Segment_2<Kernel>(p0, p1);
|
||||
}
|
||||
|
||||
template <typename Dt2_vertex_handle>
|
||||
auto delaunay_vertex_array_to_segment(const std::array<Dt2_vertex_handle, 2>& va)
|
||||
{
|
||||
return make_segment_2(va[0]->point(), va[1]->point());
|
||||
}
|
||||
|
||||
template <typename Edge>
|
||||
auto delaunay_edge_to_segment(const Edge& edge)
|
||||
{
|
||||
return delaunay_vertex_array_to_segment(delaunay_edge_to_delaunay_vertex_array(edge));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_DEMO_CONVERSION_UTILS_H
|
||||
|
|
@ -0,0 +1,404 @@
|
|||
0.135936 0.21778
|
||||
0.134995 0.21778
|
||||
0.134055 0.21778
|
||||
0.131232 0.21778
|
||||
0.121825 0.216839
|
||||
0.109595 0.216839
|
||||
0.100188 0.216839
|
||||
0.0926623 0.21778
|
||||
0.0841957 0.218721
|
||||
0.0776105 0.219661
|
||||
0.0757291 0.219661
|
||||
0.065381 0.221543
|
||||
0.0550329 0.222484
|
||||
0.0475071 0.224365
|
||||
0.0390405 0.227187
|
||||
0.033396 0.230009
|
||||
0.0315146 0.231891
|
||||
0.0296331 0.234713
|
||||
0.0296331 0.236595
|
||||
0.0286924 0.238476
|
||||
0.0277516 0.241298
|
||||
0.0277516 0.245061
|
||||
0.0277516 0.251646
|
||||
0.0286924 0.259172
|
||||
0.0305738 0.265757
|
||||
0.0315146 0.26952
|
||||
0.0324553 0.273283
|
||||
0.0352775 0.277987
|
||||
0.0390405 0.284572
|
||||
0.0428034 0.289276
|
||||
0.0465663 0.29492
|
||||
0.0503293 0.301505
|
||||
0.0531515 0.304327
|
||||
0.0559737 0.30715
|
||||
0.0606773 0.311853
|
||||
0.065381 0.317498
|
||||
0.0710254 0.322201
|
||||
0.0747883 0.326905
|
||||
0.0785513 0.329727
|
||||
0.0823142 0.332549
|
||||
0.0870179 0.337253
|
||||
0.0907808 0.340075
|
||||
0.0926623 0.342897
|
||||
0.093603 0.343838
|
||||
0.0954845 0.34572
|
||||
0.0964252 0.348542
|
||||
0.0964252 0.351364
|
||||
0.0964252 0.354186
|
||||
0.0964252 0.355127
|
||||
0.0926623 0.356068
|
||||
0.0907808 0.356068
|
||||
0.0860771 0.357008
|
||||
0.0832549 0.357008
|
||||
0.0785513 0.357949
|
||||
0.0747883 0.357949
|
||||
0.0682032 0.360771
|
||||
0.0644403 0.361712
|
||||
0.0597366 0.363594
|
||||
0.0540922 0.366416
|
||||
0.0503293 0.369238
|
||||
0.0465663 0.373942
|
||||
0.0418627 0.381468
|
||||
0.0362183 0.392756
|
||||
0.0305738 0.402164
|
||||
0.0258702 0.411571
|
||||
0.023048 0.418156
|
||||
0.0183443 0.426623
|
||||
0.0155221 0.435089
|
||||
0.0136406 0.446378
|
||||
0.0117592 0.453904
|
||||
0.0117592 0.460489
|
||||
0.0117592 0.469897
|
||||
0.0117592 0.478363
|
||||
0.0117592 0.48683
|
||||
0.0126999 0.496237
|
||||
0.0136406 0.509407
|
||||
0.0155221 0.518815
|
||||
0.0174036 0.526341
|
||||
0.019285 0.534807
|
||||
0.0221072 0.546096
|
||||
0.0239887 0.555503
|
||||
0.0268109 0.56397
|
||||
0.0296331 0.573377
|
||||
0.0315146 0.579022
|
||||
0.0352775 0.586548
|
||||
0.037159 0.59031
|
||||
0.0399812 0.595955
|
||||
0.0418627 0.600659
|
||||
0.0437441 0.607244
|
||||
0.0446849 0.611947
|
||||
0.0456256 0.617592
|
||||
0.0465663 0.619473
|
||||
0.0465663 0.620414
|
||||
0.0475071 0.623236
|
||||
0.0475071 0.626058
|
||||
0.0475071 0.62794
|
||||
0.0475071 0.629821
|
||||
0.0465663 0.632643
|
||||
0.0446849 0.634525
|
||||
0.0409219 0.635466
|
||||
0.037159 0.635466
|
||||
0.0343368 0.636406
|
||||
0.0296331 0.637347
|
||||
0.0239887 0.638288
|
||||
0.0164628 0.638288
|
||||
0.0117592 0.638288
|
||||
0.0070555 0.638288
|
||||
0.000470367 0.638288
|
||||
-0.00517404 0.639229
|
||||
-0.0070555 0.639229
|
||||
-0.0117592 0.64111
|
||||
-0.0155221 0.642992
|
||||
-0.0202258 0.644873
|
||||
-0.0221072 0.648636
|
||||
-0.0268109 0.65334
|
||||
-0.0305738 0.659925
|
||||
-0.0352775 0.667451
|
||||
-0.037159 0.675917
|
||||
-0.0390405 0.683443
|
||||
-0.0399812 0.690969
|
||||
-0.0399812 0.700376
|
||||
-0.0399812 0.714487
|
||||
-0.037159 0.731421
|
||||
-0.0324553 0.748354
|
||||
-0.0296331 0.760583
|
||||
-0.0258702 0.773754
|
||||
-0.0211665 0.785983
|
||||
-0.0174036 0.792568
|
||||
-0.0145814 0.797272
|
||||
-0.00799624 0.805738
|
||||
0.0014111 0.815146
|
||||
0.0098777 0.823612
|
||||
0.019285 0.83302
|
||||
0.0277516 0.839605
|
||||
0.0399812 0.848071
|
||||
0.0503293 0.854657
|
||||
0.0616181 0.85842
|
||||
0.0710254 0.862183
|
||||
0.0823142 0.865945
|
||||
0.0917215 0.869708
|
||||
0.0992474 0.87159
|
||||
0.104892 0.873471
|
||||
0.110536 0.875353
|
||||
0.117121 0.877234
|
||||
0.125588 0.879116
|
||||
0.133114 0.880997
|
||||
0.139699 0.881938
|
||||
0.143462 0.882879
|
||||
0.151929 0.882879
|
||||
0.159454 0.88476
|
||||
0.165099 0.885701
|
||||
0.169802 0.885701
|
||||
0.172625 0.885701
|
||||
0.176388 0.885701
|
||||
0.178269 0.886642
|
||||
0.180151 0.887582
|
||||
0.182032 0.887582
|
||||
0.184854 0.889464
|
||||
0.188617 0.891345
|
||||
0.191439 0.895108
|
||||
0.195202 0.899812
|
||||
0.200847 0.906397
|
||||
0.209313 0.915804
|
||||
0.219661 0.926152
|
||||
0.230009 0.9365
|
||||
0.249765 0.952493
|
||||
0.263876 0.96096
|
||||
0.272342 0.968485
|
||||
0.28175 0.976011
|
||||
0.297742 0.988241
|
||||
0.311853 0.998589
|
||||
0.325964 1.008
|
||||
0.347601 1.02211
|
||||
0.361712 1.02963
|
||||
0.377705 1.0381
|
||||
0.389934 1.04374
|
||||
0.40969 1.05033
|
||||
0.432267 1.05691
|
||||
0.451082 1.06256
|
||||
0.466134 1.0682
|
||||
0.477422 1.07008
|
||||
0.491533 1.07291
|
||||
0.502822 1.07385
|
||||
0.515052 1.07667
|
||||
0.529163 1.07667
|
||||
0.547977 1.07761
|
||||
0.55174 1.07761
|
||||
0.568674 1.07761
|
||||
0.587488 1.07761
|
||||
0.60254 1.07479
|
||||
0.616651 1.07385
|
||||
0.625118 1.07103
|
||||
0.62794 1.06914
|
||||
0.628881 1.06632
|
||||
0.629821 1.06068
|
||||
0.629821 1.05033
|
||||
0.629821 1.04374
|
||||
0.625118 1.03434
|
||||
0.620414 1.02681
|
||||
0.612888 1.01552
|
||||
0.606303 1.00894
|
||||
0.599718 1.00329
|
||||
0.593133 0.996707
|
||||
0.582785 0.990122
|
||||
0.566792 0.982596
|
||||
0.556444 0.977893
|
||||
0.547977 0.972248
|
||||
0.540452 0.968485
|
||||
0.531044 0.963782
|
||||
0.516933 0.957197
|
||||
0.504704 0.952493
|
||||
0.489652 0.945908
|
||||
0.470837 0.938382
|
||||
0.455786 0.932738
|
||||
0.441675 0.928034
|
||||
0.424741 0.922389
|
||||
0.394638 0.913923
|
||||
0.379586 0.909219
|
||||
0.366416 0.905456
|
||||
0.353246 0.900753
|
||||
0.338194 0.896049
|
||||
0.328786 0.893227
|
||||
0.318438 0.890405
|
||||
0.310913 0.887582
|
||||
0.303387 0.88476
|
||||
0.296802 0.881938
|
||||
0.291157 0.879116
|
||||
0.286453 0.876294
|
||||
0.279868 0.874412
|
||||
0.272342 0.869708
|
||||
0.268579 0.866886
|
||||
0.263876 0.863123
|
||||
0.259172 0.860301
|
||||
0.257291 0.857479
|
||||
0.25635 0.857479
|
||||
0.253528 0.854657
|
||||
0.251646 0.852775
|
||||
0.250706 0.851834
|
||||
0.249765 0.849953
|
||||
0.247883 0.847131
|
||||
0.246943 0.84619
|
||||
0.246002 0.844309
|
||||
0.246002 0.842427
|
||||
0.246002 0.841486
|
||||
0.246002 0.840546
|
||||
0.246002 0.839605
|
||||
0.246002 0.838664
|
||||
0.246002 0.834901
|
||||
0.246002 0.831138
|
||||
0.246002 0.828316
|
||||
0.246002 0.826435
|
||||
0.246002 0.824553
|
||||
0.246943 0.821731
|
||||
0.248824 0.818909
|
||||
0.250706 0.817027
|
||||
0.252587 0.812324
|
||||
0.255409 0.810442
|
||||
0.257291 0.806679
|
||||
0.259172 0.803857
|
||||
0.261054 0.800094
|
||||
0.262935 0.797272
|
||||
0.264817 0.793509
|
||||
0.266698 0.789746
|
||||
0.26952 0.785042
|
||||
0.271402 0.779398
|
||||
0.274224 0.775635
|
||||
0.276105 0.76905
|
||||
0.277987 0.760583
|
||||
0.278928 0.754939
|
||||
0.279868 0.749294
|
||||
0.280809 0.74365
|
||||
0.28175 0.738946
|
||||
0.28269 0.733302
|
||||
0.283631 0.729539
|
||||
0.285513 0.725776
|
||||
0.286453 0.723895
|
||||
0.288335 0.720132
|
||||
0.291157 0.71825
|
||||
0.293979 0.716369
|
||||
0.299624 0.714487
|
||||
0.305268 0.713547
|
||||
0.309972 0.713547
|
||||
0.314675 0.713547
|
||||
0.322201 0.714487
|
||||
0.328786 0.714487
|
||||
0.341016 0.715428
|
||||
0.353246 0.716369
|
||||
0.361712 0.716369
|
||||
0.37206 0.716369
|
||||
0.386171 0.716369
|
||||
0.399341 0.71825
|
||||
0.408749 0.71825
|
||||
0.415334 0.71825
|
||||
0.416275 0.71825
|
||||
0.42286 0.715428
|
||||
0.430386 0.713547
|
||||
0.438852 0.710724
|
||||
0.445437 0.708843
|
||||
0.450141 0.706961
|
||||
0.456726 0.702258
|
||||
0.46143 0.699436
|
||||
0.467074 0.693791
|
||||
0.4746 0.688147
|
||||
0.479304 0.683443
|
||||
0.482126 0.678739
|
||||
0.48683 0.673095
|
||||
0.493415 0.663688
|
||||
0.499059 0.655221
|
||||
0.503763 0.647695
|
||||
0.507526 0.639229
|
||||
0.51317 0.626999
|
||||
0.516933 0.619473
|
||||
0.518815 0.612888
|
||||
0.521637 0.606303
|
||||
0.5254 0.598777
|
||||
0.527281 0.592192
|
||||
0.530103 0.585607
|
||||
0.531985 0.579962
|
||||
0.533866 0.576199
|
||||
0.536689 0.569614
|
||||
0.53857 0.563029
|
||||
0.540452 0.554563
|
||||
0.541392 0.546096
|
||||
0.544214 0.535748
|
||||
0.545155 0.528222
|
||||
0.545155 0.519755
|
||||
0.546096 0.51223
|
||||
0.547037 0.504704
|
||||
0.547037 0.496237
|
||||
0.547037 0.489652
|
||||
0.547037 0.484008
|
||||
0.547037 0.477422
|
||||
0.547037 0.470837
|
||||
0.547037 0.462371
|
||||
0.547037 0.453904
|
||||
0.546096 0.44826
|
||||
0.546096 0.444497
|
||||
0.546096 0.442615
|
||||
0.546096 0.439793
|
||||
0.546096 0.437912
|
||||
0.549859 0.43603
|
||||
0.556444 0.435089
|
||||
0.56397 0.434149
|
||||
0.571496 0.433208
|
||||
0.57714 0.431326
|
||||
0.588429 0.430386
|
||||
0.593133 0.430386
|
||||
0.597836 0.430386
|
||||
0.609125 0.431326
|
||||
0.617592 0.431326
|
||||
0.624177 0.431326
|
||||
0.630762 0.429445
|
||||
0.633584 0.427563
|
||||
0.636406 0.424741
|
||||
0.637347 0.42286
|
||||
0.639229 0.420038
|
||||
0.642051 0.415334
|
||||
0.645814 0.401223
|
||||
0.648636 0.395579
|
||||
0.649577 0.389934
|
||||
0.651458 0.379586
|
||||
0.65334 0.371119
|
||||
0.65334 0.365475
|
||||
0.65334 0.359831
|
||||
0.65334 0.350423
|
||||
0.652399 0.341957
|
||||
0.649577 0.334431
|
||||
0.645814 0.325964
|
||||
0.64111 0.316557
|
||||
0.635466 0.309972
|
||||
0.629821 0.304327
|
||||
0.62794 0.302446
|
||||
0.620414 0.293039
|
||||
0.613829 0.288335
|
||||
0.607244 0.283631
|
||||
0.599718 0.278928
|
||||
0.595014 0.276105
|
||||
0.588429 0.272342
|
||||
0.581844 0.268579
|
||||
0.575259 0.265757
|
||||
0.568674 0.263876
|
||||
0.563029 0.260113
|
||||
0.557385 0.258231
|
||||
0.552681 0.25635
|
||||
0.548918 0.254468
|
||||
0.543274 0.253528
|
||||
0.536689 0.252587
|
||||
0.531044 0.251646
|
||||
0.524459 0.250706
|
||||
0.519755 0.250706
|
||||
0.515052 0.250706
|
||||
0.509407 0.249765
|
||||
0.504704 0.249765
|
||||
0.499059 0.248824
|
||||
0.493415 0.248824
|
||||
0.488711 0.248824
|
||||
0.482126 0.248824
|
||||
0.477422 0.248824
|
||||
0.473659 0.248824
|
||||
0.471778 0.248824
|
||||
0.469897 0.248824
|
||||
0.468015 0.248824
|
||||
0.467074 0.247883
|
||||
n
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
// Copyright (c) 2019-2020 X, The Moonshot Factory (USA).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Author(s) : Pierre Alliez pierre.alliez@inria.fr
|
||||
// : Michael Hemmer mhsaar@gmail.com
|
||||
// : Cedric Portaneri cportaneri@gmail.com
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_DEMO_PSLG_H
|
||||
#define CGAL_ALPHA_WRAP_2_DEMO_PSLG_H
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
template <class GeomTraits>
|
||||
class Component
|
||||
: public std::vector<typename GeomTraits::Point_2>
|
||||
{
|
||||
typedef GeomTraits Geom_traits;
|
||||
typedef typename Geom_traits::FT FT;
|
||||
typedef typename Geom_traits::Point_2 Point;
|
||||
typedef typename Geom_traits::Vector_2 Vector;
|
||||
|
||||
typedef typename std::vector<Point> Base;
|
||||
typedef typename Base::iterator iterator;
|
||||
|
||||
public:
|
||||
bool is_closed = false;
|
||||
|
||||
public:
|
||||
Component() {}
|
||||
Component(const Component& c) : Base(c) { }
|
||||
|
||||
virtual ~Component() {}
|
||||
|
||||
public:
|
||||
void set_is_closed(bool b) { is_closed = b; }
|
||||
|
||||
void resample(const FT d) {
|
||||
const FT sqd = CGAL::square(d);
|
||||
while(resample_once(sqd)) { }
|
||||
}
|
||||
|
||||
bool resample_once(const FT sqd)
|
||||
{
|
||||
for(iterator it = this->begin() ; it != (this->end()-1); it++) {
|
||||
const Point& curr = *it;
|
||||
const Point& next = next_loop(it);
|
||||
if(sqdistance(curr,next) > sqd) {
|
||||
this->insert(it+1, CGAL::midpoint(next,curr));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FT sqdistance(const Point& a, const Point& b) {
|
||||
return CGAL::squared_distance(a, b);
|
||||
}
|
||||
|
||||
FT distance(const Point& a, const Point& b) {
|
||||
return CGAL::approximate_sqrt(sqdistance(a, b));
|
||||
}
|
||||
|
||||
const Point& next_loop(iterator it) {
|
||||
it++;
|
||||
if(it == this->end())
|
||||
return *this->begin();
|
||||
else
|
||||
return *it;
|
||||
}
|
||||
|
||||
const Point& prev_loop(iterator it) {
|
||||
if(it == this->begin())
|
||||
it = this->end();
|
||||
return *(--it);
|
||||
}
|
||||
|
||||
void smooth(unsigned int nb_iter) {
|
||||
for(unsigned int i=0;i<nb_iter;i++)
|
||||
smooth();
|
||||
}
|
||||
|
||||
void smooth()
|
||||
{
|
||||
if(this->size() < 3)
|
||||
return;
|
||||
|
||||
Component tmp;
|
||||
for(iterator it = this->begin(); it != this->end(); it++)
|
||||
{
|
||||
Vector v1 = prev_loop(it) - CGAL::ORIGIN;
|
||||
Vector v2 = *it - CGAL::ORIGIN;
|
||||
Vector v3 = next_loop(it) - CGAL::ORIGIN;
|
||||
tmp.push_back(CGAL::ORIGIN + (v1+2*v2+v3)/4);
|
||||
}
|
||||
|
||||
if(is_closed) {
|
||||
tmp.set_is_closed(true);
|
||||
tmp.push_back(tmp[0]);
|
||||
}
|
||||
|
||||
*this = tmp;
|
||||
}
|
||||
|
||||
void range(FT* xrange, FT* yrange)
|
||||
{
|
||||
for(iterator it = this->begin(); it != this->end(); it++)
|
||||
{
|
||||
const Point& p = *it;
|
||||
xrange[0] = std::min(xrange[0],p.x());
|
||||
xrange[1] = std::max(xrange[1],p.x());
|
||||
yrange[0] = std::min(yrange[0],p.y());
|
||||
yrange[1] = std::max(yrange[1],p.y());
|
||||
}
|
||||
}
|
||||
|
||||
void normalize(const FT xmin, const FT ymin, const FT range)
|
||||
{
|
||||
for(iterator it = this->begin(); it != this->end(); it++)
|
||||
{
|
||||
Point& p = *it;
|
||||
p = Point((p.x()-xmin)/range,
|
||||
(p.y()-ymin)/range);
|
||||
}
|
||||
}
|
||||
|
||||
void translate(const FT dx, const FT dy)
|
||||
{
|
||||
for(iterator it = this->begin(); it != this->end(); it++)
|
||||
{
|
||||
Point& p = *it;
|
||||
p = p + Vector(dx,dy);
|
||||
}
|
||||
}
|
||||
|
||||
FT length() {
|
||||
FT total_length = 0.;
|
||||
for(iterator it = this->begin() ; it != this->end()-1; it++) {
|
||||
const Point& curr = *it;
|
||||
const Point& next = next_loop(it);
|
||||
total_length += distance(curr,next);
|
||||
}
|
||||
return total_length;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename GeomTraits>
|
||||
class Pslg
|
||||
: public std::vector<Component<GeomTraits> >
|
||||
{
|
||||
public:
|
||||
typedef GeomTraits Geom_traits;
|
||||
typedef typename GeomTraits::FT FT;
|
||||
|
||||
typedef internal::Component<GeomTraits> Component;
|
||||
typedef typename std::vector<Component> Base;
|
||||
typedef typename Base::iterator iterator;
|
||||
typedef typename Base::const_iterator citerator;
|
||||
|
||||
public:
|
||||
Pslg() {}
|
||||
virtual ~Pslg() {}
|
||||
|
||||
public:
|
||||
void normalize() {
|
||||
FT xrange[3] = {1e38,-1e38,0};
|
||||
FT yrange[3] = {1e38,-1e38,0};
|
||||
|
||||
for(iterator c = this->begin(); c != this->end(); ++c) {
|
||||
Component& component = *c;
|
||||
component.range(xrange,yrange);
|
||||
}
|
||||
xrange[2] = xrange[1]-xrange[0];
|
||||
yrange[2] = yrange[1]-yrange[0];
|
||||
FT range = std::max(xrange[2],yrange[2]);
|
||||
|
||||
for(iterator c = this->begin();
|
||||
c != this->end();
|
||||
c++) {
|
||||
Component& component = *c;
|
||||
component.normalize(xrange[0],yrange[0],range);
|
||||
}
|
||||
}
|
||||
|
||||
void compute_range(FT& xmin, FT& xmax,
|
||||
FT& ymin, FT& ymax,
|
||||
const FT stretch)
|
||||
{
|
||||
FT xrange[3] = {1e38,-1e38,0};
|
||||
FT yrange[3] = {1e38,-1e38,0};
|
||||
|
||||
for(iterator c = this->begin(); c != this->end(); ++c) {
|
||||
Component& component = *c;
|
||||
component.range(xrange,yrange);
|
||||
}
|
||||
|
||||
xrange[2] = xrange[1]-xrange[0];
|
||||
yrange[2] = yrange[1]-yrange[0];
|
||||
FT range = stretch * std::max(xrange[2],yrange[2]);
|
||||
FT xmid = 0.5*(xrange[0]+xrange[1]);
|
||||
FT ymid = 0.5*(yrange[0]+yrange[1]);
|
||||
xmin = xmid-0.5*range;
|
||||
xmax = xmid+0.5*range;
|
||||
ymin = ymid-0.5*range;
|
||||
ymax = ymid+0.5*range;
|
||||
}
|
||||
|
||||
void resample(const FT d)
|
||||
{
|
||||
for(iterator c = this->begin();
|
||||
c != this->end();
|
||||
c++) {
|
||||
Component& component = *c;
|
||||
component.resample(d);
|
||||
}
|
||||
}
|
||||
|
||||
void smooth(const unsigned int iter = 1)
|
||||
{
|
||||
for(iterator c = this->begin();
|
||||
c != this->end();
|
||||
c++) {
|
||||
Component& component = *c;
|
||||
component.smooth(iter);
|
||||
}
|
||||
}
|
||||
|
||||
size_t number_of_points() const
|
||||
{
|
||||
size_t num_points = 0;
|
||||
for(citerator c = this->cbegin();
|
||||
c != this->cend();
|
||||
c++) {
|
||||
const Component& component = *c;
|
||||
num_points += component.size();
|
||||
}
|
||||
return num_points;
|
||||
}
|
||||
|
||||
CGAL::Bbox_2 bbox_2() const
|
||||
{
|
||||
CGAL::Bbox_2 bbox;
|
||||
for(citerator c = this->cbegin(); c != this->cend(); ++c) {
|
||||
const Component& component = *c;
|
||||
bbox += CGAL::bbox_2(component.cbegin(), component.cend());
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Kernel, class Triangulation>
|
||||
Pslg<Kernel> extract_pslg_2_soup(const Triangulation& tr)
|
||||
{
|
||||
using Component = Component<Kernel>;
|
||||
using Pslg2 = Pslg<Kernel>;
|
||||
|
||||
using Face_handle = typename Triangulation::Face_handle;
|
||||
|
||||
Pslg2 pslg_soup;
|
||||
for (const auto &edge : tr.all_edges())
|
||||
{
|
||||
if (tr.is_infinite(edge)) continue;
|
||||
Face_handle fh2 = edge.first;
|
||||
int id = edge.second;
|
||||
const Face_handle& neighbor = fh2->neighbor(id);
|
||||
if (fh2->is_outside() == neighbor->is_outside()) continue;
|
||||
|
||||
Component new_cmp;
|
||||
new_cmp.push_back(fh2->vertex((id+1)%3)->point());
|
||||
new_cmp.push_back(fh2->vertex((id+2)%3)->point());
|
||||
pslg_soup.push_back(new_cmp);
|
||||
}
|
||||
|
||||
return pslg_soup;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_DEMO_PSLG_H
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright (c) 2019-2020 X, The Moonshot Factory (USA).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Author(s) : Pierre Alliez pierre.alliez@inria.fr
|
||||
// : Michael Hemmer mhsaar@gmail.com
|
||||
// : Cedric Portaneri cportaneri@gmail.com
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_IO_H
|
||||
#define CGAL_ALPHA_WRAP_2_IO_H
|
||||
|
||||
#include "pslg_2.h"
|
||||
|
||||
#include <CGAL/IO/io.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace IO {
|
||||
|
||||
enum IO_exit_code
|
||||
{
|
||||
VALID_INPUT_POLYLINES = 0,
|
||||
UNREADABLE_INPUT = 1,
|
||||
INPUT_IS_EMPTY = 2,
|
||||
};
|
||||
|
||||
inline int string_to_int(const std::string &str)
|
||||
{
|
||||
char *end;
|
||||
return static_cast<int>(strtol(str.c_str(), &end, 10)); // NO Large INT
|
||||
}
|
||||
|
||||
inline double string_to_double(const std::string &str)
|
||||
{
|
||||
return atof(str.c_str());
|
||||
}
|
||||
|
||||
inline bool check_extension(std::string file_name,
|
||||
const std::string& extension)
|
||||
{
|
||||
size_t find_ext = file_name.find_last_of(".");
|
||||
if (find_ext == std::string::npos)
|
||||
return false;
|
||||
std::string file_name_ext = file_name.substr(find_ext, file_name.size() - 1);
|
||||
return (file_name_ext.compare(extension) == 0);
|
||||
}
|
||||
|
||||
template <typename GeomTraits>
|
||||
inline bool read_input_dat_file(std::ifstream &in,
|
||||
internal::Pslg<GeomTraits>& input_polylines)
|
||||
{
|
||||
using Point_2 = typename GeomTraits::Point_2;
|
||||
|
||||
internal::Component<GeomTraits> component;
|
||||
std::string line;
|
||||
while(std::getline(in, line))
|
||||
{
|
||||
if(line[0] == 'n') {
|
||||
input_polylines.push_back(component);
|
||||
component.clear();
|
||||
} else {
|
||||
double x,y;
|
||||
std::istringstream iss(line);
|
||||
if(iss >> x >> y)
|
||||
component.push_back(Point_2(x,y));
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename GeomTraits>
|
||||
inline IO_exit_code read_input_polylines_file(const std::string &file_name,
|
||||
internal::Pslg<GeomTraits>& input_polylines)
|
||||
{
|
||||
std::cout << "Read surface polylines... " << file_name << std::endl;
|
||||
std::ifstream in(file_name, std::ios::in);
|
||||
if (!in) {
|
||||
std::cout << "Unable to open the file." << std::endl;
|
||||
return UNREADABLE_INPUT;
|
||||
}
|
||||
|
||||
if (!check_extension(file_name, ".dat")) {
|
||||
return UNREADABLE_INPUT;
|
||||
} else if (!read_input_dat_file(in, input_polylines)) {
|
||||
std::cout << "Unable to read stl file." << std::endl;
|
||||
return UNREADABLE_INPUT;
|
||||
}
|
||||
in.close();
|
||||
|
||||
if (input_polylines.empty()) {
|
||||
std::cout << "Input is empty." << std::endl;
|
||||
return INPUT_IS_EMPTY;
|
||||
}
|
||||
|
||||
return VALID_INPUT_POLYLINES;
|
||||
}
|
||||
|
||||
template <typename GeomTraits>
|
||||
inline bool write_outputput_dat_file(std::ofstream &out,
|
||||
const internal::Pslg<GeomTraits>& output_polylines)
|
||||
{
|
||||
for(const auto& component : output_polylines) {
|
||||
for(const auto& point : component) {
|
||||
out << point.x() << " " << point.y() << "\n";
|
||||
}
|
||||
out << "n\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename GeomTraits>
|
||||
inline void write_output_polylines_file(const std::string &file_name,
|
||||
const internal::Pslg<GeomTraits>& output_polylines)
|
||||
{
|
||||
std::ofstream out(file_name);
|
||||
if (check_extension(file_name, ".dat")) {
|
||||
write_outputput_dat_file(out, output_polylines);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace IO
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_IO_H
|
||||
|
|
@ -0,0 +1,759 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// 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) : Pierre Alliez
|
||||
// Cedric Portaneri,
|
||||
// Mael Rouxel-Labbé
|
||||
// Andreas Fabri
|
||||
// Michael Hemmer
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_DEMO_SCENE_H
|
||||
#define CGAL_ALPHA_WRAP_2_DEMO_SCENE_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <QtOpenGL>
|
||||
#include <QString>
|
||||
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
struct Stats_data_structure {
|
||||
double nb_proj = 0.;
|
||||
double nb_steiner = 0.;
|
||||
int nb_gate_traversed = 0;
|
||||
};
|
||||
|
||||
class Scene
|
||||
{
|
||||
private:
|
||||
Pslg m_input;
|
||||
Pslg m_wrap;
|
||||
|
||||
double m_alpha = 20.;
|
||||
double m_offset = 600.;
|
||||
bool is_alpha_relative = true;
|
||||
bool is_offset_relative = true;
|
||||
bool is_step_by_step = false;
|
||||
|
||||
Alpha_wrapper m_wrapper;
|
||||
Stats_data_structure m_stats;
|
||||
|
||||
Point_2 m_mouse_pos;
|
||||
|
||||
bool view_input = true;
|
||||
bool view_alpha_wrap = true;
|
||||
bool view_dt2_inside_outside = true;
|
||||
bool view_dt2_edge = true;
|
||||
bool view_voronoi = true;
|
||||
bool view_empty_alpha_pencils = false;
|
||||
bool view_steiner_point = true;
|
||||
bool view_next_gate = true;
|
||||
bool view_next_gate_pencil = false;
|
||||
|
||||
QString screenshot_folder = "";
|
||||
QString screenshot_filename = "";
|
||||
int screenshot_number = 0;
|
||||
|
||||
public:
|
||||
Scene() { }
|
||||
|
||||
~Scene() { clear(); }
|
||||
|
||||
void toggle_view_input() { view_input = !view_input; }
|
||||
|
||||
void toggle_view_alpha_wrap() { view_alpha_wrap = !view_alpha_wrap; }
|
||||
|
||||
void toggle_view_dt2_inside_outside() { view_dt2_inside_outside = !view_dt2_inside_outside; }
|
||||
|
||||
void toggle_view_dt2_edge() { view_dt2_edge = !view_dt2_edge; }
|
||||
|
||||
void toggle_view_voronoi() { view_voronoi = !view_voronoi; }
|
||||
|
||||
void toggle_view_empty_alpha_pencils() { view_empty_alpha_pencils = !view_empty_alpha_pencils; }
|
||||
|
||||
void toggle_view_steiner_point() { view_steiner_point = !view_steiner_point; }
|
||||
|
||||
void toggle_view_next_gate() { view_next_gate = !view_next_gate; }
|
||||
|
||||
void toggle_view_next_gate_pencil() { view_next_gate_pencil = !view_next_gate_pencil; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_input.clear();
|
||||
m_wrap.clear();
|
||||
m_wrapper.clear();
|
||||
m_stats = {};
|
||||
}
|
||||
|
||||
const Pslg& get_input() const {
|
||||
return m_input;
|
||||
}
|
||||
|
||||
Pslg& get_alpha_wrap() {
|
||||
return m_wrap;
|
||||
}
|
||||
|
||||
const Alpha_wrapper& get_wrapper() const {
|
||||
return m_wrapper;
|
||||
}
|
||||
|
||||
Alpha_wrapper& get_wrapper() {
|
||||
return m_wrapper;
|
||||
}
|
||||
|
||||
double get_alpha() const {
|
||||
return m_alpha;
|
||||
}
|
||||
|
||||
void set_alpha(const double a) {
|
||||
m_alpha = a;
|
||||
}
|
||||
|
||||
double get_offset() const {
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
void set_offset(const double o) {
|
||||
m_offset = o;
|
||||
}
|
||||
|
||||
bool get_is_alpha_relative() const {
|
||||
return is_alpha_relative;
|
||||
}
|
||||
|
||||
void set_is_alpha_relative(const bool b) {
|
||||
is_alpha_relative = b;
|
||||
}
|
||||
|
||||
bool get_is_offset_relative() const {
|
||||
return is_offset_relative;
|
||||
}
|
||||
|
||||
void set_is_offset_relative(const bool b) {
|
||||
is_offset_relative = b;
|
||||
}
|
||||
|
||||
bool get_is_step_by_step() const {
|
||||
return is_step_by_step;
|
||||
}
|
||||
|
||||
void set_is_step_by_step(const bool b) {
|
||||
is_step_by_step = b;
|
||||
}
|
||||
|
||||
QString get_screenshot_folder() const {
|
||||
return screenshot_folder;
|
||||
}
|
||||
|
||||
void set_screenshot_folder(QString s) {
|
||||
screenshot_folder = s;
|
||||
}
|
||||
|
||||
QString get_screenshot_filename() const {
|
||||
return screenshot_filename;
|
||||
}
|
||||
|
||||
void set_screenshot_filename(const QString& s) {
|
||||
screenshot_filename = s;
|
||||
}
|
||||
|
||||
int get_screenshot_number() const {
|
||||
return screenshot_number;
|
||||
}
|
||||
|
||||
void set_screenshot_number(const int i) {
|
||||
screenshot_number = i;
|
||||
}
|
||||
|
||||
double get_nb_proj() const {
|
||||
return m_stats.nb_proj;
|
||||
}
|
||||
|
||||
double get_nb_steiner() const {
|
||||
return m_stats.nb_steiner;
|
||||
}
|
||||
|
||||
int get_nb_gate_traversed() const {
|
||||
return m_stats.nb_gate_traversed;
|
||||
}
|
||||
|
||||
void reset_stats() {
|
||||
m_stats = Stats_data_structure();
|
||||
}
|
||||
|
||||
void set_mouse_pos(const Point_2& pos) { m_mouse_pos = pos; }
|
||||
|
||||
bool load(const QString& filename)
|
||||
{
|
||||
Pslg pslg;
|
||||
AW2::IO::IO_exit_code success_read = AW2::IO::read_input_polylines_file(filename.toUtf8().constData(), pslg);
|
||||
if(success_read == AW2::IO::UNREADABLE_INPUT) {
|
||||
std::cerr << "Warning: the input is unreadable \n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(success_read == AW2::IO::INPUT_IS_EMPTY) {
|
||||
std::cout << "The input is empty \n";
|
||||
return false;
|
||||
}
|
||||
|
||||
clear();
|
||||
m_input = std::move(pslg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void render()
|
||||
{
|
||||
if(view_input)
|
||||
gl_draw_pslg(m_input, true, 250, 0, 0);
|
||||
if(view_alpha_wrap)
|
||||
gl_draw_pslg(m_wrap, false, 0, 0, 250);
|
||||
if(view_dt2_inside_outside)
|
||||
gl_draw_dt2_inside_outside();
|
||||
if(view_dt2_edge)
|
||||
gl_draw_dt2_edge();
|
||||
if(view_voronoi)
|
||||
gl_draw_voronoi();
|
||||
if(view_empty_alpha_pencils)
|
||||
gl_draw_all_pencils();
|
||||
if(view_steiner_point)
|
||||
gl_draw_steiner_point();
|
||||
if(view_next_gate)
|
||||
gl_draw_next_gate();
|
||||
if(view_next_gate_pencil) {
|
||||
gl_draw_next_gate_pencil();
|
||||
}
|
||||
}
|
||||
|
||||
void gl_draw_pslg(const Pslg& pslg,
|
||||
bool /*force_close*/,
|
||||
const unsigned char r,
|
||||
const unsigned char g,
|
||||
const unsigned char b)
|
||||
{
|
||||
if(pslg.empty())
|
||||
return;
|
||||
|
||||
::glLineWidth(3.0f);
|
||||
::glColor3ub(r,g,b);
|
||||
::glBegin(GL_LINES);
|
||||
for(const auto& cmp : pslg) {
|
||||
if(cmp.size() < 2)
|
||||
continue;
|
||||
for(size_t i = 1; i < cmp.size(); ++i) {
|
||||
const Point_2& start = cmp[i-1];
|
||||
const Point_2& end = cmp[i];
|
||||
::glVertex2d(start.x(),start.y());
|
||||
::glVertex2d(end.x(),end.y());
|
||||
}
|
||||
}
|
||||
::glEnd();
|
||||
}
|
||||
|
||||
void gl_draw_dt2_inside_outside() const
|
||||
{
|
||||
const Triangulation& tr = m_wrapper.triangulation();
|
||||
|
||||
::glEnable(GL_BLEND);
|
||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
::glColor4f(0.25,0.25,0.25,0.8);
|
||||
::glBegin(GL_TRIANGLES);
|
||||
for(const Face_handle& fh2 : tr.finite_face_handles()) {
|
||||
if(fh2->is_outside())
|
||||
continue;
|
||||
const Point_2& p1 = fh2->vertex(0)->point();
|
||||
const Point_2& p2 = fh2->vertex(1)->point();
|
||||
const Point_2& p3 = fh2->vertex(2)->point();
|
||||
::glVertex2d(p1.x(),p1.y());
|
||||
::glVertex2d(p2.x(),p2.y());
|
||||
::glVertex2d(p3.x(),p3.y());
|
||||
}
|
||||
::glEnd();
|
||||
::glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void gl_draw_dt2_edge() const
|
||||
{
|
||||
const Triangulation& tr = m_wrapper.triangulation();
|
||||
|
||||
::glColor3ub(127,127,127);
|
||||
::glLineWidth(1.0f);
|
||||
::glBegin(GL_LINES);
|
||||
for(const Face_handle& fh2 : tr.finite_face_handles()) {
|
||||
const Point_2& p1 = fh2->vertex(0)->point();
|
||||
const Point_2& p2 = fh2->vertex(1)->point();
|
||||
const Point_2& p3 = fh2->vertex(2)->point();
|
||||
::glVertex2d(p1.x(),p1.y());
|
||||
::glVertex2d(p2.x(),p2.y());
|
||||
::glVertex2d(p2.x(),p2.y());
|
||||
::glVertex2d(p3.x(),p3.y());
|
||||
::glVertex2d(p3.x(),p3.y());
|
||||
::glVertex2d(p1.x(),p1.y());
|
||||
}
|
||||
::glEnd();
|
||||
}
|
||||
|
||||
void gl_draw_voronoi() const
|
||||
{
|
||||
const Triangulation& tr = m_wrapper.triangulation();
|
||||
|
||||
::glColor3ub(133, 193, 233);
|
||||
::glLineWidth(2.5f);
|
||||
|
||||
::glBegin(GL_LINES);
|
||||
for(const Edge& eh2 : tr.finite_edges()) {
|
||||
const Face_handle& f = eh2.first;
|
||||
const Face_handle& n = f->neighbor(eh2.second);
|
||||
Point_2 cc1 = f->circumcenter();
|
||||
Point_2 cc2 = n->circumcenter();
|
||||
if(tr.is_infinite(f)) {
|
||||
for(int i=0; i<3; ++i) {
|
||||
if(!tr.is_infinite(f->vertex(i)))
|
||||
continue;
|
||||
Edge boundary_edge(f, i);
|
||||
Segment_2 boundary_seg = AW2i::delaunay_edge_to_segment(boundary_edge);
|
||||
Line_2 boundary_edge_line(boundary_seg);
|
||||
Line_2 perpendicular = boundary_edge_line.perpendicular(cc1);
|
||||
|
||||
// look for the opposite vertex to get the direction where the triangulation is finite
|
||||
Edge mirror_boundary_edge = tr.mirror_edge(boundary_edge);
|
||||
Vertex_handle oposite_vertex = n->vertex(mirror_boundary_edge.second);
|
||||
Point_2 oposite_vertex_projection = perpendicular.projection(tr.point(oposite_vertex));
|
||||
Vector_2 infinite_cc_unit_vec = (cc1 - oposite_vertex_projection);
|
||||
infinite_cc_unit_vec /= CGAL::sqrt(CGAL::to_double(infinite_cc_unit_vec.squared_length()));
|
||||
cc1 = (cc1 + (infinite_cc_unit_vec * 1e5));
|
||||
}
|
||||
}
|
||||
if(tr.is_infinite(n)) {
|
||||
for(int i=0; i<3; ++i) {
|
||||
if(!tr.is_infinite(n->vertex(i)))
|
||||
continue;
|
||||
Edge boundary_edge(n, i);
|
||||
Segment_2 boundary_seg = AW2i::delaunay_edge_to_segment(boundary_edge);
|
||||
Line_2 boundary_edge_line(boundary_seg);
|
||||
Line_2 perpendicular = boundary_edge_line.perpendicular(cc2);
|
||||
|
||||
// look for the opposite vertex to get the direction where the triangulation is finite
|
||||
Edge mirror_boundary_edge = tr.mirror_edge(boundary_edge);
|
||||
Vertex_handle oposite_vertex = f->vertex(mirror_boundary_edge.second);
|
||||
Point_2 oposite_vertex_projection = perpendicular.projection(tr.point(oposite_vertex));
|
||||
Vector_2 infinite_cc_unit_vec = (cc2-oposite_vertex_projection);
|
||||
infinite_cc_unit_vec /= CGAL::sqrt(CGAL::to_double(infinite_cc_unit_vec.squared_length()));
|
||||
cc2 = (cc2 + (infinite_cc_unit_vec * 1e5));
|
||||
}
|
||||
}
|
||||
::glVertex2d(cc1.x(),cc1.y());
|
||||
::glVertex2d(cc2.x(),cc2.y());
|
||||
}
|
||||
::glEnd();
|
||||
}
|
||||
|
||||
void gl_draw_circle(const Point_2& center, const FT& sq_radius) const
|
||||
{
|
||||
double radius = std::sqrt(CGAL::to_double(sq_radius));
|
||||
::glBegin(GL_LINE_LOOP);
|
||||
const double deg_to_rad = 3.14159/180;
|
||||
for(int i=0; i < 360; ++i) {
|
||||
double degree_radian = i*deg_to_rad;
|
||||
::glVertex2d(center.x() + cos(degree_radian)*radius,
|
||||
center.y() + sin(degree_radian)*radius);
|
||||
}
|
||||
::glEnd();
|
||||
}
|
||||
|
||||
void gl_draw_circle_cut_by_edge(const Point_2& center, FT sq_radius,
|
||||
const Edge& eh2) const
|
||||
{
|
||||
const Point_2& p1 = eh2.first->vertex((eh2.second+1)%3)->point();
|
||||
const Point_2& p2 = eh2.first->vertex((eh2.second+2)%3)->point();
|
||||
Point_2 p3;
|
||||
if(m_wrapper.triangulation().is_infinite(eh2.first->vertex(eh2.second))) {
|
||||
p3 = m_wrapper.triangulation().mirror_edge(eh2).first->vertex(
|
||||
m_wrapper.triangulation().mirror_edge(eh2).second)->point();
|
||||
} else {
|
||||
p3 = eh2.first->vertex(eh2.second)->point();
|
||||
}
|
||||
|
||||
Line_2 eh2_line(p1,p2);
|
||||
bool is_negative_side_interior = false;
|
||||
if((eh2_line.oriented_side(p3) == CGAL::ON_NEGATIVE_SIDE) && !eh2.first->is_outside()) {
|
||||
is_negative_side_interior = true;
|
||||
} else if((eh2_line.oriented_side(p3) == CGAL::ON_POSITIVE_SIDE) && eh2.first->is_outside()) {
|
||||
is_negative_side_interior = true;
|
||||
}
|
||||
double radius = std::sqrt(CGAL::to_double(sq_radius));
|
||||
::glBegin(GL_LINE_LOOP);
|
||||
const double deg_to_rad = 3.14159/180;
|
||||
for(int i=0; i<360; ++i)
|
||||
{
|
||||
double degree_radian = i*deg_to_rad;
|
||||
Point_2 circle_pt(center.x() + cos(degree_radian)*radius,
|
||||
center.y() + sin(degree_radian)*radius);
|
||||
if((eh2_line.oriented_side(circle_pt) == CGAL::ON_NEGATIVE_SIDE) && is_negative_side_interior)
|
||||
continue;
|
||||
if((eh2_line.oriented_side(circle_pt) == CGAL::ON_POSITIVE_SIDE) && !is_negative_side_interior)
|
||||
continue;
|
||||
::glVertex2d(circle_pt.x(),circle_pt.y());
|
||||
}
|
||||
::glEnd();
|
||||
}
|
||||
|
||||
void gl_draw_first_empty_circle_on_the_voronoi_edge(FT min_empty_circle_sq_radius,
|
||||
const Edge& delaunay_edge,
|
||||
const Triangulation& tr,
|
||||
bool is_cut_by_delaunay_edge) const
|
||||
{
|
||||
const Point_2& p1 = delaunay_edge.first->vertex((delaunay_edge.second+1)%3)->point();
|
||||
const Point_2& p2 = delaunay_edge.first->vertex((delaunay_edge.second+2)%3)->point();
|
||||
const Point_2& m = CGAL::midpoint(p1,p2);
|
||||
FT edge_half_length_squared = CGAL::squared_distance(p1,p2) / 4.0;
|
||||
FT circle_center_to_edge_midpoint_sq_distance = min_empty_circle_sq_radius - edge_half_length_squared;
|
||||
|
||||
const Face_handle& f = delaunay_edge.first;
|
||||
const Face_handle& n = f->neighbor(delaunay_edge.second);
|
||||
|
||||
Point_2 cc1 = f->circumcenter();
|
||||
Point_2 cc2 = n->circumcenter();
|
||||
if(tr.is_infinite(f))
|
||||
cc1 = CGAL::midpoint(CGAL::midpoint(p1,p2),cc1);
|
||||
if(tr.is_infinite(n))
|
||||
cc2 = CGAL::midpoint(CGAL::midpoint(p1,p2),cc2);
|
||||
|
||||
Vector_2 unit;
|
||||
if(CGAL::squared_distance(cc1,m) < circle_center_to_edge_midpoint_sq_distance) {
|
||||
unit = cc2 - cc1;
|
||||
} else {
|
||||
unit = cc1 - cc2;
|
||||
}
|
||||
FT cc1_cc2_dist = CGAL::sqrt(CGAL::to_double(unit.squared_length()));
|
||||
unit /= cc1_cc2_dist;
|
||||
|
||||
Point_2 circle_center;
|
||||
if(circle_center_to_edge_midpoint_sq_distance > 0.) {
|
||||
circle_center = m + (unit * CGAL::sqrt(CGAL::to_double(circle_center_to_edge_midpoint_sq_distance)));
|
||||
} else {
|
||||
circle_center = m;
|
||||
}
|
||||
|
||||
if(is_cut_by_delaunay_edge) {
|
||||
gl_draw_circle_cut_by_edge(circle_center, min_empty_circle_sq_radius, delaunay_edge);
|
||||
} else {
|
||||
gl_draw_circle(circle_center, min_empty_circle_sq_radius);
|
||||
}
|
||||
}
|
||||
|
||||
void gl_draw_all_pencils()
|
||||
{
|
||||
const Triangulation& tr = m_wrapper.triangulation();
|
||||
Alpha_PQ& queue = m_wrapper.queue();
|
||||
FT sq_alpha = CGAL::square(m_wrapper .alpha());
|
||||
|
||||
::glEnable(GL_BLEND);
|
||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
::glColor4f(0.25,0.25,0.25,0.25);
|
||||
::glLineWidth(0.5f);
|
||||
|
||||
std::vector<Gate> gates;
|
||||
gates.reserve(queue.size());
|
||||
while(!queue.empty()) {
|
||||
gates.push_back(std::move(const_cast<Gate&>(queue.top())));
|
||||
const Gate& gate = gates.back();
|
||||
const Edge& eh2 = gate.edge();
|
||||
if(gate.has_steiner_point()) {
|
||||
gl_draw_first_empty_circle_on_the_voronoi_edge(
|
||||
CGAL::Alpha_wraps_2::internal::smallest_squared_radius_2(eh2, tr), eh2, tr, true);
|
||||
} else {
|
||||
gl_draw_first_empty_circle_on_the_voronoi_edge(
|
||||
CGAL::Alpha_wraps_2::internal::smallest_squared_radius_2(eh2, tr), eh2, tr, false);
|
||||
}
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
// restore queue
|
||||
for(auto it = gates.rbegin(); it != gates.rend(); ++it) {
|
||||
queue.push(std::move(*it));
|
||||
}
|
||||
|
||||
for(const Edge& eh2 : tr.finite_edges()) {
|
||||
const Face_handle& f = eh2.first;
|
||||
const Face_handle& n = f->neighbor(eh2.second);
|
||||
if(f->is_outside() == n->is_outside())
|
||||
continue;
|
||||
if(sq_alpha < CGAL::Alpha_wraps_2::internal::smallest_squared_radius_2(eh2, tr))
|
||||
continue;
|
||||
|
||||
const Point_2& cc1 = f->circumcenter();
|
||||
const Point_2& cc2 = n->circumcenter();
|
||||
const Point_2& p1 = eh2.first->vertex((eh2.second+1)%3)->point();
|
||||
FT empty_circle_cc1_sq_radius = CGAL::squared_distance(cc1,p1);
|
||||
FT empty_circle_cc2_sq_radius = CGAL::squared_distance(cc2,p1);
|
||||
FT max_empty_circle_sq_radius = CGAL::max(empty_circle_cc1_sq_radius,
|
||||
empty_circle_cc2_sq_radius);
|
||||
FT alpha_or_max_empty_circle_sq_radius = CGAL::min(max_empty_circle_sq_radius, sq_alpha);
|
||||
::glColor3ub(255, 166, 216);
|
||||
::glLineWidth(1.5f);
|
||||
gl_draw_first_empty_circle_on_the_voronoi_edge(alpha_or_max_empty_circle_sq_radius,
|
||||
eh2, tr, true);
|
||||
::glColor4f(0.25,0.25,0.25,0.25);
|
||||
::glLineWidth(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
void gl_draw_next_gate_pencil() const
|
||||
{
|
||||
const Triangulation& tr = m_wrapper.triangulation();
|
||||
const Alpha_PQ& queue = m_wrapper.queue();
|
||||
if(queue.empty())
|
||||
return;
|
||||
|
||||
::glLineWidth(1.9f);
|
||||
::glEnable(GL_BLEND);
|
||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
::glColor4f(0.25,0.25,0.25,0.8);
|
||||
const Gate& gate = queue.top();
|
||||
const Edge& eh2 = gate.edge();
|
||||
if(gate.has_steiner_point()) {
|
||||
gl_draw_first_empty_circle_on_the_voronoi_edge(
|
||||
CGAL::Alpha_wraps_2::internal::smallest_squared_radius_2(eh2, tr), eh2, tr, true);
|
||||
} else {
|
||||
gl_draw_first_empty_circle_on_the_voronoi_edge(
|
||||
CGAL::Alpha_wraps_2::internal::smallest_squared_radius_2(eh2, tr), eh2, tr, false);
|
||||
}
|
||||
}
|
||||
|
||||
void gl_draw_steiner_point()
|
||||
{
|
||||
Alpha_PQ& queue = m_wrapper.queue();
|
||||
if(queue.empty())
|
||||
return;
|
||||
|
||||
std::vector<Gate> gates;
|
||||
gates.reserve(queue.size());
|
||||
while(!queue.empty()) {
|
||||
gates.push_back(std::move(const_cast<Gate&>(queue.top())));
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
if(gates.empty())
|
||||
return;
|
||||
|
||||
Gate top_gate = gates.front();
|
||||
|
||||
// projection points
|
||||
::glColor3ub(35, 155, 86 );
|
||||
::glPointSize(10.f);
|
||||
::glEnable(GL_BLEND);
|
||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
::glEnable(GL_POINT_SMOOTH);
|
||||
::glBegin(GL_POINTS);
|
||||
|
||||
for(const Gate& gate : gates) {
|
||||
if(gate.has_steiner_from_projection()) {
|
||||
const Point_2& steiner_point = gate.steiner_point();
|
||||
::glVertex2d(steiner_point.x(), steiner_point.y());
|
||||
}
|
||||
}
|
||||
::glEnd();
|
||||
|
||||
// intersection points
|
||||
::glColor3ub(52, 152, 219);
|
||||
::glPointSize(10.f);
|
||||
::glBegin(GL_POINTS);
|
||||
for(const Gate& gate : gates) {
|
||||
if(gate.has_steiner_from_intersection()) {
|
||||
const Point_2& steiner_point = gate.steiner_point();
|
||||
::glVertex2d(steiner_point.x(), steiner_point.y());
|
||||
}
|
||||
}
|
||||
::glEnd();
|
||||
|
||||
// next point
|
||||
const Gate& gate = top_gate;
|
||||
if(gate.has_steiner_point()) {
|
||||
::glPointSize(15.f);
|
||||
::glBegin(GL_POINTS);
|
||||
bool is_projection = gate.has_steiner_from_projection();
|
||||
is_projection ? ::glColor3ub(35, 155, 86 ) : ::glColor3ub(52, 152, 219);
|
||||
const Point_2& steiner_point = gate.steiner_point();
|
||||
::glVertex2d(steiner_point.x(),steiner_point.y());
|
||||
::glEnd();
|
||||
}
|
||||
|
||||
// restore queue
|
||||
for(auto it = gates.rbegin(); it != gates.rend(); ++it) {
|
||||
queue.push(std::move(*it));
|
||||
}
|
||||
}
|
||||
|
||||
void gl_draw_next_gate() const
|
||||
{
|
||||
const Alpha_PQ& queue = m_wrapper.queue();
|
||||
if(queue.empty())
|
||||
return;
|
||||
const Gate& gate = queue.top();
|
||||
const Edge& e = gate.edge();
|
||||
Segment_2 seg = AW2i::delaunay_edge_to_segment(e);
|
||||
const Point_2& p1 = seg.source();
|
||||
const Point_2& p2 = seg.target();
|
||||
::glColor3ub(35, 155, 86 );
|
||||
::glLineWidth(7.f);
|
||||
::glBegin(GL_LINES);
|
||||
::glVertex2d(p1.x(),p1.y());
|
||||
::glVertex2d(p2.x(),p2.y());
|
||||
::glEnd();
|
||||
}
|
||||
|
||||
void add_vertex(const Point_2& p, bool new_cmp, bool is_closed)
|
||||
{
|
||||
if(m_input.empty() || new_cmp) {
|
||||
Pslg_component cmp;
|
||||
cmp.push_back(p);
|
||||
|
||||
cmp.set_is_closed(is_closed);
|
||||
if(is_closed) {
|
||||
cmp.push_back(p);
|
||||
}
|
||||
m_input.push_back(cmp);
|
||||
} else {
|
||||
m_input[m_input.size()-1].set_is_closed(is_closed);
|
||||
if(is_closed) {
|
||||
auto pos_before_last = m_input[m_input.size()-1].end()-1;
|
||||
m_input[m_input.size()-1].insert(pos_before_last,p);
|
||||
} else {
|
||||
m_input[m_input.size()-1].push_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void close_input()
|
||||
{
|
||||
m_input[m_input.size()-1].push_back(m_input[m_input.size()-1][0]);
|
||||
m_input[m_input.size()-1].set_is_closed(true);
|
||||
}
|
||||
|
||||
void open_input()
|
||||
{
|
||||
m_input[m_input.size()-1].pop_back();
|
||||
m_input[m_input.size()-1].set_is_closed(false);
|
||||
}
|
||||
|
||||
CGAL::Bbox_2 get_bbox() const
|
||||
{
|
||||
CGAL::Bbox_2 bbox = m_input.bbox_2();
|
||||
if(!m_wrap.empty())
|
||||
bbox += m_wrap.bbox_2();
|
||||
return bbox;
|
||||
}
|
||||
|
||||
void explode_input()
|
||||
{
|
||||
Pslg exploded_input;
|
||||
for(const Pslg_component& cmp : m_input) {
|
||||
for(size_t i = 1; i < cmp.size(); ++i) {
|
||||
Pslg_component exploded_cmp;
|
||||
exploded_cmp.push_back(random_point(cmp[i-1]));
|
||||
exploded_cmp.push_back(random_point(cmp[i]));
|
||||
exploded_input.push_back(exploded_cmp);
|
||||
}
|
||||
}
|
||||
m_input = exploded_input;
|
||||
}
|
||||
|
||||
void smooth_input()
|
||||
{
|
||||
for(auto& cmp : m_input)
|
||||
cmp.resample(cmp.length()/100.);
|
||||
m_input.smooth(1);
|
||||
}
|
||||
|
||||
double get_diagonal_bbox() const
|
||||
{
|
||||
CGAL::Bbox_2 bbox = m_input.bbox_2();
|
||||
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||
CGAL::square(bbox.ymax() - bbox.ymin()));
|
||||
return diag_length;
|
||||
}
|
||||
|
||||
Point_2 random_point(const Point_2& in) const
|
||||
{
|
||||
double random_offset = get_diagonal_bbox() / 100;
|
||||
FT x = in.x() + ((static_cast<double>(rand()) / RAND_MAX) * random_offset);
|
||||
FT y = in.y() + ((static_cast<double>(rand()) / RAND_MAX) * random_offset);
|
||||
return Point_2(x,y);
|
||||
}
|
||||
|
||||
void init_alpha_data_structure(const double alpha_value,
|
||||
const double offset_value)
|
||||
{
|
||||
Oracle oracle = m_wrapper.oracle();
|
||||
oracle.clear();
|
||||
|
||||
std::vector<Segment_2> segments;
|
||||
for(const auto& cmp : m_input) {
|
||||
if(cmp.size() < 2)
|
||||
continue;
|
||||
for(size_t i=1; i<cmp.size(); ++i) {
|
||||
const Point_2& start = cmp[i-1];
|
||||
const Point_2& end = cmp[i];
|
||||
Segment_2 seg(start, end);
|
||||
if(seg.is_degenerate())
|
||||
continue; // ignore input segments that are problematic for aabb tree
|
||||
segments.push_back(seg);
|
||||
}
|
||||
}
|
||||
|
||||
oracle.add_segments(segments);
|
||||
|
||||
m_wrapper.initialize(alpha_value, offset_value, false /*refining*/);
|
||||
}
|
||||
|
||||
void alpha_flood_fill(int max_iter = -1)
|
||||
{
|
||||
struct Demo_visitor
|
||||
: CGAL::Alpha_wraps_2::internal::Wrapping_default_visitor
|
||||
{
|
||||
Demo_visitor(Stats_data_structure& stats, int max_iter)
|
||||
: m_stats(stats), m_max_iter(max_iter), m_iter(0)
|
||||
{ }
|
||||
|
||||
public:
|
||||
// Whether the flood filling process should continue
|
||||
constexpr bool go_further(const Alpha_wrapper&) {
|
||||
return (m_iter < m_max_iter);
|
||||
}
|
||||
|
||||
void before_edge_treatment(const Alpha_wrapper&, const Gate& gate)
|
||||
{
|
||||
++m_stats.nb_gate_traversed;
|
||||
if(gate.has_steiner_point()) {
|
||||
++m_iter;
|
||||
if(gate.has_steiner_from_projection())
|
||||
++m_stats.nb_proj;
|
||||
++m_stats.nb_steiner;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Stats_data_structure& m_stats;
|
||||
std::size_t m_max_iter, m_iter;
|
||||
};
|
||||
|
||||
Demo_visitor visitor(m_stats, max_iter);
|
||||
m_wrapper.alpha_flood_fill(visitor);
|
||||
}
|
||||
|
||||
void extract_pslg_2_soup()
|
||||
{
|
||||
m_wrap = AW2i::extract_pslg_2_soup<EPICK>(m_wrapper.triangulation());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_DEMO_SCENE_H
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2019-2020 X, The Moonshot Factory (USA).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org).
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Author(s) : Pierre Alliez pierre.alliez@inria.fr
|
||||
// : Michael Hemmer mhsaar@gmail.com
|
||||
// : Cedric Portaneri cportaneri@gmail.com
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_DEMO_TYPES_H
|
||||
#define CGAL_ALPHA_WRAP_2_DEMO_TYPES_H
|
||||
|
||||
#define CGAL_AW2_USE_SORTED_PRIORITY_QUEUE
|
||||
#define CGAL_AW2_COMPUTE_AND_STORE_STEINER_INFO_AT_GATE_CREATION
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/alpha_wrap_2.h>
|
||||
|
||||
#include "pslg_2.h"
|
||||
#include "pslg_io.h"
|
||||
#include "conversion_utils.h"
|
||||
|
||||
namespace AW2 = CGAL::Alpha_wraps_2;
|
||||
namespace AW2i = CGAL::Alpha_wraps_2::internal;
|
||||
|
||||
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using FT = EPICK::FT;
|
||||
using Point_2 = EPICK::Point_2;
|
||||
using Segment_2 = EPICK::Segment_2;
|
||||
using Vector_2 = EPICK::Vector_2;
|
||||
using Line_2 = EPICK::Line_2;
|
||||
using Pslg_component = AW2i::Component<EPICK>;
|
||||
using Pslg = AW2i::Pslg<EPICK>;
|
||||
|
||||
using Segment_Oracle = AW2i::Segment_soup_oracle<EPICK>;
|
||||
using Oracle = AW2i::Point_set_oracle<EPICK, Segment_Oracle>;
|
||||
using Alpha_wrapper = AW2i::Alpha_wrapper_2<Oracle>;
|
||||
|
||||
using Triangulation = Alpha_wrapper::Triangulation;
|
||||
using Vertex_handle = Triangulation::Vertex_handle;
|
||||
using Edge = Triangulation::Edge;
|
||||
using Face_handle = Triangulation::Face_handle;
|
||||
|
||||
using Alpha_PQ = Alpha_wrapper::Alpha_PQ;
|
||||
using Gate = Alpha_wrapper::Gate;
|
||||
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_DEMO_TYPES_H
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
|
||||
|
||||
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 2D Alpha Wrapping"
|
||||
|
||||
# custom options for this package
|
||||
EXTRACT_ALL = false
|
||||
HIDE_UNDOC_MEMBERS = true
|
||||
HIDE_UNDOC_CLASSES = true
|
||||
|
||||
HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/aw2_banner.png \
|
||||
${CGAL_PACKAGE_DOC_DIR}/fig/aw2_segment_soup.png \
|
||||
${CGAL_PACKAGE_DOC_DIR}/fig/aw2_pencil.png \
|
||||
${CGAL_PACKAGE_DOC_DIR}/fig/aw2_steps.jpg \
|
||||
${CGAL_PACKAGE_DOC_DIR}/fig/aw2_double_sided.jpg \
|
||||
${CGAL_PACKAGE_DOC_DIR}/fig/aw2_alpha_croc.png \
|
||||
${CGAL_PACKAGE_DOC_DIR}/fig/aw3_sharp_feature.jpg \
|
||||
${CGAL_PACKAGE_DOC_DIR}/fig/aw3_alpha_offset_bike.jpg
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/// \defgroup PkgAlphaWrap2Ref Reference Manual
|
||||
|
||||
/// \defgroup AW2_free_functions_grp Free Functions
|
||||
/// Functions to create a wrap from point clouds, segment soups, and polygons with holes.
|
||||
/// \ingroup PkgAlphaWrap2Ref
|
||||
|
||||
/*!
|
||||
\addtogroup PkgAlphaWrap2Ref
|
||||
\cgalPkgDescriptionBegin{2D Alpha Wrapping,PkgAlphaWrap2}
|
||||
\cgalPkgPicture{alpha_wrap_2.png}
|
||||
\cgalPkgSummaryBegin
|
||||
\cgalPkgAuthors{Pierre Alliez, David Cohen-Steiner, Michael Hemmer, Cédric Portaneri, and Mael Rouxel-Labbé}
|
||||
\cgalPkgDesc{This component takes a polygon soup, a 2D segment soup, and/or a 2D point set as input,
|
||||
and generates a valid (watertight, intersection-free and 1-manifold) multi-polygon that
|
||||
strictly encloses the input. The algorithm proceeds by shrink-wrapping
|
||||
and refining a 2D Delaunay triangulation starting from a loose bounding box of the input.
|
||||
Two user-defined parameters, alpha and offset, offer control over the maximum size
|
||||
of cavities where the shrink-wrapping process can enter, and the tightness
|
||||
of the final polygon(s) to the input, respectively. Once combined, these parameters
|
||||
provide a means to trade fidelity to the input for complexity of the output.}
|
||||
\cgalPkgManuals{Chapter_2D_Alpha_wrapping,PkgAlphaWrap2Ref}
|
||||
\cgalPkgSummaryEnd
|
||||
\cgalPkgShortInfoBegin
|
||||
\cgalPkgSince{6.2}
|
||||
\cgalPkgDependsOn{\ref PkgTriangulation2 and \ref PkgPolygonRepair}
|
||||
\cgalPkgBib{cgal:achpr-aw2}
|
||||
\cgalPkgLicense{\ref licensesGPL "GPL"}
|
||||
\cgalPkgShortInfoEnd
|
||||
\cgalPkgDescriptionEnd
|
||||
|
||||
\cgalClassifedRefPages
|
||||
|
||||
\cgalCRPSection{Functions}
|
||||
- \link AW2_free_functions_grp `CGAL::alpha_wrap_2()` \endlink
|
||||
*/
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
namespace CGAL {
|
||||
|
||||
/*!
|
||||
|
||||
\mainpage User Manual
|
||||
\anchor Chapter_2D_Alpha_wrapping
|
||||
\cgalAutoToc
|
||||
|
||||
\authors Pierre Alliez, David Cohen-Steiner, Michael Hemmer, Cédric Portaneri, Mael Rouxel-Labbé
|
||||
|
||||
<center>
|
||||
<img src="aw2_banner.png" style="max-width:70%;"/>
|
||||
</center>
|
||||
|
||||
Note: a three-dimensional version of this package is also available: \ref PkgAlphaWrap3.
|
||||
|
||||
\section aw2_introduction Introduction
|
||||
|
||||
Various tasks in geometric modeling and processing require 2D objects represented as valid polygons,
|
||||
where "valid" refers to polygons that are closed, intersection-free (simple), orientable, and 1-manifold.
|
||||
Such representations offer well-defined notions of interior/exterior and geodesic neighborhoods.
|
||||
|
||||
2D data are usually acquired through measurements, possibly followed by reconstruction,
|
||||
designed by humans, or generated through imperfect automated processes.
|
||||
As a result, they can exhibit a wide variety of defects including gaps, missing data,
|
||||
self-intersections, degeneracies such as zero-area structures, and non-manifold features.
|
||||
|
||||
Given the large repertoire of possible defects, many methods and data structures have been proposed
|
||||
to repair specific defects (see for example the package \ref PkgPolygonRepair), usually with the goal
|
||||
of guaranteeing specific properties in the repaired 2D model.
|
||||
Reliably repairing all types of defects is an ill-posed problem as many valid solutions exist
|
||||
for a given 2D model with defects.
|
||||
In addition, the input model can be overly complex with unnecessary geometric details,
|
||||
spurious topological structures, nonessential inner components, or excessively fine discretizations.
|
||||
For applications such as collision avoidance, path planning, or simulation,
|
||||
getting an approximation (i.e., a silhouette) of the input can be more relevant than repairing it.
|
||||
Approximation herein refers to an approach capable of filtering out inner structures,
|
||||
fine details and cavities, as well as wrapping the input within a user-defined offset margin.
|
||||
|
||||
Given an input 2D geometry, we address the problem of computing a conservative approximation,
|
||||
where conservative means that the output is guaranteed to strictly enclose the input.
|
||||
We seek unconditional robustness in the sense that the output polygon should be valid (oriented,
|
||||
1-manifold, and without self-intersections), even for raw input with many defects
|
||||
and degeneracies.
|
||||
The default input is a soup of 2D segments, but the generic interface leaves the door open
|
||||
to other types of finite 2D primitives such as point sets.
|
||||
|
||||
\cgalFigureAnchor{aw2_inputs_fig}
|
||||
<center>
|
||||
<img src="aw2_segment_soup.png" style="max-width:70%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{aw2_inputs_fig}
|
||||
(Left) Shrink-wrapping output from a segment soup with many intersections and gaps.
|
||||
(Center & Right) Possible input degeneracies: non-manifold vertices and zero-area structures.
|
||||
The algorithm handles these cases by wrapping an offset of the input.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
\section aw2_definition Approach
|
||||
|
||||
Many approaches have been devised to enclose a 2D model within a volume, featuring different balances
|
||||
between the runtime and quality (i.e., tightness) of the approximation.
|
||||
Within the simplest cases, an axis-aligned or oriented bounding box clearly satisfies some desired properties;
|
||||
however, the approximation error is uncontrollable and often very large.
|
||||
Computing the convex hull of the input also matches some of the desired properties
|
||||
and improves the quality of the result, albeit at the price of increasing the runtime.
|
||||
However, the approximation remains crude, especially in the case of several components.
|
||||
|
||||
The convex hull is, in fact, a special case of alpha shapes (\ref Chapter_2D_Alpha_Shapes).
|
||||
Mathematically, the alpha shape is a subcomplex of the Delaunay triangulation, with simplicies
|
||||
being part of the complex depending on the size of their minimal (empty) Delaunay ball.
|
||||
Intuitively, constructing 2D alpha shapes can be thought of as carving 2D space with an empty ball
|
||||
of user-defined radius alpha.
|
||||
Alpha shapes yield provable, good piecewise-linear approximations of a shape \cgalCite{bb-srmua-97t},
|
||||
but are defined on point sets, whereas we wish to deal with more general input data, such as segment soups.
|
||||
Even after sampling the segment soup, alpha shapes do not guarantee to be conservative for any alpha.
|
||||
Finally, inner structures are also carved within the volumes, instead of being filtered out.
|
||||
|
||||
Inspired by alpha shapes, we replace the above notion of carving by <em>shrink-wrapping</em>:
|
||||
we iteratively construct a subcomplex of a 2D Delaunay triangulation by starting from
|
||||
a simple 2D Delaunay triangulation enclosing the input, and then iteratively removing eligible triangles
|
||||
that lie on the boundary of the complex.
|
||||
In addition, the underlying triangulation---and thus the complex incidentally---is refined
|
||||
as shrinking proceeds.
|
||||
Thus, instead of carving from the convex hull of the input data as in alpha shapes, we construct
|
||||
an entirely new polygon through a Delaunay refinement-like algorithm. The refinement algorithm inserts Steiner points
|
||||
on the boundary of an offset volume, defined as a level set of the unsigned distance field to the input.
|
||||
|
||||
This process both prevents the creation of inner structures within the output and avoids superfluous computations.
|
||||
In addition, detaching our wrap construction from the geometry and discretization of the input has several advantages:
|
||||
(1) the underlying data is not restricted to a specific format (polygon soups, segment soups, point sets, etc.)
|
||||
as all that is required is answering three basic geometric queries: (a) the distance between a point
|
||||
and the input, (b) the projection of a query point onto the input, (c) an intersection test
|
||||
between a triangle and the input, and (2) The user has more freedom to trade tightness
|
||||
to the input for final polgyon complexity, as constructing a conservative approximation on a large offset
|
||||
of the input requires fewer polygon edges.
|
||||
|
||||
\subsection aw2_algorithm Algorithm
|
||||
|
||||
<b>Initialization</b>. The algorithm is initialized by inserting the four corner vertices
|
||||
of a loose bounding box into a 2D Delaunay triangulation.
|
||||
In the 2D Delaunay triangulation of \cgal, all edges are adjacent to two triangle faces.
|
||||
Each edge of the boundary of the Delaunay triangulation, which coincides with one edge
|
||||
of the convex hull of the triangulation vertices, is adjacent to a so-called <em>infinite</em> triangle face,
|
||||
an abstract face connected to the so-called <em>infinite vertex</em> to ensure the aforementioned double-edge adjacency.
|
||||
Initially, all infinite faces are tagged as outside, and all finite triangle faces are tagged as inside.
|
||||
|
||||
<b>Shrink-wrapping</b>. The shrink-wrapping algorithm proceeds by traversing the faces
|
||||
of the Delaunay triangulation from outside to inside, flood-filling from one face to its adjacent face,
|
||||
and tagging the adjacent face as outside whenever possible (the term "possible" is specified later).
|
||||
Flood filling is implemented via a priority queue of Delaunay triangle edges representing
|
||||
the traversal between the two adjacent faces of the edge, from outside to inside.
|
||||
These edges are referred to as <em>gates</em> in the following.
|
||||
|
||||
Given an outside face and its adjacent inside face, the common edge (i.e., a gate) is said
|
||||
to be <em>alpha-traversable</em> if its circumradius is larger than the user-defined parameter alpha,
|
||||
where circumradius refers to the radius of the relating segment's Delaunay ball.
|
||||
Intuitively, cavities smaller than alpha are not accessible as their gates are not alpha-traversable.
|
||||
|
||||
Initialized by the alpha-traversable gates on the convex hull, the priority queue contains only
|
||||
alpha-traversable gates and is sorted by decreasing order of the circumradius of the gate.
|
||||
Traversal can be seen as a continuous process that advances along dual Voronoi edges of the gates,
|
||||
with a pencil of empty balls circumscribing the gate.
|
||||
|
||||
\cgalFigureAnchor{aw2_pencil_fig}
|
||||
<center>
|
||||
<img src="aw2_pencil.png" style="max-width:30%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{aw2_pencil_fig}
|
||||
(Left) Pencil of empty circles (blue) circumscribing a Delaunay edge (green) in a 2D Delaunay triangulation (black).
|
||||
From the top triangle circumcenter <em>c1</em> to the bottom triangle circumcenter <em>c2</em>, the dual Voronoi edge denoted by <em>e</em> (doted red) is the trace of centers of the largest circles that are empty of Delaunay vertex.
|
||||
(Right) The graph corresponding to the left example. The x-axis corresponds to the position of empty circle centers located on the Voronoi edge <em>e</em>, from <em>c1</em> to <em>c2</em>. The y-axis is the radius value of the corresponding empty circles. In this case, the minimum radius of this pencil of empty circle is located at the midpoint of the green Delaunay edge.
|
||||
In our algorithm, a gate (green Delaunay edge) is said to be not alpha-traversable when the minimum radius of the pencil of empty circle is smaller than alpha.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
When traversing from an outside face \f$ f_o \f$ to an inside face \f$ f_i \f$ through an alpha-traversable edge \f$ e \f$,
|
||||
two criteria are tested to prevent the wrapping process from colliding with the input:
|
||||
|
||||
(1) We check for an intersection between the dual Voronoi edge of \f$ e \f$, i.e. the segment between
|
||||
the circumcenters of the two incident faces, and the <em>offset surface</em>, defined as the level set
|
||||
of unsigned isosurface to the input.
|
||||
If one or several intersections exists, the first intersection point, along the dual Voronoi edge
|
||||
oriented from outside to inside is inserted into the triangulation as a Steiner point.
|
||||
|
||||
(2) If the dual Voronoi edge does not intersect the offset surface but the neighboring face \f$ f_i \f$
|
||||
intersects the input, we compute the projection of the circumcenter of \f$ f_i \f$
|
||||
onto the offset surface, and insert it into the triangulation as a Steiner point (which destroys \f$ f_i \f$).
|
||||
|
||||
After each of the above Steiner point insertions, all new incident faces are tagged as inside,
|
||||
and the newly alpha-traversable gates are pushed into the priority queue.
|
||||
|
||||
If none of the above two criteria are met, the neighboring face \f$ f_i \f$ is traversed and tagged as outside.
|
||||
Alpha-Traversable edges of \f$ f_i \f$ that are separating inside from outside faces are pushed as new gates into the priority queue.
|
||||
|
||||
Once the queue empties---a process that is guaranteed as edges (and their circumradii) become smaller
|
||||
due to the insertion of new Steiner points---the construction phase terminates.
|
||||
The output (multi)polygon is extracted from the Delaunay triangulation as the set of edges
|
||||
separating inside from outside faces.
|
||||
|
||||
The figure below depicts the steps of the algorithm.
|
||||
|
||||
\cgalFigureAnchor{aw2_steps_fig}
|
||||
<center>
|
||||
<img src="aw2_steps.jpg" style="max-width:95%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{aw2_steps_fig}
|
||||
Steps of the shrink-wrapping algorithm in 2D.
|
||||
The algorithm is initialized by inserting the corners of the loose bounding box of the input (red)
|
||||
into a Delaunay triangulation, and all finite triangles are tagged inside (grey).
|
||||
The current gate (green edge) popped out from the queue is alpha-traversable. The triangle adjacent
|
||||
to the gate is tagged outside when it does not intersect the input, and new alpha-traversable gates
|
||||
are pushed to the queue. When the adjacent triangle intersects the input, a new Steiner point (large green disc)
|
||||
is computed and inserted into the triangulation, all neighboring triangles are tagged inside,
|
||||
new alpha-traversable gates are pushed to the queue, and traversal is resumed.
|
||||
Grey edges depict the Delaunay triangulation. Blue edges depict the Voronoi diagram.
|
||||
Pink circles depict the empty circle of radius alpha. The output edges (dark blue) separate inside from outside triangles.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
\subsection aw2_guarantees Guarantees
|
||||
|
||||
The algorithm is proven to terminate and to produce a set of 1-manifold, simple polygons
|
||||
that strictly enclose the input data.
|
||||
The key element to the proof is that we wrap from outside to inside and never allow a face
|
||||
that intersects the input to be flagged inside.
|
||||
Furthermore, both criteria that lead to refinement of the triangulation insert Steiner points
|
||||
that are guaranteed to break the faces in need of refinement and reduce the neighbor edges' circumradii.
|
||||
|
||||
Because the main refinement criterion is the insertion of an intersection between a dual Voronoi edge
|
||||
and an offset of the input, or the projection of a Voronoi vertex onto the offset of the input,
|
||||
the algorithm has similarities to popular meshing algorithms based on Delaunay filtering
|
||||
and refinement (see \ref Chapter_3D_Mesh_Generation).
|
||||
|
||||
\section aw2_interface Interface
|
||||
|
||||
Our algorithm takes as input a set of segments in 2D, provided either as a segment soup or
|
||||
as a set of polygons, and two user-defined scalar parameters: the <em>alpha</em> and the <em>offset</em> values.
|
||||
It proceeds by shrink-wrapping and refining a 2D Delaunay triangulation starting from a loose bounding box of the input.
|
||||
The parameter <em>alpha</em> refers to the size of cavities or holes that cannot be traversed during wrapping,
|
||||
and hence to the final level of detail, as alpha acts like a sizing field in a common Delaunay
|
||||
refinement algorithm (\ref Chapter_3D_Mesh_Generation).
|
||||
The parameter <em>offset</em> refers to the distance between the vertices of the refined triangulation
|
||||
and the input, so that a large offset translates into a loose enclosing of the input.
|
||||
This second parameter offers a means to control the trade-off between tightness and complexity.
|
||||
|
||||
The main entry point of the component is the global function `CGAL::alpha_wrap_2()` that generates the alpha wrap;
|
||||
this function takes as input a point set, a polyline soup, or a polygon soup.
|
||||
There is no prerequisite on the input connectivity so that it can take inputs
|
||||
with islands, self-intersections, or overlaps, as well as combinatorial or geometrical degeneracies.
|
||||
|
||||
The underlying traits class must be a model of the `Kernel` concept. It should use
|
||||
a floating point number type as inexactness is inherent to the algorithm since there is no closed
|
||||
form description of new vertices on the offset surface.
|
||||
|
||||
The output is a multi polygon, i.e., a set of polygons, whose type is chosen by the user,
|
||||
under the constraint that it must be a model of the `MultipolygonWithHoles_2` concept.
|
||||
|
||||
\section aw2_parameters Choosing Parameters
|
||||
|
||||
The two parameters of the algorithm impact both the level of detail and complexity of the output wrap.
|
||||
|
||||
\subsection aw2_alpha Alpha
|
||||
|
||||
The main parameter, alpha, controls whether a Delaunay edge is traversable during shrink-wrapping.
|
||||
Alpha's main purpose is to control the size
|
||||
of the empty balls used during wrapping, and thus to determine which features will appear in the output:
|
||||
indeed, a edge is alpha-traversable if its circumradius is larger than alpha; hence, the algorithm
|
||||
can only shrink-wrap through straits or holes with diameters larger than alpha.
|
||||
A second, less direct consequence is that as long as a edge has a circumradius larger than alpha,
|
||||
the incident inside face will be visited and possibly refined.
|
||||
Therefore, when the algorithm terminates, all edges have a circumradius smaller than alpha.
|
||||
This parameter thus also behaves like a sizing criterion on the edges of the output.
|
||||
|
||||
\cgalFigureAnchor{aw2_alpha_param_fig}
|
||||
<center>
|
||||
<img src="aw2_alpha_croc.png" style="max-width:90%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{aw2_alpha_param_fig}
|
||||
Impact of the alpha parameter on the output.
|
||||
(Left) The input segment soup, generated from an SVG file. The input
|
||||
has many self-intersections, non-manifold vertices, superfluous geometric details
|
||||
and spurious topological structures.
|
||||
(Middle & Right) This component approximates the input conservatively and produces valid polygons
|
||||
with different complexity and fidelity to the input, depending on the alpha parameter.
|
||||
The smaller the alpha, the deeper the shrink-wrapping process will enter cavities.
|
||||
The alpha parameter is decreasing from left to right, to respectively 1/50, 1/100 and 1/300 of the longest diagonal of the input bounding box.
|
||||
A large alpha will produce an output less complex but less faithful to the input.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
\subsection aw2_offset Offset
|
||||
|
||||
The second parameter, the offset distance, controls the distance from the input and thus the definition
|
||||
of the offset isosurface onto which the vertices of the output polygon are located.
|
||||
This parameter controls the tightness of the result, which has, in turn, a few consequences.
|
||||
Firstly, locating vertices away from the input enables the algorithm to generate
|
||||
a less complex result, especially in convex areas. A trivial example of this behavior would be a very dense
|
||||
circle-shaped polygon, for which an as-tight-as-possible envelope would also need to be very dense.
|
||||
Secondly, the farther the isosurface is from the input, the more new points are inserted
|
||||
through the first criterion (i.e., through intersection with dual Voronoi edge, see Section \ref aw2_algorithm);
|
||||
thus, the quality of the output improves in terms of angles of the triangle elements.
|
||||
Finally, and depending on the value of the alpha parameter, a large offset can also offer defeaturing capabilities.
|
||||
However, using a small offset parameter will tend to better preserve sharp features as projection
|
||||
Steiner points tend to project onto convex sharp features.
|
||||
|
||||
\cgalFigureAnchor{aw2_offset_param_fig}
|
||||
<center>
|
||||
<img src="aw3_sharp_feature.jpg" style="max-width:90%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{aw2_offset_param_fig}
|
||||
[TO BE UPDATED]
|
||||
Impact of the offset parameter on the output.
|
||||
(Left) Input mesh generated by meshing a NURBS CAD model in parameter space.
|
||||
(Right) The smaller the offset, the closest sample points are to the input.
|
||||
The offset parameter is decreasing from left to right, to respectively 1/50, 1/200 and 1/1000 of the longest diagonal of the input bounding box.
|
||||
The alpha parameter is equal to 1/50 of the longest diagonal of the input bounding box for all level of details.
|
||||
A larger offset will produce an output less complex with better triangle quality.
|
||||
However, the sharp features (red edges) are well-preserved when the offset parameter is small.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
\cgalFigureAnchor{aw2_steiner_fig}
|
||||
<center>
|
||||
<img src="aw2_steiner.jpg" style="max-width:90%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{aw2_steiner_fig}
|
||||
Steiner points.
|
||||
The projection Steiner points (green) are computed by projecting the triangle circumcenter onto the offset.
|
||||
The intersection Steiner points (blue) are computed as the first intersection point between the Voronoi edge and the offset.
|
||||
(Left) When the offset parameter is small, the algorithm produces more projection Steiner points,
|
||||
which tends to improve the preservation of convex sharp features.
|
||||
(Right) When the offset parameter is large, the algorithm produces more intersection Steiner points.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
By default, we recommend to set the offset parameter to a small fraction of alpha, such that alpha
|
||||
becomes the main parameter that controls the final level of detail.
|
||||
|
||||
The image below illustrates the impact of both parameters.
|
||||
|
||||
\cgalFigureAnchor{aw2_param_grid_fig}
|
||||
<center>
|
||||
<img src="aw3_alpha_offset_bike.jpg" style="max-width:80%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{aw2_param_grid_fig}
|
||||
[UPDATE]
|
||||
Different alpha and offset values on the bike model (533,000 triangles).
|
||||
The x-axis represents the offset value equal to 1/5000, 1/2000, 1/500, 1/200, 1/50, 1/20 and 1/5 of the longest diagonal of the input bounding box, from left to right.
|
||||
The y-axis represents the alpha value equal to 1/300, 1/100, 1/50, 1/20 and 1/5 of the longest diagonal of the input bounding box, from bottom to top.
|
||||
The numbers below each level of detail represents their number of triangles.
|
||||
Depending on the alpha value, an offset too small or too large will produce outputs with higher complexity.
|
||||
For each alpha, the models with lower complexity can be used as a scale-space representations for collision detection, from near to far distances.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
\subsection aw2_two_side A Note on "Two-Sided" Wraps
|
||||
|
||||
The offset parameter is crucial to our approach because it guarantees that the output is a
|
||||
set of 1-manifold, simple polygons.
|
||||
Indeed, and even when the input is a zero-area structure such as a single segment,
|
||||
the output wrap is a thin domain enclosing the said segment \cgalFigureRef{aw2_inputs_fig}.
|
||||
|
||||
Users should keep in mind that the wrapping algorithm performs with an the unsigned distance field,
|
||||
and has no means of determining whether it is acting on both sides of a same input segment.
|
||||
It will thus produce two-sided wraps in the case of holes in the input and
|
||||
values of alpha smaller than the size of the holes.
|
||||
|
||||
\cgalFigureAnchor{aw2_double_sided_fig}
|
||||
<center>
|
||||
<img src="aw2_double_sided.jpg" style="max-width:40%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{aw2_double_sided_fig}
|
||||
Wrapping a Bunny in 2D, with decreasing values for alpha.
|
||||
When alpha is small enough with respect the diameter of the holes, the algorithm generates a two-sided wrap.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
\section aw2_examples Examples
|
||||
|
||||
\subsection aw2_examples_polylines Wrapping a Set of Polylines
|
||||
|
||||
The following example illustrates how to construct the wrap of a soup of 2D segments.
|
||||
Alpha is set to 1/20 of the bounding box's longest diagonal edge length,
|
||||
and offset set to 1/30 of alpha (i.e., 1/600 of the bounding box diagonal edge length).
|
||||
|
||||
\cgalExample{Alpha_wrap_2/polyline_wrap_2.cpp}
|
||||
|
||||
\subsection aw2_examples_polygons Wrapping a Multipolygon
|
||||
|
||||
For polygons, we use multipolygons to represent input sets of polygons (possibly with holes).
|
||||
The following example uses the \link IOStreamWKT WKT file format\endlink to read such input
|
||||
and wrap it. Note the possible usage of the \ref PkgPolygonRepair package if a pre-emptive
|
||||
repair of the polygons is desired.
|
||||
|
||||
\cgalExample{Alpha_wrap_2/polygon_wrap_2.cpp}
|
||||
|
||||
\subsection aw2_examples_points Wrapping a Point Cloud
|
||||
|
||||
Finally, the following example demonstrates how to wrap a 2D point set constructed from the projection
|
||||
of an input 3D point cloud.
|
||||
|
||||
\cgalExample{Alpha_wrap_2/point_set_wrap_2.cpp}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Manual
|
||||
Kernel_23
|
||||
STL_Extension
|
||||
Algebraic_foundations
|
||||
Circulator
|
||||
Stream_support
|
||||
AABB_tree
|
||||
Alpha_shapes_2
|
||||
BGL
|
||||
Mesh_2
|
||||
Triangulation_2
|
||||
Alpha_wrap_3
|
||||
Mesh_3
|
||||
Polygon
|
||||
Polygon_repair
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
\example Alpha_wrap_2/polygon_wrap_2.cpp
|
||||
\example Alpha_wrap_2/polyline_wrap_2.cpp
|
||||
\example Alpha_wrap_2/triangle_soup_wrap_2.cpp
|
||||
\example Alpha_wrap_2/point_set_wrap_2.cpp
|
||||
*/
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 347 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 230 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 408 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
|
@ -0,0 +1,14 @@
|
|||
# Created by the script cgal_create_cmake_script
|
||||
# This is the CMake script for compiling a CGAL application.
|
||||
|
||||
cmake_minimum_required(VERSION 3.12...3.31)
|
||||
project(Alpha_wrap_2_Examples)
|
||||
|
||||
find_package(CGAL REQUIRED)
|
||||
|
||||
# create a target per cppfile
|
||||
create_single_source_cgal_program("point_set_wrap_2.cpp")
|
||||
create_single_source_cgal_program("polyline_wrap_2.cpp")
|
||||
create_single_source_cgal_program("polygon_wrap_2.cpp")
|
||||
create_single_source_cgal_program("triangle_soup_wrap_2.cpp")
|
||||
create_single_source_cgal_program("mixed_inputs_wrap_2.cpp")
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
#define CGAL_AW2_DEBUG_PP
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/alpha_wrap_2.h>
|
||||
#include <CGAL/Multipolygon_with_holes_2.h>
|
||||
|
||||
#include <CGAL/point_generators_2.h>
|
||||
#include <CGAL/Real_timer.h>
|
||||
#include <CGAL/IO/WKT.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using Point_2 = K::Point_2;
|
||||
using Segment_2 = K::Segment_2;
|
||||
using Triangle_2 = K::Triangle_2;
|
||||
|
||||
using Segments = std::vector<Segment_2>;
|
||||
using Points = std::vector<Point_2>;
|
||||
using Triangles = std::vector<Triangle_2>;
|
||||
|
||||
using Multipolygon = CGAL::Multipolygon_with_holes_2<K>;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Generate random points in a unit square
|
||||
const int num_points = (argc > 1) ? std::stoi(argv[1]) : 20;
|
||||
const int num_segments = (argc > 2) ? std::stoi(argv[2]) : 5;
|
||||
const int num_triangles = (argc > 3) ? std::stoi(argv[3]) : 5;
|
||||
|
||||
// Generate random points
|
||||
Points points;
|
||||
CGAL::Random_points_in_square_2<Point_2> point_gen(0.5); // radius 0.5 for unit square centered at origin
|
||||
std::copy_n(point_gen, num_points, std::back_inserter(points));
|
||||
std::cout << points.size() << " random points generated" << std::endl;
|
||||
|
||||
// Generate random segments between random points in the square
|
||||
Segments segments;
|
||||
CGAL::Random_points_in_square_2<Point_2> segment_point_gen(0.5);
|
||||
for(int i=0; i<num_segments; ++i)
|
||||
{
|
||||
Point_2 p = *segment_point_gen++;
|
||||
Point_2 q = *segment_point_gen++;
|
||||
segments.emplace_back(p, q);
|
||||
}
|
||||
std::cout << segments.size() << " random segments generated" << std::endl;
|
||||
|
||||
// Generate random triangles in the square
|
||||
Triangles triangles;
|
||||
CGAL::Random_points_in_square_2<Point_2> triangle_point_gen(0.5);
|
||||
for(int i=0; i<num_triangles; ++i)
|
||||
{
|
||||
Point_2 p = *triangle_point_gen++;
|
||||
Point_2 q = *triangle_point_gen++;
|
||||
Point_2 r = *triangle_point_gen++;
|
||||
triangles.emplace_back(p, q, r);
|
||||
}
|
||||
std::cout << triangles.size() << " random triangles generated" << std::endl;
|
||||
|
||||
std::ofstream out_points("mixed_input_points.xyz");
|
||||
out_points.precision(17);
|
||||
for(const auto& p : points)
|
||||
out_points << p.x() << " " << p.y() << " 0\n";
|
||||
out_points.close();
|
||||
|
||||
std::ofstream out_segments("mixed_input_segments.txt");
|
||||
out_segments.precision(17);
|
||||
for(const auto& s : segments)
|
||||
out_segments << "2 " << s.source().x() << " " << s.source().y() << " 0 "
|
||||
<< s.target().x() << " " << s.target().y() << " 0\n";
|
||||
out_segments.close();
|
||||
|
||||
std::ofstream out_triangles("mixed_input_triangles.txt");
|
||||
out_triangles.precision(17);
|
||||
for(const auto& t : triangles)
|
||||
out_triangles << "4 " << t.vertex(0).x() << " " << t.vertex(0).y() << " 0 "
|
||||
<< t.vertex(1).x() << " " << t.vertex(1).y() << " 0 "
|
||||
<< t.vertex(2).x() << " " << t.vertex(2).y() << " 0 "
|
||||
<< t.vertex(0).x() << " " << t.vertex(0).y() << " 0\n";
|
||||
out_triangles.close();
|
||||
|
||||
const double relative_alpha = (argc > 4) ? std::stod(argv[4]) : 15.;
|
||||
const double relative_offset = (argc > 5) ? std::stod(argv[5]) : 450.;
|
||||
|
||||
// Since we're using a unit square, diagonal length is sqrt(2)
|
||||
const double diag_length = std::sqrt(2.0);
|
||||
const double alpha = diag_length / relative_alpha;
|
||||
const double offset = diag_length / relative_offset;
|
||||
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
|
||||
using Segment_Oracle = CGAL::Alpha_wraps_2::internal::Segment_soup_oracle<K>;
|
||||
using Oracle = CGAL::Alpha_wraps_2::internal::Point_set_oracle<K, Segment_Oracle>;
|
||||
|
||||
Segment_Oracle segment_oracle(K{});
|
||||
Oracle oracle(segment_oracle);
|
||||
|
||||
oracle.add_points(points);
|
||||
oracle.add_segments(segments);
|
||||
oracle.add_triangles(triangles);
|
||||
|
||||
CGAL::Alpha_wraps_2::internal::Alpha_wrapper_2<Oracle> aw2(oracle);
|
||||
|
||||
Multipolygon wrap;
|
||||
aw2(alpha, offset, wrap);
|
||||
|
||||
t.stop();
|
||||
std::cout << "Took " << t.time() << " seconds" << std::endl;
|
||||
|
||||
std::string output_name = "mixed_wrap_" +
|
||||
std::to_string(num_points) + "_" +
|
||||
std::to_string(num_segments) + "_" +
|
||||
std::to_string(num_triangles) + "_" +
|
||||
std::to_string(static_cast<int>(relative_alpha)) + "_" +
|
||||
std::to_string(static_cast<int>(relative_offset)) + ".wkt";
|
||||
std::cout << "Writing to " << output_name << std::endl;
|
||||
|
||||
std::ofstream out(output_name);
|
||||
out.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_polygon_WKT(out, wrap);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef CGAL_ALPHA_WRAP_2_EXAMPLES_OUTPUT_HELPER_H
|
||||
#define CGAL_ALPHA_WRAP_2_EXAMPLES_OUTPUT_HELPER_H
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
std::string generate_output_name(std::string input_name,
|
||||
const double alpha,
|
||||
const double offset)
|
||||
{
|
||||
input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1);
|
||||
input_name = input_name.substr(0, input_name.find_last_of("."));
|
||||
|
||||
std::string output_name = input_name
|
||||
+ "_" + std::to_string(static_cast<int>(alpha))
|
||||
+ "_" + std::to_string(static_cast<int>(offset)) + "-wrap.wkt";
|
||||
|
||||
return output_name;
|
||||
}
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_EXAMPLES_OUTPUT_HELPER_H
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
#define CGAL_AW2_DEBUG_PP
|
||||
|
||||
#include "output_helper.h"
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/alpha_wrap_2.h>
|
||||
#include <CGAL/Multipolygon_with_holes_2.h>
|
||||
|
||||
#include <CGAL/Point_set_3.h>
|
||||
#include <CGAL/IO/read_points.h>
|
||||
#include <CGAL/IO/WKT.h>
|
||||
#include <CGAL/Real_timer.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using Point_2 = K::Point_2;
|
||||
using Point_3 = K::Point_3;
|
||||
using Vector_2 = K::Vector_2;
|
||||
using Vector_3 = K::Vector_3;
|
||||
|
||||
using Point_set_2 = CGAL::Point_set_3<Point_2, Vector_2>;
|
||||
using Point_set_3 = CGAL::Point_set_3<Point_3, Vector_3>;
|
||||
|
||||
using Multipolygon = CGAL::Multipolygon_with_holes_2<K>;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout.precision(17);
|
||||
std::cerr.precision(17);
|
||||
|
||||
const std::string filename = argc > 1 ? argv[1] : CGAL::data_file_path("points_3/circles.ply");
|
||||
|
||||
// This code reads a _3D_ point file
|
||||
Point_set_3 point_set_3;
|
||||
if(!CGAL::IO::read_points(filename,
|
||||
point_set_3.index_back_inserter(),
|
||||
CGAL::parameters::point_map(point_set_3.point_push_map())))
|
||||
{
|
||||
std::cerr << "Can't read input file " << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Project onto the xy plane
|
||||
Point_set_2 point_set_2;
|
||||
for (const auto& p3 : point_set_3.points())
|
||||
point_set_2.insert(Point_2(p3.x(), p3.y()));
|
||||
|
||||
std::cout << point_set_2.size() << " points" << std::endl;
|
||||
|
||||
// Compute the alpha and offset values
|
||||
const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 10.;
|
||||
const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 300.;
|
||||
|
||||
CGAL::Bbox_2 bbox;
|
||||
for (const auto& p2 : point_set_2.points())
|
||||
bbox += p2.bbox();
|
||||
|
||||
std::ofstream proj_out("projected.xyz");
|
||||
proj_out.precision(std::numeric_limits<double>::max_digits10);
|
||||
for (const auto& p2 : point_set_2.points())
|
||||
proj_out << p2 << " 0\n";
|
||||
proj_out.close();
|
||||
|
||||
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||
CGAL::square(bbox.ymax() - bbox.ymin()));
|
||||
const double alpha = diag_length / relative_alpha;
|
||||
const double offset = diag_length / relative_offset;
|
||||
std::cout << "absolute alpha = " << alpha << " absolute offset = " << offset << std::endl;
|
||||
|
||||
// Construct the wrap
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
|
||||
Multipolygon wrap;
|
||||
CGAL::alpha_wrap_2(point_set_2.points(), alpha, offset, wrap);
|
||||
|
||||
t.stop();
|
||||
std::cout << "Result: " << wrap.polygons_with_holes().size() << " polygon(s)" << std::endl;
|
||||
std::cout << "Took " << t.time() << " s." << std::endl;
|
||||
|
||||
// Save the result
|
||||
const std::string output_name = generate_output_name(filename, relative_alpha, relative_offset);
|
||||
std::cout << "Writing to " << output_name << std::endl;
|
||||
|
||||
std::ofstream out(output_name);
|
||||
out.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_polygon_WKT(out, wrap);
|
||||
|
||||
std::cout << "Done." << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
#define CGAL_AW2_DEBUG_PP // @tmp (here and at other places)
|
||||
|
||||
#include "output_helper.h"
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/alpha_wrap_2.h>
|
||||
#include <CGAL/Multipolygon_with_holes_2.h>
|
||||
#include <CGAL/Polygon_repair/repair.h>
|
||||
|
||||
#include <CGAL/Real_timer.h>
|
||||
#include <CGAL/IO/WKT.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using Point_2 = K::Point_2;
|
||||
|
||||
using Multipolygon = CGAL::Multipolygon_with_holes_2<K>;
|
||||
using Polygon_with_holes = Multipolygon::Polygon_with_holes_2;
|
||||
using Polygon = Polygon_with_holes::Polygon_2;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout.precision(17);
|
||||
std::cerr.precision(17);
|
||||
|
||||
const std::string filename = argc > 1 ? argv[1] : CGAL::data_file_path("wkt/issue.wkt");
|
||||
std::ifstream in(filename);
|
||||
Multipolygon mp_in;
|
||||
if(!in || !CGAL::IO::read_multi_polygon_WKT(in, mp_in))
|
||||
{
|
||||
std::cerr << "Can't read input file " << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << mp_in.polygons_with_holes().size() << " input polygons" << std::endl;
|
||||
|
||||
// Optional:
|
||||
// use Polygon_repair to specify which strategy should be used to determine
|
||||
// what is inside and what is outside for invalid polygons.
|
||||
// We could also not repair, but then all edges (whether from the outer boundaries
|
||||
// or from the hole boundaries) are taken into account.
|
||||
auto rule = CGAL::Polygon_repair::Even_odd_rule();
|
||||
Multipolygon mp_repaired = CGAL::Polygon_repair::repair(mp_in, rule);
|
||||
|
||||
std::cout << "post repair # = " << mp_repaired.polygons_with_holes().size() << std::endl;
|
||||
|
||||
// Compute the alpha and offset values
|
||||
const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 10.;
|
||||
const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 300.;
|
||||
|
||||
CGAL::Bbox_2 bbox = mp_repaired.bbox();
|
||||
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||
CGAL::square(bbox.ymax() - bbox.ymin()));
|
||||
const double alpha = diag_length / relative_alpha;
|
||||
const double offset = diag_length / relative_offset;
|
||||
std::cout << "absolute alpha = " << alpha << " absolute offset = " << offset << std::endl;
|
||||
|
||||
// Construct the wrap
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
|
||||
Multipolygon wrap;
|
||||
CGAL::alpha_wrap_2(mp_repaired, alpha, offset, wrap);
|
||||
|
||||
t.stop();
|
||||
std::cout << "Result: " << wrap.polygons_with_holes().size() << " polygons" << std::endl;
|
||||
std::cout << "Took " << t.time() << " s." << std::endl;
|
||||
|
||||
// Save the result
|
||||
const std::string output_name = generate_output_name(filename, relative_alpha, relative_offset);
|
||||
std::cout << "Writing to " << output_name << std::endl;
|
||||
|
||||
std::ofstream out(output_name);
|
||||
out.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_polygon_WKT(out, wrap);
|
||||
|
||||
std::cout << "Done." << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
#define CGAL_AW2_DEBUG_PP // @tmp
|
||||
|
||||
#include "output_helper.h"
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/alpha_wrap_2.h>
|
||||
#include <CGAL/Multipolygon_with_holes_2.h>
|
||||
|
||||
#include <CGAL/Real_timer.h>
|
||||
#include <CGAL/IO/WKT.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using Point_2 = K::Point_2;
|
||||
|
||||
using Points = std::vector<Point_2>;
|
||||
using Polyline = std::vector<Point_2>;
|
||||
using Polylines = std::vector<Polyline>;
|
||||
|
||||
using Multipolygon = CGAL::Multipolygon_with_holes_2<K>;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout.precision(17);
|
||||
std::cerr.precision(17);
|
||||
|
||||
const std::string filename = argc > 1 ? argv[1] : CGAL::data_file_path("wkt/LetterAbis.wkt");
|
||||
|
||||
// read_multi_linestring() expects an actual MULTILINESTRING entry whereas read_WKT() will read
|
||||
// all MULTILINESTRING and LINESTRING into a multi-linestring.
|
||||
Points pts_in;
|
||||
Polylines mls_in;
|
||||
Multipolygon mp_in;
|
||||
std::ifstream in(filename);
|
||||
if(!in || !CGAL::IO::read_WKT(in, pts_in, mls_in, mp_in))
|
||||
{
|
||||
std::cerr << "Can't read input file " << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << mls_in.size() << " input polylines" << std::endl;
|
||||
|
||||
// Compute the alpha and offset values
|
||||
const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 20.;
|
||||
const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 300.;
|
||||
|
||||
CGAL::Bbox_2 bbox;
|
||||
for(const Polyline& ls : mls_in) {
|
||||
for(const Point_2& pt : ls) {
|
||||
bbox += pt.bbox();
|
||||
}
|
||||
}
|
||||
|
||||
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||
CGAL::square(bbox.ymax() - bbox.ymin()));
|
||||
const double alpha = diag_length / relative_alpha;
|
||||
const double offset = diag_length / relative_offset;
|
||||
std::cout << "absolute alpha = " << alpha << " absolute offset = " << offset << std::endl;
|
||||
|
||||
// Construct the wrap
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
|
||||
Multipolygon wrap;
|
||||
CGAL::alpha_wrap_2(mls_in, alpha, offset, wrap);
|
||||
|
||||
t.stop();
|
||||
std::cout << "Result: " << wrap.polygons_with_holes().size() << " polygons" << std::endl;
|
||||
std::cout << "Took " << t.time() << " s." << std::endl;
|
||||
|
||||
// Save the result
|
||||
const std::string output_name = generate_output_name(filename, relative_alpha, relative_offset);
|
||||
std::cout << "Writing to " << output_name << std::endl;
|
||||
|
||||
std::ofstream out(output_name);
|
||||
out.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_polygon_WKT(out, wrap);
|
||||
|
||||
std::cout << "Done." << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
#define CGAL_AW2_DEBUG_PP
|
||||
|
||||
#include "output_helper.h"
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/alpha_wrap_2.h>
|
||||
#include <CGAL/Multipolygon_with_holes_2.h>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/bbox.h>
|
||||
#include <CGAL/Real_timer.h>
|
||||
#include <CGAL/IO/polygon_soup_io.h>
|
||||
#include <CGAL/IO/WKT.h>
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using Point_2 = K::Point_2;
|
||||
using Point_3 = K::Point_3;
|
||||
|
||||
using Multipolygon = CGAL::Multipolygon_with_holes_2<K>;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout.precision(17);
|
||||
std::cerr.precision(17);
|
||||
|
||||
// Read the input
|
||||
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/blobby-shuffled.off");
|
||||
std::cout << "Reading " << filename << "..." << std::endl;
|
||||
|
||||
std::vector<Point_3> points;
|
||||
std::vector<std::array<std::size_t, 3> > faces;
|
||||
if(!CGAL::IO::read_polygon_soup(filename, points, faces) || faces.empty())
|
||||
{
|
||||
std::cerr << "Invalid input:" << filename << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << "Input: " << points.size() << " points, " << faces.size() << " faces" << std::endl;
|
||||
|
||||
// Project onto the XY plane
|
||||
std::vector<Point_2> points_2(points.size());
|
||||
for(std::size_t i = 0; i < points.size(); ++i)
|
||||
points_2[i] = Point_2(points[i].x(), points[i].y());
|
||||
|
||||
std::vector<Point_3> points_3(points_2.size());
|
||||
for(std::size_t i = 0; i < points_2.size(); ++i)
|
||||
points_3[i] = Point_3(points_2[i].x(), points_2[i].y(), 0.);
|
||||
CGAL::IO::write_polygon_soup("projected.off", points_3, faces, CGAL::parameters::stream_precision(17));
|
||||
|
||||
// Compute the alpha and offset values
|
||||
const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 20.;
|
||||
const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 600.;
|
||||
|
||||
CGAL::Bbox_2 bbox;
|
||||
for(const Point_2& p : points_2)
|
||||
bbox += p.bbox();
|
||||
|
||||
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||
CGAL::square(bbox.ymax() - bbox.ymin()));
|
||||
|
||||
const double alpha = diag_length / relative_alpha;
|
||||
const double offset = diag_length / relative_offset;
|
||||
|
||||
// Construct the wrap
|
||||
CGAL::Real_timer t;
|
||||
t.start();
|
||||
|
||||
Multipolygon wrap;
|
||||
CGAL::alpha_wrap_2(points_2, faces, alpha, offset, wrap);
|
||||
|
||||
t.stop();
|
||||
std::cout << "Result: " << wrap.polygons_with_holes().size() << " polygons" << std::endl;
|
||||
std::cout << "Took " << t.time() << " s." << std::endl;
|
||||
|
||||
// Save the result
|
||||
const std::string output_name = generate_output_name(filename, relative_alpha, relative_offset);
|
||||
std::cout << "Writing to " << output_name << std::endl;
|
||||
|
||||
std::ofstream out(output_name);
|
||||
out.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_polygon_WKT(out, wrap);
|
||||
|
||||
std::cout << "Done." << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// Copyright (c) 2025 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) : Pierre Alliez
|
||||
// Cedric Portaneri,
|
||||
// Mael Rouxel-Labbé
|
||||
// Andreas Fabri
|
||||
// Michael Hemmer
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_INTERNAL_ALPHA_WRAP_AABB_GEOM_TRAITS_H
|
||||
#define CGAL_ALPHA_WRAP_2_INTERNAL_ALPHA_WRAP_AABB_GEOM_TRAITS_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Bbox_2.h>
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
template <typename K>
|
||||
struct Triangle_with_outside_info
|
||||
{
|
||||
using Kernel = K;
|
||||
using Triangle_2 = typename Kernel::Triangle_2;
|
||||
using Segment_2 = typename Kernel::Segment_2;
|
||||
|
||||
template <typename FaceHandle>
|
||||
Triangle_with_outside_info(const FaceHandle f, const K& k)
|
||||
{
|
||||
typename K::Construct_bbox_2 bbox = k.construct_bbox_2_object();
|
||||
typename K::Construct_triangle_2 triangle = k.construct_triangle_2_object();
|
||||
typename K::Construct_segment_2 segment = k.construct_segment_2_object();
|
||||
|
||||
m_tr = triangle(f->vertex(0)->point(), f->vertex(1)->point(), f->vertex(2)->point());
|
||||
m_bbox = bbox(m_tr);
|
||||
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
if(f->neighbor(i)->is_outside())
|
||||
m_b.set(i, true);
|
||||
|
||||
m_segments[i] = segment(f->vertex((i+1)%3)->point(),
|
||||
f->vertex((i+2)%3)->point());
|
||||
m_sbox[i] = bbox(m_segments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Triangle_with_outside_info& operator=(const Triangle_with_outside_info& rhs) = default;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Triangle_with_outside_info& t)
|
||||
{
|
||||
os << t.m_tr;
|
||||
return os;
|
||||
}
|
||||
|
||||
Triangle_2 m_tr;
|
||||
Bbox_2 m_bbox;
|
||||
std::array<Bbox_2, 3> m_sbox;
|
||||
std::array<Segment_2, 3> m_segments;
|
||||
std::bitset<3> m_b;
|
||||
};
|
||||
|
||||
template <typename K>
|
||||
class Disk_2
|
||||
: private K::Circle_2
|
||||
{
|
||||
using FT = typename K::FT;
|
||||
using Point_2 = typename K::Point_2;
|
||||
using Circle_2 = typename K::Circle_2;
|
||||
|
||||
public:
|
||||
explicit Disk_2(const Circle_2& c) : Circle_2(c) { }
|
||||
|
||||
public:
|
||||
const Circle_2& boundary() const { return static_cast<const Circle_2&>(*this); }
|
||||
};
|
||||
|
||||
template <typename GT>
|
||||
class Alpha_wrap_AABB_geom_traits
|
||||
: public GT
|
||||
{
|
||||
public:
|
||||
using Disk_2 = internal::Disk_2<GT>;
|
||||
|
||||
public:
|
||||
Alpha_wrap_AABB_geom_traits(const GT& gt = GT()) : GT(gt) { }
|
||||
|
||||
public:
|
||||
class Construct_disk_2
|
||||
{
|
||||
using FT = typename GT::FT;
|
||||
using Point_2 = typename GT::Point_2;
|
||||
using Disk_2 = internal::Disk_2<GT>;
|
||||
|
||||
const GT& m_base_traits;
|
||||
|
||||
public:
|
||||
Construct_disk_2(const GT& base_traits) : m_base_traits(base_traits) { }
|
||||
|
||||
Disk_2 operator()(const Point_2& p, const FT sqr)
|
||||
{
|
||||
return Disk_2(m_base_traits.construct_circle_2_object()(p, sqr));
|
||||
}
|
||||
};
|
||||
|
||||
// Enrich the base's Do_intersect_2 with Triangle_with_outside_info<K> and Disk_2<K> overloads
|
||||
class Do_intersect_2
|
||||
: public GT::Do_intersect_2
|
||||
{
|
||||
using Base = typename GT::Do_intersect_2;
|
||||
|
||||
using Point_2 = typename GT::Point_2;
|
||||
using Segment_2 = typename GT::Segment_2;
|
||||
using Triangle_2 = typename GT::Triangle_2;
|
||||
using Disk_2 = internal::Disk_2<GT>;
|
||||
|
||||
const GT& m_base_traits;
|
||||
|
||||
public:
|
||||
Do_intersect_2(const GT& base_traits)
|
||||
: Base(base_traits.do_intersect_2_object()),
|
||||
m_base_traits(base_traits)
|
||||
{ }
|
||||
|
||||
using Base::operator();
|
||||
|
||||
// ======
|
||||
// Disk_2
|
||||
|
||||
bool operator()(const Disk_2& disk,
|
||||
const Point_2& p) const
|
||||
{
|
||||
return !m_base_traits.has_on_unbounded_side_2_object()(disk.boundary(), p);
|
||||
}
|
||||
|
||||
bool operator()(const Disk_2& disk,
|
||||
const Segment_2& s) const
|
||||
{
|
||||
typename GT::Construct_bbox_2 bbox = m_base_traits.construct_bbox_2_object();
|
||||
typename GT::Construct_vertex_2 vertex = m_base_traits.construct_vertex_2_object();
|
||||
typename GT::Has_on_bounded_side_2 has_on_bounded_side = m_base_traits.has_on_bounded_side_2_object();
|
||||
|
||||
if(!do_overlap(bbox(disk.boundary()), bbox(s)))
|
||||
return false;
|
||||
|
||||
if(Base::operator()(disk.boundary(), s))
|
||||
return true;
|
||||
|
||||
return has_on_bounded_side(disk.boundary(), vertex(s, 0));
|
||||
}
|
||||
|
||||
bool operator()(const Disk_2& disk,
|
||||
const Triangle_2& tr) const
|
||||
{
|
||||
typename GT::Construct_bbox_2 bbox = m_base_traits.construct_bbox_2_object();
|
||||
typename GT::Construct_vertex_2 vertex = m_base_traits.construct_vertex_2_object();
|
||||
typename GT::Has_on_bounded_side_2 has_on_bounded_side = m_base_traits.has_on_bounded_side_2_object();
|
||||
|
||||
if(!do_overlap(bbox(disk.boundary()), bbox(tr)))
|
||||
return false;
|
||||
|
||||
if(Base::operator()(disk.boundary(), tr))
|
||||
return true;
|
||||
|
||||
return has_on_bounded_side(disk.boundary(), vertex(tr, 0));
|
||||
}
|
||||
|
||||
bool operator()(const Disk_2& disk,
|
||||
const CGAL::Bbox_2& bb) const
|
||||
{
|
||||
typename GT::Construct_bbox_2 bbox = m_base_traits.construct_bbox_2_object();
|
||||
typename GT::Construct_point_2 point = m_base_traits.construct_point_2_object();
|
||||
typename GT::Has_on_bounded_side_2 has_on_bounded_side = m_base_traits.has_on_bounded_side_2_object();
|
||||
|
||||
if(!do_overlap(bbox(disk.boundary()), bb))
|
||||
return false;
|
||||
|
||||
if(Base::operator()(disk.boundary(), bb))
|
||||
return true;
|
||||
|
||||
const Point_2 bbp = point(bb.xmin(), bb.ymin());
|
||||
|
||||
return has_on_bounded_side(disk.boundary(), bbp);
|
||||
}
|
||||
|
||||
// ======
|
||||
// Triangle_with_outside_info
|
||||
|
||||
template <typename K>
|
||||
bool operator()(const Triangle_with_outside_info<K>& q,
|
||||
const Point_2& p) const
|
||||
{
|
||||
typename GT::Construct_bbox_2 bbox = m_base_traits.construct_bbox_2_object();
|
||||
|
||||
const CGAL::Bbox_2 pbox = bbox(p);
|
||||
if(!do_overlap(q.m_bbox, pbox))
|
||||
return false;
|
||||
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
if(!q.m_b.test(i) && do_overlap(q.m_sbox[i], pbox) && Base::operator()(q.m_segments[i], p))
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_base_traits.has_on_bounded_side_2_object()(q.m_tr, p);
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
bool operator()(const Triangle_with_outside_info<K>& q,
|
||||
const Segment_2& s) const
|
||||
{
|
||||
typename GT::Construct_bbox_2 bbox = m_base_traits.construct_bbox_2_object();
|
||||
typename GT::Construct_vertex_2 vertex = m_base_traits.construct_vertex_2_object();
|
||||
|
||||
const CGAL::Bbox_2 sbox = bbox(s);
|
||||
if(!do_overlap(q.m_bbox, sbox))
|
||||
return false;
|
||||
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
if(!q.m_b.test(i) && do_overlap(q.m_sbox[i], sbox) && Base::operator()(q.m_segments[i], s))
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_base_traits.has_on_bounded_side_2_object()(q.m_tr, vertex(s, 0));
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
bool operator()(const Triangle_with_outside_info<K>& q,
|
||||
const Triangle_2& tr) const
|
||||
{
|
||||
typename GT::Construct_bbox_2 bbox = m_base_traits.construct_bbox_2_object();
|
||||
typename GT::Construct_vertex_2 vertex = m_base_traits.construct_vertex_2_object();
|
||||
|
||||
const CGAL::Bbox_2 tbox = bbox(tr);
|
||||
if(!do_overlap(q.m_bbox, tbox))
|
||||
return false;
|
||||
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
if(!q.m_b.test(i) && do_overlap(q.m_sbox[i], tbox) && Base::operator()(q.m_segments[i], tr))
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_base_traits.has_on_bounded_side_2_object()(q.m_tr, vertex(tr, 0));
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
bool operator()(const Triangle_with_outside_info<K>& q,
|
||||
const CGAL::Bbox_2& bb) const
|
||||
{
|
||||
if(!do_overlap(q.m_bbox, bb))
|
||||
return false;
|
||||
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
// this overload of do_intersect() must not filter based on q.m_b because
|
||||
// it is called from the AABB_tree's traversal with a node's bounding box,
|
||||
// and the fact that an edge is incident to an outside face is irrelevant
|
||||
// for the hierarchy of bounding boxes of the primitives.
|
||||
if(do_overlap(q.m_sbox[i], bb) && Base::operator()(q.m_segments[i], bb))
|
||||
return true;
|
||||
}
|
||||
|
||||
const Point_2 bbp = m_base_traits.construct_point_2_object()(bb.xmin(), bb.ymin());
|
||||
return m_base_traits.has_on_bounded_side_2_object()(q.m_tr, bbp);
|
||||
}
|
||||
};
|
||||
|
||||
Construct_disk_2 construct_disk_2_object() const
|
||||
{
|
||||
return Construct_disk_2(static_cast<const GT&>(*this));
|
||||
}
|
||||
|
||||
Do_intersect_2 do_intersect_2_object() const
|
||||
{
|
||||
return Do_intersect_2(static_cast<const GT&>(*this));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_INTERNAL_ALPHA_WRAP_AABB_GEOM_TRAITS_H
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright (c) 2019-2023 Google LLC (USA).
|
||||
// Copyright (c) 2019-2023 Google LLC (USA).
|
||||
// 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_ALPHA_WRAP_TRIANGULATION_FACE_BASE_2_H
|
||||
#define CGAL_ALPHA_WRAP_TRIANGULATION_FACE_BASE_2_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Delaunay_triangulation_face_base_with_circumcenter_2.h>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
enum class Face_label
|
||||
{
|
||||
// Faces that have been carved
|
||||
OUTSIDE = 0,
|
||||
// Faces that have not yet been carved
|
||||
INSIDE,
|
||||
// OUTSIDE faces that have been labeled "inside" again as to make the result manifold
|
||||
MANIFOLD
|
||||
};
|
||||
|
||||
inline std::string label_string(const Face_label& label) {
|
||||
switch(label) {
|
||||
case Face_label::OUTSIDE:
|
||||
return "OUTSIDE";
|
||||
case Face_label::INSIDE:
|
||||
return "INSIDE";
|
||||
case Face_label::MANIFOLD:
|
||||
return "MANIFOLD";
|
||||
default:
|
||||
CGAL_assertion_msg(false, "Unknown label");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
inline std::ostream& operator <<(std::ostream& os, const Face_label& label)
|
||||
{
|
||||
os << static_cast<std::underlying_type<Face_label>::type>(label);
|
||||
return os;
|
||||
}
|
||||
|
||||
template < typename GT,
|
||||
typename Fb = CGAL::Delaunay_triangulation_face_base_with_circumcenter_2<GT> >
|
||||
class Alpha_wrap_triangulation_face_base_2
|
||||
: public Fb
|
||||
{
|
||||
public:
|
||||
typedef typename Fb::Vertex_handle Vertex_handle;
|
||||
typedef typename Fb::Face_handle Face_handle;
|
||||
|
||||
public:
|
||||
template < typename TDS2 >
|
||||
struct Rebind_TDS
|
||||
{
|
||||
using Cb2 = typename Fb::template Rebind_TDS<TDS2>::Other;
|
||||
using Other = Alpha_wrap_triangulation_face_base_2<GT, Cb2>;
|
||||
};
|
||||
|
||||
private:
|
||||
Face_label m_label = Face_label::INSIDE;
|
||||
int m_region_label = 0; // used during boundary extraction
|
||||
|
||||
#ifndef CGAL_AW2_USE_SORTED_PRIORITY_QUEUE
|
||||
unsigned int m_erase_counter;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Alpha_wrap_triangulation_face_base_2()
|
||||
: Fb()
|
||||
{}
|
||||
|
||||
Alpha_wrap_triangulation_face_base_2(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2)
|
||||
: Fb(v0, v1, v2)
|
||||
{}
|
||||
|
||||
Alpha_wrap_triangulation_face_base_2(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2,
|
||||
Face_handle n0, Face_handle n1, Face_handle n2)
|
||||
: Fb(v0, v1, v2, n0, n1, n2)
|
||||
{}
|
||||
|
||||
public:
|
||||
Face_label label() const { return m_label; }
|
||||
void set_label(const Face_label label) { m_label = label; }
|
||||
bool is_inside() const { return m_label == Face_label::INSIDE; }
|
||||
bool is_outside() const { return m_label == Face_label::OUTSIDE; }
|
||||
int region_label() const { return m_region_label; }
|
||||
void set_region_label(const int region_label) { m_region_label = region_label; }
|
||||
|
||||
#ifndef CGAL_AW2_USE_SORTED_PRIORITY_QUEUE
|
||||
unsigned int erase_counter() const
|
||||
{
|
||||
return m_erase_counter;
|
||||
}
|
||||
void set_erase_counter(unsigned int c)
|
||||
{
|
||||
m_erase_counter = c;
|
||||
}
|
||||
void increment_erase_counter()
|
||||
{
|
||||
++m_erase_counter;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename Fb>
|
||||
class Face_base_with_timestamp
|
||||
: public Fb
|
||||
{
|
||||
std::size_t time_stamp_ = std::size_t(-2);
|
||||
|
||||
public:
|
||||
using Has_timestamp = CGAL::Tag_true;
|
||||
|
||||
template <class TDS>
|
||||
struct Rebind_TDS
|
||||
{
|
||||
using Fb2 = typename Fb::template Rebind_TDS<TDS>::Other;
|
||||
using Other = Face_base_with_timestamp<Fb2>;
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
Face_base_with_timestamp(const Args&... args)
|
||||
: Fb(args...)
|
||||
{ }
|
||||
|
||||
Face_base_with_timestamp(const Face_base_with_timestamp& other)
|
||||
: Fb(other), time_stamp_(other.time_stamp_)
|
||||
{ }
|
||||
|
||||
public:
|
||||
std::size_t time_stamp() const { return time_stamp_; }
|
||||
void set_time_stamp(const std::size_t& ts) { time_stamp_ = ts; }
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_TRIANGULATION_FACE_BASE_2_H
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2019-2023 Google LLC (USA).
|
||||
// Copyright (c) 2025 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_ALPHA_WRAP_TRIANGULATION_VERTEX_BASE_2_H
|
||||
#define CGAL_ALPHA_WRAP_TRIANGULATION_VERTEX_BASE_2_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Triangulation_vertex_base_2.h>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
enum class Vertex_type
|
||||
{
|
||||
DEFAULT = 0,
|
||||
BBOX_VERTEX,
|
||||
SEED_VERTEX
|
||||
};
|
||||
|
||||
template <typename GT,
|
||||
typename Vb = Triangulation_vertex_base_2<GT> >
|
||||
class Alpha_wrap_triangulation_vertex_base_2
|
||||
: public Vb
|
||||
{
|
||||
private:
|
||||
Vertex_type vertex_type = Vertex_type::DEFAULT;
|
||||
|
||||
public:
|
||||
using Face_handle = typename Vb::Face_handle;
|
||||
using Point = typename Vb::Point;
|
||||
|
||||
template <typename TDS2>
|
||||
struct Rebind_TDS
|
||||
{
|
||||
using Vb2 = typename Vb::template Rebind_TDS<TDS2>::Other;
|
||||
using Other = Alpha_wrap_triangulation_vertex_base_2<GT, Vb2>;
|
||||
};
|
||||
|
||||
public:
|
||||
Alpha_wrap_triangulation_vertex_base_2()
|
||||
: Vb() {}
|
||||
|
||||
Alpha_wrap_triangulation_vertex_base_2(const Point& p)
|
||||
: Vb(p) {}
|
||||
|
||||
Alpha_wrap_triangulation_vertex_base_2(const Point& p, Face_handle f)
|
||||
: Vb(p, f) {}
|
||||
|
||||
Alpha_wrap_triangulation_vertex_base_2(Face_handle f)
|
||||
: Vb(f) {}
|
||||
|
||||
public:
|
||||
const Vertex_type& type() const { return vertex_type; }
|
||||
Vertex_type& type() { return vertex_type; }
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_TRIANGULATION_VERTEX_BASE_2_H
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// Copyright (c) 2025 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_ALPHA_WRAP_2_INTERNAL_ORACLE_BASE_H
|
||||
#define CGAL_ALPHA_WRAP_2_INTERNAL_ORACLE_BASE_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Alpha_wrap_2/internal/offset_intersection.h>
|
||||
|
||||
#include <CGAL/AABB_tree/internal/AABB_traversal_traits.h>
|
||||
#include <CGAL/Default.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
template <typename AABBTraits>
|
||||
struct Default_traversal_traits
|
||||
{
|
||||
using Projection_traits = CGAL::internal::AABB_tree::Projection_traits<AABBTraits>;
|
||||
|
||||
template <typename Query>
|
||||
using Do_intersect_traits = CGAL::internal::AABB_tree::Do_intersect_traits<AABBTraits, Query>;
|
||||
|
||||
template <typename Query>
|
||||
using First_intersection_traits = CGAL::internal::AABB_tree::First_intersection_traits<AABBTraits, Query>;
|
||||
};
|
||||
|
||||
// Factorize the implementation of the functions calling the AABB tree
|
||||
template <typename AABBTree,
|
||||
typename AABBTraversalTraits>
|
||||
struct AABB_tree_oracle_helper
|
||||
{
|
||||
using Self = AABB_tree_oracle_helper<AABBTree, AABBTraversalTraits>;
|
||||
|
||||
using AABB_traits = typename AABBTree::AABB_traits;
|
||||
using GT = typename AABB_traits::Geom_traits;
|
||||
|
||||
using FT = typename AABB_traits::FT;
|
||||
using Point_2 = typename AABB_traits::Point;
|
||||
|
||||
template <typename Query>
|
||||
static bool do_intersect(const Query& query,
|
||||
const AABBTree& tree)
|
||||
{
|
||||
CGAL_precondition(!tree.empty());
|
||||
|
||||
using Do_intersect_traits = typename AABBTraversalTraits::template Do_intersect_traits<Query>;
|
||||
|
||||
Do_intersect_traits traversal_traits(tree.traits());
|
||||
tree.traversal(query, traversal_traits);
|
||||
return traversal_traits.is_intersection_found();
|
||||
}
|
||||
|
||||
static Point_2 closest_point(const Point_2& p,
|
||||
const AABBTree& tree)
|
||||
{
|
||||
CGAL_precondition(!tree.empty());
|
||||
|
||||
using Projection_traits = typename AABBTraversalTraits::Projection_traits;
|
||||
|
||||
const auto& hint = tree.best_hint(p);
|
||||
|
||||
Projection_traits projection_traits(hint.first, hint.second, tree.traits());
|
||||
tree.traversal(p, projection_traits);
|
||||
return projection_traits.closest_point();
|
||||
}
|
||||
|
||||
static FT squared_distance(const Point_2& p,
|
||||
const AABBTree& tree)
|
||||
{
|
||||
CGAL_precondition(!tree.empty());
|
||||
|
||||
const Point_2 closest = Self::closest_point(p, tree);
|
||||
return tree.traits().squared_distance_object()(p, closest);
|
||||
}
|
||||
|
||||
static bool first_intersection(const Point_2& p, const Point_2& q, Point_2& o,
|
||||
const FT offset_size,
|
||||
const FT intersection_precision,
|
||||
const AABBTree& tree)
|
||||
{
|
||||
CGAL_precondition(!tree.empty());
|
||||
|
||||
using AABB_distance_oracle = internal::AABB_distance_oracle<AABBTree, AABBTraversalTraits>;
|
||||
using Offset_intersection = internal::Offset_intersection<GT, AABB_distance_oracle>;
|
||||
|
||||
AABB_distance_oracle dist_oracle(tree);
|
||||
Offset_intersection offset_intersection(dist_oracle, offset_size, intersection_precision, 1 /*lip*/);
|
||||
return offset_intersection.first_intersection(p, q, o);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename GT,
|
||||
typename AABBTree,
|
||||
typename AABBTraversalTraits = CGAL::Default,
|
||||
typename BaseOracle = int> // base oracle
|
||||
class AABB_tree_oracle
|
||||
: public BaseOracle
|
||||
{
|
||||
protected:
|
||||
using Geom_traits = GT;
|
||||
using FT = typename GT::FT;
|
||||
using Point_2 = typename GT::Point_2;
|
||||
|
||||
// When building oracle stacks, there are copies of (empty) trees, which isn't possible, thus pointer
|
||||
using AABB_tree = AABBTree;
|
||||
using AABB_tree_ptr = std::shared_ptr<AABB_tree>;
|
||||
using AABB_traits = typename AABB_tree::AABB_traits;
|
||||
using AABB_traversal_traits = typename Default::Get<AABBTraversalTraits,
|
||||
Default_traversal_traits<AABB_traits> >::type;
|
||||
|
||||
using AABB_helper = AABB_tree_oracle_helper<AABB_tree, AABB_traversal_traits>;
|
||||
|
||||
public:
|
||||
AABB_tree_oracle(const BaseOracle& base,
|
||||
const GT& gt)
|
||||
: BaseOracle(base),
|
||||
m_gt(gt),
|
||||
m_tree_ptr(std::make_shared<AABB_tree>())
|
||||
{ }
|
||||
|
||||
AABB_tree_oracle(const AABB_tree_oracle&) = default;
|
||||
|
||||
public:
|
||||
const Geom_traits& geom_traits() const { return m_gt; }
|
||||
|
||||
AABB_tree& tree() { return *m_tree_ptr; }
|
||||
const AABB_tree& tree() const { return *m_tree_ptr; }
|
||||
BaseOracle& base() { return static_cast<BaseOracle&>(*this); }
|
||||
const BaseOracle& base() const { return static_cast<const BaseOracle&>(*this); }
|
||||
|
||||
bool empty() const { return m_tree_ptr->empty(); }
|
||||
bool do_call() const { return (!empty() || base().do_call()); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_tree_ptr->clear();
|
||||
base().clear();
|
||||
}
|
||||
|
||||
public:
|
||||
typename AABB_tree::Bounding_box bbox() const
|
||||
{
|
||||
CGAL_precondition(do_call());
|
||||
|
||||
typename AABB_tree::Bounding_box bb;
|
||||
|
||||
if(base().do_call())
|
||||
bb += base().bbox();
|
||||
|
||||
if(!empty())
|
||||
bb += tree().bbox();
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool do_intersect(const T& t) const
|
||||
{
|
||||
CGAL_precondition(do_call());
|
||||
|
||||
if(base().do_call() && base().do_intersect(t))
|
||||
return true;
|
||||
|
||||
if(!empty())
|
||||
return AABB_helper::do_intersect(t, tree());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FT squared_distance(const Point_2& p) const
|
||||
{
|
||||
CGAL_precondition(do_call());
|
||||
|
||||
if(base().do_call())
|
||||
{
|
||||
if(!empty()) // both non empty
|
||||
{
|
||||
const FT base_sqd = base().squared_distance(p);
|
||||
// @speed could do a smarter traversal, no need to search deeper than the current best
|
||||
const FT this_sqd = AABB_helper::squared_distance(p, tree());
|
||||
return (std::min)(base_sqd, this_sqd);
|
||||
}
|
||||
else // this level is empty
|
||||
{
|
||||
return base().squared_distance(p);
|
||||
}
|
||||
}
|
||||
else // empty base
|
||||
{
|
||||
return AABB_helper::squared_distance(p, tree());
|
||||
}
|
||||
}
|
||||
|
||||
Point_2 closest_point(const Point_2& p) const
|
||||
{
|
||||
CGAL_precondition(do_call());
|
||||
|
||||
if(base().do_call())
|
||||
{
|
||||
if(!empty()) // both non empty
|
||||
{
|
||||
const Point_2 base_c = base().closest_point(p);
|
||||
// @speed could do a smarter traversal, no need to search deeper than the current best
|
||||
const Point_2 this_c = AABB_helper::closest_point(p, tree());
|
||||
return (compare_distance_to_point(p, base_c, this_c) == CGAL::SMALLER) ? base_c : this_c;
|
||||
}
|
||||
else // this level is empty
|
||||
{
|
||||
return base().closest_point(p);
|
||||
}
|
||||
}
|
||||
else // empty base
|
||||
{
|
||||
return AABB_helper::closest_point(p, tree());
|
||||
}
|
||||
}
|
||||
|
||||
bool first_intersection(const Point_2& p, const Point_2& q,
|
||||
Point_2& o,
|
||||
const FT offset_size,
|
||||
const FT intersection_precision) const
|
||||
{
|
||||
CGAL_precondition(do_call());
|
||||
|
||||
if(base().do_call())
|
||||
{
|
||||
if(!empty()) // both non empty
|
||||
{
|
||||
Point_2 base_o;
|
||||
bool base_b = base().first_intersection(p, q, base_o, offset_size, intersection_precision);
|
||||
|
||||
if(base_b) // intersection found in base
|
||||
{
|
||||
// @speed could do a smarter traversal, no need to search deeper than the current best
|
||||
Point_2 this_o;
|
||||
bool this_b = AABB_helper::first_intersection(p, q, this_o, offset_size, intersection_precision, tree());
|
||||
if(this_b)
|
||||
o = (compare_distance_to_point(p, base_o, this_o) == SMALLER) ? base_o : this_o;
|
||||
else
|
||||
o = base_o;
|
||||
|
||||
return true;
|
||||
}
|
||||
else // no intersection found in non-empty base
|
||||
{
|
||||
return AABB_helper::first_intersection(p, q, o, offset_size, intersection_precision, tree());
|
||||
}
|
||||
}
|
||||
else // this level is empty
|
||||
{
|
||||
return base().first_intersection(p, q, o, offset_size, intersection_precision);
|
||||
}
|
||||
}
|
||||
else // empty base
|
||||
{
|
||||
return AABB_helper::first_intersection(p, q, o, offset_size, intersection_precision, tree());
|
||||
}
|
||||
}
|
||||
|
||||
bool first_intersection(const Point_2& p, const Point_2& q,
|
||||
Point_2& o,
|
||||
const FT offset_size) const
|
||||
{
|
||||
return first_intersection(p, q, o, offset_size, 1e-2 * offset_size);
|
||||
}
|
||||
|
||||
protected:
|
||||
Geom_traits m_gt;
|
||||
AABB_tree_ptr m_tree_ptr;
|
||||
};
|
||||
|
||||
// partial specialization, when there is no further oracle beneath in the stack.
|
||||
//
|
||||
// `int` is used to denote the absence of base rather than `void`,
|
||||
// as to use the same constructor for both versions (thus requires a default construction)
|
||||
template <typename GT,
|
||||
typename AABBTree,
|
||||
typename AABBTraversalTraits>
|
||||
class AABB_tree_oracle<GT, AABBTree, AABBTraversalTraits, int>
|
||||
{
|
||||
protected:
|
||||
using Geom_traits = GT;
|
||||
using FT = typename GT::FT;
|
||||
using Point_2 = typename GT::Point_2;
|
||||
|
||||
using AABB_tree = AABBTree;
|
||||
using AABB_tree_ptr = std::shared_ptr<AABB_tree>;
|
||||
using AABB_traits = typename AABB_tree::AABB_traits;
|
||||
using AABB_traversal_traits = typename Default::Get<AABBTraversalTraits,
|
||||
Default_traversal_traits<AABB_traits> >::type;
|
||||
|
||||
using AABB_helper = AABB_tree_oracle_helper<AABB_tree, AABB_traversal_traits>;
|
||||
|
||||
public:
|
||||
AABB_tree_oracle(const int, // to have a common constructor API between both versions
|
||||
const GT& gt)
|
||||
: m_gt(gt), m_tree_ptr(std::make_shared<AABB_tree>())
|
||||
{ }
|
||||
|
||||
public:
|
||||
const Geom_traits& geom_traits() const { return m_gt; }
|
||||
AABB_tree& tree() { return *m_tree_ptr; }
|
||||
const AABB_tree& tree() const { return *m_tree_ptr; }
|
||||
|
||||
bool empty() const { return m_tree_ptr->empty(); }
|
||||
bool do_call() const { return !empty(); }
|
||||
|
||||
void clear() { m_tree_ptr->clear(); }
|
||||
|
||||
public:
|
||||
typename AABB_tree::Bounding_box bbox() const
|
||||
{
|
||||
CGAL_precondition(!empty());
|
||||
return tree().bbox();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool do_intersect(const T& t) const
|
||||
{
|
||||
CGAL_precondition(!empty());
|
||||
return AABB_helper::do_intersect(t, tree());
|
||||
}
|
||||
|
||||
FT squared_distance(const Point_2& p) const
|
||||
{
|
||||
CGAL_precondition(!empty());
|
||||
return AABB_helper::squared_distance(p, tree());
|
||||
}
|
||||
|
||||
Point_2 closest_point(const Point_2& p) const
|
||||
{
|
||||
CGAL_precondition(!empty());
|
||||
return AABB_helper::closest_point(p, tree());
|
||||
}
|
||||
|
||||
bool first_intersection(const Point_2& p, const Point_2& q, Point_2& o,
|
||||
const FT offset_size, const FT intersection_precision) const
|
||||
{
|
||||
CGAL_precondition(!empty());
|
||||
return AABB_helper::first_intersection(p, q, o, offset_size, intersection_precision, tree());
|
||||
}
|
||||
|
||||
bool first_intersection(const Point_2& p, const Point_2& q, Point_2& o, const FT offset_size) const
|
||||
{
|
||||
CGAL_precondition(!empty());
|
||||
return AABB_helper::first_intersection(p, q, o, offset_size, 1e-2 * offset_size, tree());
|
||||
}
|
||||
|
||||
private:
|
||||
Geom_traits m_gt;
|
||||
AABB_tree_ptr m_tree_ptr;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_INTERNAL_ORACLE_BASE_H
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// 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_ALPHA_WRAP_2_INTERNAL_POINT_SET_ORACLE_H
|
||||
#define CGAL_ALPHA_WRAP_2_INTERNAL_POINT_SET_ORACLE_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Alpha_wrap_2/internal/Alpha_wrap_AABB_geom_traits.h>
|
||||
#include <CGAL/Alpha_wrap_2/internal/Oracle_base.h>
|
||||
|
||||
#include <CGAL/AABB_primitive.h>
|
||||
#include <CGAL/AABB_traits_2.h>
|
||||
#include <CGAL/AABB_tree.h>
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
#include <CGAL/Named_function_parameters.h>
|
||||
#include <CGAL/property_map.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
// Just some typedefs for readability
|
||||
template <typename GT_>
|
||||
struct PS_oracle_traits
|
||||
{
|
||||
using Geom_traits = Alpha_wrap_AABB_geom_traits<GT_>; // Wrap the kernel to add Disk_2 + custom Do_intersect_2
|
||||
|
||||
using Points = std::vector<typename GT_::Point_2>;
|
||||
using Points_ptr = std::shared_ptr<Points>;
|
||||
using PR_iterator = typename Points::const_iterator;
|
||||
|
||||
using Primitive = AABB_primitive<PR_iterator,
|
||||
Input_iterator_property_map<PR_iterator> /*DPM*/,
|
||||
Input_iterator_property_map<PR_iterator> /*RPM*/,
|
||||
CGAL::Tag_false, // not external
|
||||
CGAL::Tag_false>; // no caching
|
||||
|
||||
using AABB_traits = CGAL::AABB_traits_2<Geom_traits, Primitive>;
|
||||
using AABB_tree = CGAL::AABB_tree<AABB_traits>;
|
||||
};
|
||||
|
||||
template <typename GT_,
|
||||
typename BaseOracle = int>
|
||||
class Point_set_oracle
|
||||
: public AABB_tree_oracle<typename PS_oracle_traits<GT_>::Geom_traits,
|
||||
typename PS_oracle_traits<GT_>::AABB_tree,
|
||||
CGAL::Default, // Default_traversal_traits<AABB_traits>
|
||||
BaseOracle>
|
||||
{
|
||||
using PSOT = PS_oracle_traits<GT_>;
|
||||
using Base_GT = GT_;
|
||||
|
||||
public:
|
||||
using Geom_traits = typename PSOT::Geom_traits;
|
||||
|
||||
private:
|
||||
using Points = typename PSOT::Points;
|
||||
using Points_ptr = typename PSOT::Points_ptr;
|
||||
using AABB_tree = typename PSOT::AABB_tree;
|
||||
using Oracle_base = AABB_tree_oracle<Geom_traits, AABB_tree, CGAL::Default, BaseOracle>;
|
||||
|
||||
private:
|
||||
Points_ptr m_points_ptr;
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
Point_set_oracle(const BaseOracle& base_oracle,
|
||||
const Base_GT& gt = Base_GT())
|
||||
: Oracle_base(base_oracle, gt)
|
||||
{
|
||||
m_points_ptr = std::make_shared<Points>();
|
||||
}
|
||||
|
||||
Point_set_oracle(const Base_GT& gt,
|
||||
const BaseOracle& base_oracle = BaseOracle())
|
||||
: Point_set_oracle(base_oracle, gt)
|
||||
{ }
|
||||
|
||||
Point_set_oracle()
|
||||
: Point_set_oracle(BaseOracle(), Base_GT())
|
||||
{ }
|
||||
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
m_points_ptr->clear();
|
||||
Oracle_base::clear();
|
||||
}
|
||||
|
||||
// adds a range of points to the oracle
|
||||
template <typename PointRange,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_points(const PointRange& points,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Insert into AABB tree (points)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(points.empty())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Warning: Input is empty (PS)" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t old_size = m_points_ptr->size();
|
||||
m_points_ptr->insert(std::cend(*m_points_ptr), std::cbegin(points), std::cend(points));
|
||||
|
||||
this->tree().rebuild(std::cbegin(*m_points_ptr), std::cend(*m_points_ptr));
|
||||
|
||||
// Manually constructing it here purely for profiling reasons: if we keep the lazy approach,
|
||||
// it will be done at the first treatment of an edge that needs a Steiner point.
|
||||
// So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes
|
||||
// to accelerate the tree.
|
||||
this->tree().accelerate_distance_queries();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "PS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_INTERNAL_POINT_SET_ORACLE_H
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// Copyright (c) 2025 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_ALPHA_WRAP_2_INTERNAL_SEGMENT_SOUP_ORACLE_H
|
||||
#define CGAL_ALPHA_WRAP_2_INTERNAL_SEGMENT_SOUP_ORACLE_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Alpha_wrap_2/internal/Alpha_wrap_AABB_geom_traits.h>
|
||||
#include <CGAL/Alpha_wrap_2/internal/Oracle_base.h>
|
||||
|
||||
#include <CGAL/AABB_traits_2.h>
|
||||
#include <CGAL/AABB_tree.h>
|
||||
#include <CGAL/AABB_segment_primitive_2.h>
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
#include <CGAL/Named_function_parameters.h>
|
||||
#include <CGAL/property_map.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
// Just some typedefs for readability
|
||||
template <typename GT_>
|
||||
struct SS_oracle_traits
|
||||
{
|
||||
using Geom_traits = Alpha_wrap_AABB_geom_traits<GT_>; // Wrap the kernel to add Disk_2 + custom Do_intersect_2
|
||||
|
||||
using Point = typename GT_::Point_2;
|
||||
using Segment = typename GT_::Segment_2;
|
||||
using Segments = std::vector<Segment>;
|
||||
using Segments_ptr = std::shared_ptr<Segments>;
|
||||
using SR_iterator = typename Segments::const_iterator;
|
||||
|
||||
using Primitive = AABB_primitive<SR_iterator,
|
||||
Input_iterator_property_map<SR_iterator>, // DPM
|
||||
CGAL::internal::Source_of_segment_2_iterator_property_map<Geom_traits, SR_iterator>, // RPM
|
||||
CGAL::Tag_false, // not external
|
||||
CGAL::Tag_false>; // no caching
|
||||
|
||||
using AABB_traits = CGAL::AABB_traits_2<Geom_traits, Primitive>;
|
||||
using AABB_tree = CGAL::AABB_tree<AABB_traits>;
|
||||
};
|
||||
|
||||
template <typename GT_,
|
||||
typename BaseOracle = int>
|
||||
class Segment_soup_oracle
|
||||
: public AABB_tree_oracle<typename SS_oracle_traits<GT_>::Geom_traits,
|
||||
typename SS_oracle_traits<GT_>::AABB_tree,
|
||||
CGAL::Default, // Default_traversal_traits<AABB_traits>
|
||||
BaseOracle>
|
||||
{
|
||||
using SSOT = SS_oracle_traits<GT_>;
|
||||
using Base_GT = GT_;
|
||||
|
||||
public:
|
||||
using Geom_traits = typename SSOT::Geom_traits;
|
||||
|
||||
private:
|
||||
using Point = typename SSOT::Point;
|
||||
using Segment = typename SSOT::Segment;
|
||||
using Segments = typename SSOT::Segments;
|
||||
using Segments_ptr = typename SSOT::Segments_ptr;
|
||||
using AABB_tree = typename SSOT::AABB_tree;
|
||||
using Oracle_base = AABB_tree_oracle<Geom_traits, AABB_tree, CGAL::Default, BaseOracle>;
|
||||
|
||||
private:
|
||||
Segments_ptr m_segments_ptr;
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
Segment_soup_oracle(const BaseOracle& base_oracle,
|
||||
const Base_GT& gt = Base_GT())
|
||||
: Oracle_base(base_oracle, gt)
|
||||
{
|
||||
m_segments_ptr = std::make_shared<Segments>();
|
||||
}
|
||||
|
||||
Segment_soup_oracle(const Base_GT& gt,
|
||||
const BaseOracle& base_oracle = BaseOracle())
|
||||
: Segment_soup_oracle(base_oracle, gt)
|
||||
{ }
|
||||
|
||||
Segment_soup_oracle()
|
||||
: Segment_soup_oracle(BaseOracle(), Base_GT())
|
||||
{ }
|
||||
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
m_segments_ptr->clear();
|
||||
Oracle_base::clear();
|
||||
}
|
||||
|
||||
template <typename SegmentRange,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_segments(const SegmentRange& segments,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Insert into AABB tree (" << segments.size() << " segments)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(segments.empty())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Warning: Input is empty (SS)" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t old_size = m_segments_ptr->size();
|
||||
|
||||
typename Geom_traits::Is_degenerate_2 is_degenerate = this->geom_traits().is_degenerate_2_object();
|
||||
|
||||
for(const Segment& s : segments)
|
||||
{
|
||||
if(is_degenerate(s))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Warning: ignoring degenerate segment " << s << std::endl;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
m_segments_ptr->push_back(s);
|
||||
}
|
||||
|
||||
this->tree().rebuild(std::cbegin(*m_segments_ptr), std::cend(*m_segments_ptr));
|
||||
|
||||
// Manually constructing it here purely for profiling reasons: if we keep the lazy approach,
|
||||
// it will be done at the first treatment of an edge that needs a Steiner point.
|
||||
// So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes
|
||||
// to accelerate the tree.
|
||||
this->tree().accelerate_distance_queries();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "SS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename TriangleRange,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_triangles(const TriangleRange& triangles,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Insert into AABB Tree (" << triangles.size() << " triangles)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(triangles.empty())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Warning: Input is empty (TS)" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t old_size = m_segments_ptr->size();
|
||||
|
||||
typename Geom_traits::Construct_segment_2 segment = this->geom_traits().construct_segment_2_object();
|
||||
typename Geom_traits::Is_degenerate_2 is_degenerate = this->geom_traits().is_degenerate_2_object();
|
||||
|
||||
for(const auto& tr : triangles)
|
||||
{
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
Segment s = segment(tr[i], tr[(i+1)%3]);
|
||||
if(is_degenerate(s))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Warning: ignoring degenerate segment " << s << std::endl;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
m_segments_ptr->push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
this->tree().rebuild(std::cbegin(*m_segments_ptr), std::cend(*m_segments_ptr));
|
||||
|
||||
// Manually constructing it here purely for profiling reasons: if we keep the lazy approach,
|
||||
// it will be done at the first treatment of an edge that needs a Steiner point.
|
||||
// So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes
|
||||
// to accelerate the tree.
|
||||
this->tree().accelerate_distance_queries();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "SS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename PointRange, typename FaceRange,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_polygon_soup(const PointRange& points,
|
||||
const FaceRange& faces,
|
||||
const CGAL_NP_CLASS& np = CGAL::parameters::default_values())
|
||||
{
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
|
||||
using PPM = typename GetPointMap<PointRange, CGAL_NP_CLASS>::const_type;
|
||||
using Point_ref = typename boost::property_traits<PPM>::reference;
|
||||
|
||||
using Face = typename boost::range_value<FaceRange>::type;
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Insert into AABB tree (" << faces.size() << " polygons)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(points.empty() || faces.empty())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Warning: Input is empty (PS)" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t old_size = m_segments_ptr->size();
|
||||
|
||||
PPM pm = choose_parameter<PPM>(get_parameter(np, internal_np::point_map));
|
||||
static_assert(std::is_same<typename boost::property_traits<PPM>::value_type, Point>::value);
|
||||
|
||||
typename Geom_traits::Construct_segment_2 segment = this->geom_traits().construct_segment_2_object();
|
||||
typename Geom_traits::Is_degenerate_2 is_degenerate = this->geom_traits().is_degenerate_2_object();
|
||||
|
||||
for(const Face& f : faces)
|
||||
{
|
||||
if(f.size() < 2)
|
||||
continue;
|
||||
|
||||
for(std::size_t i=0,n=f.size(); i<n; ++i)
|
||||
{
|
||||
Segment s = segment(get(pm, points[f[i]]), get(pm, points[f[(i+1)%n]]));
|
||||
if(is_degenerate(s))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Warning: ignoring degenerate segment " << s << std::endl;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
m_segments_ptr->push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
this->tree().rebuild(std::cbegin(*m_segments_ptr), std::cend(*m_segments_ptr));
|
||||
|
||||
// Manually constructing it here purely for profiling reasons: if we keep the lazy approach,
|
||||
// it will be done at the first treatment of an edge that needs a Steiner point.
|
||||
// So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes
|
||||
// to accelerate the tree.
|
||||
this->tree().accelerate_distance_queries();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "SS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename MultiLineString,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_multilinestring(const MultiLineString& mls,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
{
|
||||
using LineString = typename boost::range_value<MultiLineString>::type;
|
||||
|
||||
const std::size_t old_size = m_segments_ptr->size();
|
||||
|
||||
typename Geom_traits::Construct_segment_2 segment = this->geom_traits().construct_segment_2_object();
|
||||
typename Geom_traits::Is_degenerate_2 is_degenerate = this->geom_traits().is_degenerate_2_object();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Insert into AABB tree (multi-linestring)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(mls.empty())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Warning: Input is empty (multi-linestring)" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
for(const LineString& ls : mls)
|
||||
{
|
||||
for(std::size_t i=0; i<ls.size()-1; ++i)
|
||||
{
|
||||
const Segment s = segment(ls[i], ls[i+1]);
|
||||
if(is_degenerate(s))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Warning: ignoring degenerate segment " << s << std::endl;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
m_segments_ptr->push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
this->tree().rebuild(std::cbegin(*m_segments_ptr), std::cend(*m_segments_ptr));
|
||||
|
||||
// Manually constructing it here purely for profiling reasons: if we keep the lazy approach,
|
||||
// it will be done at the first treatment of an edge that needs a Steiner point.
|
||||
// So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes
|
||||
// to accelerate the tree.
|
||||
this->tree().accelerate_distance_queries();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "SS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename MultipolygonWithHoles,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_multipolygon(const MultipolygonWithHoles& mp,
|
||||
const CGAL_NP_CLASS& np = CGAL::parameters::default_values())
|
||||
{
|
||||
using Polygon_with_holes_2 = typename MultipolygonWithHoles::Polygon_with_holes_2;
|
||||
|
||||
const std::size_t old_size = m_segments_ptr->size();
|
||||
|
||||
typename Geom_traits::Is_degenerate_2 is_degenerate = this->geom_traits().is_degenerate_2_object();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Insert into AABB tree (multi-polygon)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(mp.polygons_with_holes().empty())
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "Warning: Input is empty (multi-polygon)" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
for(const Polygon_with_holes_2& polygon : mp.polygons_with_holes()) {
|
||||
for(const Segment& s : polygon.outer_boundary().edges()) {
|
||||
if(is_degenerate(s))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Warning: ignoring degenerate segment " << s << std::endl;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
m_segments_ptr->push_back(s);
|
||||
}
|
||||
for(const auto& hole : polygon.holes()) {
|
||||
for(const Segment& s : hole.edges()) {
|
||||
if(is_degenerate(s))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Warning: ignoring degenerate segment " << s << std::endl;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
m_segments_ptr->push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->tree().rebuild(std::cbegin(*m_segments_ptr), std::cend(*m_segments_ptr));
|
||||
|
||||
// Manually constructing it here purely for profiling reasons: if we keep the lazy approach,
|
||||
// it will be done at the first treatment of an edge that needs a Steiner point.
|
||||
// So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes
|
||||
// to accelerate the tree.
|
||||
this->tree().accelerate_distance_queries();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "SS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_INTERNAL_SEGMENT_SOUP_ORACLE_H
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// Copyright (c) 2025 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) : Pierre Alliez
|
||||
// Cedric Portaneri,
|
||||
// Mael Rouxel-Labbé
|
||||
// Andreas Fabri
|
||||
// Michael Hemmer
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_INTERNAL_GATE_PRIORITY_QUEUE_H
|
||||
#define CGAL_ALPHA_WRAP_2_INTERNAL_GATE_PRIORITY_QUEUE_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <boost/property_map/property_map.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
enum class Steiner_status
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
NO_STEINER_POINT,
|
||||
RULE_1,
|
||||
RULE_2
|
||||
};
|
||||
|
||||
#ifdef CGAL_AW2_COMPUTE_AND_STORE_STEINER_INFO_AT_GATE_CREATION
|
||||
template <typename Tr>
|
||||
struct Gate_steiner_info
|
||||
{
|
||||
using Point_2 = typename Tr::Geom_traits::Point_2;
|
||||
|
||||
Gate_steiner_info() : m_steiner_status(Steiner_status::UNKNOWN), m_steiner_point(std::nullopt) { }
|
||||
|
||||
Steiner_status m_steiner_status;
|
||||
std::optional<Point_2> m_steiner_point;
|
||||
|
||||
bool has_steiner_point() const { return m_steiner_point.has_value(); }
|
||||
bool has_steiner_from_intersection() const { return (m_steiner_status == Steiner_status::RULE_1); }
|
||||
bool has_steiner_from_projection() const { return (m_steiner_status == Steiner_status::RULE_2); }
|
||||
const Point_2& steiner_point() const {
|
||||
CGAL_precondition(has_steiner_point());
|
||||
return *m_steiner_point;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CGAL_AW2_USE_SORTED_PRIORITY_QUEUE
|
||||
|
||||
// Represents an alpha-traversable edge in the mutable priority queue
|
||||
template <typename Tr>
|
||||
class Gate
|
||||
#ifdef CGAL_AW2_COMPUTE_AND_STORE_STEINER_INFO_AT_GATE_CREATION
|
||||
: public Gate_steiner_info<Tr>
|
||||
#endif
|
||||
{
|
||||
using Edge = typename Tr::Edge;
|
||||
using FT = typename Tr::Geom_traits::FT;
|
||||
|
||||
private:
|
||||
Edge m_edge;
|
||||
FT m_priority; // circumcircle sq_radius
|
||||
bool m_is_permissive_edge;
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
Gate(const Edge& edge,
|
||||
const FT& priority,
|
||||
const bool is_permissive_edge)
|
||||
:
|
||||
m_edge(edge),
|
||||
m_priority(priority),
|
||||
m_is_permissive_edge(is_permissive_edge)
|
||||
{
|
||||
CGAL_assertion(priority >= 0);
|
||||
}
|
||||
|
||||
// This overload is only used for contains() and erase(), priority and bbox flag are dummy value
|
||||
Gate(const Edge& edge)
|
||||
: Gate(edge, 0, false)
|
||||
{ }
|
||||
|
||||
public:
|
||||
const Edge& edge() const { return m_edge; }
|
||||
const FT& priority() const { return m_priority; }
|
||||
bool is_permissive_edge() const { return m_is_permissive_edge; }
|
||||
};
|
||||
|
||||
struct Less_gate
|
||||
{
|
||||
template <typename Tr>
|
||||
bool operator()(const Gate<Tr>& a, const Gate<Tr>& b) const
|
||||
{
|
||||
// If one is permissive and the other is not, give priority to the permissive edge.
|
||||
//
|
||||
// The permissive edge are given highest priority because they need to be treated
|
||||
// regardless of their circumradius. Treating them first allow the part that depends
|
||||
// on alpha to be treated uniformly in a way: whatever the alpha, all permissive faces
|
||||
// will first be treated.
|
||||
if(a.is_permissive_edge() != b.is_permissive_edge())
|
||||
return a.is_permissive_edge();
|
||||
|
||||
if(a.priority() == b.priority())
|
||||
{
|
||||
// arbitrary, the sole purpose is to make it a total order for determinism
|
||||
if(a.edge().first->time_stamp() == b.edge().first->time_stamp())
|
||||
return a.edge().second < b.edge().second;
|
||||
|
||||
return a.edge().first->time_stamp() < b.edge().first->time_stamp();
|
||||
}
|
||||
|
||||
return a.priority() > b.priority();
|
||||
}
|
||||
};
|
||||
|
||||
#else // CGAL_AW2_USE_SORTED_PRIORITY_QUEUE
|
||||
|
||||
// Represents an alpha-traversable edge in the mutable priority queue
|
||||
template <typename Tr>
|
||||
class Gate
|
||||
#ifdef CGAL_AW2_COMPUTE_AND_STORE_STEINER_INFO_AT_GATE_CREATION
|
||||
: public Gate_steiner_info<Tr>
|
||||
#endif
|
||||
{
|
||||
using Edge = typename Tr::Edge;
|
||||
using FT = typename Tr::Geom_traits::FT;
|
||||
|
||||
private:
|
||||
Edge m_edge, m_mirror_edge;
|
||||
const unsigned int m_erase_counter_mem;
|
||||
const unsigned int m_mirror_erase_counter_mem;
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
Gate(const Edge& edge,
|
||||
const Tr& tr)
|
||||
:
|
||||
m_edge(edge),
|
||||
m_mirror_edge(tr.mirror_edge(edge)),
|
||||
m_erase_counter_mem(m_edge.first->erase_counter()),
|
||||
m_mirror_erase_counter_mem(m_mirror_edge.first->erase_counter())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
const Edge& edge() const { return m_edge; }
|
||||
|
||||
bool is_zombie() const
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_PP
|
||||
std::cout << "Zombie check of " << std::endl;
|
||||
std::cout << " " << &*(m_edge.first) << ": " << m_edge.first->erase_counter() << " " << m_erase_counter_mem << std::endl;
|
||||
std::cout << " " << &*(m_mirror_edge.first) << ": " << m_mirror_edge.first->erase_counter() << " " << m_mirror_erase_counter_mem << std::endl;
|
||||
#endif
|
||||
|
||||
return (m_edge.first->erase_counter() != m_erase_counter_mem) ||
|
||||
(m_mirror_edge.first->erase_counter() != m_mirror_erase_counter_mem);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CGAL_AW2_USE_SORTED_PRIORITY_QUEUE
|
||||
|
||||
template <typename Tr>
|
||||
struct Gate_ID_PM
|
||||
{
|
||||
using key_type = Gate<Tr>;
|
||||
using value_type = std::size_t;
|
||||
using reference = std::size_t;
|
||||
using category = boost::readable_property_map_tag;
|
||||
|
||||
inline friend value_type get(Gate_ID_PM, const key_type& k)
|
||||
{
|
||||
using Edge = typename Tr::Edge;
|
||||
|
||||
const Edge& f = k.edge();
|
||||
return (3 * f.first->time_stamp() + f.second);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_INTERNAL_GATE_PRIORITY_QUEUE_H
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// Copyright (c) 2025 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) : Pierre Alliez
|
||||
// Cedric Portaneri,
|
||||
// Mael Rouxel-Labbé
|
||||
// Andreas Fabri
|
||||
// Michael Hemmer
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_INTERNAL_GEOMETRY_UTILS_H
|
||||
#define CGAL_ALPHA_WRAP_2_INTERNAL_GEOMETRY_UTILS_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
|
||||
#include <CGAL/Simple_cartesian.h>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
template <typename K>
|
||||
struct Orientation_of_circumcenter
|
||||
{
|
||||
typedef typename K::Point_2 Point_2;
|
||||
|
||||
typedef Orientation result_type;
|
||||
|
||||
Orientation operator()(const Point_2& p, const Point_2& q,
|
||||
const Point_2& ccp, const Point_2& ccq, const Point_2& ccr) const
|
||||
{
|
||||
Point_2 cc = circumcenter(ccp, ccq, ccr);
|
||||
return orientation(p, q, cc);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Tr>
|
||||
bool
|
||||
less_squared_radius_of_min_empty_circle(typename Tr::Geom_traits::FT sq_alpha,
|
||||
const typename Tr::Edge& e,
|
||||
const Tr& tr)
|
||||
{
|
||||
using Face_handle = typename Tr::Face_handle;
|
||||
using Point = typename Tr::Point;
|
||||
|
||||
using CK = typename Tr::Geom_traits;
|
||||
using Exact_kernel = typename Exact_kernel_selector<CK>::Exact_kernel;
|
||||
using Approximate_kernel = Simple_cartesian<Interval_nt_advanced>;
|
||||
using C2A = Cartesian_converter<CK, Approximate_kernel>;
|
||||
using C2E = typename Exact_kernel_selector<CK>::C2E;
|
||||
|
||||
using Orientation_of_circumcenter = Filtered_predicate<Orientation_of_circumcenter<Exact_kernel>,
|
||||
Orientation_of_circumcenter<Approximate_kernel>,
|
||||
C2E, C2A>;
|
||||
|
||||
Orientation_of_circumcenter orientation_of_circumcenter;
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "Checking for traversability of edge" << std::endl;
|
||||
#endif
|
||||
|
||||
const Face_handle f = e.first;
|
||||
const int ic = e.second;
|
||||
const Face_handle n = f->neighbor(ic);
|
||||
|
||||
const Point& p1 = tr.point(f, Tr::ccw(ic));
|
||||
const Point& p2 = tr.point(f, Tr::cw(ic));
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "diametrical squared radius: " << squared_radius(p1, p2) << std::endl;
|
||||
#endif
|
||||
|
||||
// This is not actually possible in the context of alpha wrapping, but keeping it for genericity
|
||||
// and because it does not cost anything.
|
||||
if(tr.is_infinite(n))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cerr << "Warning: computing less_squared_radius_of_min_empty_circle() with an infinite neighbor?" << std::endl;
|
||||
#endif
|
||||
CGAL_assertion(!tr.is_infinite(f));
|
||||
|
||||
Orientation ori = orientation_of_circumcenter(p1, p2,
|
||||
tr.point(f, 0), tr.point(f, 1), tr.point(f, 2));
|
||||
|
||||
if(ori == POSITIVE)
|
||||
{
|
||||
Comparison_result cr = compare_squared_distance(p1, p2, 4 * sq_alpha);
|
||||
return cr == LARGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
Comparison_result cr = compare_squared_radius(tr.point(f, 0), tr.point(f, 1), tr.point(f, 2),
|
||||
sq_alpha);
|
||||
return cr == LARGER;
|
||||
}
|
||||
}
|
||||
|
||||
if(tr.is_infinite(f))
|
||||
{
|
||||
Orientation ori = orientation_of_circumcenter(p1, p2,
|
||||
tr.point(n, 0), tr.point(n, 1), tr.point(n, 2));
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "Face 'f' is infinite; Orientation: " << ori << std::endl;
|
||||
#endif
|
||||
|
||||
if(ori == NEGATIVE)
|
||||
{
|
||||
Comparison_result cr = compare_squared_distance(p1, p2, 4 * sq_alpha);
|
||||
return cr == LARGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
Comparison_result cr = compare_squared_radius(tr.point(n, 0), tr.point(n, 1), tr.point(n, 2),
|
||||
sq_alpha);
|
||||
return cr == LARGER;
|
||||
}
|
||||
}
|
||||
|
||||
// both f and n are finite
|
||||
if(orientation_of_circumcenter(p1, p2, tr.point(f, 0), tr.point(f, 1), tr.point(f, 2)) !=
|
||||
orientation_of_circumcenter(p1, p2, tr.point(n, 0), tr.point(n, 1), tr.point(n, 2)))
|
||||
{
|
||||
Comparison_result cr = compare_squared_distance(p1, p2, 4 * sq_alpha);
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "dual crosses the face; CR: "
|
||||
<< typename Tr::Geom_traits().compute_squared_distance_2_object()(p1, p2)
|
||||
<< " sq alpha " << sq_alpha << std::endl;
|
||||
#endif
|
||||
return cr == LARGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
Comparison_result cr = compare_squared_radius(tr.point(f, 0), tr.point(f, 1), tr.point(f, 2),
|
||||
sq_alpha);
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "dual does not cross the face; CR(f): "
|
||||
<< typename Tr::Geom_traits().compute_squared_radius_2_object()(tr.point(f, 0), tr.point(f, 1),
|
||||
tr.point(f, 2))
|
||||
<< " sq alpha " << sq_alpha << std::endl;
|
||||
std::cout << "cr = " << cr << std::endl; // @tmp
|
||||
std::cout << "check = " << CGAL::compare(typename Tr::Geom_traits().compute_squared_radius_2_object()(tr.point(f, 0), tr.point(f, 1),
|
||||
tr.point(f, 2)), sq_alpha) << std::endl;
|
||||
#endif
|
||||
|
||||
if(cr != LARGER)
|
||||
return false;
|
||||
|
||||
cr = compare_squared_radius(tr.point(n, 0), tr.point(n, 1), tr.point(n, 2),
|
||||
sq_alpha);
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "dual does not cross the face; CR(n): "
|
||||
<< typename Tr::Geom_traits().compute_squared_radius_2_object()(tr.point(n, 0), tr.point(n, 1),
|
||||
tr.point(n, 2))
|
||||
<< " sq alpha " << sq_alpha << std::endl;
|
||||
std::cout << "cr = " << cr << std::endl; // @tmp
|
||||
std::cout << "check = " << CGAL::compare(typename Tr::Geom_traits().compute_squared_radius_2_object()(tr.point(n, 0), tr.point(n, 1),
|
||||
tr.point(n, 2)), sq_alpha) << std::endl;
|
||||
#endif
|
||||
|
||||
return cr == LARGER;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tr>
|
||||
typename Tr::Geom_traits::FT
|
||||
smallest_squared_radius_2(const typename Tr::Edge& e,
|
||||
const Tr& tr)
|
||||
{
|
||||
using Face_handle = typename Tr::Face_handle;
|
||||
using Point = typename Tr::Point;
|
||||
using FT = typename Tr::Geom_traits::FT;
|
||||
|
||||
using CK = typename Tr::Geom_traits;
|
||||
using Exact_kernel = typename Exact_kernel_selector<CK>::Exact_kernel;
|
||||
using Approximate_kernel = Simple_cartesian<Interval_nt_advanced>;
|
||||
using C2A = Cartesian_converter<CK, Approximate_kernel>;
|
||||
using C2E = typename Exact_kernel_selector<CK>::C2E;
|
||||
|
||||
using Orientation_of_circumcenter = Filtered_predicate<Orientation_of_circumcenter<Exact_kernel>,
|
||||
Orientation_of_circumcenter<Approximate_kernel>,
|
||||
C2E, C2A>;
|
||||
|
||||
Orientation_of_circumcenter orientation_of_circumcenter;
|
||||
|
||||
auto squared_radius = tr.geom_traits().compute_squared_radius_2_object();
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "Computing circumradius of edge" << std::endl;
|
||||
#endif
|
||||
|
||||
CGAL_precondition(!tr.is_infinite(e));
|
||||
|
||||
const Face_handle f = e.first;
|
||||
const int ic = e.second;
|
||||
const Face_handle n = f->neighbor(ic);
|
||||
|
||||
const Point& p1 = tr.point(f, Tr::ccw(ic));
|
||||
const Point& p2 = tr.point(f, Tr::cw(ic));
|
||||
|
||||
// This is not actually possible in the context of alpha wrapping, but keeping it for genericity
|
||||
// and because it does not cost anything.
|
||||
if(tr.is_infinite(n))
|
||||
{
|
||||
CGAL_assertion(!tr.is_infinite(f));
|
||||
|
||||
Orientation ori = orientation_of_circumcenter(p1, p2,
|
||||
tr.point(f, 0), tr.point(f, 1), tr.point(f, 2));
|
||||
if(ori == POSITIVE)
|
||||
return squared_radius(p1, p2);
|
||||
else
|
||||
return squared_radius(tr.point(f, 0), tr.point(f, 1), tr.point(f, 2));
|
||||
}
|
||||
|
||||
if(tr.is_infinite(f))
|
||||
{
|
||||
Orientation ori = orientation_of_circumcenter(p1, p2,
|
||||
tr.point(n, 0), tr.point(n, 1), tr.point(n, 2));
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "Face 'f' is infinite; Orientation: " << ori << std::endl;
|
||||
#endif
|
||||
|
||||
if(ori == NEGATIVE)
|
||||
return squared_radius(p1, p2);
|
||||
else
|
||||
return squared_radius(tr.point(n, 0), tr.point(n, 1), tr.point(n, 2));
|
||||
}
|
||||
|
||||
// both f and n are finite
|
||||
if(orientation_of_circumcenter(p1, p2, tr.point(f, 0), tr.point(f, 1), tr.point(f, 2)) !=
|
||||
orientation_of_circumcenter(p1, p2, tr.point(n, 0), tr.point(n, 1), tr.point(n, 2)))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "dual crosses the face; CR: " << squared_radius(p1, p2) << std::endl;
|
||||
#endif
|
||||
|
||||
return squared_radius(p1, p2);
|
||||
}
|
||||
else
|
||||
{
|
||||
const FT cr = squared_radius(tr.point(f, 0), tr.point(f, 1), tr.point(f, 2));
|
||||
const FT cnr = squared_radius(tr.point(n, 0), tr.point(n, 1), tr.point(n, 2));
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
std::cout << "dual does not cross the face; CR(f): " << cr << " CR(n): " << cnr << std::endl;
|
||||
#endif
|
||||
|
||||
return (CGAL::min)(cr, cnr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_INTERNAL_GEOMETRY_UTILS_H
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// Copyright (c) 2025 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_ALPHA_WRAP_2_INTERNAL_OFFSET_INTERSECTION_H
|
||||
#define CGAL_ALPHA_WRAP_2_INTERNAL_OFFSET_INTERSECTION_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/number_utils.h>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
template <typename AABBTree,
|
||||
typename AABBTraversalTraits>
|
||||
struct AABB_tree_oracle_helper;
|
||||
|
||||
template <typename AABBTree, typename AABBTraversalTraits>
|
||||
struct AABB_distance_oracle
|
||||
{
|
||||
using FT = typename AABBTree::FT;
|
||||
using Point_2 = typename AABBTree::Point;
|
||||
|
||||
using AABB_helper = AABB_tree_oracle_helper<AABBTree, AABBTraversalTraits>;
|
||||
|
||||
AABB_distance_oracle(const AABBTree& tree) : tree(tree) { }
|
||||
|
||||
FT operator()(const Point_2& p) const
|
||||
{
|
||||
return approximate_sqrt(AABB_helper::squared_distance(p, tree));
|
||||
}
|
||||
|
||||
public:
|
||||
const AABBTree& tree;
|
||||
};
|
||||
|
||||
// @todo even with EPECK, the precision cannot be 0 (otherwise it will not converge),
|
||||
// thus exactness is pointless. Might as well use a cheap kernel (e.g. SC<double>), as long
|
||||
// as there exists a mechanism to catch when the cheap kernel fails to converge (iterations?)
|
||||
template <class Kernel, class DistanceOracle>
|
||||
class Offset_intersection
|
||||
{
|
||||
using FT = typename Kernel::FT;
|
||||
using Point_2 = typename Kernel::Point_2;
|
||||
using Vector_2 = typename Kernel::Vector_2;
|
||||
|
||||
public:
|
||||
Offset_intersection(const DistanceOracle& oracle,
|
||||
const FT& off,
|
||||
const FT& prec,
|
||||
const FT& lip)
|
||||
: dist_oracle(oracle), offset(off), precision(prec), lipschitz(lip),
|
||||
sq_offset_minus_precision(CGAL::square(offset - precision)),
|
||||
sq_offset_plus_precision(CGAL::square(offset + precision))
|
||||
{
|
||||
CGAL_assertion(offset > precision);
|
||||
}
|
||||
|
||||
bool first_intersection(const Point_2& s,
|
||||
const Point_2& t,
|
||||
Point_2& output_pt)
|
||||
{
|
||||
return SQsphere_marching_search(s, t, output_pt);
|
||||
}
|
||||
|
||||
private:
|
||||
Point_2 source;
|
||||
Point_2 target;
|
||||
FT seg_length;
|
||||
Vector_2 seg_unit_v;
|
||||
DistanceOracle dist_oracle;
|
||||
FT offset;
|
||||
FT precision;
|
||||
FT lipschitz;
|
||||
|
||||
FT sq_offset_minus_precision;
|
||||
FT sq_offset_plus_precision;
|
||||
|
||||
bool sphere_marching_search(const Point_2& s,
|
||||
const Point_2& t,
|
||||
Point_2& output_pt)
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "Sphere march between " << s << " and " << t << std::endl;
|
||||
#endif
|
||||
|
||||
const FT sq_seg_length = squared_distance(s, t);
|
||||
const FT seg_length = approximate_sqrt(sq_seg_length);
|
||||
const Vector_2 seg_unit_v = (t - s) / seg_length;
|
||||
|
||||
Point_2 current_pt = s;
|
||||
Point_2 closest_point = dist_oracle.tree.closest_point(current_pt);
|
||||
FT current_dist = approximate_sqrt(squared_distance(current_pt, closest_point)) - offset;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "current point " << current_pt << std::endl;
|
||||
std::cout << "current dist " << current_dist << std::endl;
|
||||
#endif
|
||||
|
||||
if(CGAL::abs(current_dist) < precision)
|
||||
{
|
||||
output_pt = current_pt;
|
||||
return true;
|
||||
}
|
||||
|
||||
// use the previous closest point as a hint: it's an upper bound
|
||||
current_pt = current_pt + (current_dist * seg_unit_v);
|
||||
|
||||
if(squared_distance(s, current_pt) > sq_seg_length)
|
||||
return false;
|
||||
|
||||
closest_point = dist_oracle.tree.closest_point(current_pt, closest_point /*hint*/);
|
||||
current_dist = approximate_sqrt(squared_distance(current_pt, closest_point)) - offset;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SQsphere_marching_search(const Point_2& s,
|
||||
const Point_2& t,
|
||||
Point_2& output_pt)
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "Sphere march between " << s << " and " << t << std::endl;
|
||||
#endif
|
||||
|
||||
const FT sq_seg_length = squared_distance(s, t);
|
||||
const FT seg_length = approximate_sqrt(sq_seg_length);
|
||||
const Vector_2 seg_unit_v = (t - s) / seg_length;
|
||||
|
||||
Point_2 current_pt = s;
|
||||
Point_2 closest_point = dist_oracle.tree.closest_point(current_pt);
|
||||
FT sq_current_dist = squared_distance(current_pt, closest_point);
|
||||
FT step = 0;
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "bounds: " << sq_offset_minus_precision << " " << sq_offset_plus_precision << std::endl;
|
||||
#endif
|
||||
|
||||
for(;;)
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "current point " << current_pt << std::endl;
|
||||
std::cout << "current sq dist " << sq_current_dist << std::endl;
|
||||
std::cout << "closest point: " << closest_point << std::endl;
|
||||
std::cout << "sq dist to closest: " << sq_current_dist << std::endl;
|
||||
#endif
|
||||
|
||||
// abs(dist - offset) < epsilon
|
||||
if((sq_current_dist > sq_offset_minus_precision) &&
|
||||
(sq_current_dist < sq_offset_plus_precision))
|
||||
{
|
||||
output_pt = current_pt;
|
||||
return true;
|
||||
}
|
||||
|
||||
step += (std::max)(approximate_sqrt(sq_current_dist) - offset, 2 * precision);
|
||||
CGAL_assertion(step > 0);
|
||||
current_pt = s + (step * seg_unit_v);
|
||||
|
||||
if(squared_distance(s, current_pt) > sq_seg_length)
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "Next target farther than the segment's extremity: " << current_pt << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// the previous closest point gives an upper bound so it's a good hint
|
||||
// @todo
|
||||
// closest_point = dist_oracle.tree.closest_point(current_pt, closest_point /*hint*/);
|
||||
closest_point = dist_oracle.tree.closest_point(current_pt);
|
||||
sq_current_dist = squared_distance(current_pt, closest_point);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SQsphere_marching_search_pp(const Point_2& s,
|
||||
const Point_2& t,
|
||||
Point_2& output_pt)
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "Sphere march between " << s << " and " << t << std::endl;
|
||||
#endif
|
||||
|
||||
const FT seg_length = approximate_sqrt(squared_distance(s, t));
|
||||
const Vector_2 seg_unit_v = (t - s) / seg_length;
|
||||
|
||||
Point_2 current_pt = s;
|
||||
Point_2 closest_point = dist_oracle.tree.closest_point(current_pt);
|
||||
FT sq_current_dist = squared_distance(current_pt, closest_point);
|
||||
FT step = 0;
|
||||
|
||||
bool relaxing = true;
|
||||
FT w = 1.8; // over-extending factor
|
||||
|
||||
for(;;)
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "current point " << current_pt << std::endl;
|
||||
std::cout << "current sq dist " << sq_current_dist << std::endl;
|
||||
std::cout << "bounds: " << sq_offset_minus_precision << " " << sq_offset_plus_precision << std::endl;
|
||||
#endif
|
||||
|
||||
// If abs(dist - offset) < precision, we're done
|
||||
if((sq_current_dist > sq_offset_minus_precision) &&
|
||||
(sq_current_dist < sq_offset_plus_precision))
|
||||
{
|
||||
output_pt = current_pt;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Point_2 previous_pt = current_pt;
|
||||
const Point_2 previous_hint = closest_point;
|
||||
const FT previous_radius = approximate_sqrt(sq_current_dist) - offset;
|
||||
const FT previous_step = step;
|
||||
|
||||
const FT local_step = (std::max)(previous_radius, 2 * precision);
|
||||
|
||||
if(relaxing)
|
||||
{
|
||||
step += w * local_step;
|
||||
w = 1.1; // take bigger and bigger steps
|
||||
}
|
||||
else
|
||||
{
|
||||
step += local_step;
|
||||
}
|
||||
|
||||
CGAL_assertion(step > 0);
|
||||
|
||||
// move to the next point on the segment
|
||||
current_pt = s + (step * seg_unit_v);
|
||||
|
||||
// the previous closest point gives an upper bound so it's a good hint
|
||||
closest_point = dist_oracle.tree.closest_point(current_pt, closest_point /*hint*/);
|
||||
sq_current_dist = squared_distance(current_pt, closest_point);
|
||||
|
||||
// check if we have over-relaxed (the sphere are disjoint)
|
||||
if(relaxing)
|
||||
{
|
||||
const FT centers_dist = approximate_sqrt(squared_distance(previous_pt, current_pt));
|
||||
const FT current_radius = approximate_sqrt(sq_current_dist) - offset;
|
||||
if(previous_radius + current_radius < centers_dist)
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
std::cout << "Over relaxed, reverting" << std::endl;
|
||||
#endif
|
||||
|
||||
// revert the last step, and no more relaxation
|
||||
relaxing = false;
|
||||
|
||||
current_pt = previous_pt;
|
||||
closest_point = previous_hint;
|
||||
sq_current_dist = squared_distance(current_pt, closest_point);
|
||||
step = previous_step;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check if we ran out of segment to test
|
||||
if(step > seg_length)
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_INTERNAL_OFFSET_INTERSECTION_H
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// Copyright (c) 2025 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_ALPHA_WRAP_2_INTERNAL_ORACLES_H
|
||||
#define CGAL_ALPHA_WRAP_2_INTERNAL_ORACLES_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Alpha_wrap_2/internal/Alpha_wrap_AABB_geom_traits.h>
|
||||
#include <CGAL/Alpha_wrap_2/internal/offset_intersection.h>
|
||||
|
||||
#include <CGAL/Alpha_wrap_2/internal/Point_set_oracle.h>
|
||||
#include <CGAL/Alpha_wrap_2/internal/Segment_soup_oracle.h>
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_INTERNAL_ORACLES_H
|
||||
|
|
@ -0,0 +1,440 @@
|
|||
// Copyright (c) 2025 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_ALPHA_WRAP_2_TEST_ALPHA_WRAP_VALIDATION_H
|
||||
#define CGAL_ALPHA_WRAP_2_TEST_ALPHA_WRAP_VALIDATION_H
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/boost/graph/helpers.h>
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
#include <CGAL/box_intersection_d.h>
|
||||
#include <CGAL/Constrained_triangulation_2.h>
|
||||
#include <CGAL/Kernel_traits.h>
|
||||
#include <CGAL/mark_domain_in_triangulation.h>
|
||||
#include <CGAL/Polygon_repair/repair.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
template <typename MultipolygonWithHoles>
|
||||
bool is_empty(const MultipolygonWithHoles& wrap)
|
||||
{
|
||||
using Polygon_with_holes_2 = typename MultipolygonWithHoles::Polygon_with_holes_2;
|
||||
|
||||
if (wrap.polygons_with_holes().empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The real logic would be that it's empty if everything is empty,
|
||||
// but an empty pwh in the range is an error, so the logic here is
|
||||
// to mark it as empty as soon as one polygon with holes is empty.
|
||||
for (const Polygon_with_holes_2& pwh : wrap.polygons_with_holes()) {
|
||||
if(pwh.outer_boundary().size() == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename MultipolygonWithHoles>
|
||||
auto cdt_from_wrap(const MultipolygonWithHoles& wrap)
|
||||
{
|
||||
using Polygon_with_holes_2 = typename MultipolygonWithHoles::Polygon_with_holes_2;
|
||||
using Polygon_2 = typename Polygon_with_holes_2::Polygon_2;
|
||||
using Point_2 = typename Polygon_2::Point_2;
|
||||
using K = typename CGAL::Kernel_traits<Point_2>::type;
|
||||
|
||||
using Itag = CGAL::No_constraint_intersection_tag;
|
||||
using CDT = Constrained_triangulation_2<K, CGAL::Default, Itag>;
|
||||
using CDT_FH = typename CDT::Face_handle;
|
||||
|
||||
CDT cdt;
|
||||
|
||||
try
|
||||
{
|
||||
for (const Polygon_with_holes_2& pwh : wrap.polygons_with_holes()) {
|
||||
cdt.insert_constraint(pwh.outer_boundary().vertices_begin(),
|
||||
pwh.outer_boundary().vertices_end(),
|
||||
true /*close*/);
|
||||
|
||||
for (const Polygon_2& hole : pwh.holes()) {
|
||||
cdt.insert_constraint(hole.vertices_begin(), hole.vertices_end(), true /*close*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(const typename CDT::Intersection_of_constraints_exception&)
|
||||
{
|
||||
CGAL_assertion_msg(false, "Intersections in CDT2 of wrap are not allowed");
|
||||
return CDT{};
|
||||
}
|
||||
|
||||
return cdt;
|
||||
}
|
||||
|
||||
// This is not a generic weakly simple multipolygon detection algorithm: we know that
|
||||
// the multipolygon is a wrap and was constructed as the boundary of a set of inside cells.
|
||||
// As such, it cannot twist, only touch, and it's enough to check that there are
|
||||
// no intersections between edges which are not a subface of the edges.
|
||||
template <typename MultipolygonWithHoles>
|
||||
bool is_weakly_simple(const MultipolygonWithHoles& wrap)
|
||||
{
|
||||
auto cdt = cdt_from_wrap(wrap);
|
||||
return cdt.number_of_vertices() != 0;
|
||||
}
|
||||
|
||||
template <typename MultipolygonWithHoles>
|
||||
bool is_simple(const MultipolygonWithHoles& wrap)
|
||||
{
|
||||
auto cdt = cdt_from_wrap(wrap);
|
||||
|
||||
using CDT = decltype(cdt);
|
||||
using Edge_circulator = typename CDT::Edge_circulator;
|
||||
|
||||
for (auto vh : cdt.finite_vertex_handles()) {
|
||||
Edge_circulator ec = cdt.incident_edges(vh), done(ec);
|
||||
unsigned int cntr = 0;
|
||||
do {
|
||||
if(cdt.is_constrained(*ec)) {
|
||||
++cntr;
|
||||
}
|
||||
} while (++ec != done);
|
||||
|
||||
if (cntr != 2) {
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Vertex " << cdt.point(vh) << " has " << cntr << " incident constraints" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Edge length is bounded by twice the circumradius
|
||||
template <typename MultipolygonWithHoles>
|
||||
bool has_bounded_edge_length(const MultipolygonWithHoles& wrap,
|
||||
const double alpha)
|
||||
{
|
||||
using Polygon_with_holes_2 = typename MultipolygonWithHoles::Polygon_with_holes_2;
|
||||
using Polygon_2 = typename Polygon_with_holes_2::Polygon_2;
|
||||
using Edges = typename Polygon_2::Edges;
|
||||
using Segment_2 = typename Polygon_2::Segment_2;
|
||||
|
||||
const auto sq_alpha_bound = 4 * square(alpha);
|
||||
|
||||
for (const Polygon_with_holes_2& pwh : wrap.polygons_with_holes()) {
|
||||
for (const Segment_2& edge : pwh.outer_boundary().edges()) {
|
||||
if (CGAL::compare_squared_distance(edge.source(), edge.target(), sq_alpha_bound) == CGAL::LARGER) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const Polygon_2& hole : pwh.holes()) {
|
||||
for (const Segment_2& edge : hole.edges()) {
|
||||
if (CGAL::compare_squared_distance(edge.source(), edge.target(), sq_alpha_bound) == CGAL::LARGER) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// template <typename ConcurrencyTag = CGAL::Sequential_tag,
|
||||
// typename MultipolygonWithHoles, typename FT,
|
||||
// typename InputNamedParameters = parameters::Default_named_parameters,
|
||||
// typename OutputNamedParameters = parameters::Default_named_parameters>
|
||||
// bool has_expected_Hausdorff_distance(const MultipolygonWithHoles& wrap,
|
||||
// const TriangleMesh& input,
|
||||
// const FT alpha, const FT offset,
|
||||
// const InputNamedParameters& in_np = parameters::default_values(),
|
||||
// const OutputNamedParameters& out_np = parameters::default_values())
|
||||
// {
|
||||
// // @todo
|
||||
// return true;
|
||||
// }
|
||||
|
||||
template <typename MultipolygonWithHoles,
|
||||
typename NamedParameters = parameters::Default_named_parameters>
|
||||
bool is_valid_wrap(const MultipolygonWithHoles& wrap,
|
||||
const bool check_manifoldness,
|
||||
const NamedParameters& np = parameters::default_values())
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
if(Alpha_wraps_2::internal::is_empty(wrap))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Error: empty wrap" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Polygon_repair::is_valid(wrap))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Error: invalid wrap" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if(check_manifoldness)
|
||||
{
|
||||
if(!Alpha_wraps_2::internal::is_simple(wrap))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Error: Wrap is not simple" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!Alpha_wraps_2::internal::is_weakly_simple(wrap))
|
||||
{
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Error: Wrap is not weakly simple" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Point, typename CDT, typename InDomain>
|
||||
bool is_point_inside_marked_CDT(const Point& p,
|
||||
const CDT& cdt,
|
||||
const InDomain in_domain)
|
||||
{
|
||||
using CDT_FH = typename CDT::Face_handle;
|
||||
using CDT_VH = typename CDT::Vertex_handle;
|
||||
using Locate_type = typename CDT::Locate_type;
|
||||
using Face_circulator = typename CDT::Face_circulator;
|
||||
|
||||
CDT_FH fh;
|
||||
int li;
|
||||
Locate_type lt;
|
||||
fh = cdt.locate(p, lt, li);
|
||||
|
||||
if(lt == CDT::VERTEX) {
|
||||
CDT_VH vh = fh->vertex(li);
|
||||
bool is_in = false;
|
||||
Face_circulator fc = cdt.incident_faces(vh), done(fc);
|
||||
do {
|
||||
if (get(in_domain, fc)) {
|
||||
is_in = true;
|
||||
break;
|
||||
}
|
||||
} while(++fc != done);
|
||||
if (!is_in) {
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "A polyline point [on vertex] is outside the wrap: " << p << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
} else if (lt == CDT::EDGE) {
|
||||
if (!get(in_domain, fh) && !get(in_domain, fh->neighbor(li))) {
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "A polyline point [on edge] is outside the wrap: " << p << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
} else if (lt == CDT::FACE) {
|
||||
if (!get(in_domain, fh)) {
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "A polyline point [on face] is outside the wrap: " << p << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "A polyline point is outside of convex/affine hull?! " << p << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////// POLYLINES /////////////////////////////////////////////////
|
||||
|
||||
template <typename MultipolygonWithHoles, typename PolylineRange,
|
||||
typename OutputNamedParameters = parameters::Default_named_parameters,
|
||||
typename InputNamedParameters = parameters::Default_named_parameters>
|
||||
bool is_outer_wrap_of_polylines(const MultipolygonWithHoles& wrap,
|
||||
const PolylineRange& polylines,
|
||||
const OutputNamedParameters& out_np = parameters::default_values(),
|
||||
const InputNamedParameters& in_np = parameters::default_values())
|
||||
{
|
||||
using parameters::get_parameter;
|
||||
using parameters::choose_parameter;
|
||||
|
||||
using IPM = typename GetPointMap<PolylineRange, InputNamedParameters>::const_type;
|
||||
IPM in_pm = choose_parameter<IPM>(get_parameter(in_np, internal_np::point_map));
|
||||
|
||||
using Polygon_with_holes_2 = typename MultipolygonWithHoles::Polygon_with_holes_2;
|
||||
using Polygon_2 = typename Polygon_with_holes_2::Polygon_2;
|
||||
using Segment_2 = typename Polygon_2::Segment_2;
|
||||
using Point_2 = typename Polygon_2::Point_2;
|
||||
using Box = CGAL::Box_intersection_d::Box_with_handle_d<double, 2, Segment_2*>;
|
||||
|
||||
// Build boxed segments for wrap
|
||||
std::vector<Segment_2> wrap_segments;
|
||||
for(const Polygon_with_holes_2& pwh : wrap.polygons_with_holes()) {
|
||||
for(auto eit = pwh.outer_boundary().edges_begin(); eit != pwh.outer_boundary().edges_end(); ++eit)
|
||||
wrap_segments.push_back(*eit);
|
||||
for(const Polygon_2& hole : pwh.holes())
|
||||
for(auto eit = hole.edges_begin(); eit != hole.edges_end(); ++eit)
|
||||
wrap_segments.push_back(*eit);
|
||||
}
|
||||
std::vector<Box> wrap_boxes;
|
||||
wrap_boxes.reserve(wrap_segments.size());
|
||||
for(auto& seg : wrap_segments) {
|
||||
wrap_boxes.emplace_back(seg.bbox(), &seg);
|
||||
}
|
||||
|
||||
// Build boxed segments for input polylines
|
||||
std::vector<Segment_2> input_segments;
|
||||
for(const auto& polyline : polylines) {
|
||||
if(polyline.size() < 2)
|
||||
continue;
|
||||
for(std::size_t i=1; i<polyline.size(); ++i)
|
||||
input_segments.emplace_back(polyline[i-1], polyline[i]);
|
||||
}
|
||||
std::vector<Box> input_boxes;
|
||||
input_boxes.reserve(input_segments.size());
|
||||
for(Segment_2& seg : input_segments) {
|
||||
input_boxes.emplace_back(seg.bbox(), &seg);
|
||||
}
|
||||
|
||||
// Intersection callback
|
||||
struct FirstIntersection {};
|
||||
struct Overlap {
|
||||
void operator()(const Box& a, const Box& b) const {
|
||||
if(CGAL::do_intersect(*(a.handle()), *(b.handle())))
|
||||
throw FirstIntersection();
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
CGAL::box_intersection_d(wrap_boxes.begin(), wrap_boxes.end(),
|
||||
input_boxes.begin(), input_boxes.end(),
|
||||
Overlap());
|
||||
} catch(const FirstIntersection&) {
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cerr << "Polyline segment intersects wrap boundary" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark domain
|
||||
auto cdt = cdt_from_wrap(wrap);
|
||||
|
||||
using CDT = decltype(cdt);
|
||||
using CDT_FH = typename CDT::Face_handle;
|
||||
|
||||
std::unordered_map<CDT_FH, bool> in_domain_map;
|
||||
boost::associative_property_map<std::unordered_map<CDT_FH, bool> > in_domain(in_domain_map);
|
||||
CGAL::mark_domain_in_triangulation(cdt, in_domain);
|
||||
|
||||
for(const auto& polyline : polylines) {
|
||||
for(const auto& p : polyline) {
|
||||
if(!is_point_inside_marked_CDT(p, cdt, in_domain))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename MultipolygonWithHoles, typename PolylineRange,
|
||||
typename OutputNamedParameters = parameters::Default_named_parameters,
|
||||
typename InputNamedParameters = parameters::Default_named_parameters>
|
||||
bool is_valid_wrap_of_polylines(const MultipolygonWithHoles& wrap,
|
||||
const PolylineRange& polylines,
|
||||
const OutputNamedParameters& out_np = parameters::default_values(),
|
||||
const InputNamedParameters& in_np = parameters::default_values())
|
||||
{
|
||||
if(!is_valid_wrap(wrap, out_np))
|
||||
return false;
|
||||
|
||||
if(!is_outer_wrap_of_polylines(wrap, polylines, out_np, in_np))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////// POINT SET /////////////////////////////////////////////////
|
||||
|
||||
template <typename MultipolygonWithHoles, typename PointRange,
|
||||
typename OutputNamedParameters = parameters::Default_named_parameters,
|
||||
typename InputNamedParameters = parameters::Default_named_parameters>
|
||||
bool is_outer_wrap_of_point_set(const MultipolygonWithHoles& wrap,
|
||||
const PointRange& points,
|
||||
const OutputNamedParameters& out_np = parameters::default_values(),
|
||||
const InputNamedParameters& in_np = parameters::default_values())
|
||||
{
|
||||
using parameters::get_parameter;
|
||||
using parameters::choose_parameter;
|
||||
|
||||
using IPM = typename GetPointMap<PointRange, InputNamedParameters>::const_type;
|
||||
using K = typename Kernel_traits<typename boost::property_traits<IPM>::value_type>::Kernel;
|
||||
|
||||
IPM in_pm = choose_parameter<IPM>(get_parameter(in_np, internal_np::point_map));
|
||||
|
||||
auto cdt = cdt_from_wrap(wrap);
|
||||
|
||||
using CDT = decltype(cdt);
|
||||
using CDT_VH = typename CDT::Vertex_handle;
|
||||
using CDT_FH = typename CDT::Face_handle;
|
||||
using Locate_type = typename CDT::Locate_type;
|
||||
using Face_circulator = typename CDT::Face_circulator;
|
||||
|
||||
std::unordered_map<CDT_FH, bool> in_domain_map;
|
||||
boost::associative_property_map<std::unordered_map<CDT_FH, bool> > in_domain(in_domain_map);
|
||||
CGAL::mark_domain_in_triangulation(cdt, in_domain);
|
||||
|
||||
for(const auto& p : points)
|
||||
if(!is_point_inside_marked_CDT(get(in_pm, p), cdt, in_domain))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename MultipolygonWithHoles, typename PointRange,
|
||||
typename OutputNamedParameters = parameters::Default_named_parameters,
|
||||
typename InputNamedParameters = parameters::Default_named_parameters>
|
||||
bool is_valid_wrap_of_point_set(const MultipolygonWithHoles& wrap,
|
||||
const PointRange& points,
|
||||
const OutputNamedParameters& out_np = parameters::default_values(),
|
||||
const InputNamedParameters& in_np = parameters::default_values())
|
||||
{
|
||||
if(!is_valid_wrap(wrap, out_np))
|
||||
return false;
|
||||
|
||||
if(!is_outer_wrap_of_point_set(wrap, points, out_np, in_np))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_TEST_ALPHA_WRAP_VALIDATION_H
|
||||
|
|
@ -0,0 +1,635 @@
|
|||
// Copyright (c) 2019-2022 Google LLC (USA).
|
||||
// Copyright (c) 2025 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) : Pierre Alliez
|
||||
// Cedric Portaneri,
|
||||
// Mael Rouxel-Labbé
|
||||
// Andreas Fabri
|
||||
// Michael Hemmer
|
||||
//
|
||||
#ifndef CGAL_ALPHA_WRAP_2_H
|
||||
#define CGAL_ALPHA_WRAP_2_H
|
||||
|
||||
// #define CGAL_AW2_USE_SORTED_PRIORITY_QUEUE
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG_PP
|
||||
#ifndef CGAL_AW2_DEBUG
|
||||
#define CGAL_AW2_DEBUG
|
||||
#define CGAL_AW2_DEBUG_INITIALIZATION
|
||||
#define CGAL_AW2_DEBUG_STEINER_COMPUTATION
|
||||
#define CGAL_AW2_DEBUG_QUEUE_PP
|
||||
#define CGAL_AW2_DEBUG_QUEUE
|
||||
#define CGAL_AW2_DEBUG_EDGE_STATUS
|
||||
#define CGAL_AW2_DEBUG_MANIFOLDNESS
|
||||
#define CGAL_AW2_DEBUG_TRAVERSABILITY
|
||||
#define CGAL_AW2_DEBUG_SPHERE_MARCHING
|
||||
|
||||
#define CGAL_AW2_DEBUG_DUMP_INTERMEDIATE_WRAPS
|
||||
#define CGAL_AW2_DEBUG_DUMP_EVERY_STEP
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <CGAL/license/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/Alpha_wrap_2/internal/Alpha_wrap_2.h>
|
||||
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
#include <CGAL/Kernel_traits.h>
|
||||
#include <CGAL/Named_function_parameters.h>
|
||||
#include <CGAL/property_map.h>
|
||||
|
||||
#include <boost/range/has_range_iterator.hpp>
|
||||
#include <boost/range/value_type.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace Alpha_wraps_2 {
|
||||
namespace internal {
|
||||
|
||||
// We must first check that Kernel_traits does not return the dummy kernel,
|
||||
// otherwise writing `Kernel_traits<value_type>::type::Point_2` is a real compilation error.
|
||||
|
||||
#define CGAL_IS_RANGE_OF_KERNEL_OBJECT(TYPE) \
|
||||
template <typename Input> \
|
||||
struct is_##TYPE##_range \
|
||||
{ \
|
||||
static constexpr bool value = []() -> bool { \
|
||||
if constexpr (!boost::has_range_const_iterator<Input>::value) { \
|
||||
return false; \
|
||||
} else { \
|
||||
using raw_value_type = typename boost::range_value<Input>::type; \
|
||||
using value_type = CGAL::cpp20::remove_cvref_t<raw_value_type>; \
|
||||
using Kernel = typename CGAL::Kernel_traits<value_type>::type; \
|
||||
using Dummy_kernel = CGAL::internal_kernel_traits::Dummy_kernel<value_type>; \
|
||||
if constexpr(std::is_same_v<Kernel, Dummy_kernel>) \
|
||||
return false; \
|
||||
else \
|
||||
return std::is_same_v<value_type, typename Kernel::TYPE>; \
|
||||
} \
|
||||
}(); \
|
||||
};
|
||||
|
||||
CGAL_IS_RANGE_OF_KERNEL_OBJECT(Point_2)
|
||||
CGAL_IS_RANGE_OF_KERNEL_OBJECT(Segment_2)
|
||||
CGAL_IS_RANGE_OF_KERNEL_OBJECT(Triangle_2)
|
||||
|
||||
#undef CGAL_IS_RANGE_OF_KERNEL_OBJECT
|
||||
|
||||
// multipolygon, check for a typedef 'Polygon_with_holes_2' required by `MultipolygonWithHoles_2`
|
||||
template <typename Input>
|
||||
struct is_MultipolygonWithHoles
|
||||
{
|
||||
BOOST_MPL_HAS_XXX_TRAIT_DEF(Polygon_with_holes_2)
|
||||
static constexpr bool value = has_Polygon_with_holes_2<Input>::value;
|
||||
};
|
||||
|
||||
// A multi-linestring is a range of ranges of points
|
||||
template <typename Input>
|
||||
struct is_MultiLineString
|
||||
{
|
||||
static constexpr bool value = [] -> bool {
|
||||
if constexpr (boost::has_range_const_iterator<Input>::value) {
|
||||
using Outer_range = typename boost::range_value<Input>::type;
|
||||
return is_Point_2_range<Outer_range>::value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}();
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace Alpha_wraps_2
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// WITH AN INDEXED FACE SET ------------------------------------------------------------------------
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
* \ingroup AW2_free_functions_grp
|
||||
*
|
||||
* \brief computes a watertight, 1-manifold, simple multipolygon that strictly contains
|
||||
* an input indexed face set.
|
||||
*
|
||||
* The parameters `alpha` and `offset` respectively control which features will appear in the output,
|
||||
* and the distance from the input. See Section \ref aw2_parameters for a detailed breakdown of their influence.
|
||||
*
|
||||
* \tparam PointRange a model of `RandomAccessContainer` whose value type is a point type model of `Kernel::Point_2`
|
||||
* \tparam FaceRange a model of `Range` whose value type is a model of `RandomAccessContainer`
|
||||
* whose value type is an integral type
|
||||
* \tparam MultipolygonWithHoles a model of `MultipolygonWithHoles_2`
|
||||
* \tparam InputNamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* \param points the input points
|
||||
* \param faces the input faces, with each element of the range being a range of indices corresponding to points in `points`
|
||||
* \param alpha the value of the parameter `alpha`
|
||||
* \param offset the value of the parameter `offset`
|
||||
* \param alpha_wrap the output multipolygon
|
||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `Kernel`}
|
||||
* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
* \cgalParamExtra{<ul><li>The geometric traits class must be compatible with the point type.</li>
|
||||
* <li>The geometric traits should use a floating point number type (see \ref aw2_interface).</li></ul>}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \pre `alpha` and `offset` are strictly positive values.
|
||||
*/
|
||||
template <typename PointRange, typename FaceRange,
|
||||
typename MultipolygonWithHoles,
|
||||
typename InputNamedParameters>
|
||||
void alpha_wrap_2(const PointRange& points,
|
||||
const FaceRange& faces,
|
||||
const double alpha,
|
||||
const double offset,
|
||||
MultipolygonWithHoles& alpha_wrap,
|
||||
const InputNamedParameters& np)
|
||||
{
|
||||
using parameters::get_parameter;
|
||||
using parameters::choose_parameter;
|
||||
|
||||
using NP_helper = Point_set_processing_3_np_helper<PointRange, InputNamedParameters>;
|
||||
using Geom_traits = typename NP_helper::Geom_traits;
|
||||
using Oracle = Alpha_wraps_2::internal::Segment_soup_oracle<Geom_traits>;
|
||||
using Wrapper = Alpha_wraps_2::internal::Alpha_wrapper_2<Oracle>;
|
||||
|
||||
Geom_traits gt = choose_parameter<Geom_traits>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
Oracle oracle(alpha, gt);
|
||||
oracle.add_polygon_soup(points, faces, np);
|
||||
|
||||
Wrapper alpha_wrap_builder(oracle);
|
||||
alpha_wrap_builder(alpha, offset, alpha_wrap, np);
|
||||
}
|
||||
|
||||
// Convenience overloads
|
||||
|
||||
template <typename PointRange, typename FaceRange, typename MultipolygonWithHoles>
|
||||
void alpha_wrap_2(const PointRange& points,
|
||||
const FaceRange& faces,
|
||||
const double alpha,
|
||||
const double offset,
|
||||
MultipolygonWithHoles& alpha_wrap)
|
||||
{
|
||||
return alpha_wrap_2(points, faces, alpha, offset, alpha_wrap, CGAL::parameters::default_values());
|
||||
}
|
||||
|
||||
// without offset
|
||||
template <typename PointRange, typename FaceRange, typename MultipolygonWithHoles,
|
||||
typename T_I, typename Tag_I, typename Base_I>
|
||||
void alpha_wrap_2(const PointRange& points,
|
||||
const FaceRange& faces,
|
||||
const double alpha,
|
||||
MultipolygonWithHoles& alpha_wrap,
|
||||
const CGAL::Named_function_parameters<T_I, Tag_I, Base_I>& np,
|
||||
std::enable_if_t<boost::has_range_const_iterator<FaceRange>::value>* = nullptr)
|
||||
{
|
||||
return alpha_wrap_2(points, faces, alpha, alpha / 30., alpha_wrap, np);
|
||||
}
|
||||
|
||||
template <typename PointRange, typename FaceRange, typename MultipolygonWithHoles>
|
||||
void alpha_wrap_2(const PointRange& points,
|
||||
const FaceRange& faces,
|
||||
const double alpha,
|
||||
MultipolygonWithHoles& alpha_wrap,
|
||||
std::enable_if_t<boost::has_range_const_iterator<FaceRange>::value>* = nullptr)
|
||||
{
|
||||
return alpha_wrap_2(points, faces, alpha, alpha / 30., alpha_wrap, CGAL::parameters::default_values());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// WITH A TRIANGLE SOUP ----------------------------------------------------------------------------
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
* \ingroup AW2_free_functions_grp
|
||||
*
|
||||
* \brief computes a watertight, 1-manifold, simple multipolygon that strictly contains
|
||||
* an input triangle soup.
|
||||
*
|
||||
* The parameters `alpha` and `offset` respectively control which features will appear in the output,
|
||||
* and the distance from the input. See Section \ref aw2_parameters for a detailed breakdown of their influence.
|
||||
*
|
||||
* \tparam TriangleRange a model of `Range` whose value type is a model of `RandomAccessContainer`
|
||||
* of size 3 whose value type is a point type model of `Kernel::Point_2`
|
||||
* \tparam MultipolygonWithHoles a model of `MultipolygonWithHoles_2`
|
||||
* \tparam InputNamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* \param triangles the input triangles
|
||||
* \param alpha the value of the parameter `alpha`
|
||||
* \param offset the value of the parameter `offset`
|
||||
* \param alpha_wrap the output multipolygon
|
||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `Kernel`}
|
||||
* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
* \cgalParamExtra{<ul><li>The geometric traits class must be compatible with the triangle type.</li>
|
||||
* <li>The geometric traits should use a floating point number type (see \ref aw2_interface).</li></ul>}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \pre `alpha` and `offset` are strictly positive values.
|
||||
*/
|
||||
template <typename TriangleRange,
|
||||
typename MultipolygonWithHoles,
|
||||
typename InputNamedParameters>
|
||||
void alpha_wrap_2(const TriangleRange& triangles,
|
||||
const double alpha,
|
||||
const double offset,
|
||||
MultipolygonWithHoles& alpha_wrap,
|
||||
const InputNamedParameters& np
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
, std::enable_if_t<Alpha_wraps_2::internal::is_Triangle_2_range<TriangleRange>::value>* = nullptr
|
||||
#endif
|
||||
)
|
||||
{
|
||||
using parameters::get_parameter;
|
||||
using parameters::choose_parameter;
|
||||
|
||||
using In_triangle_2 = typename boost::range_value<TriangleRange>::type;
|
||||
using In_K = typename CGAL::Kernel_traits<In_triangle_2>::type;
|
||||
|
||||
using Geom_traits = typename internal_np::Lookup_named_param_def<internal_np::geom_traits_t,
|
||||
InputNamedParameters,
|
||||
In_K>::type;
|
||||
|
||||
using Polygon_with_holes_2 = typename MultipolygonWithHoles::Polygon_with_holes_2;
|
||||
using Polygon_2 = typename Polygon_with_holes_2::Polygon_2;
|
||||
using Out_point_2 = typename boost::range_value<Polygon_2>::type;
|
||||
using Out_K = typename CGAL::Kernel_traits<Out_point_2>::type;
|
||||
|
||||
// could imagine this being just a conversion, but ask for equality for now
|
||||
static_assert(std::is_same_v<In_K, Out_K>);
|
||||
|
||||
using Oracle = Alpha_wraps_2::internal::Segment_soup_oracle<Geom_traits>;
|
||||
using Wrapper = Alpha_wraps_2::internal::Alpha_wrapper_2<Oracle>;
|
||||
|
||||
Geom_traits gt = choose_parameter<Geom_traits>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
Oracle oracle(alpha, gt);
|
||||
oracle.add_triangles(triangles);
|
||||
|
||||
Wrapper alpha_wrap_builder(oracle);
|
||||
alpha_wrap_builder(alpha, offset, alpha_wrap);
|
||||
}
|
||||
|
||||
// The convenience overloads are common to all ranges
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// WITH A MULTI-POLYGON ----------------------------------------------------------------------------
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
* \ingroup AW2_free_functions_grp
|
||||
*
|
||||
* \brief computes a watertight, 1-manifold, simple multipolygon that strictly contains
|
||||
* an input range of polygons.
|
||||
*
|
||||
* The parameters `alpha` and `offset` respectively control which features will appear in the output,
|
||||
* and the distance from the input. See Section \ref aw2_parameters for a detailed breakdown of their influence.
|
||||
*
|
||||
* \note This function merely inserts and wraps all edges from the outer boundaries and from the
|
||||
* holes of the multipolygon. It does *not* repair the multipolygon first. Consequently, for example,
|
||||
* if a polygon were to be invalid with an edge that lays outside of the region enclosed by its outer
|
||||
* boundary, then the wrap would still be constructed as to contain this edge.
|
||||
* In some use cases, it may be desirable to first repair invalid polygons. We refer
|
||||
* to the \cgal component \link Chapter_2D_Polygon_repair Polygon Repair\endlink for various
|
||||
* polygon repair functions using different strategies.
|
||||
*
|
||||
* \tparam InputMultipolygonWithHoles a model of `MultipolygonWithHoles_2`
|
||||
* \tparam OutputMultipolygonWithHoles a model of `MultipolygonWithHoles_2`
|
||||
* \tparam InputNamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* \param multipolygon a multipolygon
|
||||
* \param alpha the value of the parameter `alpha`
|
||||
* \param offset the value of the parameter `offset`
|
||||
* \param alpha_wrap the output multipolygon
|
||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `Kernel`}
|
||||
* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits` and
|
||||
* the point type of the multipolygon}
|
||||
* \cgalParamExtra{<ul><li>The geometric traits class must be compatible with the point type.</li>
|
||||
* <li>The geometric traits should use a floating point number type (see \ref aw2_interface).</li></ul>}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \pre `alpha` and `offset` are strictly positive values.
|
||||
*/
|
||||
template <typename InputMultipolygonWithHoles,
|
||||
typename OutputMultipolygonWithHoles,
|
||||
typename InputNamedParameters>
|
||||
void alpha_wrap_2(const InputMultipolygonWithHoles& multipolygon,
|
||||
const double alpha,
|
||||
const double offset,
|
||||
OutputMultipolygonWithHoles& alpha_wrap,
|
||||
const InputNamedParameters& np
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
, std::enable_if_t<Alpha_wraps_2::internal::is_MultipolygonWithHoles<InputMultipolygonWithHoles>::value>* = nullptr
|
||||
#endif
|
||||
)
|
||||
{
|
||||
using parameters::get_parameter;
|
||||
using parameters::choose_parameter;
|
||||
|
||||
using In_polygon_with_holes_2 = typename InputMultipolygonWithHoles::Polygon_with_holes_2;
|
||||
using In_polygon_2 = typename In_polygon_with_holes_2::Polygon_2;
|
||||
using In_point_2 = typename boost::range_value<In_polygon_2>::type;
|
||||
using In_K = typename CGAL::Kernel_traits<In_point_2>::type;
|
||||
|
||||
using Geom_traits = typename internal_np::Lookup_named_param_def<internal_np::geom_traits_t,
|
||||
InputNamedParameters,
|
||||
In_K>::type;
|
||||
|
||||
using Out_polygon_with_holes_2 = typename OutputMultipolygonWithHoles::Polygon_with_holes_2;
|
||||
using Out_polygon_2 = typename Out_polygon_with_holes_2::Polygon_2;
|
||||
using Out_point_2 = typename boost::range_value<Out_polygon_2>::type;
|
||||
using Out_K = typename CGAL::Kernel_traits<Out_point_2>::type;
|
||||
|
||||
// could imagine this being just a conversion, but ask for equality for now
|
||||
static_assert(std::is_same_v<In_K, Out_K>);
|
||||
|
||||
using Oracle = Alpha_wraps_2::internal::Segment_soup_oracle<Geom_traits>;
|
||||
using Wrapper = Alpha_wraps_2::internal::Alpha_wrapper_2<Oracle>;
|
||||
|
||||
Geom_traits gt = choose_parameter<Geom_traits>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
Oracle oracle(alpha, gt);
|
||||
oracle.add_multipolygon(multipolygon);
|
||||
|
||||
Wrapper alpha_wrap_builder(oracle);
|
||||
alpha_wrap_builder(alpha, offset, alpha_wrap);
|
||||
}
|
||||
|
||||
// The convenience overloads are common to all ranges
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// WITH A SEGMENT SOUP -----------------------------------------------------------------------------
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
* \ingroup AW2_free_functions_grp
|
||||
*
|
||||
* \brief computes a watertight, 1-manifold, simple multipolygon that strictly contains
|
||||
* an input segment soup.
|
||||
*
|
||||
* The parameters `alpha` and `offset` respectively control which features will appear in the output,
|
||||
* and the distance from the input. See Section \ref aw2_parameters for a detailed breakdown of their influence.
|
||||
*
|
||||
* \tparam SegmentRange a model of `Range` whose value type is a segment type model of `Kernel::Segment_2`
|
||||
* \tparam MultipolygonWithHoles a model of `MultipolygonWithHoles_2`
|
||||
* \tparam InputNamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* \param segments the input segments
|
||||
* \param alpha the value of the parameter `alpha`
|
||||
* \param offset the value of the parameter `offset`
|
||||
* \param alpha_wrap the output multipolygon
|
||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `Kernel`}
|
||||
* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
* \cgalParamExtra{<ul><li>The geometric traits class must be compatible with the segment type.</li>
|
||||
* <li>The geometric traits should use a floating point number type (see \ref aw2_interface).</li></ul>}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \pre `alpha` and `offset` are strictly positive values.
|
||||
*/
|
||||
template <typename SegmentRange,
|
||||
typename MultipolygonWithHoles,
|
||||
typename InputNamedParameters>
|
||||
void alpha_wrap_2(const SegmentRange& segments,
|
||||
const double alpha,
|
||||
const double offset,
|
||||
MultipolygonWithHoles& alpha_wrap,
|
||||
const InputNamedParameters& np
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
, std::enable_if_t<Alpha_wraps_2::internal::is_Segment_2_range<SegmentRange>::value>* = nullptr
|
||||
#endif
|
||||
)
|
||||
{
|
||||
using parameters::get_parameter;
|
||||
using parameters::choose_parameter;
|
||||
|
||||
using In_segment_2 = typename boost::range_value<SegmentRange>::type;
|
||||
using In_K = typename CGAL::Kernel_traits<In_segment_2>::type;
|
||||
|
||||
using Geom_traits = typename internal_np::Lookup_named_param_def<internal_np::geom_traits_t,
|
||||
InputNamedParameters,
|
||||
In_K>::type;
|
||||
|
||||
using Polygon_with_holes_2 = typename MultipolygonWithHoles::Polygon_with_holes_2;
|
||||
using Polygon_2 = typename Polygon_with_holes_2::Polygon_2;
|
||||
using Out_point_2 = typename boost::range_value<Polygon_2>::type;
|
||||
using Out_K = typename CGAL::Kernel_traits<Out_point_2>::type;
|
||||
|
||||
using Oracle = Alpha_wraps_2::internal::Segment_soup_oracle<Geom_traits>;
|
||||
using Wrapper = Alpha_wraps_2::internal::Alpha_wrapper_2<Oracle>;
|
||||
|
||||
Geom_traits gt = choose_parameter<Geom_traits>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
Oracle oracle(alpha, gt);
|
||||
oracle.add_segments(segments);
|
||||
|
||||
Wrapper alpha_wrap_builder(oracle);
|
||||
alpha_wrap_builder(alpha, offset, alpha_wrap);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// WITH A MULTI-POLYLINE ---------------------------------------------------------------------------
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
* \ingroup AW2_free_functions_grp
|
||||
*
|
||||
* \brief computes a watertight, 1-manifold, simple multipolygon that strictly contains
|
||||
* input polylines.
|
||||
*
|
||||
* The parameters `alpha` and `offset` respectively control which features will appear in the output,
|
||||
* and the distance from the input. See Section \ref aw2_parameters for a detailed breakdown of their influence.
|
||||
*
|
||||
* \tparam MultiLineString a model of `RandomAccessContainer` whose value type is a model of `RandomAccessContainer`
|
||||
* whose value type is a point type model of `Kernel::Point_2`
|
||||
* \tparam MultipolygonWithHoles a model of `MultipolygonWithHoles_2`
|
||||
* \tparam InputNamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* \param multilinestring the input polylines
|
||||
* \param alpha the value of the parameter `alpha`
|
||||
* \param offset the value of the parameter `offset`
|
||||
* \param alpha_wrap the output multipolygon
|
||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `Kernel`}
|
||||
* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
* \cgalParamExtra{<ul><li>The geometric traits class must be compatible with the segment type.</li>
|
||||
* <li>The geometric traits should use a floating point number type (see \ref aw2_interface).</li></ul>}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \pre `alpha` and `offset` are strictly positive values.
|
||||
*/
|
||||
template <typename MultiLineString,
|
||||
typename MultipolygonWithHoles,
|
||||
typename InputNamedParameters>
|
||||
void alpha_wrap_2(const MultiLineString& multilinestring,
|
||||
const double alpha,
|
||||
const double offset,
|
||||
MultipolygonWithHoles& alpha_wrap,
|
||||
const InputNamedParameters& np
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
, std::enable_if_t<Alpha_wraps_2::internal::is_MultiLineString<MultiLineString>::value>* = nullptr
|
||||
#endif
|
||||
)
|
||||
{
|
||||
using parameters::get_parameter;
|
||||
using parameters::choose_parameter;
|
||||
|
||||
using Linestring = typename boost::range_value<MultiLineString>::type;
|
||||
using In_Point_2 = typename boost::range_value<Linestring>::type;
|
||||
using In_K = typename CGAL::Kernel_traits<In_Point_2>::type;
|
||||
|
||||
using Geom_traits = typename internal_np::Lookup_named_param_def<internal_np::geom_traits_t,
|
||||
InputNamedParameters,
|
||||
In_K>::type;
|
||||
|
||||
using Polygon_with_holes_2 = typename MultipolygonWithHoles::Polygon_with_holes_2;
|
||||
using Polygon_2 = typename Polygon_with_holes_2::Polygon_2;
|
||||
using Out_point_2 = typename boost::range_value<Polygon_2>::type;
|
||||
using Out_K = typename CGAL::Kernel_traits<Out_point_2>::type;
|
||||
|
||||
using Oracle = Alpha_wraps_2::internal::Segment_soup_oracle<Geom_traits>;
|
||||
using Wrapper = Alpha_wraps_2::internal::Alpha_wrapper_2<Oracle>;
|
||||
|
||||
Geom_traits gt = choose_parameter<Geom_traits>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
Oracle oracle(alpha, gt);
|
||||
oracle.add_multilinestring(multilinestring);
|
||||
|
||||
Wrapper alpha_wrap_builder(oracle);
|
||||
alpha_wrap_builder(alpha, offset, alpha_wrap);
|
||||
}
|
||||
|
||||
// The convenience overloads are common to all ranges
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// WITH A POINT SET / MULTI POINT ------------------------------------------------------------------
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
* \ingroup AW2_free_functions_grp
|
||||
*
|
||||
* \brief computes a watertight, 1-manifold, simple multipolygon that strictly contains
|
||||
* an input point set.
|
||||
*
|
||||
* The parameters `alpha` and `offset` respectively control which features will appear in the output,
|
||||
* and the distance from the input. See Section \ref aw2_parameters for a detailed breakdown of their influence.
|
||||
*
|
||||
* \tparam PointRange model of `Range` whose value type is a point type model of `Kernel::Point_2`
|
||||
* \tparam MultipolygonWithHoles model of `MultipolygonWithHoles_2`
|
||||
* \tparam InputNamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* \param points the input points
|
||||
* \param alpha the value of the parameter `alpha`
|
||||
* \param offset the value of the parameter `offset`
|
||||
* \param alpha_wrap the output multipolygon
|
||||
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `Kernel`}
|
||||
* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
* \cgalParamExtra{<ul><li>The geometric traits class must be compatible with the point type.</li>
|
||||
* <li>The geometric traits should use a floating point number type (see \ref aw2_interface).</li></ul>}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* \pre `alpha` and `offset` are strictly positive values.
|
||||
*/
|
||||
template <typename PointRange,
|
||||
typename MultipolygonWithHoles,
|
||||
typename InputNamedParameters>
|
||||
void alpha_wrap_2(const PointRange& points,
|
||||
const double alpha,
|
||||
const double offset,
|
||||
MultipolygonWithHoles& alpha_wrap,
|
||||
const InputNamedParameters& np
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
, std::enable_if_t<Alpha_wraps_2::internal::is_Point_2_range<PointRange>::value>* = nullptr
|
||||
#endif
|
||||
)
|
||||
{
|
||||
using parameters::get_parameter;
|
||||
using parameters::choose_parameter;
|
||||
|
||||
using NP_helper = Point_set_processing_3_np_helper<PointRange, InputNamedParameters>;
|
||||
using Geom_traits = typename NP_helper::Geom_traits;
|
||||
using Oracle = Alpha_wraps_2::internal::Point_set_oracle<Geom_traits>;
|
||||
using Wrapper = Alpha_wraps_2::internal::Alpha_wrapper_2<Oracle>;
|
||||
|
||||
Geom_traits gt = choose_parameter<Geom_traits>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
Oracle oracle(gt);
|
||||
oracle.add_points(points, np);
|
||||
|
||||
Wrapper alpha_wrap_builder(oracle);
|
||||
alpha_wrap_builder(alpha, offset, alpha_wrap, np);
|
||||
}
|
||||
|
||||
// Convenience overloads, common to all ranges
|
||||
|
||||
template <typename Input, typename MultipolygonWithHoles>
|
||||
void alpha_wrap_2(const Input& input,
|
||||
const double alpha,
|
||||
const double offset,
|
||||
MultipolygonWithHoles& alpha_wrap)
|
||||
{
|
||||
return alpha_wrap_2(input, alpha, offset, alpha_wrap, CGAL::parameters::default_values());
|
||||
}
|
||||
|
||||
// without offset
|
||||
template <typename Input, typename MultipolygonWithHoles,
|
||||
typename T_I, typename Tag_I, typename Base_I>
|
||||
void alpha_wrap_2(const Input& input,
|
||||
const double alpha,
|
||||
MultipolygonWithHoles& alpha_wrap,
|
||||
const CGAL::Named_function_parameters<T_I, Tag_I, Base_I>& np)
|
||||
{
|
||||
return alpha_wrap_2(input, alpha, alpha / 30., alpha_wrap, np);
|
||||
}
|
||||
|
||||
template <typename Input, typename MultipolygonWithHoles>
|
||||
void alpha_wrap_2(const Input& input,
|
||||
const double alpha,
|
||||
MultipolygonWithHoles& alpha_wrap)
|
||||
{
|
||||
return alpha_wrap_2(input, alpha, alpha / 30., alpha_wrap, CGAL::parameters::default_values());
|
||||
}
|
||||
|
||||
} // namespace CGAL
|
||||
|
||||
#endif // CGAL_ALPHA_WRAP_2_H
|
||||
|
|
@ -0,0 +1 @@
|
|||
GeometryFactory (France)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
AABB_tree
|
||||
Algebraic_foundations
|
||||
Alpha_wrap_2
|
||||
Arithmetic_kernel
|
||||
BGL
|
||||
CGAL_Core
|
||||
Cartesian_kernel
|
||||
Circulator
|
||||
Distance_2
|
||||
Distance_3
|
||||
Filtered_kernel
|
||||
Hash_map
|
||||
Homogeneous_kernel
|
||||
Installation
|
||||
Intersections_2
|
||||
Intersections_3
|
||||
Interval_support
|
||||
Kernel_23
|
||||
Kernel_d
|
||||
Modular_arithmetic
|
||||
Number_types
|
||||
Polygon
|
||||
Profiling_tools
|
||||
Property_map
|
||||
Random_numbers
|
||||
STL_Extension
|
||||
Spatial_searching
|
||||
Spatial_sorting
|
||||
Stream_support
|
||||
TDS_2
|
||||
Triangulation_2
|
||||
|
|
@ -0,0 +1 @@
|
|||
The 2D Alpha Wrapping package is a generic meshing algorithm to create valid and input-enclosing polygons.
|
||||
|
|
@ -0,0 +1 @@
|
|||
GPL (v3 or later)
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1 @@
|
|||
GeometryFactory
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Created by the script cgal_create_cmake_script
|
||||
# This is the CMake script for compiling a CGAL application.
|
||||
|
||||
cmake_minimum_required(VERSION 3.12...3.31)
|
||||
project(Alpha_wrap_2_Tests)
|
||||
|
||||
find_package(CGAL REQUIRED)
|
||||
|
||||
# create a target per cppfile
|
||||
create_single_source_cgal_program("test_alpha_wrap_2_points.cpp")
|
||||
create_single_source_cgal_program("test_alpha_wrap_2_polylines.cpp")
|
||||
|
|
@ -0,0 +1 @@
|
|||
MULTIPOINT (1 1, 1 1, 2 2)
|
||||
|
|
@ -0,0 +1 @@
|
|||
MULTIPOINT (10 40, 40 30, 20 20, 30 10)
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
0.00605396 0.00360027
|
||||
0.0117095 0.00496933
|
||||
0.00292489 -0.0056444
|
||||
0.018654 -0.00345866
|
||||
0.0208731 -0.00712699
|
||||
0.0349622 0.00520127
|
||||
0.0226514 0.00273598
|
||||
0.0443469 0.00641652
|
||||
0.0320264 -0.00785089
|
||||
0.0536853 -0.00492172
|
||||
0.0477706 0.00445479
|
||||
0.0639807 0.00509629
|
||||
0.0673864 -0.000544755
|
||||
0.068878 0.00636891
|
||||
0.0786834 -0.00880306
|
||||
0.0838299 0.00977294
|
||||
0.087326 -0.0021897
|
||||
0.079062 0.000772423
|
||||
0.0984893 0.00905454
|
||||
0.0994487 -0.00770074
|
||||
0.100736 0.00717826
|
||||
0.0994229 0.00250389
|
||||
0.100252 0.0167278
|
||||
0.0960604 0.00802011
|
||||
0.103545 0.0289233
|
||||
0.108446 0.0183656
|
||||
0.106763 0.0262313
|
||||
0.106452 0.0420934
|
||||
0.0997256 0.0427598
|
||||
0.107064 0.0403298
|
||||
0.0928101 0.0560955
|
||||
0.10136 0.0583232
|
||||
0.104819 0.0562105
|
||||
0.0902899 0.0706163
|
||||
0.10994 0.0770702
|
||||
0.0923621 0.0704878
|
||||
0.0919434 0.0865538
|
||||
0.0963674 0.0842679
|
||||
0.103725 0.0803259
|
||||
0.102273 0.101166
|
||||
0.100319 0.0952791
|
||||
0.108403 0.0942299
|
||||
0.113529 0.0981625
|
||||
0.108027 0.103066
|
||||
0.126272 0.0950435
|
||||
0.133506 0.0939314
|
||||
0.124776 0.107205
|
||||
0.131076 0.107853
|
||||
0.136759 0.109119
|
||||
0.15444 0.102357
|
||||
0.143707 0.104111
|
||||
0.160272 0.0974776
|
||||
0.165379 0.103348
|
||||
0.173751 0.0916309
|
||||
0.174657 0.0937715
|
||||
0.167267 0.0980068
|
||||
0.170889 0.0905988
|
||||
0.185414 0.102092
|
||||
0.189813 0.10002
|
||||
0.199397 0.0909473
|
||||
0.198222 0.107717
|
||||
0.198974 0.099872
|
||||
0.201479 0.108827
|
||||
0.205074 0.107075
|
||||
0.202 0.124977
|
||||
0.191185 0.121976
|
||||
0.206848 0.134009
|
||||
0.196679 0.137767
|
||||
0.19255 0.148035
|
||||
0.190151 0.143856
|
||||
0.195263 0.155428
|
||||
0.20595 0.148822
|
||||
0.204421 0.152387
|
||||
0.191967 0.169495
|
||||
0.197981 0.169699
|
||||
0.191872 0.176798
|
||||
0.207398 0.170317
|
||||
0.194859 0.178978
|
||||
0.190444 0.183389
|
||||
0.196073 0.192833
|
||||
0.200019 0.190352
|
||||
0.205824 0.198579
|
||||
0.217043 0.198723
|
||||
0.210708 0.208976
|
||||
0.225591 0.209213
|
||||
0.224774 0.208331
|
||||
0.228376 0.201784
|
||||
0.233852 0.192014
|
||||
0.230703 0.196273
|
||||
0.241172 0.192107
|
||||
0.241027 0.203219
|
||||
0.257393 0.199803
|
||||
0.266244 0.190504
|
||||
0.263176 0.1902
|
||||
0.279822 0.191442
|
||||
0.267419 0.200092
|
||||
0.270919 0.209937
|
||||
0.294279 0.199399
|
||||
0.292596 0.208336
|
||||
0.302111 0.206854
|
||||
0.297261 0.193606
|
||||
0.302447 0.195568
|
||||
0.307461 0.217454
|
||||
0.302133 0.219113
|
||||
0.300152 0.216012
|
||||
0.296763 0.223723
|
||||
0.302571 0.234727
|
||||
0.298522 0.237272
|
||||
0.307834 0.234066
|
||||
0.296568 0.250613
|
||||
0.298385 0.251664
|
||||
0.29308 0.261943
|
||||
0.295426 0.266549
|
||||
0.293096 0.259791
|
||||
0.292439 0.271056
|
||||
0.291263 0.275271
|
||||
0.300944 0.286063
|
||||
0.308624 0.284206
|
||||
0.306603 0.285177
|
||||
0.302574 0.289769
|
||||
0.303807 0.303483
|
||||
0.308102 0.301263
|
||||
0.316854 0.306492
|
||||
0.313448 0.299638
|
||||
0.325862 0.304911
|
||||
0.328301 0.305416
|
||||
0.335535 0.300855
|
||||
0.327652 0.299601
|
||||
0.334895 0.301131
|
||||
0.339451 0.303238
|
||||
0.356128 0.293215
|
||||
0.359167 0.306227
|
||||
0.350648 0.309557
|
||||
0.359385 0.291005
|
||||
0.360515 0.305818
|
||||
0.377582 0.301763
|
||||
0.373333 0.308693
|
||||
0.375172 0.299768
|
||||
0.398744 0.298911
|
||||
0.390985 0.295462
|
||||
0.39465 0.305079
|
||||
0.397266 0.302934
|
||||
0.391293 0.303944
|
||||
0.401355 0.307406
|
||||
0.391301 0.312749
|
||||
0.401141 0.331346
|
||||
0.403843 0.339273
|
||||
0.397447 0.32984
|
||||
0.401007 0.345187
|
||||
0.401435 0.350856
|
||||
0.404534 0.358367
|
||||
0.40019 0.350997
|
||||
0.401021 0.359769
|
||||
0.398586 0.362409
|
||||
0.403735 0.370503
|
||||
0.400571 0.381428
|
||||
0.409145 0.374727
|
||||
0.402981 0.379619
|
||||
0.406312 0.38398
|
||||
0.405032 0.387826
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
#define CGAL_AW2_TIMER
|
||||
|
||||
#include <CGAL/alpha_wrap_2.h>
|
||||
#include <CGAL/Alpha_wrap_2/internal/validation.h>
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/bounding_box.h>
|
||||
#include <CGAL/IO/WKT.h>
|
||||
#include <CGAL/Multipolygon_with_holes_2.h>
|
||||
#include <CGAL/Random.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace CGAL::Alpha_wraps_2::internal;
|
||||
|
||||
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
|
||||
using FT = Kernel::FT;
|
||||
using Point_2 = Kernel::Point_2;
|
||||
using Vector_2 = Kernel::Vector_2;
|
||||
using Iso_rectangle_2 = Kernel::Iso_rectangle_2;
|
||||
|
||||
using Points = std::vector<Point_2>;
|
||||
|
||||
using Multipolygon = CGAL::Multipolygon_with_holes_2<Kernel>;
|
||||
|
||||
// small utility to construct a multipoint .wkt from a .xy or .xyz
|
||||
void xyz_to_multipoint(const std::string& filename)
|
||||
{
|
||||
std::ifstream in(filename);
|
||||
if (!in) {
|
||||
std::cerr << "Error: cannot open file '" << filename << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Points points;
|
||||
std::string line;
|
||||
int lineNumber = 0;
|
||||
while (std::getline(in, line)) {
|
||||
++lineNumber;
|
||||
// Trim whitespace
|
||||
line.erase(line.find_last_not_of(" \t\r\n") + 1);
|
||||
line.erase(0, line.find_first_not_of(" \t\r\n"));
|
||||
// Skip empty or comment lines
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::istringstream iss(line);
|
||||
std::vector<double> values;
|
||||
double x, y;
|
||||
// ignore everything except the (X,Y) coordinates
|
||||
if (iss >> x >> y) {
|
||||
points.emplace_back(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Read " << points.size() << " points" << std::endl;
|
||||
|
||||
std::string out_filename = filename;
|
||||
std::size_t dot_pos = out_filename.find_last_of('.');
|
||||
if (dot_pos != std::string::npos) {
|
||||
out_filename = out_filename.substr(0, dot_pos) + ".wkt";
|
||||
} else {
|
||||
out_filename += ".wkt";
|
||||
}
|
||||
|
||||
std::ofstream out(out_filename);
|
||||
if (!out) {
|
||||
std::cerr << "Error: cannot write to file '" << out_filename << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
out.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_point_WKT(out, points);
|
||||
|
||||
std::cout << "Wrote " << points.size() << " points to '" << out_filename << "'" << std::endl;
|
||||
}
|
||||
|
||||
bool alpha_wrap_point_set(Points& input_points,
|
||||
const double alpha,
|
||||
const double offset)
|
||||
{
|
||||
namespace AW2 = CGAL::Alpha_wraps_2;
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
bool result = true;
|
||||
|
||||
std::cout << "Input: " << input_points.size() << " points" << std::endl;
|
||||
std::cout << "Alpha: " << alpha << " Offset: " << offset << std::endl;
|
||||
|
||||
const bool enforce_manifoldness = true;
|
||||
|
||||
Multipolygon wrap;
|
||||
CGAL::alpha_wrap_2(input_points, alpha, offset, wrap,
|
||||
CGAL::parameters::do_enforce_manifoldness(enforce_manifoldness));
|
||||
|
||||
std::cout << "Result: " << wrap.polygons_with_holes().size() << " polygons" << std::endl;
|
||||
|
||||
std::ofstream out("last.off");
|
||||
out.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_polygon_WKT(out, wrap);
|
||||
|
||||
if(!AW2::internal::is_valid_wrap(wrap, enforce_manifoldness)) {
|
||||
std::cerr << "Error: invalid wrap" << std::endl;
|
||||
result = false;
|
||||
}
|
||||
if(!AW2::internal::is_outer_wrap_of_point_set(wrap, input_points)) {
|
||||
std::cerr << "Error: wrap does not contain the input" << std::endl;
|
||||
result = false;
|
||||
}
|
||||
|
||||
// if(!enforce_manifoldness)
|
||||
// assert(AW2::internal::has_expected_Hausdorff_distance(wrap, input_points, alpha, offset));
|
||||
|
||||
if(!enforce_manifoldness) {
|
||||
if(!AW2::internal::has_bounded_edge_length(wrap, alpha)) {
|
||||
std::cerr << "Error: edge length check failure" << std::endl;
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool alpha_wrap_point_set(const std::string& filename,
|
||||
const double alpha_rel,
|
||||
const double offset_rel)
|
||||
{
|
||||
std::ifstream in(filename);
|
||||
Points input_points;
|
||||
bool res = CGAL::IO::read_multi_point_WKT(in, input_points);
|
||||
assert(res);
|
||||
assert(!input_points.empty());
|
||||
|
||||
const Iso_rectangle_2 bbox = CGAL::bounding_box(input_points.begin(), input_points.end());
|
||||
const Vector_2 longest_diag = Point_2(bbox.xmax(), bbox.ymax()) -
|
||||
Point_2(bbox.xmin(), bbox.ymin());
|
||||
double longest_diag_length = CGAL::to_double(CGAL::approximate_sqrt(longest_diag.squared_length()));
|
||||
|
||||
const double alpha = longest_diag_length / alpha_rel;
|
||||
const double offset = longest_diag_length / offset_rel;
|
||||
|
||||
return alpha_wrap_point_set(input_points, alpha, offset);
|
||||
}
|
||||
|
||||
bool alpha_wrap_point_set(const std::string& filename)
|
||||
{
|
||||
std::cout << "\n== " << filename << "==" << std::endl;
|
||||
|
||||
bool result = true;
|
||||
|
||||
CGAL::Random r;
|
||||
|
||||
std::ifstream in(filename);
|
||||
assert(in);
|
||||
Points input_points;
|
||||
bool res = CGAL::IO::read_multi_point_WKT(in, input_points);
|
||||
assert(res);
|
||||
assert(!input_points.empty());
|
||||
|
||||
const Iso_rectangle_2 bbox = CGAL::bounding_box(input_points.begin(), input_points.end());
|
||||
const Vector_2 longest_diag = Point_2(bbox.xmax(), bbox.ymax()) -
|
||||
Point_2(bbox.xmin(), bbox.ymin());
|
||||
double longest_diag_length = CGAL::to_double(CGAL::approximate_sqrt(longest_diag.squared_length()));
|
||||
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
const double alpha_expo = r.get_double(2., 6); // to have alpha_rel between 1 and 64
|
||||
const double offset_expo = r.get_double(2., 6);
|
||||
const double alpha_rel = std::pow(2, alpha_expo);
|
||||
const double offset_rel = std::pow(2, offset_expo);
|
||||
const double alpha = longest_diag_length / alpha_rel;
|
||||
const double offset = longest_diag_length / offset_rel;
|
||||
|
||||
std::cout << "===================================================" << std::endl;
|
||||
std::cout << filename << " " << alpha << " (rel " << alpha_rel << ")"
|
||||
<< " " << offset << " (rel " << offset_rel << ")" << std::endl;
|
||||
std::cout << "Random seed = " << r.get_seed() << std::endl;
|
||||
|
||||
if(!alpha_wrap_point_set(input_points, alpha, offset)) {
|
||||
result = false;
|
||||
#ifdef CGAL_AW2_BREAK_ON_TEST_FAILURE
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout.precision(17);
|
||||
std::cerr.precision(17);
|
||||
|
||||
// xyz_to_multipoint(argv[1]);
|
||||
// return EXIT_SUCCESS;
|
||||
|
||||
bool result = true;
|
||||
|
||||
// For convenience to do manual testing
|
||||
if(argc > 1)
|
||||
{
|
||||
const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 20.;
|
||||
const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 600.;
|
||||
|
||||
result = alpha_wrap_point_set(argv[1], relative_alpha, relative_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> default_inputs = { "data/buildings_outline.wkt",
|
||||
"data/circles.wkt",
|
||||
"data/duplicate_points.wkt",
|
||||
"data/four_points.wkt",
|
||||
"data/hippo2.wkt",
|
||||
"data/kitten.wkt",
|
||||
"data/spheres.wkt" };
|
||||
|
||||
for(const std::string& filename : default_inputs) {
|
||||
if(!alpha_wrap_point_set(filename)) {
|
||||
result = false;
|
||||
#ifdef CGAL_AW2_BREAK_ON_TEST_FAILURE
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Done!" << std::endl;
|
||||
return result ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
#define CGAL_AW2_TIMER
|
||||
// #define CGAL_AW2_DEBUG_PP
|
||||
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/alpha_wrap_2.h>
|
||||
#include <CGAL/Alpha_wrap_2/internal/validation.h>
|
||||
|
||||
#include <CGAL/bounding_box.h>
|
||||
#include <CGAL/IO/WKT.h>
|
||||
#include <CGAL/IO/OBJ.h>
|
||||
#include <CGAL/IO/helpers.h>
|
||||
#include <CGAL/Multipolygon_with_holes_2.h>
|
||||
#include <CGAL/Random.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
// #define CGAL_AW2_BREAK_ON_TEST_FAILURE
|
||||
|
||||
using namespace CGAL::Alpha_wraps_2::internal;
|
||||
|
||||
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
|
||||
using FT = Kernel::FT;
|
||||
using Point_2 = Kernel::Point_2;
|
||||
using Vector_2 = Kernel::Vector_2;
|
||||
|
||||
using Points = std::vector<Point_2>;
|
||||
using Polyline = std::vector<Point_2>;
|
||||
using Polylines = std::vector<Polyline>;
|
||||
|
||||
using Multipolygon = CGAL::Multipolygon_with_holes_2<Kernel>;
|
||||
|
||||
CGAL::Bbox_2 compute_bbox(const Polylines& pls)
|
||||
{
|
||||
CGAL::Bbox_2 bbox;
|
||||
for (const Polyline& pl : pls) {
|
||||
for (const Point_2& p : pl) {
|
||||
bbox += p.bbox();
|
||||
}
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
// Reads polylines from .wkt or .obj
|
||||
bool read_polylines(const std::string& filename,
|
||||
Polylines& polylines)
|
||||
{
|
||||
std::ifstream in(filename);
|
||||
if(!in)
|
||||
return false;
|
||||
|
||||
const std::string ext = CGAL::IO::internal::get_file_extension(filename);
|
||||
if(ext == "wkt")
|
||||
{
|
||||
// read_WKT() reads ALL multi-linestrings whereas read_multilinestring() reads only the first one
|
||||
Points pts;
|
||||
Multipolygon mp;
|
||||
return CGAL::IO::read_WKT(in, pts, polylines, mp);
|
||||
}
|
||||
else if (ext == "obj")
|
||||
{
|
||||
std::vector<Point_2> points;
|
||||
std::vector<std::vector<std::size_t> > id_polylines;
|
||||
std::vector<std::vector<std::size_t> > unused_id_polygons;
|
||||
bool success = CGAL::IO::internal::read_OBJ(in, points, id_polylines, unused_id_polygons);
|
||||
if(!success)
|
||||
return false;
|
||||
|
||||
for(const std::vector<std::size_t>& id_pl : id_polylines) {
|
||||
polylines.emplace_back();
|
||||
for(const std::size_t pid : id_pl) {
|
||||
polylines.back().push_back(points[pid]);
|
||||
}
|
||||
}
|
||||
|
||||
return !polylines.empty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void read_translate_and_write(const std::string& filename)
|
||||
{
|
||||
std::ifstream in(filename);
|
||||
if (!in) {
|
||||
std::cerr << "Error: cannot open file '" << filename << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Polylines polylines;
|
||||
bool res = read_polylines(filename, polylines);
|
||||
if(!res) {
|
||||
std::cerr << "Error: cannot read polylines from file '" << filename << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "Read " << polylines.size() << " polylines" << std::endl;
|
||||
|
||||
std::string out_filename = filename;
|
||||
std::size_t dot_pos = out_filename.find_last_of('.');
|
||||
if (dot_pos != std::string::npos) {
|
||||
out_filename = out_filename.substr(0, dot_pos) + "_translated.wkt";
|
||||
} else {
|
||||
out_filename += "_translated.wkt";
|
||||
}
|
||||
|
||||
// compute the bbox of all polylines
|
||||
CGAL::Bbox_2 bbox = compute_bbox(polylines);
|
||||
|
||||
// Translate all points by 20% of the vertical size downwards
|
||||
// and by 50% of the horizontal size to the left
|
||||
const double translate_x = -0.5 * (bbox.xmax() - bbox.xmin());
|
||||
const double translate_y = -0.2 * (bbox.ymax() - bbox.ymin());
|
||||
const Vector_2 translation(translate_x, translate_y);
|
||||
|
||||
Polylines translated_polylines = polylines;
|
||||
for(Polyline& pl : translated_polylines) {
|
||||
for(Point_2& p : pl) {
|
||||
p = p + translation;
|
||||
}
|
||||
}
|
||||
|
||||
polylines.insert(polylines.end(), translated_polylines.begin(), translated_polylines.end());
|
||||
|
||||
std::ofstream out(out_filename);
|
||||
if (!out) {
|
||||
std::cerr << "Error: cannot write to file '" << out_filename << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
out.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_linestring_WKT(out, polylines);
|
||||
|
||||
std::cout << "Wrote " << polylines.size() << " translated polylines to '" << out_filename << "'" << std::endl;
|
||||
}
|
||||
|
||||
// Wrap polylines, similar to alpha_wrap_point_set
|
||||
bool alpha_wrap_polylines(Polylines& input_polylines,
|
||||
const double alpha, const double offset)
|
||||
{
|
||||
namespace AW2 = CGAL::Alpha_wraps_2;
|
||||
|
||||
bool result = true;
|
||||
|
||||
std::cout << "Input: " << input_polylines.size() << " polylines" << std::endl;
|
||||
std::cout << "Alpha: " << alpha << " Offset: " << offset << std::endl;
|
||||
|
||||
const bool enforce_manifoldness = true;
|
||||
|
||||
Multipolygon wrap;
|
||||
CGAL::alpha_wrap_2(input_polylines, alpha, offset, wrap,
|
||||
CGAL::parameters::do_enforce_manifoldness(enforce_manifoldness));
|
||||
|
||||
std::cout << "Result: " << wrap.polygons_with_holes().size() << " polygons" << std::endl;
|
||||
|
||||
std::ofstream out_i("input.wkt");
|
||||
out_i.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_linestring_WKT(out_i, input_polylines);
|
||||
std::ofstream out_w("wrap.wkt");
|
||||
out_w.precision(std::numeric_limits<double>::max_digits10);
|
||||
CGAL::IO::write_multi_polygon_WKT(out_w, wrap);
|
||||
|
||||
if(!AW2::internal::is_valid_wrap(wrap, enforce_manifoldness)) {
|
||||
std::cerr << "Error: invalid wrap" << std::endl;
|
||||
result = false;
|
||||
}
|
||||
|
||||
if(!AW2::internal::is_outer_wrap_of_polylines(wrap, input_polylines)) {
|
||||
std::cerr << "Error: wrap does not contain the input" << std::endl;
|
||||
result = false;
|
||||
}
|
||||
|
||||
if(!enforce_manifoldness) {
|
||||
if(!AW2::internal::has_bounded_edge_length(wrap, alpha)) {
|
||||
std::cerr << "Error: edge length check failure" << std::endl;
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Compute alpha/offset and wrap polylines
|
||||
bool alpha_wrap_polylines(const std::string& filename,
|
||||
const double alpha_rel, const double offset_rel)
|
||||
{
|
||||
Polylines input_polylines;
|
||||
bool res = read_polylines(filename, input_polylines);
|
||||
assert(res);
|
||||
assert(!input_polylines.empty());
|
||||
|
||||
const CGAL::Bbox_2 bbox = compute_bbox(input_polylines);
|
||||
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||
CGAL::square(bbox.ymax() - bbox.ymin()));
|
||||
|
||||
const double alpha = diag_length / alpha_rel;
|
||||
const double offset = diag_length / offset_rel;
|
||||
|
||||
return alpha_wrap_polylines(input_polylines, alpha, offset);
|
||||
}
|
||||
|
||||
// Randomized test, similar to point version
|
||||
bool alpha_wrap_polylines(const std::string& filename)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
CGAL::Random r;
|
||||
|
||||
Polylines input_polylines;
|
||||
bool res = read_polylines(filename, input_polylines);
|
||||
assert(res);
|
||||
assert(!input_polylines.empty());
|
||||
|
||||
const CGAL::Bbox_2 bbox = compute_bbox(input_polylines);
|
||||
const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
|
||||
CGAL::square(bbox.ymax() - bbox.ymin()));
|
||||
|
||||
for(int i=0; i<3; ++i)
|
||||
{
|
||||
const double alpha_expo = r.get_double(3., 8);
|
||||
const double offset_expo = r.get_double(3., 8);
|
||||
const double alpha_rel = std::pow(2, alpha_expo);
|
||||
const double offset_rel = std::pow(2, offset_expo);
|
||||
const double alpha = diag_length / alpha_rel;
|
||||
const double offset = diag_length / offset_rel;
|
||||
|
||||
std::cout << "===================================================" << std::endl;
|
||||
std::cout << filename << " " << alpha << " (rel " << alpha_rel << ")"
|
||||
<< " " << offset << " (rel " << offset_rel << ")" << std::endl;
|
||||
std::cout << "Random seed = " << r.get_seed() << std::endl;
|
||||
|
||||
if(!alpha_wrap_polylines(input_polylines, alpha, offset)) {
|
||||
result = false;
|
||||
#ifdef CGAL_AW2_BREAK_ON_TEST_FAILURE
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout.precision(17);
|
||||
std::cerr.precision(17);
|
||||
|
||||
// read_translate_and_write(argv[1]);
|
||||
// return EXIT_SUCCESS;
|
||||
|
||||
bool result = true;
|
||||
|
||||
// For convenience to do manual testing
|
||||
if(argc > 1)
|
||||
{
|
||||
std::string arg1(argv[1]);
|
||||
if(std::filesystem::is_directory(arg1))
|
||||
{
|
||||
for(const auto& entry : std::filesystem::directory_iterator(arg1))
|
||||
{
|
||||
const std::string fname = entry.path().string();
|
||||
std::cout << "\n== " << fname << " ==" << std::endl;
|
||||
if(!alpha_wrap_polylines(fname)) {
|
||||
result = false;
|
||||
#ifdef CGAL_AW2_BREAK_ON_TEST_FAILURE
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 20.;
|
||||
const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 600.;
|
||||
|
||||
result = alpha_wrap_polylines(arg1, relative_alpha, relative_offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> default_inputs = { "data/polylines.wkt" };
|
||||
for(const std::string& filename : default_inputs) {
|
||||
if(!alpha_wrap_polylines(filename)) {
|
||||
result = false;
|
||||
#ifdef CGAL_AW2_BREAK_ON_TEST_FAILURE
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Done!" << std::endl;
|
||||
return result ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ namespace CGAL {
|
|||
<img src="aw3_bike_lod.jpg" style="max-width:70%;"/>
|
||||
</center>
|
||||
|
||||
Note: a two-dimensional version of this package is also available: \ref PkgAlphaWrap2.
|
||||
|
||||
\section aw3_introduction Introduction
|
||||
|
||||
Various tasks in geometric modeling and processing require 3D objects represented as valid surface meshes,
|
||||
|
|
@ -42,20 +44,20 @@ and degeneracies.
|
|||
The default input is a soup of 3D triangles, but the generic interface leaves the door open
|
||||
to other types of finite 3D primitives such as triangle soups and point sets.
|
||||
|
||||
\cgalFigureAnchor{1}
|
||||
\cgalFigureAnchor{aw3_input_1_fig}
|
||||
<center>
|
||||
<img src="aw3_triangle_soup.jpg" style="max-width:70%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{1}
|
||||
\cgalFigureCaptionBegin{aw3_input_1_fig}
|
||||
Shrink-wrapping output from a triangle soup, with many intersections and gaps.
|
||||
From left to right, input model, output wrap, and superposition.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
\cgalFigureAnchor{2}
|
||||
\cgalFigureAnchor{aw3_input_2_fig}
|
||||
<center>
|
||||
<img src="aw3_non_manifold_cases.jpg" style="max-width:75%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{2}
|
||||
\cgalFigureCaptionBegin{aw3_input_2_fig}
|
||||
Dealing with non-manifold features and degeneracies.
|
||||
From left to right, a non-manifold vertex, self-intersecting faces and two adjacent triangles
|
||||
representing a zero-volume structure.
|
||||
|
|
@ -129,11 +131,11 @@ of the circumradius of the gate.
|
|||
Traversal can be seen as a continuous process that advances along dual Voronoi edges of the gates,
|
||||
with a pencil of empty balls circumscribing the gate.
|
||||
|
||||
\cgalFigureAnchor{3}
|
||||
\cgalFigureAnchor{aw3_pencil_fig}
|
||||
<center>
|
||||
<img src="aw3_pencil.png" style="max-width:40%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{3}
|
||||
\cgalFigureCaptionBegin{aw3_pencil_fig}
|
||||
(Left) Pencil of empty circles (blue) circumscribing a Delaunay edge (green) in a 2D Delaunay triangulation (black).
|
||||
From the top triangle circumcenter <em>c1</em> to the bottom triangle circumcenter <em>c2</em>, the dual Voronoi edge denoted by <em>e</em> (doted red) is the trace of centers of the largest circles that are empty of Delaunay vertex.
|
||||
(Right) The graph corresponding to the left example. The x-axis corresponds to the position of empty circle centers located on the Voronoi edge <em>e</em>, from <em>c1</em> to <em>c2</em>. The y-axis is the radius value of the corresponding empty circles. In this case, the minimum radius of this pencil of empty circle is located at the midpoint of the green Delaunay edge.
|
||||
|
|
@ -166,11 +168,11 @@ separating inside from outside cells.
|
|||
|
||||
The figure below depicts the steps of the algorithm in 2D.
|
||||
|
||||
\cgalFigureAnchor{4}
|
||||
\cgalFigureAnchor{aw3_steps_fig}
|
||||
<center>
|
||||
<img src="aw3_steps.jpg" style="max-width:95%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{4}
|
||||
\cgalFigureCaptionBegin{aw3_steps_fig}
|
||||
Steps of the shrink-wrapping algorithm in 2D.
|
||||
The algorithm is initialized by inserting the corners of the loose bounding box of the input (red)
|
||||
into a Delaunay triangulation, and all finite triangles are tagged inside (grey).
|
||||
|
|
@ -237,11 +239,11 @@ the incident inside cell will be visited and possibly refined.
|
|||
Therefore, when the algorithm terminates, all facets have a circumradius smaller than alpha.
|
||||
This parameter thus also behaves like a sizing criterion on the triangle facets of the output.
|
||||
|
||||
\cgalFigureAnchor{5}
|
||||
\cgalFigureAnchor{aw3_alpha_param_fig}
|
||||
<center>
|
||||
<img src="aw3_church_lod.jpg" style="max-width:90%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{5}
|
||||
\cgalFigureCaptionBegin{aw3_alpha_param_fig}
|
||||
Impact of the alpha parameter on the output.
|
||||
(Left) The input triangle mesh, generated by surface reconstruction from a raw point cloud,
|
||||
has many non-manifold edges and vertices, superfluous geometric details and spurious topological structures.
|
||||
|
|
@ -267,11 +269,11 @@ Finally, and depending on the value of the alpha parameter, a large offset can a
|
|||
However, using a small offset parameter will tend to better preserve sharp features as projection
|
||||
Steiner points tend to project onto convex sharp features.
|
||||
|
||||
\cgalFigureAnchor{6}
|
||||
\cgalFigureAnchor{aw3_offset_param_fig}
|
||||
<center>
|
||||
<img src="aw3_sharp_feature.jpg" style="max-width:90%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{6}
|
||||
\cgalFigureCaptionBegin{aw3_offset_param_fig}
|
||||
Impact of the offset parameter on the output.
|
||||
(Left) Input mesh generated by meshing a NURBS CAD model in parameter space.
|
||||
(Right) The smaller the offset, the closest sample points are to the input.
|
||||
|
|
@ -281,11 +283,11 @@ A larger offset will produce an output less complex with better triangle quality
|
|||
However, the sharp features (red edges) are well-preserved when the offset parameter is small.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
\cgalFigureAnchor{7}
|
||||
\cgalFigureAnchor{aw3_steiner_fig}
|
||||
<center>
|
||||
<img src="aw3_steiner.jpg" style="max-width:90%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{7}
|
||||
\cgalFigureCaptionBegin{aw3_steiner_fig}
|
||||
Steiner points.
|
||||
The projection Steiner points (green) are computed by projecting the triangle circumcenter onto the offset.
|
||||
The intersection Steiner points (blue) are computed as the first intersection point between the Voronoi edge and the offset.
|
||||
|
|
@ -295,16 +297,16 @@ which tends to improve the preservation of convex sharp features.
|
|||
which tends to generate triangles with better quality in terms of angles, in 3D.
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
By default, we recommend to set the offset parameter to a small fraction of alpha, so that alpha
|
||||
By default, we recommend to set the offset parameter to a small fraction of alpha, such that alpha
|
||||
becomes the main parameter that controls the final level of detail.
|
||||
|
||||
The image below illustrates the impact of both parameters.
|
||||
|
||||
\cgalFigureAnchor{8}
|
||||
\cgalFigureAnchor{aw3_param_grid_fig}
|
||||
<center>
|
||||
<img src="aw3_alpha_offset_bike.jpg" style="max-width:80%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{8}
|
||||
\cgalFigureCaptionBegin{aw3_param_grid_fig}
|
||||
Different alpha and offset values on the bike model (533,000 triangles).
|
||||
The x-axis represents the offset value equal to 1/5000, 1/2000, 1/500, 1/200, 1/50, 1/20 and 1/5 of the longest diagonal of the input bounding box, from left to right.
|
||||
The y-axis represents the alpha value equal to 1/300, 1/100, 1/50, 1/20 and 1/5 of the longest diagonal of the input bounding box, from bottom to top.
|
||||
|
|
@ -318,17 +320,17 @@ For each alpha, the models with lower complexity can be used as a scale-space re
|
|||
The offset parameter is crucial to our approach because it guarantees that the output is a closed,
|
||||
2-manifold surface mesh.
|
||||
Indeed, and even when the input is a zero-volume structure such as a single 3D triangle,
|
||||
the output wrap is a thin volume enclosing the said triangle \cgalFigureRef{2}.
|
||||
the output wrap is a thin volume enclosing the said triangle \cgalFigureRef{aw3_input_2_fig}.
|
||||
|
||||
Users should keep in mind that the wrapping algorithm has no means of determining whether it is acting on the inside or the outside
|
||||
of the unsigned distance field, and will thus produce two-sided wraps in the case of holes in the input
|
||||
and values of alpha smaller than the size of the holes.
|
||||
|
||||
\cgalFigureAnchor{9}
|
||||
\cgalFigureAnchor{aw3_double_sided_fig}
|
||||
<center>
|
||||
<img src="aw3_double_sided.jpg" style="max-width:80%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{9}
|
||||
\cgalFigureCaptionBegin{aw3_double_sided_fig}
|
||||
Two-sided wrap.
|
||||
(Left) Wrapping a Bunny in 2D, with decreasing values for alpha.
|
||||
(Right) Wrapping a defect-laden Bunny in 3D. The rightmost column depicts a clipped visualization
|
||||
|
|
@ -339,11 +341,11 @@ of the inside. When alpha is small enough with respect the diameter of the holes
|
|||
|
||||
The charts below plots the computation times of the wrapping algorithm on the Thingi10k dataset, as well as the complexity of the output triangle mesh.
|
||||
|
||||
\cgalFigureAnchor{10}
|
||||
\cgalFigureAnchor{aw3_bench_fig}
|
||||
<center>
|
||||
<img src="aw3_thingi10k_benchmark.jpg" style="max-width:80%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{9}
|
||||
\cgalFigureCaptionBegin{aw3_bench_fig}
|
||||
Execution times and output complexity for different values of alpha on the Thingi10k data set.
|
||||
Alpha increases from 1/20 to 1/200 of the length of bounding box diagonal.
|
||||
The x-axis represents the complexity of the output wrap mesh in number of triangle facets.
|
||||
|
|
|
|||
|
|
@ -9,3 +9,4 @@ Alpha_shapes_3
|
|||
BGL
|
||||
Mesh_3
|
||||
Triangulation_3
|
||||
Alpha_wrap_2
|
||||
|
|
|
|||
|
|
@ -100,8 +100,8 @@ int main(int argc, char** argv)
|
|||
Oracle oracle(ss_oracle);
|
||||
|
||||
oracle.add_triangle_soup(points, faces, CGAL::parameters::default_values());
|
||||
oracle.add_segment_soup(segments, CGAL::parameters::default_values());
|
||||
oracle.add_point_set(ps_points, CGAL::parameters::default_values());
|
||||
oracle.add_segments(segments, CGAL::parameters::default_values());
|
||||
oracle.add_points(ps_points, CGAL::parameters::default_values());
|
||||
|
||||
CGAL::Alpha_wraps_3::internal::Alpha_wrapper_3<Oracle> aw3(oracle);
|
||||
|
||||
|
|
|
|||
|
|
@ -207,13 +207,27 @@ public:
|
|||
static_assert(std::is_floating_point<FT>::value);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_oracle.clear();
|
||||
m_bbox = {};
|
||||
m_alpha = m_sq_alpha = FT(-1);
|
||||
m_offset = m_sq_offset = FT(-1);
|
||||
m_seeds.clear();
|
||||
m_tr.clear();
|
||||
m_queue.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
const Geom_traits& geom_traits() const { return m_tr.geom_traits(); }
|
||||
Oracle& oracle() { return m_oracle; }
|
||||
const Oracle& oracle() const { return m_oracle; }
|
||||
Triangulation& triangulation() { return m_tr; }
|
||||
const Triangulation& triangulation() const { return m_tr; }
|
||||
Alpha_PQ& queue() { return m_queue; }
|
||||
const Alpha_PQ& queue() const { return m_queue; }
|
||||
const FT& alpha() const { return m_alpha; }
|
||||
const FT& offset() const { return m_offset; }
|
||||
|
||||
double default_alpha() const
|
||||
{
|
||||
|
|
@ -365,6 +379,11 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG_DUMP_INTERMEDIATE_WRAPS
|
||||
IO::write_polygon_mesh("final_wrap.off", output_mesh, CGAL::parameters::stream_precision(17));
|
||||
dump_triangulation_faces("final_tr.off", false /*only_boundary_faces*/);
|
||||
#endif
|
||||
|
||||
extract_surface(output_mesh, ovpm, !do_enforce_manifoldness);
|
||||
|
||||
#ifdef CGAL_AW3_TIMER
|
||||
|
|
@ -375,11 +394,6 @@ public:
|
|||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Alpha wrap vertices: " << vertices(output_mesh).size() << std::endl;
|
||||
std::cout << "Alpha wrap faces: " << faces(output_mesh).size() << std::endl;
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG_DUMP_INTERMEDIATE_WRAPS
|
||||
IO::write_polygon_mesh("final_wrap.off", output_mesh, CGAL::parameters::stream_precision(17));
|
||||
dump_triangulation_faces("final_tr.off", false /*only_boundary_faces*/);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
visitor.on_alpha_wrapping_end(*this);
|
||||
|
|
@ -501,7 +515,7 @@ private:
|
|||
// which yields
|
||||
// r = a * sin(2pi / 5) =~ 0.95105651629515353 * a
|
||||
// Faces of an icosahedron are equilateral triangles with size a and circumradius a / sqrt(3)
|
||||
// Since we want faces of the icosahedron to be traversable, we want a such that
|
||||
// Since we want faces of the icosahedron to be traversable, we want `a` such that
|
||||
// a / sqrt(3) > alpha
|
||||
// Hence r such that
|
||||
// r / (sqrt(3) * sin(2pi/5)) > alpha
|
||||
|
|
@ -509,7 +523,7 @@ private:
|
|||
//
|
||||
// Furthermore, the triangles between edges of the icosahedron and the center of the icosahedron
|
||||
// are not equilateral triangles since a is slightly bigger than r. They are
|
||||
// slightly flattened isocele triangles with base 'a' and the circumradius is smaller.
|
||||
// slightly flattened isocele triangles with base `a` and the circumradius is smaller.
|
||||
// The circumradius is
|
||||
// r_iso = r² / (2 * h) = r² / (2 * sqrt(r² - (a / 2)²))
|
||||
// Since r = a * sin(2pi / 5)
|
||||
|
|
@ -896,9 +910,19 @@ private:
|
|||
return less_squared_radius_of_min_empty_sphere(m_sq_alpha, f, m_tr);
|
||||
}
|
||||
|
||||
bool compute_steiner_point(const Cell_handle ch,
|
||||
const Cell_handle neighbor,
|
||||
Point_3& steiner_point) const
|
||||
Steiner_status compute_steiner_point(const Gate& gate,
|
||||
Point_3& steiner_point) const
|
||||
{
|
||||
const Facet& f = gate.facet();
|
||||
const Cell_handle ch = f.first;
|
||||
const int s = f.second;
|
||||
const Cell_handle nh = ch->neighbor(s);
|
||||
return compute_steiner_point(ch, nh, steiner_point);
|
||||
}
|
||||
|
||||
Steiner_status compute_steiner_point(const Cell_handle ch,
|
||||
const Cell_handle neighbor,
|
||||
Point_3& steiner_point) const
|
||||
{
|
||||
CGAL_precondition(!m_tr.is_infinite(neighbor));
|
||||
|
||||
|
|
@ -950,7 +974,7 @@ private:
|
|||
#ifdef CGAL_AW3_DEBUG_STEINER_COMPUTATION
|
||||
std::cout << "Steiner found through first_intersection(): " << steiner_point << std::endl;
|
||||
#endif
|
||||
return true;
|
||||
return Steiner_status::RULE_1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -959,6 +983,10 @@ private:
|
|||
{
|
||||
// steiner point is the closest point on input from cell centroid with offset
|
||||
const Point_3 closest_pt = m_oracle.closest_point(neighbor_cc);
|
||||
#ifdef CGAL_AW3_DEBUG_STEINER_COMPUTATION
|
||||
std::cout << "Steiner found through neighboring triangle intersecting the input" << std::endl;
|
||||
std::cout << "Closest point: " << closest_pt << std::endl;
|
||||
#endif
|
||||
CGAL_assertion(closest_pt != neighbor_cc);
|
||||
|
||||
Vector_3 unit = vector(closest_pt, neighbor_cc);
|
||||
|
|
@ -968,19 +996,18 @@ private:
|
|||
steiner_point = translate(closest_pt, unit);
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG_STEINER_COMPUTATION
|
||||
std::cout << "Steiner found through neighboring tet intersecting the input: " << steiner_point << std::endl;
|
||||
std::cout << "Closest point: " << closest_pt << std::endl;
|
||||
std::cout << "Direction: " << vector(closest_pt, neighbor_cc) << std::endl;
|
||||
std::cout << "Direction: " << unit << std::endl;
|
||||
std::cout << "Steiner: " << steiner_point << std::endl;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
return Steiner_status::RULE_2;
|
||||
}
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG_STEINER_COMPUTATION
|
||||
std::cout << "No Steiner point" << std::endl;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
return Steiner_status::NO_STEINER_POINT;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -1043,7 +1070,12 @@ public:
|
|||
CGAL_precondition(ch->label() == Cell_label::INSIDE || ch->label() == Cell_label::OUTSIDE);
|
||||
|
||||
if(m_tr.is_infinite(nh))
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG_FACET_STATUS
|
||||
std::cout << "Infinite neighboring cell" << std::endl;
|
||||
#endif
|
||||
return Facet_status::HAS_INFINITE_NEIGHBOR;
|
||||
}
|
||||
|
||||
if(nh->is_outside())
|
||||
{
|
||||
|
|
@ -1104,9 +1136,23 @@ private:
|
|||
const FT sqr = smallest_squared_radius_3(f, m_tr);
|
||||
const bool is_permissive = (status == Facet_status::HAS_INFINITE_NEIGHBOR ||
|
||||
status == Facet_status::SCAFFOLDING);
|
||||
m_queue.resize_and_push(Gate(f, sqr, is_permissive));
|
||||
Gate new_gate(f, sqr, is_permissive);
|
||||
#else
|
||||
m_queue.push(Gate(f, m_tr));
|
||||
Gate new_gate(f, m_tr);
|
||||
#endif
|
||||
|
||||
#ifdef CGAL_AW2_COMPUTE_AND_STORE_STEINER_INFO_AT_GATE_CREATION
|
||||
Point_3 steiner_point;
|
||||
Steiner_status steiner_status = compute_steiner_point(new_gate, steiner_point);
|
||||
new_gate.m_steiner_status = steiner_status;
|
||||
if(steiner_status == Steiner_status::RULE_1 || steiner_status == Steiner_status::RULE_2)
|
||||
new_gate.m_steiner_point = steiner_point;
|
||||
#endif
|
||||
|
||||
#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE
|
||||
m_queue.resize_and_push(new_gate);
|
||||
#else
|
||||
m_queue.push(new_gate);
|
||||
#endif
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG_QUEUE
|
||||
|
|
@ -1241,26 +1287,25 @@ private:
|
|||
#endif
|
||||
|
||||
const Facet& f = gate.facet();
|
||||
CGAL_precondition(!m_tr.is_infinite(f));
|
||||
|
||||
const Cell_handle ch = f.first;
|
||||
const int s = f.second;
|
||||
CGAL_precondition(ch->is_outside());
|
||||
|
||||
const Cell_handle nh = ch->neighbor(s);
|
||||
CGAL_precondition(nh->label() == Cell_label::INSIDE || nh->label() == Cell_label::OUTSIDE);
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG_QUEUE
|
||||
static int fid = 0;
|
||||
std::cout << m_tr.number_of_vertices() << " DT vertices" << std::endl;
|
||||
std::cout << m_queue.size() << " facets in the queue" << std::endl;
|
||||
std::cout << "Face " << fid++ << "\n"
|
||||
<< "c = " << &*ch << " (" << m_tr.is_infinite(ch) << "), n = " << &*nh << " (" << m_tr.is_infinite(nh) << ")" << "\n"
|
||||
<< m_tr.point(ch, Triangulation::vertex_triple_index(s, 0)) << "\n"
|
||||
<< m_tr.point(ch, Triangulation::vertex_triple_index(s, 1)) << "\n"
|
||||
<< m_tr.point(ch, Triangulation::vertex_triple_index(s, 2)) << std::endl;
|
||||
std::cout << "Face " << fid++ << " ["
|
||||
<< m_tr.point(ch, Triangulation::vertex_triple_index(s, 0)) << " "
|
||||
<< m_tr.point(ch, Triangulation::vertex_triple_index(s, 1)) << " "
|
||||
<< m_tr.point(ch, Triangulation::vertex_triple_index(s, 2)) << "]" << std::endl;
|
||||
std::cout << "c = " << &*ch << " (inf: " << m_tr.is_infinite(ch) << ", label: " << ch->label() << "), n = "
|
||||
<< &*nh << " (inf: " << m_tr.is_infinite(nh) << ", label: " << nh->label() << ")" << "\n"
|
||||
|
||||
# ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE
|
||||
std::cout << "Priority: " << gate.priority() << " (sq alpha: " << m_sq_alpha << ")" << std::endl;
|
||||
std::cout << "Permissiveness: " << gate.is_permissive_facet() << std::endl;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG_DUMP_EVERY_STEP
|
||||
|
|
@ -1277,6 +1322,10 @@ private:
|
|||
face_out.close();
|
||||
#endif
|
||||
|
||||
CGAL_precondition(!m_tr.is_infinite(f));
|
||||
CGAL_precondition(ch->is_outside());
|
||||
CGAL_precondition(nh->label() == Cell_label::INSIDE || nh->label() == Cell_label::OUTSIDE);
|
||||
|
||||
visitor.before_facet_treatment(*this, gate);
|
||||
|
||||
m_queue.pop();
|
||||
|
|
@ -1291,7 +1340,8 @@ private:
|
|||
}
|
||||
|
||||
Point_3 steiner_point;
|
||||
if(compute_steiner_point(ch, nh, steiner_point))
|
||||
Steiner_status ss = compute_steiner_point(ch, nh, steiner_point);
|
||||
if(ss != Steiner_status::NO_STEINER_POINT)
|
||||
{
|
||||
// std::cout << CGAL::abs(CGAL::approximate_sqrt(m_oracle.squared_distance(steiner_point)) - m_offset)
|
||||
// << " vs " << 1e-2 * m_offset << std::endl;
|
||||
|
|
|
|||
|
|
@ -23,13 +23,33 @@ namespace internal {
|
|||
enum class Cell_label
|
||||
{
|
||||
// Cells that have been carved
|
||||
OUTSIDE,
|
||||
OUTSIDE = 0,
|
||||
// Cells that have not yet been carved
|
||||
INSIDE,
|
||||
// OUTSIDE cells that have been labeled "inside" again as to make the result manifold
|
||||
MANIFOLD
|
||||
};
|
||||
|
||||
inline std::string label_string(const Cell_label& label) {
|
||||
switch(label) {
|
||||
case Cell_label::OUTSIDE:
|
||||
return "OUTSIDE";
|
||||
case Cell_label::INSIDE:
|
||||
return "INSIDE";
|
||||
case Cell_label::MANIFOLD:
|
||||
return "MANIFOLD";
|
||||
default:
|
||||
CGAL_assertion_msg(false, "Unknown label");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
inline std::ostream& operator <<(std::ostream& os, const Cell_label& label)
|
||||
{
|
||||
os << static_cast<std::underlying_type<Cell_label>::type>(label);
|
||||
return os;
|
||||
}
|
||||
|
||||
template < typename GT,
|
||||
typename Cb = CGAL::Delaunay_triangulation_cell_base_with_circumcenter_3<GT> >
|
||||
class Alpha_wrap_triangulation_cell_base_3
|
||||
|
|
|
|||
|
|
@ -145,7 +145,11 @@ public:
|
|||
bool empty() const { return m_tree_ptr->empty(); }
|
||||
bool do_call() const { return (!empty() || base().do_call()); }
|
||||
|
||||
void clear() { m_tree_ptr->clear() && base().clear(); }
|
||||
void clear()
|
||||
{
|
||||
m_tree_ptr->clear();
|
||||
base().clear();
|
||||
}
|
||||
|
||||
public:
|
||||
typename AABB_tree::Bounding_box bbox() const
|
||||
|
|
|
|||
|
|
@ -97,12 +97,22 @@ public:
|
|||
{ }
|
||||
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
m_points_ptr->clear();
|
||||
Oracle_base::clear();
|
||||
}
|
||||
|
||||
// adds a range of points to the oracle
|
||||
template <typename PointRange,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_point_set(const PointRange& points,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
void add_points(const PointRange& points,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB tree (points)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(points.empty())
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
|
|
@ -114,20 +124,18 @@ public:
|
|||
const std::size_t old_size = m_points_ptr->size();
|
||||
m_points_ptr->insert(std::cend(*m_points_ptr), std::cbegin(points), std::cend(points));
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB tree (points)..." << std::endl;
|
||||
#endif
|
||||
|
||||
this->tree().insert(std::next(std::cbegin(*m_points_ptr), old_size), std::cend(*m_points_ptr));
|
||||
this->tree().rebuild(std::cbegin(*m_points_ptr), std::cend(*m_points_ptr));
|
||||
|
||||
// Manually constructing it here purely for profiling reasons: if we keep the lazy approach,
|
||||
// it will be done at the first treatment of a facet that needs a Steiner point.
|
||||
// So if one wanted to bench the flood fill runtime, it would be skewed by the time it takes
|
||||
// to accelerate the tree.
|
||||
this->tree().accelerate_distance_queries();
|
||||
|
||||
CGAL_postcondition(this->tree().size() == m_points_ptr->size());
|
||||
}
|
||||
|
||||
#ifdef CGAL_AW2_DEBUG
|
||||
std::cout << "PS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
|
|
|||
|
|
@ -99,11 +99,21 @@ public:
|
|||
{ }
|
||||
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
m_segments_ptr->clear();
|
||||
Oracle_base::clear();
|
||||
}
|
||||
|
||||
template <typename SegmentRange,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_segment_soup(const SegmentRange& segments,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
void add_segments(const SegmentRange& segments,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB Tree (" << segments.size() << " segments)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(segments.empty())
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
|
|
@ -129,10 +139,7 @@ public:
|
|||
m_segments_ptr->push_back(s);
|
||||
}
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB tree (segments)..." << std::endl;
|
||||
#endif
|
||||
this->tree().insert(std::next(std::cbegin(*m_segments_ptr), old_size), std::cend(*m_segments_ptr));
|
||||
this->tree().rebuild(std::cbegin(*m_segments_ptr), std::cend(*m_segments_ptr));
|
||||
|
||||
// Manually constructing it here purely for profiling reasons: if we keep the lazy approach,
|
||||
// it will be done at the first treatment of a facet that needs a Steiner point.
|
||||
|
|
@ -140,7 +147,9 @@ public:
|
|||
// to accelerate the tree.
|
||||
this->tree().accelerate_distance_queries();
|
||||
|
||||
CGAL_postcondition(this->tree().size() == m_segments_ptr->size());
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "SS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,10 @@ public:
|
|||
using VPM = typename GetVertexPointMap<TriangleMesh>::const_type;
|
||||
using Point_ref = typename boost::property_traits<VPM>::reference;
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB tree (" << faces(tmesh).size() << " faces)..." << std::endl;
|
||||
#endif
|
||||
|
||||
CGAL_precondition(CGAL::is_triangle_mesh(tmesh));
|
||||
|
||||
if(is_empty(tmesh))
|
||||
|
|
@ -140,10 +144,6 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB tree (faces)..." << std::endl;
|
||||
#endif
|
||||
|
||||
VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
|
||||
get_const_property_map(vertex_point, tmesh));
|
||||
static_assert(std::is_same<typename boost::property_traits<VPM>::value_type, Point_3>::value);
|
||||
|
|
@ -176,7 +176,7 @@ public:
|
|||
this->tree().accelerate_distance_queries();
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Tree: " << this->tree().size() << " primitives (" << num_faces(tmesh) << " faces in input)" << std::endl;
|
||||
std::cout << "Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -130,6 +130,10 @@ public:
|
|||
|
||||
using Face = typename boost::range_value<FaceRange>::type;
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB Tree (" << faces.size() << " faces)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(points.empty() || faces.empty())
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
|
|
@ -138,10 +142,6 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB Tree (triangles)..." << std::endl;
|
||||
#endif
|
||||
|
||||
PPM pm = choose_parameter<PPM>(get_parameter(np, internal_np::point_map));
|
||||
static_assert(std::is_same<typename boost::property_traits<PPM>::value_type, Point_3>::value);
|
||||
|
||||
|
|
@ -181,15 +181,19 @@ public:
|
|||
this->tree().accelerate_distance_queries();
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Tree: " << this->tree().size() << " primitives (" << faces.size() << " faces in input)" << std::endl;
|
||||
std::cout << "TS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename TriangleRange,
|
||||
typename CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
void add_triangle_soup(const TriangleRange& triangles,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
void add_triangles(const TriangleRange& triangles,
|
||||
const CGAL_NP_CLASS& /*np*/ = CGAL::parameters::default_values())
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB Tree (" << triangles.size() << " triangles)..." << std::endl;
|
||||
#endif
|
||||
|
||||
if(triangles.empty())
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
|
|
@ -198,10 +202,6 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "Insert into AABB Tree (triangles)..." << std::endl;
|
||||
#endif
|
||||
|
||||
typename Geom_traits::Is_degenerate_3 is_degenerate = this->geom_traits().is_degenerate_3_object();
|
||||
|
||||
Splitter_base::reserve(triangles.size());
|
||||
|
|
@ -218,6 +218,9 @@ public:
|
|||
|
||||
Splitter_base::split_and_insert_datum(tr, this->tree(), this->geom_traits());
|
||||
}
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cout << "TS Tree: " << this->tree().size() << " primitives" << std::endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,16 +22,47 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
namespace CGAL {
|
||||
namespace Alpha_wraps_3 {
|
||||
namespace internal {
|
||||
|
||||
enum class Steiner_status
|
||||
{
|
||||
UNKONWN = 0,
|
||||
NO_STEINER_POINT,
|
||||
RULE_1,
|
||||
RULE_2
|
||||
};
|
||||
|
||||
#ifdef CGAL_AW2_COMPUTE_AND_STORE_STEINER_INFO_AT_GATE_CREATION
|
||||
template <typename Tr>
|
||||
struct Gate_steiner_info
|
||||
{
|
||||
using Point_3 = typename Tr::Geom_traits::Point_3;
|
||||
|
||||
Steiner_status m_steiner_status = Steiner_status::UNKNOWN;
|
||||
std::optional<Point_3> m_steiner_point = std::nullopt;
|
||||
|
||||
bool has_steiner_point() const { return m_steiner_point.has_value(); }
|
||||
bool has_steiner_from_intersection() const { return (m_steiner_status == Steiner_status::RULE_1); }
|
||||
bool has_steiner_from_projection() const { return (m_steiner_status == Steiner_status::RULE_2); }
|
||||
const Point_3& steiner_point() const {
|
||||
CGAL_precondition(has_steiner_point());
|
||||
return *m_steiner_point;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CGAL_AW3_USE_SORTED_PRIORITY_QUEUE
|
||||
|
||||
// Represents an alpha-traversable facet in the mutable priority queue
|
||||
template <typename Tr>
|
||||
class Gate
|
||||
#ifdef CGAL_AW2_COMPUTE_AND_STORE_STEINER_INFO_AT_GATE_CREATION
|
||||
: public Gate_steiner_info<Tr>
|
||||
#endif
|
||||
{
|
||||
using Facet = typename Tr::Facet;
|
||||
using FT = typename Tr::Geom_traits::FT;
|
||||
|
|
@ -97,6 +128,9 @@ struct Less_gate
|
|||
// Represents an alpha-traversable facet in the mutable priority queue
|
||||
template <typename Tr>
|
||||
class Gate
|
||||
#ifdef CGAL_AW2_COMPUTE_AND_STORE_STEINER_INFO_AT_GATE_CREATION
|
||||
: public Gate_steiner_info<Tr>
|
||||
#endif
|
||||
{
|
||||
using Facet = typename Tr::Facet;
|
||||
using FT = typename Tr::Geom_traits::FT;
|
||||
|
|
|
|||
|
|
@ -65,20 +65,30 @@ bool has_degenerated_faces(const TriangleMesh& mesh,
|
|||
// Edge length is bounded by twice the circumradius
|
||||
template <typename TriangleMesh,
|
||||
typename NamedParameters = parameters::Default_named_parameters>
|
||||
bool check_edge_length(const TriangleMesh& output_mesh,
|
||||
const double alpha,
|
||||
const NamedParameters& np = CGAL::parameters::default_values())
|
||||
bool has_bounded_edge_length(const TriangleMesh& wrap,
|
||||
const double alpha,
|
||||
const NamedParameters& np = CGAL::parameters::default_values())
|
||||
{
|
||||
const auto sq_alpha_bound = 4 * square(alpha);
|
||||
for(auto e : edges(output_mesh))
|
||||
using Geom_traits = typename GetGeomTraits<TriangleMesh, NamedParameters>::type;
|
||||
using FT = typename Geom_traits::FT;
|
||||
using VPM = typename CGAL::GetVertexPointMap<PolygonMesh, NamedParameters>::type;
|
||||
|
||||
Geom_traits gt = choose_parameter<Geom_traits>(get_parameter(in_np, internal_np::geom_traits));
|
||||
VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
|
||||
get_const_property_map(vertex_point, tmesh));
|
||||
|
||||
const FT sq_alpha_bound = 4 * square(alpha);
|
||||
for(auto e : edges(wrap))
|
||||
{
|
||||
const auto sqd = Polygon_mesh_processing::squared_edge_length(e, output_mesh, np);
|
||||
if(sqd > sq_alpha_bound) // alpha is the circumradius
|
||||
if(gt.compare_squared_distance(get(vpm, source(e, wrap)),
|
||||
get(vpm, target(e, wrap)),
|
||||
sq_alpha_bound) == LARGER)
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
const FT sqd = Polygon_mesh_processing::squared_edge_length(e, wrap, np);
|
||||
std::cerr << "Error: " << sqd << " greater than " << sq_alpha_bound << std::endl;
|
||||
std::cerr << get(CGAL::vertex_point, output_mesh, source(e, output_mesh)) << std::endl;
|
||||
std::cerr << get(CGAL::vertex_point, output_mesh, target(e, output_mesh)) << std::endl;
|
||||
std::cerr << "source pt: " << get(vpm, source(e, wrap)) << std::endl;
|
||||
std::cerr << "target pt: " << get(vpm, target(e, wrap)) << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
|
@ -346,13 +356,12 @@ bool is_outer_wrap_of_point_set(const TriangleMesh& wrap,
|
|||
|
||||
CGAL::Side_of_triangle_mesh<TriangleMesh, K, OVPM> side_of_wrap(wrap, out_vpm);
|
||||
|
||||
// @speed a single vertex per CC would be sufficient
|
||||
for(const auto& p : points)
|
||||
{
|
||||
if(side_of_wrap(get(in_pm, p)) != CGAL::ON_BOUNDED_SIDE)
|
||||
{
|
||||
#ifdef CGAL_AW3_DEBUG
|
||||
std::cerr << "Part(s) of the input mesh are outside the wrap: " << get(in_pm, p) << std::endl;
|
||||
std::cerr << "An input point is outside the wrap: " << get(in_pm, p) << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ void alpha_wrap_3(const PointRange& points,
|
|||
Geom_traits gt = choose_parameter<Geom_traits>(get_parameter(in_np, internal_np::geom_traits));
|
||||
|
||||
Oracle oracle(gt);
|
||||
oracle.add_point_set(points, in_np);
|
||||
oracle.add_points(points, in_np);
|
||||
AW3 alpha_wrap_builder(oracle);
|
||||
alpha_wrap_builder(alpha, offset, alpha_wrap, in_np, out_np);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ void alpha_wrap_triangle_mesh(Mesh& input_mesh,
|
|||
}
|
||||
|
||||
if(!enforce_manifoldness)
|
||||
assert(AW3::internal::check_edge_length(wrap, alpha));
|
||||
assert(AW3::internal::has_bounded_edge_length(wrap, alpha));
|
||||
}
|
||||
|
||||
void alpha_wrap_triangle_mesh(Mesh& input_mesh,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ void alpha_wrap_triangle_manifoldness(Mesh& input_mesh,
|
|||
assert(AW3::internal::has_expected_Hausdorff_distance(nm_wrap, input_mesh, alpha, offset));
|
||||
}
|
||||
|
||||
assert(AW3::internal::check_edge_length(nm_wrap, alpha));
|
||||
assert(AW3::internal::has_bounded_edge_length(nm_wrap, alpha));
|
||||
|
||||
FT base_vol = 0;
|
||||
if(!is_closed(nm_wrap))
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ void alpha_wrap_triangle_soup(Points& pr,
|
|||
|
||||
// AW3
|
||||
Oracle oracle;
|
||||
oracle.add_triangle_soup(pr, fr);
|
||||
oracle.add_triangles(pr, fr);
|
||||
AW3::internal::Alpha_wrapper_3<Oracle> aw3(oracle);
|
||||
|
||||
Mesh wrap;
|
||||
|
|
@ -65,7 +65,7 @@ void alpha_wrap_triangle_soup(Points& pr,
|
|||
assert(AW3::internal::is_valid_wrap(wrap, false /*manifoldness*/));
|
||||
assert(AW3::internal::is_outer_wrap_of_triangle_soup(wrap, pr, fr));
|
||||
assert(AW3::internal::has_expected_Hausdorff_distance(wrap, input_mesh, alpha, offset));
|
||||
assert(AW3::internal::check_edge_length(wrap, alpha));
|
||||
assert(AW3::internal::has_bounded_edge_length(wrap, alpha));
|
||||
|
||||
alpha *= 2;
|
||||
offset *= 2;
|
||||
|
|
@ -83,7 +83,7 @@ void alpha_wrap_triangle_soup(Points& pr,
|
|||
assert(AW3::internal::is_valid_wrap(wrap_2, false /*manifoldness*/));
|
||||
assert(AW3::internal::is_outer_wrap_of_triangle_soup(wrap_2, pr, fr));
|
||||
assert(AW3::internal::has_expected_Hausdorff_distance(wrap_2, input_mesh, alpha, offset));
|
||||
assert(AW3::internal::check_edge_length(wrap_2, alpha));
|
||||
assert(AW3::internal::has_bounded_edge_length(wrap_2, alpha));
|
||||
}
|
||||
|
||||
void alpha_wrap_triangle_soup(const std::string& filename)
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ void alpha_wrap_triangle_mesh(Mesh& input_mesh,
|
|||
}
|
||||
|
||||
if(!enforce_manifoldness)
|
||||
assert(AW3::internal::check_edge_length(wrap, alpha));
|
||||
assert(AW3::internal::has_bounded_edge_length(wrap, alpha));
|
||||
}
|
||||
|
||||
void alpha_wrap_triangle_mesh(const std::string& filename,
|
||||
|
|
|
|||
|
|
@ -656,6 +656,42 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename K>
|
||||
class Compare_squared_radius_2
|
||||
{
|
||||
typedef typename K::Comparison_result Comparison_result;
|
||||
typedef typename K::Point_2 Point_2;
|
||||
typedef typename K::FT FT;
|
||||
|
||||
public:
|
||||
Comparison_result
|
||||
operator()(const Point_2& p, const Point_2& q, const Point_2& r, const FT& ft) const
|
||||
{
|
||||
FT num, den;
|
||||
squared_radiusC2(p.x(), p.y(),
|
||||
q.x(), q.y(),
|
||||
r.x(), r.y(),
|
||||
num, den);
|
||||
return CGAL::compare(num, den * ft);
|
||||
}
|
||||
|
||||
Comparison_result
|
||||
operator()(const Point_2& p, const Point_2& q, const FT& ft) const
|
||||
{
|
||||
FT num, den;
|
||||
squared_radiusC2(p.x(), p.y(),
|
||||
q.x(), q.y(),
|
||||
num, den);
|
||||
return CGAL::compare(num, den * ft);
|
||||
}
|
||||
|
||||
Comparison_result
|
||||
operator()(const Point_2&, const FT& ft) const
|
||||
{
|
||||
return - CGAL_NTS sign(ft);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K>
|
||||
class Compare_squared_radius_3
|
||||
{
|
||||
|
|
@ -1178,11 +1214,24 @@ public:
|
|||
|
||||
FT
|
||||
operator()( const Point_2& p, const Point_2& q) const
|
||||
{ return squared_radiusC2(p.x(), p.y(), q.x(), q.y()); }
|
||||
{
|
||||
FT num, den;
|
||||
squared_radiusC2(p.x(), p.y(),
|
||||
q.x(), q.y(),
|
||||
num, den);
|
||||
return num / den;
|
||||
}
|
||||
|
||||
FT
|
||||
operator()( const Point_2& p, const Point_2& q, const Point_2& r) const
|
||||
{ return squared_radiusC2(p.x(), p.y(), q.x(), q.y(), r.x(), r.y()); }
|
||||
{
|
||||
FT num, den;
|
||||
squared_radiusC2(p.x(), p.y(),
|
||||
q.x(), q.y(),
|
||||
r.x(), r.y(),
|
||||
num, den);
|
||||
return num / den;
|
||||
}
|
||||
};
|
||||
|
||||
} //namespace CartesianKernelFunctors
|
||||
|
|
@ -3128,6 +3177,10 @@ public:
|
|||
Point_2
|
||||
operator()(const Triangle_2& t, const Point_2& p) const
|
||||
{ return CommonKernelFunctors::Construct_projected_point_2<K>()(t, p, K()); }
|
||||
|
||||
const Point_2&
|
||||
operator()(const Point_2& p, const Point_2& q) const
|
||||
{ return CommonKernelFunctors::Construct_projected_point_2<K>()(p, q, K()); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,25 @@ midpointC2( const FT &px, const FT &py,
|
|||
y = (py+qy) / 2;
|
||||
}
|
||||
|
||||
template < class FT >
|
||||
CGAL_KERNEL_LARGE_INLINE
|
||||
void
|
||||
determinants_for_circumcenterC2(const FT &px, const FT &py,
|
||||
const FT &qx, const FT &qy,
|
||||
const FT &rx, const FT &ry,
|
||||
FT &num_x, FT &num_y, FT& den)
|
||||
{
|
||||
FT dqx = qx - px;
|
||||
FT dqy = qy - py;
|
||||
FT drx = rx - px;
|
||||
FT dry = ry - py;
|
||||
FT r2 = CGAL_NTS square(drx) + CGAL_NTS square(dry);
|
||||
FT q2 = CGAL_NTS square(dqx) + CGAL_NTS square(dqy);
|
||||
den = determinant(dqx, dqy, drx, dry);
|
||||
num_x = determinant(dry, dqy, r2, q2);
|
||||
num_y = determinant(drx, dqx, r2, q2);
|
||||
}
|
||||
|
||||
template < class FT >
|
||||
CGAL_KERNEL_LARGE_INLINE
|
||||
void
|
||||
|
|
@ -338,47 +357,49 @@ line_project_pointC2(const FT &la, const FT &lb, const FT &lc,
|
|||
|
||||
template < class FT >
|
||||
CGAL_KERNEL_MEDIUM_INLINE
|
||||
FT
|
||||
void
|
||||
squared_radiusC2(const FT &px, const FT &py,
|
||||
const FT &qx, const FT &qy,
|
||||
const FT &rx, const FT &ry,
|
||||
FT &x, FT &y )
|
||||
FT &num, FT &den)
|
||||
{
|
||||
circumcenter_translateC2(qx-px, qy-py, rx-px, ry-py, x, y);
|
||||
FT r2 = CGAL_NTS square(x) + CGAL_NTS square(y);
|
||||
x += px;
|
||||
y += py;
|
||||
return r2;
|
||||
}
|
||||
// Translate r to origin to simplify the expression.
|
||||
FT prx = px - rx;
|
||||
FT pry = py - ry;
|
||||
FT pr2 = CGAL_NTS square(prx) + CGAL_NTS square(pry);
|
||||
FT qrx = qx - rx;
|
||||
FT qry = qy - ry;
|
||||
FT qr2 = CGAL_NTS square(qrx) + CGAL_NTS square(qry);
|
||||
|
||||
template < class FT >
|
||||
CGAL_KERNEL_MEDIUM_INLINE
|
||||
FT
|
||||
squared_radiusC2(const FT &px, const FT &py,
|
||||
const FT &qx, const FT &qy,
|
||||
const FT &rx, const FT &ry)
|
||||
{
|
||||
FT x, y;
|
||||
circumcenter_translateC2<FT>(qx-px, qy-py, rx-px, ry-py, x, y);
|
||||
return CGAL_NTS square(x) + CGAL_NTS square(y);
|
||||
FT num_x = determinant(qry, qr2,
|
||||
pry, pr2);
|
||||
FT num_y = determinant(qrx, qr2,
|
||||
prx, pr2);
|
||||
FT dden = determinant(qrx, qry,
|
||||
prx, pry);
|
||||
|
||||
num = CGAL_NTS square(num_x) + CGAL_NTS square(num_y);
|
||||
den = CGAL_NTS square(2 * dden);
|
||||
}
|
||||
|
||||
template < class FT >
|
||||
inline
|
||||
FT
|
||||
squared_distanceC2( const FT &px, const FT &py,
|
||||
const FT &qx, const FT &qy)
|
||||
squared_distanceC2(const FT &px, const FT &py,
|
||||
const FT &qx, const FT &qy)
|
||||
{
|
||||
return CGAL_NTS square(px-qx) + CGAL_NTS square(py-qy);
|
||||
}
|
||||
|
||||
template < class FT >
|
||||
inline
|
||||
FT
|
||||
void
|
||||
squared_radiusC2(const FT &px, const FT &py,
|
||||
const FT &qx, const FT &qy)
|
||||
const FT &qx, const FT &qy,
|
||||
FT &num, FT &den)
|
||||
{
|
||||
return squared_distanceC2(px, py,qx, qy) / 4;
|
||||
num = squared_distanceC2(px, py,qx, qy);
|
||||
den = 4;
|
||||
}
|
||||
|
||||
template < class FT >
|
||||
|
|
@ -412,6 +433,26 @@ scaled_distance_to_lineC2( const FT &px, const FT &py,
|
|||
return determinant<FT>(px-rx, py-ry, qx-rx, qy-ry);
|
||||
}
|
||||
|
||||
template < class RT >
|
||||
void
|
||||
determinants_for_weighted_circumcenterC2(const RT &px, const RT &py, const RT &pw,
|
||||
const RT &qx, const RT &qy, const RT &qw,
|
||||
const RT &rx, const RT &ry, const RT &rw,
|
||||
RT &num_x, RT &num_y, RT& den)
|
||||
{
|
||||
RT dqx = qx - px;
|
||||
RT dqy = qy - py;
|
||||
RT dqw = qw - pw;
|
||||
RT drx = rx - px;
|
||||
RT dry = ry - py;
|
||||
RT drw = rw - pw;
|
||||
RT r2 = CGAL_NTS square(drx) + CGAL_NTS square(dry) - drw;
|
||||
RT q2 = CGAL_NTS square(dqx) + CGAL_NTS square(dqy) - dqw;
|
||||
den = determinant(dqx, dqy, drx, dry);
|
||||
num_x = determinant(dry, dqy, r2, q2);
|
||||
num_y = determinant(drx, dqx, r2, q2);
|
||||
}
|
||||
|
||||
template < class RT >
|
||||
void
|
||||
weighted_circumcenter_translateC2(const RT &dqx, const RT &dqy, const RT &dqw,
|
||||
|
|
@ -438,7 +479,6 @@ weighted_circumcenter_translateC2(const RT &dqx, const RT &dqy, const RT &dqw,
|
|||
dcy = - determinant (drx, dqx, r2, q2) / den;
|
||||
}
|
||||
|
||||
//template < class RT >
|
||||
template < class RT, class We>
|
||||
void
|
||||
weighted_circumcenterC2( const RT &px, const RT &py, const We &pw,
|
||||
|
|
@ -454,6 +494,40 @@ weighted_circumcenterC2( const RT &px, const RT &py, const We &pw,
|
|||
y += py;
|
||||
}
|
||||
|
||||
template < class RT >
|
||||
void
|
||||
weighted_circumcenter_translateC2(const RT &dqx, const RT &dqy, const RT &dqw,
|
||||
RT &dcx, RT &dcy)
|
||||
{
|
||||
// Given 2 points P, Q, this function takes as input:
|
||||
// qx-px, qy-py,qw-pw. And returns cx-px, cy-py,
|
||||
// where (cx, cy) are the coordinates of the circumcenter C.
|
||||
|
||||
// What we do is intersect the radical axis
|
||||
RT dqx2 = CGAL_NTS square(dqx);
|
||||
RT dqy2 = CGAL_NTS square(dqy);
|
||||
RT q2 = dqx2 + dqy2 - dqw;
|
||||
|
||||
RT den = RT(2) * dqx2 + RT(2) * dqy2;
|
||||
CGAL_assertion ( den != RT(0) );
|
||||
|
||||
dcx = determinant (dqy, q2, dqw) / den;
|
||||
dcy = - determinant (dqx, q2, dqw) / den;
|
||||
}
|
||||
|
||||
template < class RT, class We>
|
||||
void
|
||||
weighted_circumcenterC2(const RT &px, const RT &py, const We &pw,
|
||||
const RT &qx, const RT &qy, const We &qw,
|
||||
RT &x, RT &y)
|
||||
{
|
||||
RT dqw = RT(qw-pw);
|
||||
|
||||
weighted_circumcenter_translateC2<RT>(qx-px, qy-py, dqw, x, y);
|
||||
x += px;
|
||||
y += py;
|
||||
}
|
||||
|
||||
template< class FT >
|
||||
FT
|
||||
power_productC2(const FT &px, const FT &py, const FT &pw,
|
||||
|
|
|
|||
|
|
@ -612,7 +612,7 @@ weighted_circumcenterC3(const FT &px, const FT &py, const FT &pz, const FT &pw,
|
|||
qx, qy, qz, qw,
|
||||
rx, ry, rz, rw,
|
||||
sx, sy, sz, sw,
|
||||
num_x, num_y, num_z,den);
|
||||
num_x, num_y, num_z, den);
|
||||
|
||||
CGAL_assertion( ! CGAL_NTS is_zero(den) );
|
||||
FT inv = FT(1)/(FT(2) * den);
|
||||
|
|
@ -636,7 +636,7 @@ weighted_circumcenterC3(const FT &px, const FT &py, const FT &pz, const FT &pw,
|
|||
qx, qy, qz, qw,
|
||||
rx, ry, rz, rw,
|
||||
sx, sy, sz, sw,
|
||||
num_x, num_y, num_z, den);
|
||||
num_x, num_y, num_z, den);
|
||||
|
||||
CGAL_assertion( ! CGAL_NTS is_zero(den) );
|
||||
FT inv = FT(1)/(FT(2) * den);
|
||||
|
|
|
|||