diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index a5bd3863f5f..850c3ad3764 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -84,6 +84,17 @@ CGAL_add_named_parameter(use_angle_smoothing_t, use_angle_smoothing, use_angle_s CGAL_add_named_parameter(use_area_smoothing_t, use_area_smoothing, use_area_smoothing) CGAL_add_named_parameter(use_Delaunay_flips_t, use_Delaunay_flips, use_Delaunay_flips) CGAL_add_named_parameter(do_project_t, do_project, do_project) +CGAL_add_named_parameter(do_orientation_tests_t, do_orientation_tests, do_orientation_tests) +CGAL_add_named_parameter(do_self_intersection_tests_t, do_self_intersection_tests, do_self_intersection_tests) +CGAL_add_named_parameter(error_codes_t, error_codes, error_codes) +CGAL_add_named_parameter(volume_inclusions_t, volume_inclusions, volume_inclusions) +CGAL_add_named_parameter(face_cc_map_t, face_connected_component_map, face_connected_component_map) +CGAL_add_named_parameter(ccid_to_vid_vector_t, connected_component_id_to_volume_id, connected_component_id_to_volume_id) +CGAL_add_named_parameter(is_cc_outward_oriented_bs_t, is_cc_outward_oriented, is_cc_outward_oriented); +CGAL_add_named_parameter(intersecting_volume_pairs_t, intersecting_volume_pairs_output_iterator, intersecting_volume_pairs_output_iterator); +CGAL_add_named_parameter(i_used_as_a_predicate_t, i_used_as_a_predicate, i_used_as_a_predicate); +CGAL_add_named_parameter(nesting_levels_t, nesting_levels, nesting_levels); +CGAL_add_named_parameter(i_used_for_volume_orientation_t, i_used_for_volume_orientation, i_used_for_volume_orientation); CGAL_add_named_parameter(area_threshold_t, area_threshold, area_threshold) CGAL_add_named_parameter(volume_threshold_t, volume_threshold, volume_threshold) CGAL_add_named_parameter(dry_run_t, dry_run, dry_run) diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index 5aa0021d0b3..dd88f358857 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -180,6 +180,19 @@ void test(const NamedParameters& np) check_same_type<54>(get_parameter(np, CGAL::internal_np::use_area_smoothing)); check_same_type<55>(get_parameter(np, CGAL::internal_np::use_Delaunay_flips)); check_same_type<56>(get_parameter(np, CGAL::internal_np::use_safety_constraints)); + + check_same_type<12340>(get_parameter(np, CGAL::internal_np::do_self_intersection_tests)); + check_same_type<12341>(get_parameter(np, CGAL::internal_np::do_orientation_tests)); + check_same_type<12342>(get_parameter(np, CGAL::internal_np::error_codes)); + check_same_type<12343>(get_parameter(np, CGAL::internal_np::volume_inclusions)); + check_same_type<12344>(get_parameter(np, CGAL::internal_np::face_connected_component_map)); + check_same_type<12345>(get_parameter(np, CGAL::internal_np::connected_component_id_to_volume_id)); + check_same_type<12346>(get_parameter(np, CGAL::internal_np::is_cc_outward_oriented)); + check_same_type<12347>(get_parameter(np, CGAL::internal_np::intersecting_volume_pairs_output_iterator)); + check_same_type<12348>(get_parameter(np, CGAL::internal_np::i_used_as_a_predicate)); + check_same_type<12349>(get_parameter(np, CGAL::internal_np::nesting_levels)); + check_same_type<12350>(get_parameter(np, CGAL::internal_np::i_used_for_volume_orientation)); + check_same_type<57>(get_parameter(np, CGAL::internal_np::area_threshold)); check_same_type<58>(get_parameter(np, CGAL::internal_np::volume_threshold)); check_same_type<59>(get_parameter(np, CGAL::internal_np::dry_run)); @@ -312,6 +325,17 @@ int main() .use_area_smoothing(A<54>(54)) .use_Delaunay_flips(A<55>(55)) .use_safety_constraints(A<56>(56)) + .do_self_intersection_tests(A<12340>(12340)) + .do_orientation_tests(A<12341>(12341)) + .error_codes(A<12342>(12342)) + .volume_inclusions(A<12343>(12343)) + .face_connected_component_map(A<12344>(12344)) + .connected_component_id_to_volume_id(A<12345>(12345)) + .is_cc_outward_oriented(A<12346>(12346)) + .intersecting_volume_pairs_output_iterator(A<12347>(12347)) + .i_used_as_a_predicate(A<12348>(12348)) + .nesting_levels(A<12349>(12349)) + .i_used_for_volume_orientation(A<12350>(12350)) .area_threshold(A<57>(57)) .volume_threshold(A<58>(58)) .dry_run(A<59>(59)) diff --git a/GraphicsView/include/CGAL/Buffer_for_vao.h b/GraphicsView/include/CGAL/Buffer_for_vao.h index 932cb56f784..41e92e460df 100644 --- a/GraphicsView/include/CGAL/Buffer_for_vao.h +++ b/GraphicsView/include/CGAL/Buffer_for_vao.h @@ -460,9 +460,9 @@ public: static void add_point_in_buffer(const KPoint& kp, std::vector& buffer) { Local_point p=get_local_point(kp); - buffer.push_back(p.x()); - buffer.push_back(p.y()); - buffer.push_back(p.z()); + buffer.push_back(static_cast(p.x())); + buffer.push_back(static_cast(p.y())); + buffer.push_back(static_cast(p.z())); } /// adds `kv` coordinates to `buffer` @@ -471,9 +471,9 @@ public: bool inverse_normal=false) { Local_vector n=(inverse_normal?-get_local_vector(kv):get_local_vector(kv)); - buffer.push_back(n.x()); - buffer.push_back(n.y()); - buffer.push_back(n.z()); + buffer.push_back(static_cast(n.x())); + buffer.push_back(static_cast(n.y())); + buffer.push_back(static_cast(n.z())); } ///adds `acolor` RGB components to `buffer` diff --git a/GraphicsView/include/CGAL/Qt/manipulatedFrame_impl.h b/GraphicsView/include/CGAL/Qt/manipulatedFrame_impl.h index 617f1df0f7d..96d03db9adc 100644 --- a/GraphicsView/include/CGAL/Qt/manipulatedFrame_impl.h +++ b/GraphicsView/include/CGAL/Qt/manipulatedFrame_impl.h @@ -251,7 +251,7 @@ CGAL_INLINE_FUNCTION void ManipulatedFrame::computeMouseSpeed(const QMouseEvent *const e) { const QPoint delta = (e->pos() - prevPos_); const qreal dist = sqrt(qreal(delta.x() * delta.x() + delta.y() * delta.y())); - delay_ = last_move_time.restart(); + delay_ = static_cast(last_move_time.restart()); if (delay_ == 0) // Less than a millisecond: assume delay = 1ms mouseSpeed_ = dist; diff --git a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h index 9ff0b99157f..b097030d1ea 100644 --- a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h +++ b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h @@ -2248,7 +2248,7 @@ void CGAL::QGLViewer::keyPressEvent(QKeyEvent *e) { static QElapsedTimer doublePress; if (modifiers == playPathKeyboardModifiers()) { - int elapsed = doublePress.restart(); + qint64 elapsed = doublePress.restart(); if ((elapsed < 250) && (index == previousPathId_)) camera()->resetPath(index); else { @@ -2263,7 +2263,7 @@ void CGAL::QGLViewer::keyPressEvent(QKeyEvent *e) { } previousPathId_ = index; } else if (modifiers == addKeyFrameKeyboardModifiers()) { - int elapsed = doublePress.restart(); + qint64 elapsed = doublePress.restart(); if ((elapsed < 250) && (index == previousPathId_)) { if (camera()->keyFrameInterpolator(index)) { disconnect(camera()->keyFrameInterpolator(index), diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 957ffd4cfa8..fcd93c9ac8d 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -30,6 +30,9 @@ Release date: June 2020 - Introduced a new function, `CGAL::Polygon_mesh_processing::remove_connected_components_of_negligible_size()`, which can be used to remove connected components whose area or volume is under a certain threshold. Area and volume thresholds are either specified by the user or deduced from the bounding box of the mesh. +- Added the function `CGAL::Polygon_mesh_processing::volume_connected_component()` that can be used to + get information about the nesting of the connected components of a given triangle mesh and about + the volumes defined. - Added a new named parameter for `keep_large_connected_components()` and `remove_connected_components_of_negligible_size` that can be used to perform a dry run of the operation, meaning that the function will return the number of connected components that would be removed with the specified threshold, but without actually removing them. diff --git a/Intersections_2/include/CGAL/Intersection_traits_2.h b/Intersections_2/include/CGAL/Intersection_traits_2.h index d83740ede75..d87c599548c 100644 --- a/Intersections_2/include/CGAL/Intersection_traits_2.h +++ b/Intersections_2/include/CGAL/Intersection_traits_2.h @@ -105,6 +105,28 @@ template struct Intersection_traits : public Intersection_traits {}; +template +struct Intersection_traits { + typedef typename Intersection_traits::result_type result_type; +}; + +template +struct Intersection_traits { + typedef typename Intersection_traits::result_type result_type; +}; + +template +struct Intersection_traits { + typedef typename boost::variant variant_type; + typedef boost::optional result_type; +}; + +template +struct Intersection_traits { + typedef typename boost::variant variant_type; + typedef boost::optional result_type; +}; + } // namespace CGAL #endif /* CGAL_INTERSECTION_TRAITS_2_H */ diff --git a/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Bbox_2.h b/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Bbox_2.h new file mode 100644 index 00000000000..9b75f968c2e --- /dev/null +++ b/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Bbox_2.h @@ -0,0 +1,50 @@ +// Copyright (c) 2019 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno + +#ifndef CGAL_INTERSECTIONS_BBOX_2_BBOX_2_H +#define CGAL_INTERSECTIONS_BBOX_2_BBOX_2_H + +#include + +namespace CGAL { + +bool +inline +do_intersect(const CGAL::Bbox_2& c, + const CGAL::Bbox_2& bbox) +{ + return CGAL::do_overlap(c, bbox); +} + +typename boost::optional< typename boost::variant > +inline +intersection(const CGAL::Bbox_2& a, + const CGAL::Bbox_2& b) +{ + typedef typename boost::variant variant_type; + typedef typename boost::optional result_type; + + if(!do_intersect(a, b)) + return result_type(); + + double xmin = (std::max)(a.xmin(), b.xmin()); + double xmax = (std::min)(a.xmax(), b.xmax()); + + double ymin = (std::max)(a.ymin(), b.ymin()); + double ymax = (std::min)(a.ymax(), b.ymax()); + + return result_type(std::forward(Bbox_2(xmin, ymin, xmax, ymax))); +} + +} // namespace CGAL + +#endif // CGAL_INTERSECTIONS_BBOX_2_BBOX_2_H diff --git a/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Iso_rectangle_2.h b/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Iso_rectangle_2.h new file mode 100644 index 00000000000..1ada75030d3 --- /dev/null +++ b/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Iso_rectangle_2.h @@ -0,0 +1,56 @@ +// Copyright (c) 2019 +// GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno + + +#ifndef CGAL_INTERSECTIONS_BBOX_2_ISO_RECTANGLE_2_H +#define CGAL_INTERSECTIONS_BBOX_2_ISO_RECTANGLE_2_H + +#include +#include +#include + +namespace CGAL { + +template +inline bool do_intersect(const Iso_rectangle_2 &rect, + const Bbox_2 &box) +{ + return do_intersect(K::Iso_rectangle_2(box), rect); +} + +template +inline bool do_intersect(const Bbox_2 &box, + const Iso_rectangle_2 &rect) +{ + return do_intersect(rect, box); +} + +template +typename Intersection_traits::result_type +intersection(const Bbox_2& box, + const Iso_rectangle_2& rect) +{ + typename K::Iso_rectangle_2 rec(box.xmin(), box.ymin(), box.xmax(), box.ymax()); + return intersection(rec, rect); +} + +template +typename Intersection_traits::result_type +intersection(const Iso_rectangle_2& rect, + const Bbox_2& box) +{ + return intersection(box, rect); +} + +} // namespace CGAL + +#endif // CGAL_INTERSECTIONS_BBOX_2_ISO_RECTANGLE_2_H diff --git a/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Segment_2.h b/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Segment_2.h new file mode 100644 index 00000000000..1bb05d61c91 --- /dev/null +++ b/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Segment_2.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 +// GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno + + +#ifndef CGAL_INTERSECTIONS_BBOX_2_SEGMENT_2_H +#define CGAL_INTERSECTIONS_BBOX_2_SEGMENT_2_H + +#include +#include +#include + +namespace CGAL { + + +template +inline bool do_intersect( + const Segment_2 &seg, + const Bbox_2 &box) +{ + typename K::Iso_rectangle_2 rec(box.xmin(), box.ymin(), box.xmax(), box.ymax()); + return do_intersect(rec, seg); +} + +template +inline bool do_intersect( + const Bbox_2 &box, + const Segment_2 &seg) +{ + return do_intersect(seg, box); +} + +template +typename Intersection_traits::result_type +intersection(const CGAL::Bbox_2& box, + const Segment_2& seg) { + typename K::Iso_rectangle_2 rec(box.xmin(), box.ymin(), box.xmax(), box.ymax()); + return intersection(rec, seg); +} + +template +typename Intersection_traits::result_type +intersection(const Segment_2& seg, + const CGAL::Bbox_2& box) { + return intersection(box, seg); +} + +} +#endif // CGAL_INTERSECTIONS_BBOX_2_SEGMENT_2_H diff --git a/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Triangle_2.h b/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Triangle_2.h new file mode 100644 index 00000000000..854499c7151 --- /dev/null +++ b/Intersections_2/include/CGAL/Intersections_2/Bbox_2_Triangle_2.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 +// GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno + + +#ifndef CGAL_INTERSECTIONS_BBOX_2_TRIANGLE_2_H +#define CGAL_INTERSECTIONS_BBOX_2_TRIANGLE_2_H + +#include +#include +#include + +namespace CGAL { + + +template +inline bool do_intersect( + const Triangle_2 &tr, + const Bbox_2 &box) +{ + typename K::Iso_rectangle_2 rec(box.xmin(), box.ymin(), box.xmax(), box.ymax()); + return do_intersect(rec, tr); +} + +template +inline bool do_intersect( + const Bbox_2 &box, + const Triangle_2 &tr) +{ + return do_intersect(tr, box); +} + +template +typename Intersection_traits::result_type +intersection(const Bbox_2& box, + const Triangle_2& tr) { + typename K::Iso_rectangle_2 rec(box.xmin(), box.ymin(), box.xmax(), box.ymax()); + return intersection(rec, tr); +} + +template +typename Intersection_traits::result_type +intersection(const Triangle_2& tr, + const Bbox_2& box) { + return intersection(box, tr); +} + +} +#endif // CGAL_INTERSECTIONS_BBOX_2_TRIANGLE_2_H diff --git a/Intersections_2/include/CGAL/Intersections_2/Circle_2_Ray_2.h b/Intersections_2/include/CGAL/Intersections_2/Circle_2_Ray_2.h new file mode 100644 index 00000000000..6a4c9d8b25d --- /dev/null +++ b/Intersections_2/include/CGAL/Intersections_2/Circle_2_Ray_2.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 +// GeometryFactory. All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno + +#ifndef CGAL_INTERSECTIONS_2_CIRCLE_2_RAY_2_H +#define CGAL_INTERSECTIONS_2_CIRCLE_2_RAY_2_H + +#include +#include +#include +#include + +namespace CGAL { +namespace Intersections { +namespace internal { + +template +bool +do_intersect(const typename K::Circle_2 & c, + const typename K::Ray_2& r, + const K&) +{ + return squared_distance(c.center(), r) <= c.squared_radius(); +} + +template +bool +do_intersect(const typename K::Ray_2& r, + const typename K::Circle_2 & c, + const K&) +{ + return squared_distance(c.center(), r) <= c.squared_radius(); +} + +} // namespace internal +} // namespace Intersections + +CGAL_DO_INTERSECT_FUNCTION(Circle_2, Ray_2, 2) + +} // namespace CGAL +#endif // CGAL_INTERSECTIONS_2_CIRCLE_2_RAY_2_H diff --git a/Intersections_2/include/CGAL/Intersections_2/Circle_2_Segment_2.h b/Intersections_2/include/CGAL/Intersections_2/Circle_2_Segment_2.h new file mode 100644 index 00000000000..efed12af6e3 --- /dev/null +++ b/Intersections_2/include/CGAL/Intersections_2/Circle_2_Segment_2.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 +// GeometryFactory. All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno + +#ifndef CGAL_INTERSECTIONS_2_CIRCLE_2_SEGMENT_2_H +#define CGAL_INTERSECTIONS_2_CIRCLE_2_SEGMENT_2_H + +#include +#include +#include +#include + +namespace CGAL { +namespace Intersections { +namespace internal { + +template +bool +do_intersect(const typename K::Circle_2 & c, + const typename K::Segment_2& s, + const K&) +{ + return squared_distance(c.center(), s) <= c.squared_radius(); +} + +template +bool +do_intersect(const typename K::Segment_2& s, + const typename K::Circle_2 & c, + const K&) +{ + return squared_distance(c.center(), s) <= c.squared_radius(); +} + +} // namespace internal +} // namespace Intersections + +CGAL_DO_INTERSECT_FUNCTION(Circle_2, Segment_2, 2) + +} // namespace CGAL +#endif // CGAL_INTERSECTIONS_2_CIRCLE_2_SEGMENT_2_H diff --git a/Intersections_2/include/CGAL/Intersections_2/Circle_2_Triangle_2.h b/Intersections_2/include/CGAL/Intersections_2/Circle_2_Triangle_2.h new file mode 100644 index 00000000000..35cfdd3ac01 --- /dev/null +++ b/Intersections_2/include/CGAL/Intersections_2/Circle_2_Triangle_2.h @@ -0,0 +1,63 @@ +// Copyright (c) 2019 +// GeometryFactory. All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno + +#ifndef CGAL_INTERSECTIONS_2_CIRCLE_2_TRIANGLE_2_H +#define CGAL_INTERSECTIONS_2_CIRCLE_2_TRIANGLE_2_H + +#include +#include +#include +#include + +namespace CGAL { +namespace Intersections { +namespace internal { + +template +bool +do_intersect(const typename K::Circle_2 & c, + const typename K::Triangle_2& t, + const K&) +{ + char nb_close(0); +//if all three points are inside the circle, there is no intersection. + if(squared_distance(c.center(), t) < c.squared_radius()) + { + for(int i = 0; i< 3; ++i) + { + if(squared_distance(c.center(), t[i]) == c.squared_radius()) + return true; + if(squared_distance(c.center(), t[i]) < c.squared_radius()) + ++nb_close; + } + return (nb_close < 3); + } + else + return squared_distance(c.center(), t) == c.squared_radius(); +} + +template +bool +do_intersect(const typename K::Triangle_2& t, + const typename K::Circle_2 & c, + const K&) +{ + return do_intersect(c,t); +} + +} // namespace internal +} // namespace Intersections + +CGAL_DO_INTERSECT_FUNCTION(Circle_2, Triangle_2, 2) + +} // namespace CGAL +#endif // CGAL_INTERSECTIONS_2_CIRCLE_2_TRIANGLE_2_H diff --git a/Intersections_2/include/CGAL/Intersections_2/internal/Triangle_2_Triangle_2_intersection_impl.h b/Intersections_2/include/CGAL/Intersections_2/internal/Triangle_2_Triangle_2_intersection_impl.h index 76fe7cf9526..896c6bf5748 100644 --- a/Intersections_2/include/CGAL/Intersections_2/internal/Triangle_2_Triangle_2_intersection_impl.h +++ b/Intersections_2/include/CGAL/Intersections_2/internal/Triangle_2_Triangle_2_intersection_impl.h @@ -21,6 +21,7 @@ #include #include +#include namespace CGAL { @@ -277,12 +278,22 @@ typename K::Triangle_2 Triangle_2_Triangle_2_pair::intersection_triangle() const { typedef typename K::Triangle_2 Triangle_2; - if (!_known) - intersection_type(); - CGAL_kernel_assertion(_result == TRIANGLE); + if (!_known) + intersection_type(); + CGAL_kernel_assertion(_result == TRIANGLE); + if(CGAL::left_turn(_pointlist.first->point, + _pointlist.first->next->point, + _pointlist.first->next->next->point)) + { return Triangle_2(_pointlist.first->point, _pointlist.first->next->point, _pointlist.first->next->next->point); + } + else { + return Triangle_2(_pointlist.first->point, + _pointlist.first->next->next->point, + _pointlist.first->next->point); + } } template @@ -308,6 +319,29 @@ Triangle_2_Triangle_2_pair::intersection_point() const } +//algorithm taken from here : https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order +template +struct Is_cw +{ + bool operator()(const std::vector& ps) + { + typename K::FT res(0); + std::size_t length = ps.size(); + for(std::size_t i = 0; i 0; + } +}; + +template +struct Is_cw +{ + bool operator()(const std::vector& ps) + { + return CGAL::right_turn(ps[0], ps[1], ps[2]); + } +}; template typename CGAL::Intersection_traits @@ -316,27 +350,34 @@ intersection(const typename K::Triangle_2 &tr1, const typename K::Triangle_2 &tr2, const K&) { - typedef Triangle_2_Triangle_2_pair is_t; - is_t ispair(&tr1, &tr2); - switch (ispair.intersection_type()) { - case is_t::NO_INTERSECTION: + typedef Triangle_2_Triangle_2_pair Intersection_type; + Intersection_type ispair(&tr1, &tr2); + switch (ispair.intersection_type()) + { + case Intersection_type::NO_INTERSECTION: default: - return intersection_return(); - case is_t::POINT: - return intersection_return(ispair.intersection_point()); - case is_t::SEGMENT: - return intersection_return(ispair.intersection_segment()); - case is_t::TRIANGLE: - return intersection_return(ispair.intersection_triangle()); - case is_t::POLYGON: { - typedef std::vector Container; - Container points(ispair.vertex_count()); - for (int i =0; i < ispair.vertex_count(); i++) { - points[i] = ispair.vertex(i); - } - return intersection_return(points); - } + return intersection_return(); + case Intersection_type::POINT: + return intersection_return(ispair.intersection_point()); + case Intersection_type::SEGMENT: + return intersection_return(ispair.intersection_segment()); + case Intersection_type::TRIANGLE: + return intersection_return(ispair.intersection_triangle()); + case Intersection_type::POLYGON: + { + typedef std::vector Container; + Container points(ispair.vertex_count()); + for (int i =0; i < ispair.vertex_count(); i++) + points[i] = ispair.vertex(i); + + if(Is_cw::Is_exact>()(points)) + { + std::reverse(points.begin(), points.end()); + + } + return intersection_return(points); } + } } } // namespace internal diff --git a/Intersections_2/include/CGAL/intersection_2.h b/Intersections_2/include/CGAL/intersection_2.h index b6acfadc5c1..4b22e327369 100644 --- a/Intersections_2/include/CGAL/intersection_2.h +++ b/Intersections_2/include/CGAL/intersection_2.h @@ -28,6 +28,10 @@ #include #include #include +#include +#include +#include + #include #include diff --git a/Intersections_2/test/Intersections_2/test_intersections_2.cpp b/Intersections_2/test/Intersections_2/test_intersections_2.cpp index 2939c44bdef..7e0ff4ca84c 100644 --- a/Intersections_2/test/Intersections_2/test_intersections_2.cpp +++ b/Intersections_2/test/Intersections_2/test_intersections_2.cpp @@ -98,7 +98,9 @@ struct Test { if (p.size() != q.size()) return false; - + if (CGAL::Intersections::internal::Is_cw::Is_exact>()(p)) + return false; for(typename Pol::const_iterator itp = p.begin(), itq = q.begin(); itp != p.end(); ++itp, ++itq) if (!approx_equal(*itp, *itq)) return false; @@ -106,6 +108,20 @@ struct Test return true; } + bool approx_equal(const T& p, const T& q) + { + if(p.size() != q.size()) + return false; + + std::vector

vec { p[0], p[1], p[2] }; + + if (CGAL::Intersections::internal::Is_cw::Is_exact>(vec)) + return false; + + return approx_equal(p[0], q[0]) && approx_equal(p[1], q[1]) && approx_equal(p[2], q[2]); + } + template void check_no_do_intersect(const O1& o1, const O2& o2) { @@ -318,6 +334,48 @@ struct Test check_intersection

(C(p( 0, 0), 25) , p( 5, 0)); } + void C_S() + { + std::cout << "Circle - Segment" << std::endl; + + check_no_do_intersect (C(p( 2, 8), 6), S(p(-3, -2), p( 2, -4))); + check_no_do_intersect (C(p( 2, 8), 6), S(p(-3, 22), p( 2, 34))); + check_no_do_intersect (C(p( 4, 16), 18), S(p(5, 7), p(6, 8))); + + check_do_intersect (C(p( 3, 4), 0), S(p(-3, 8), p( 6, 2))); + check_do_intersect (C(p( 4, 3), 4), S(p( 6, -7), p( 5, 2))); + + check_do_intersect (C(p(-3, 1), 7), S(p(-1, -3), p(-6, 7))); + + check_do_intersect (C(p(1, 1), 4), S(p(-1, 1), p(-6, -8))); + check_do_intersect (C(p(1, 1), 4), S(p(-1, 4), p(-1, -4))); + } + + void C_R() + { + std::cout << "Circle - Ray" << std::endl; + + check_no_do_intersect (C(p( 2, 8), 6), R(p(-3, -2), p( 2, -4))); + check_no_do_intersect (C(p( 2, 8), 6), R(p(-3, 22), p( 2, 34))); + + check_do_intersect (C(p( 3, 4), 0), R(p(-3, 8), p( 6, 2))); + check_do_intersect (C(p( 4, 3), 4), R(p( 6, -7), p( 5, 2))); + + check_do_intersect (C(p(-3, 1), 7), R(p(-1, -3), p(-6, 7))); + } + + void C_T() + { + std::cout << "Circle - Triangle" << std::endl; + check_no_do_intersect (C(p( 2, 8), 6), T(p( 6, 0), p( 8, 0), p(8, 2))); + check_no_do_intersect (C(p( 0, 0), 9), T(p( 1, 1), p( 1, -1), p(0, 0))); + + check_do_intersect (C(p( 3, 4), 0), T(p(-3, 8), p( 6, 2), p(4,6))); + check_do_intersect (C(p( 4, 3), 4), T(p( 6, -7), p( 5, 2), p(2,-3))); + + check_do_intersect (C(p( 4, 3), 4), T(p( 6, -7), p( 4, 6), p(2,-3))); + } + void L_L() { std::cout << "Line - Line" << std::endl; @@ -478,18 +536,12 @@ struct Test // polygon intersection Pol pol0; - pol0.push_back(P(-6, -4)); - pol0.push_back(P( -5.11111, -0.222222 )); - pol0.push_back(P( 0, 10 )); pol0.push_back(P( 8, 4 )); + pol0.push_back(P( 0, 10 )); + pol0.push_back(P( -5.11111, -0.222222 )); + pol0.push_back(P(-6, -4)); check_intersection (T(p( 0, 10), p(-10, -10), p( 20, -5)), T(p( 2, 30), p( -6, -4), p(15, 8)), pol0, false); - - Pol pol1; - pol1.push_back(P( 8, 4)); - pol1.push_back(P( 0, 10 )); - pol1.push_back(P( -5.11111, -0.222222 )); - pol1.push_back(P(-6, -4)); - check_intersection (T(p( -10,-10), p( 0, 10), p( 20, -5)), T(p( 2, 30), p( -6, -4), p(15, 8)), pol1, false); + check_intersection (T(p( -10,-10), p( 0, 10), p( 20, -5)), T(p( 2, 30), p( -6, -4), p(15, 8)), pol0, false); Pol pol2; pol2.push_back(P( 10.2222, 2.33333 )); @@ -761,6 +813,9 @@ struct Test C_Rec(); C_L(); C_P(); + C_S(); + C_R(); + C_T(); Rec_Rec(); Rec_L(); diff --git a/Intersections_3/include/CGAL/Intersection_traits_3.h b/Intersections_3/include/CGAL/Intersection_traits_3.h index 176b3a8d829..dee4d2e3e8b 100644 --- a/Intersections_3/include/CGAL/Intersection_traits_3.h +++ b/Intersections_3/include/CGAL/Intersection_traits_3.h @@ -32,6 +32,9 @@ CGAL_INTERSECTION_TRAITS_2(Segment_3, Line_3, Point_3, Segment_3) CGAL_INTERSECTION_TRAITS_2(Line_3, Triangle_3, Point_3, Segment_3) CGAL_INTERSECTION_TRAITS_2(Triangle_3, Line_3, Point_3, Segment_3) +CGAL_INTERSECTION_TRAITS_2(Line_3, Tetrahedron_3, Point_3, Segment_3) +CGAL_INTERSECTION_TRAITS_2(Tetrahedron_3, Line_3, Point_3, Segment_3) + CGAL_INTERSECTION_TRAITS_2(Plane_3, Plane_3, Line_3, Plane_3) CGAL_INTERSECTION_TRAITS_2(Plane_3, Ray_3, Point_3, Ray_3) @@ -54,11 +57,17 @@ CGAL_INTERSECTION_TRAITS_2(Segment_3, Ray_3, Point_3, Segment_3) CGAL_INTERSECTION_TRAITS_2(Ray_3, Triangle_3, Point_3, Segment_3) CGAL_INTERSECTION_TRAITS_2(Triangle_3, Ray_3, Point_3, Segment_3) +CGAL_INTERSECTION_TRAITS_2(Ray_3, Tetrahedron_3, Point_3, Segment_3) +CGAL_INTERSECTION_TRAITS_2(Tetrahedron_3, Ray_3, Point_3, Segment_3) + CGAL_INTERSECTION_TRAITS_2(Segment_3, Segment_3, Point_3, Segment_3) CGAL_INTERSECTION_TRAITS_2(Segment_3, Triangle_3, Point_3, Segment_3) CGAL_INTERSECTION_TRAITS_2(Triangle_3, Segment_3, Point_3, Segment_3) +CGAL_INTERSECTION_TRAITS_2(Segment_3, Tetrahedron_3, Point_3, Segment_3) +CGAL_INTERSECTION_TRAITS_2(Tetrahedron_3, Segment_3, Point_3, Segment_3) + CGAL_INTERSECTION_TRAITS_3(Sphere_3, Sphere_3, Point_3, Circle_3, Sphere_3) template @@ -147,11 +156,43 @@ struct Intersection_traits { typedef typename boost::optional< variant_type > result_type; }; + +// Bbox_3 Iso_cuboid_3, variant of 1 +template +struct Intersection_traits { + typedef typename + boost::variant< typename K::Iso_cuboid_3 > variant_type; + typedef typename boost::optional< variant_type > result_type; +}; + +template +struct Intersection_traits + : public Intersection_traits {}; + // Iso_cuboid_3 Point_3, variant of one template struct Intersection_traits { typedef typename - boost::variant< typename K::Point_3 > variant_type; + boost::variant< typename K::Point_3 > variant_type; + typedef typename boost::optional< variant_type > result_type; +}; + +// Iso_cuboid_3 Plane_3, variant of 4 +template +struct Intersection_traits { + typedef typename + boost::variant< typename K::Point_3, typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + + typedef typename boost::optional< variant_type > result_type; +}; + +template +struct Intersection_traits { + typedef typename + boost::variant< typename K::Point_3, typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + typedef typename boost::optional< variant_type > result_type; }; @@ -160,7 +201,7 @@ template struct Intersection_traits { typedef typename boost::variant< typename K::Point_3, typename K::Segment_3, - typename K::Triangle_3, std::vector > variant_type; + typename K::Triangle_3, std::vector > variant_type; typedef typename boost::optional< variant_type > result_type; }; @@ -169,7 +210,45 @@ template struct Intersection_traits { typedef typename boost::variant< typename K::Point_3, typename K::Segment_3, - typename K::Triangle_3, std::vector > variant_type; + typename K::Triangle_3, std::vector > variant_type; + + typedef typename boost::optional< variant_type > result_type; +}; + +// Bbox_3 Plane_3, variant of 4 +template +struct Intersection_traits { + typedef typename + boost::variant< typename K::Point_3, typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + + typedef typename boost::optional< variant_type > result_type; +}; + +template +struct Intersection_traits { + typedef typename + boost::variant< typename K::Point_3, typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + + typedef typename boost::optional< variant_type > result_type; +}; + +// Bbox_3 Triangle_3, variant of 4 +template +struct Intersection_traits { + typedef typename + boost::variant< typename K::Point_3, typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + + typedef typename boost::optional< variant_type > result_type; +}; + +template +struct Intersection_traits { + typedef typename + boost::variant< typename K::Point_3, typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; typedef typename boost::optional< variant_type > result_type; }; @@ -253,6 +332,7 @@ struct Intersection_traits { boost::variant< typename K::Point_3 > variant_type; typedef typename boost::optional< variant_type > result_type; }; + // Triangle_3 Point_3, variant of one template struct Intersection_traits { @@ -268,6 +348,7 @@ struct Intersection_traits { boost::variant< typename K::Point_3 > variant_type; typedef typename boost::optional< variant_type > result_type; }; + // Tetrahedron_3 Point_3, variant of one template struct Intersection_traits { @@ -283,6 +364,7 @@ struct Intersection_traits { boost::variant< typename K::Point_3 > variant_type; typedef typename boost::optional< variant_type > result_type; }; + // Sphere_3 Point_3, variant of one template struct Intersection_traits { @@ -290,6 +372,48 @@ struct Intersection_traits { boost::variant< typename K::Point_3 > variant_type; typedef typename boost::optional< variant_type > result_type; }; + +//Tetrahedron_3 Plane_3, variant of 4 +template +struct Intersection_traits +{ + typedef typename + boost::variant< typename K::Point_3 , typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + typedef typename boost::optional< variant_type > result_type; +}; + +//Plane_3 Tetrahedron_3, variant of 4 +template +struct Intersection_traits +{ + typedef typename + boost::variant< typename K::Point_3 , typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + typedef typename boost::optional< variant_type > result_type; +}; + +//Triangle_3 Tetrahedron_3, variant of 4 +template +struct Intersection_traits +{ + typedef typename + boost::variant< typename K::Point_3 , typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + typedef typename boost::optional< variant_type > result_type; +}; + +// Tetrahedron_3 Triangle_3, variant of 4 +template +struct Intersection_traits +{ + typedef typename + boost::variant< typename K::Point_3 , typename K::Segment_3, + typename K::Triangle_3, std::vector > variant_type; + typedef typename boost::optional< variant_type > result_type; +}; + + } // namespace #endif /* CGAL_INTERSECTION_TRAITS_3_H */ diff --git a/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Bbox_3.h b/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Bbox_3.h index 974e40d57c7..b0220d8ad8f 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Bbox_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Bbox_3.h @@ -14,10 +14,11 @@ #ifndef CGAL_INTERSECTIONS_3_BBOX_3_BBOX_3_H #define CGAL_INTERSECTIONS_3_BBOX_3_BBOX_3_H - #include +#include namespace CGAL { + bool inline do_intersect(const CGAL::Bbox_3& c, @@ -26,6 +27,27 @@ do_intersect(const CGAL::Bbox_3& c, return CGAL::do_overlap(c, bbox); } +typename boost::optional< typename boost::variant< Bbox_3> > +inline +intersection(const CGAL::Bbox_3& a, + const CGAL::Bbox_3& b) +{ + typedef typename boost::variant variant_type; + typedef typename boost::optional result_type; + + if(!do_intersect(a,b)) + return result_type(); + + double xmin = (std::max)(a.xmin(), b.xmin()); + double xmax = (std::min)(a.xmax(), b.xmax()); + double ymin = (std::max)(a.ymin(), b.ymin()); + double ymax = (std::min)(a.ymax(), b.ymax()); + double zmin = (std::max)(a.zmin(), b.zmin()); + double zmax = (std::min)(a.zmax(), b.zmax()); + + return result_type(std::forward(Bbox_3(xmin, ymin, zmin, xmax, ymax, zmax))); +} + } //namespace CGAL diff --git a/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Iso_cuboid_3.h b/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Iso_cuboid_3.h index aa6b98ccaa1..5457d7eab46 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Iso_cuboid_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Iso_cuboid_3.h @@ -18,6 +18,7 @@ #include #include +#include namespace CGAL { @@ -33,6 +34,19 @@ bool do_intersect(const Iso_cuboid_3& a, return K().do_intersect_3_object()(b, a); } +template +typename Intersection_traits::result_type +intersection(const CGAL::Bbox_3& a, + const Iso_cuboid_3& b) { + return K().intersect_3_object()(a, b); +} + +template +typename Intersection_traits::result_type +intersection(const Iso_cuboid_3& a, + const CGAL::Bbox_3& b) { + return K().intersect_3_object()(a, b); +} } // namespace CGAL #endif // CGAL_INTERSECTIONS_3_BBOX_3_ISO_CUBOID_3_H diff --git a/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Plane_3.h b/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Plane_3.h index 6384e3a8674..616d11b38f7 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Plane_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Plane_3.h @@ -18,6 +18,7 @@ #include #include +#include namespace CGAL { @@ -34,6 +35,23 @@ bool do_intersect(const Plane_3& a, return K().do_intersect_3_object()(a, b); } +template +typename Intersection_traits::result_type +intersection(const CGAL::Bbox_3& box, + const Plane_3& pl) { + typename K::Iso_cuboid_3 cub(box.xmin(), box.ymin(), box.zmin(), + box.xmax(), box.ymax(), box.zmax()); + return typename K::Intersect_3()(cub, pl); +} + +template +typename Intersection_traits::result_type +intersection(const Plane_3& a, + const CGAL::Bbox_3& b) { + return intersection(b,a); +} + + } #endif // CGAL_INTERSECTIONS_3_BBOX_3_PLANE_3_H diff --git a/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Triangle_3.h b/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Triangle_3.h index 91c64e563b5..d11de75760c 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Triangle_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Bbox_3_Triangle_3.h @@ -18,6 +18,7 @@ #include #include +#include namespace CGAL { @@ -33,6 +34,21 @@ bool do_intersect(const Triangle_3& a, return K().do_intersect_3_object()(a, b); } +template +typename Intersection_traits::result_type +intersection(const CGAL::Bbox_3& box, + const Triangle_3& tr) { + typename K::Iso_cuboid_3 cub(box.xmin(), box.ymin(), box.zmin(), + box.xmax(), box.ymax(), box.zmax()); + return typename K::Intersect_3()(cub, tr); +} + +template +typename Intersection_traits::result_type +intersection(const Triangle_3& a, + const CGAL::Bbox_3& b) { + return intersection(b,a); +} } // namespace CGAL diff --git a/Intersections_3/include/CGAL/Intersections_3/Iso_cuboid_3_Plane_3.h b/Intersections_3/include/CGAL/Intersections_3/Iso_cuboid_3_Plane_3.h index eec8345000e..ceff9a4d83c 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Iso_cuboid_3_Plane_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Iso_cuboid_3_Plane_3.h @@ -17,10 +17,12 @@ #include #include #include +#include namespace CGAL { CGAL_DO_INTERSECT_FUNCTION(Iso_cuboid_3, Plane_3, 3) + CGAL_INTERSECTION_FUNCTION(Iso_cuboid_3, Plane_3, 3) } #endif // CGAL_INTERSECTIONS_3_ISO_CUBOID_3_PLANE_3_H diff --git a/Intersections_3/include/CGAL/Intersections_3/Line_3_Point_3.h b/Intersections_3/include/CGAL/Intersections_3/Line_3_Point_3.h index 6f0df12e9ef..abbfd75146f 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Line_3_Point_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Line_3_Point_3.h @@ -27,18 +27,18 @@ template inline bool do_intersect(const typename K::Point_3 &pt, const typename K::Line_3 &line, - const K&) + const K& k) { - return line.has_on(pt); + return k.has_on_3_object()(line,pt); } template inline bool do_intersect(const typename K::Line_3 &line, const typename K::Point_3 &pt, - const K&) + const K& k) { - return line.has_on(pt); + return k.has_on_3_object()(line, pt); } template diff --git a/Intersections_3/include/CGAL/Intersections_3/Line_3_Tetrahedron_3.h b/Intersections_3/include/CGAL/Intersections_3/Line_3_Tetrahedron_3.h index e67bedb3422..6f3af29ab63 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Line_3_Tetrahedron_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Line_3_Tetrahedron_3.h @@ -17,10 +17,24 @@ #include #include -#include +#include namespace CGAL { CGAL_DO_INTERSECT_FUNCTION(Line_3, Tetrahedron_3, 3) + + template + typename Intersection_traits::result_type + intersection(const Line_3& line, + const Tetrahedron_3& tet) { + return K().intersect_3_object()(line, tet); + } + + template + typename Intersection_traits::result_type + intersection(const Tetrahedron_3& tet, + const Line_3& line) { + return K().intersect_3_object()(tet, line); + } } #endif // CGAL_INTERSECTIONS_3_LINE_3_TETRAHEDRON_3_H diff --git a/Intersections_3/include/CGAL/Intersections_3/Plane_3_Point_3.h b/Intersections_3/include/CGAL/Intersections_3/Plane_3_Point_3.h index 5ad93c3e9c4..3f862cba645 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Plane_3_Point_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Plane_3_Point_3.h @@ -27,18 +27,18 @@ template inline bool do_intersect(const typename K::Point_3 &pt, const typename K::Plane_3 &plane, - const K&) + const K& k) { - return plane.has_on(pt); + return k.has_on_3_object()(plane,pt); } template inline bool do_intersect(const typename K::Plane_3 &plane, const typename K::Point_3 &pt, - const K&) + const K& k) { - return plane.has_on(pt); + return k.has_on_3_object()(plane,pt); } template diff --git a/Intersections_3/include/CGAL/Intersections_3/Plane_3_Tetrahedron_3.h b/Intersections_3/include/CGAL/Intersections_3/Plane_3_Tetrahedron_3.h index 33fc6e532df..8b19bf63b8d 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Plane_3_Tetrahedron_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Plane_3_Tetrahedron_3.h @@ -18,9 +18,11 @@ #include #include +#include namespace CGAL { CGAL_DO_INTERSECT_FUNCTION(Tetrahedron_3, Plane_3, 3) + CGAL_INTERSECTION_FUNCTION(Tetrahedron_3, Plane_3, 3) } #endif // CGAL_INTERSECTIONS_3_PLANE_3_TETRAHEDRON_3_H diff --git a/Intersections_3/include/CGAL/Intersections_3/Point_3_Ray_3.h b/Intersections_3/include/CGAL/Intersections_3/Point_3_Ray_3.h index a7162964189..7d41e283a89 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Point_3_Ray_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Point_3_Ray_3.h @@ -28,9 +28,9 @@ inline bool do_intersect(const typename K::Point_3 &pt, const typename K::Ray_3 &ray, - const K&) + const K& k) { - return ray.has_on(pt); + return k.has_on_3_object()(ray,pt); } @@ -39,9 +39,9 @@ inline bool do_intersect(const typename K::Ray_3 &ray, const typename K::Point_3 &pt, - const K&) + const K& k) { - return ray.has_on(pt); + return k.has_on_3_object()(ray,pt); } diff --git a/Intersections_3/include/CGAL/Intersections_3/Point_3_Segment_3.h b/Intersections_3/include/CGAL/Intersections_3/Point_3_Segment_3.h index 74fb8ac0d26..b1860dbbb95 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Point_3_Segment_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Point_3_Segment_3.h @@ -28,9 +28,9 @@ inline bool do_intersect(const typename K::Point_3 &pt, const typename K::Segment_3 &seg, - const K&) + const K& k) { - return seg.has_on(pt); + return k.has_on_3_object()(seg, pt); } template @@ -38,9 +38,9 @@ inline bool do_intersect(const typename K::Segment_3 &seg, const typename K::Point_3 &pt, - const K&) + const K& k) { - return seg.has_on(pt); + return k.has_on_3_object()(seg, pt); } diff --git a/Intersections_3/include/CGAL/Intersections_3/Ray_3_Tetrahedron_3.h b/Intersections_3/include/CGAL/Intersections_3/Ray_3_Tetrahedron_3.h index 01f8721459d..b2b00e4e716 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Ray_3_Tetrahedron_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Ray_3_Tetrahedron_3.h @@ -18,9 +18,11 @@ #include #include +#include namespace CGAL { CGAL_DO_INTERSECT_FUNCTION(Ray_3, Tetrahedron_3, 3) + CGAL_INTERSECTION_FUNCTION(Ray_3, Tetrahedron_3, 3) } #endif // CGAL_INTERSECTIONS_3_RAY_3_TETRAHEDRON_3_H diff --git a/Intersections_3/include/CGAL/Intersections_3/Segment_3_Tetrahedron_3.h b/Intersections_3/include/CGAL/Intersections_3/Segment_3_Tetrahedron_3.h index 5d1a0b525b3..4bc0e32e635 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Segment_3_Tetrahedron_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Segment_3_Tetrahedron_3.h @@ -18,9 +18,11 @@ #include #include +#include namespace CGAL { CGAL_DO_INTERSECT_FUNCTION(Segment_3, Tetrahedron_3, 3) + CGAL_INTERSECTION_FUNCTION(Segment_3, Tetrahedron_3, 3) } #endif // CGAL_INTERSECTIONS_3_SEGMENT_3_TETRAHEDRON_3_H diff --git a/Intersections_3/include/CGAL/Intersections_3/Tetrahedron_3_Triangle_3.h b/Intersections_3/include/CGAL/Intersections_3/Tetrahedron_3_Triangle_3.h index 236e3da9383..593c6481c7f 100644 --- a/Intersections_3/include/CGAL/Intersections_3/Tetrahedron_3_Triangle_3.h +++ b/Intersections_3/include/CGAL/Intersections_3/Tetrahedron_3_Triangle_3.h @@ -18,9 +18,11 @@ #include #include +#include namespace CGAL { CGAL_DO_INTERSECT_FUNCTION(Tetrahedron_3, Triangle_3, 3) + CGAL_INTERSECTION_FUNCTION(Tetrahedron_3, Triangle_3, 3) } #endif // CGAL_INTERSECTIONS_3_TETRAHEDRON_3_TRIANGLE_3_H diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Iso_cuboid_3_Plane_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Iso_cuboid_3_Plane_3_intersection.h new file mode 100644 index 00000000000..977c7dcc584 --- /dev/null +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Iso_cuboid_3_Plane_3_intersection.h @@ -0,0 +1,347 @@ +// Copyright (c) 2019 GeometryFactory(France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno +// + +#ifndef CGAL_INTERNAL_INTERSECTIONS_3_ISO_CUBOID_3_PLANE_3_INTERSECTION_H +#define CGAL_INTERNAL_INTERSECTIONS_3_ISO_CUBOID_3_PLANE_3_INTERSECTION_H + +#include +#include + +#include +#include +#include + +#include + +namespace CGAL { +namespace Intersections { +namespace internal { + +template +void filter_points(std::vector& input, + std::vector& output) +{ + std::sort(input.begin(), input.end()); + auto last = std::unique(input.begin(), input.end()); + input.erase(last, input.end()); + output = std::move(input); +} + +//Tetrahedron_3 Line_3 +template +typename Intersection_traits::result_type +intersection( + const typename K::Iso_cuboid_3 &cub, + const typename K::Plane_3 &pl, + const K& k) +{ + typedef typename K::Point_3 Point_3; + typedef typename K::Segment_3 Segment_3; + typedef typename K::Line_3 Line_3; + typedef typename K::Plane_3 Plane_3; + typedef std::vector Poly; + + typedef typename Intersection_traits, + CGAL::Plane_3 >::result_type result_type; + + typedef typename Intersection_traits, + CGAL::Plane_3 >::result_type Inter_type; + + std::vector edges; + edges.reserve(12); + + //get all edges of cub + for(int i=0; i< 4; ++i) + edges.emplace_back(cub.vertex(i), cub.vertex((i+1)%4)); + for(int i=0; i < 4; ++i) + edges.emplace_back(cub.vertex(i+4), cub.vertex((i+1)%4+4)); + for(int i=0; i < 4; ++i) + edges.emplace_back(cub.vertex(i), cub.vertex((i+1)%4+4)); + + //get all intersections between pl and cub edges + std::vector segments; + std::vector points; + + for(int i=0; i < 12; ++i) + { + // Intersect_3 checks the orientation of the segment's extremities to avoid actually computing + // the intersection if possible + Inter_type inter = typename K::Intersect_3()(pl, edges[i]); + if(inter) + { + if(const Segment_3* seg = boost::get(&*inter)) + segments.push_back(*seg); + else if(const Point_3* p = boost::get(&*inter)) + points.push_back(*p); + } + } + + if(segments.empty() && points.empty()) + return result_type(); + + switch(segments.size()) + { + case 0: + //points dealt with later + break; + case 1: + { + //adj to an edge + if(points.size() == 4) + { + return result_type(std::forward(segments.front())); + } + //plane intersecting through an edge (not 2) + else + { + Poly res(4); + const Segment_3& entry_seg(segments.front()); + Point_3 p1, p2; + bool p1_found(false), + p2_found(false); + + for(const Point_3& p : points) + { + if(!k.equal_3_object()(entry_seg.source(), p) + && ! k.equal_3_object()(entry_seg.target(), p)) + { + if(!p1_found) + { + p1 = p; + p1_found = true; + } + else { + p2 = p; + p2_found = true; + break; + } + } + } + CGAL_USE(p2_found); + CGAL_assertion(p1_found && p2_found); + res[0] = entry_seg.target(); + res[1] = p2; + res[2] = p1; + res[3] = entry_seg.source(); + + typename K::Coplanar_orientation_3 coplanar_orientation = + k.coplanar_orientation_3_object(); + + if( coplanar_orientation(res[0], res[1], res[2]) + != coplanar_orientation(res[0], res[1], res[3])) + { + std::swap(res[1], res[2]); + } + + return result_type(std::forward(res)); + } + } + break; + + case 2: //intersects diagonally + { + Poly res(4); + Segment_3 &front(segments.front()), + &back(segments.back()); + res[0] = front.target(); + res[1] = back.target(); + res[2] = back.source(); + res[3] = front.source(); + typename K::Coplanar_orientation_3 coplanar_orientation = + k.coplanar_orientation_3_object(); + + if( coplanar_orientation(res[0], res[1], res[2]) + != coplanar_orientation(res[0], res[1], res[3])) + { + std::swap(res[1], res[2]); + } + + return result_type(std::forward(res)); + } + break; + case 4: // intersects a face + { + Poly res; + res.reserve(4); + typename K::Has_on_3 has_on; + if(has_on(pl, cub.vertex(0)) + && has_on(pl, cub.vertex(5)) + && has_on(pl, cub.vertex(4))) + { + res.push_back(cub.vertex(0)); + res.push_back(cub.vertex(5)); + res.push_back(cub.vertex(4)); + res.push_back(cub.vertex(3)); + } + else if(has_on(pl, cub.vertex(0)) + && has_on(pl, cub.vertex(1)) + && has_on(pl, cub.vertex(6))) + { + res.push_back(cub.vertex(0)); + res.push_back(cub.vertex(1)); + res.push_back(cub.vertex(6)); + res.push_back(cub.vertex(5)); + + } + else if(has_on(pl, cub.vertex(1)) + && has_on(pl, cub.vertex(2)) + && has_on(pl, cub.vertex(7))) + { + res.push_back(cub.vertex(1)); + res.push_back(cub.vertex(2)); + res.push_back(cub.vertex(7)); + res.push_back(cub.vertex(6)); + } + else if(has_on(pl, cub.vertex(2)) + && has_on(pl, cub.vertex(3)) + && has_on(pl, cub.vertex(4))) + { + res.push_back(cub.vertex(2)); + res.push_back(cub.vertex(3)); + res.push_back(cub.vertex(4)); + res.push_back(cub.vertex(7)); + } + else if(has_on(pl, cub.vertex(6)) + && has_on(pl, cub.vertex(7)) + && has_on(pl, cub.vertex(4))) + { + res.push_back(cub.vertex(6)); + res.push_back(cub.vertex(7)); + res.push_back(cub.vertex(4)); + res.push_back(cub.vertex(5)); + } + else if(has_on(pl, cub.vertex(2)) + && has_on(pl, cub.vertex(1)) + && has_on(pl, cub.vertex(0))) + { + res.push_back(cub.vertex(2)); + res.push_back(cub.vertex(1)); + res.push_back(cub.vertex(0)); + res.push_back(cub.vertex(3)); + } + return result_type(std::forward(res)); + } + break; + default: + break; + } + + Poly filtered_points; + filter_points(points, filtered_points); + + //adjacent to a vertex + if(filtered_points.size() == 1) + { + return result_type(std::forward(filtered_points.front())); + } + + //get intersections between pl and each face -> line. Foreach line, creates segment with points. Then use helper_function to recover polygon. + typedef typename Intersection_traits, + CGAL::Plane_3 >::result_type Pl_pl_type; + + std::vector plane_intersections; + Pl_pl_type pl_inter = CGAL::intersection(pl, Plane_3(cub.vertex(0), + cub.vertex(1), + cub.vertex(5))); + if(const Line_3* line = boost::get(&*pl_inter)){ + plane_intersections.push_back(*line); + } + pl_inter = CGAL::intersection(pl, Plane_3(cub.vertex(0), + cub.vertex(3), + cub.vertex(4))); + if(const Line_3* line = boost::get(&*pl_inter)){ + plane_intersections.push_back(*line); + } + pl_inter = CGAL::intersection(pl, Plane_3(cub.vertex(0), + cub.vertex(1), + cub.vertex(3))); + if(const Line_3* line = boost::get(&*pl_inter)){ + plane_intersections.push_back(*line); + } + pl_inter = CGAL::intersection(pl, Plane_3(cub.vertex(7), + cub.vertex(6), + cub.vertex(1))); + if(const Line_3* line = boost::get(&*pl_inter)){ + plane_intersections.push_back(*line); + } + pl_inter = CGAL::intersection(pl, Plane_3(cub.vertex(7), + cub.vertex(4), + cub.vertex(3))); + if(const Line_3* line = boost::get(&*pl_inter)){ + plane_intersections.push_back(*line); + } + pl_inter = CGAL::intersection(pl, Plane_3(cub.vertex(7), + cub.vertex(6), + cub.vertex(4))); + if(const Line_3* line = boost::get(&*pl_inter)){ + plane_intersections.push_back(*line); + } + + std::list tmp_segs; + for(const auto& line : plane_intersections) + { + bool first_found = false; + Point_3 first_p; + typename K::Has_on_3 has_on; + for(const auto &p : filtered_points) + { + if(has_on(line, p)) + { + if(!first_found) + { + first_found = true; + first_p = p; + } + else + { + tmp_segs.emplace_back(first_p, p); + break; + } + } + } + } + + if(tmp_segs.size() < 3) + return result_type(); + + std::list tmp_pts; + fill_points_list(tmp_segs,tmp_pts); + + Poly res; + for(const auto& p : tmp_pts) + res.push_back(p); + + if(res.size() == 3){ + typename K::Triangle_3 tr(res[0], res[1], res[2]); + return result_type(std::forward(tr)); + } + else + { + return result_type(std::forward(res)); + } +} + +template +typename Intersection_traits::result_type +intersection( + const typename K::Plane_3 &pl, + const typename K::Iso_cuboid_3 &cub, + const K& k) +{ + return intersection(cub, pl, k); +} + +}}} + +#endif // CGAL_INTERNAL_INTERSECTIONS_3_ISO_CUBOID_3_PLANE_3_INTERSECTION_H diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Line_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Line_3_intersection.h new file mode 100644 index 00000000000..a36871403a7 --- /dev/null +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Line_3_intersection.h @@ -0,0 +1,75 @@ +// Copyright (c) 2019 GeometryFactory(France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno +// + +#ifndef CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_LINE_3_INTERSECTION_H +#define CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_LINE_3_INTERSECTION_H + +#include +#include +#include + +namespace CGAL { + +namespace Intersections { + +namespace internal { + +template +struct Tetrahedron_Line_intersection_3 + :public Tetrahedron_lines_intersection_3_base > +{ + typedef Tetrahedron_lines_intersection_3_base > Base; + typedef typename Base::Result_type Result_type; + Tetrahedron_Line_intersection_3( + const typename K::Tetrahedron_3& tet, + const typename K::Line_3& o):Base(tet,o) + {} + + bool all_inside_test() + { + return false; + } + + bool are_extremities_inside_test() + { + return false; + } + +}; + +//Tetrahedron_3 Line_3 +template +typename Intersection_traits::result_type +intersection( + const typename K::Tetrahedron_3 &tet, + const typename K::Line_3 &line, + const K&) +{ + Tetrahedron_Line_intersection_3 solver(tet, line); + solver.do_procede(); + return solver.output; +} + +template +typename Intersection_traits::result_type +intersection( + const typename K::Line_3 &line, + const typename K::Tetrahedron_3 &tet, + const K& k) +{ + return intersection(tet, line, k); +} + +}}} +#endif // CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_LINE_3_INTERSECTION_H diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Plane_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Plane_3_intersection.h new file mode 100644 index 00000000000..fa55cb268f2 --- /dev/null +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Plane_3_intersection.h @@ -0,0 +1,143 @@ +// Copyright (c) 2019 GeometryFactory(France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno +// + + +#ifndef CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_PLANE_3_INTERSECTIONS_H +#define CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_PLANE_3_INTERSECTIONS_H + +#include +#include +#include +#include +#include +namespace CGAL { + +namespace Intersections { + +namespace internal { + + +//Tetrahedron_3 Plane_3 +template +typename Intersection_traits::result_type +intersection( + const typename K::Tetrahedron_3 &tet, + const typename K::Plane_3 &pl, + const K&) +{ + typedef typename Intersection_traits::result_type result_type; + + typedef typename Intersection_traits::result_type Inter_type; + + typedef typename K::Segment_3 Segment_3; + Inter_type intersections[4]; + int p_id = -1; + std::vector points; + std::vector segments; + for(int i = 0; i < 4; ++i) + { + const typename K::Triangle_3 triangle(tet.vertex((i+1)%4), + tet.vertex((i+2)%4), + tet.vertex((i+3)%4)); + intersections[i] = typename K::Intersect_3()(pl, triangle); + if(intersections[i]){ + if(const typename K::Triangle_3* tr = boost::get(&*intersections[i])) + { + typename K::Triangle_3 res = *tr; + return result_type(std::forward(res)); + } + else if( const Segment_3* s + = boost::get(&*intersections[i])) + { + segments.push_back(*s); + } + else if( const typename K::Point_3* p + = boost::get(&*intersections[i])) + { + points.push_back(*p); + p_id = i; + } + } + } + CGAL_assertion(segments.size() != 1); + + switch(segments.size()) + { + case 0: + { + if(p_id == -1) + return result_type(); + else + { + typename K::Point_3 p + = *boost::get(&*intersections[p_id]); + + return result_type(std::forward(p)); + } + } + break; + case 2: + { + return result_type(std::forward(segments.back())); + } + break; + default: + { + std::set all_points; + for (const auto& s : segments) + { + all_points.insert(s.source()); + all_points.insert(s.target()); + } + if(all_points.size() == 3) + { + auto p_it = all_points.begin(); + ++p_it; + typename K::Point_3 mid_point = *p_it; + ++p_it; + typename K::Triangle_3 result(*all_points.begin(), mid_point, *p_it ); + return result_type(std::forward(result)); + } + else //size = 4 + { + std::list segs(segments.begin(), segments.end()); + std::list tmp; + fill_points_list(segs, tmp); + std::vector res; + for( const auto& p : tmp) + res.push_back(p); + return result_type(std::forward >(res)); + } + } + break; + } + CGAL_assertion(false); + return result_type(); +} + +template +typename Intersection_traits::result_type +intersection( + const typename K::Plane_3 &pl, + const typename K::Tetrahedron_3 &tet, + const K& k) +{ + return intersection(tet, pl, k); +} + +}}} +#endif // CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_PLANE_3_INTERSECTIONS_H diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Ray_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Ray_3_intersection.h new file mode 100644 index 00000000000..4738c5e3a71 --- /dev/null +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Ray_3_intersection.h @@ -0,0 +1,78 @@ +// Copyright (c) 2019 GeometryFactory(France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno +// + +#ifndef CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_RAY_3_INTERSECTION_H +#define CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_RAY_3_INTERSECTION_H + +#include +#include +#include + +namespace CGAL { + +namespace Intersections { + +namespace internal { + +template +struct Tetrahedron_ray_intersection_3 + :public Tetrahedron_lines_intersection_3_base > +{ + typedef typename K::Ray_3 O; + typedef Tetrahedron_lines_intersection_3_base > Base; + typedef typename Base::Result_type Result_type; + + Tetrahedron_ray_intersection_3(const typename K::Tetrahedron_3& tet, + const O& o):Base(tet,o) {} + + bool are_extremities_inside_test() + { + //If one extremity is inside tet : return a segment + if(this->tet.has_on_bounded_side(this->o.source())) + { + typename K::Segment_3 result(this->o.source(), this->res_points.front()); + this->output = Result_type(std::forward(result)); + return true; + } + return false; + } +}; + + +//Tetrahedron_3 Ray_3 +template +typename Intersection_traits::result_type +intersection( + const typename K::Tetrahedron_3 &tet, + const typename K::Ray_3 &ray, + const K&) +{ + Tetrahedron_ray_intersection_3 solver(tet, ray); + solver.do_procede(); + return solver.output; +} + +template +typename Intersection_traits::result_type +intersection( + const typename K::Ray_3 &seg, + const typename K::Tetrahedron_3 &tet, + const K& k) +{ + return intersection(tet, seg, k); +} + +}}} +#endif // CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_RAY_3_INTERSECTION_H diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Segment_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Segment_3_intersection.h new file mode 100644 index 00000000000..a79424649f6 --- /dev/null +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Segment_3_intersection.h @@ -0,0 +1,96 @@ +// Copyright (c) 2019 GeometryFactory(France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno +// + +#ifndef CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_SEGMENT_3_INTERSECTION_H +#define CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_SEGMENT_3_INTERSECTION_H + +#include +#include +#include + +namespace CGAL { + +namespace Intersections { + +namespace internal { + +template +struct Tetrahedron_segment_intersection_3 + :public Tetrahedron_lines_intersection_3_base > +{ + typedef typename K::Segment_3 O; + typedef Tetrahedron_lines_intersection_3_base > Base; + typedef typename Base::Result_type result_type; + + Tetrahedron_segment_intersection_3(const typename K::Tetrahedron_3& tet, + const O& o):Base(tet,o) {} + + bool all_inside_test() + { + //if both segment extremities are inside tet, return the input segment. + if(this->tet.has_on_bounded_side(this->o.source()) + && this->tet.has_on_bounded_side(this->o.target())){ + typename K::Segment_3 result = this->o; + this->output = result_type(std::forward(result)); + return true; + } + return false; + } + + bool are_extremities_inside_test() + { + //If one extremity is inside tet : return a segment + if(this->tet.has_on_bounded_side(this->o.source())) + { + typename K::Segment_3 result(this->o.source(), this->res_points.front()); + this->output = result_type(std::forward(result)); + return true; + } + else if(this->tet.has_on_bounded_side(this->o.target())){ + typename K::Segment_3 result(this->res_points.front(), this->o.target()); + this->output = result_type(std::forward(result)); + return true; + } + return false; + } +}; + + +//Tetrahedron_3 Segment_3 +template +typename Intersection_traits::result_type +intersection( + const typename K::Tetrahedron_3 &tet, + const typename K::Segment_3 &seg, + const K&) +{ + Tetrahedron_segment_intersection_3 solver(tet, seg); + solver.do_procede(); + return solver.output; + +} + +template +typename Intersection_traits::result_type +intersection( + const typename K::Segment_3 &seg, + const typename K::Tetrahedron_3 &tet, + const K& k) +{ + return intersection(tet, seg, k); +} + +}}} +#endif // CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_Segment_3_INTERSECTION_H diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Triangle_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Triangle_3_intersection.h new file mode 100644 index 00000000000..0a3839ec258 --- /dev/null +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Tetrahedron_3_Triangle_3_intersection.h @@ -0,0 +1,296 @@ +// Copyright (c) 2019 GeometryFactory(France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno +// + + +#ifndef CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_TRIANGLE_3_INTERSECTIONS_H +#define CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_TRIANGLE_3_INTERSECTIONS_H + +#include +#include +#include +#include +#include +namespace CGAL { + +namespace Intersections { + +namespace internal { + +//Tetrahedron_3 Segment_3 +template +typename Intersection_traits::result_type +intersection( + const typename K::Tetrahedron_3 &tet, + const typename K::Triangle_3 &tr, + const K& k) +{ + typedef typename Intersection_traits::result_type result_type; + + typedef typename Intersection_traits::result_type Inter_type; + + typedef typename K::Segment_3 Segment_3; + typedef typename K::Point_3 Point_3; + typedef typename K::Triangle_3 Triangle_3; + typedef std::vector Poly; + + std::vector inside_points; + for(int i = 0; i< 3; ++i) + { + if(tet.has_on_bounded_side(tr.vertex(i)) + || tet.has_on_boundary(tr.vertex(i))){ + inside_points.push_back(tr.vertex(i)); + } + } + switch(inside_points.size()) + { + case 0: + { + Inter_type intersections[4]; + std::vector segments; + std::vector seg_ids; + std::vector points; + for(std::size_t i = 0; i < 4; ++i) + { + const typename K::Triangle_3 triangle(tet.vertex((i+1)%4), + tet.vertex((i+2)%4), + tet.vertex((i+3)%4)); + intersections[i] = typename K::Intersect_3()(tr, triangle); + if(intersections[i]){ + //a face is inside the input tr + if(const Triangle_3* t = boost::get(&*intersections[i])) + { + Triangle_3 res = *t; + return result_type(std::forward(res)); + } + //get segs and pts to construct poly + else if( const Segment_3* s + = boost::get(&*intersections[i])) + { + segments.push_back(*s); + seg_ids.push_back(i); + } + else if( const typename K::Point_3* p + = boost::get(&*intersections[i])) + { + points.push_back(*p); + } + //if poly : then the input is in a supporting plane of a face, return the poly. + else if( const Poly* p + = boost::get(&*intersections[i])) + { + Poly res = *p; + return result_type(std::forward(res)); + } + } + } + if(segments.size() > 1) + { + std::vector filtered; + filter_segments(segments, filtered); + segments = filtered; + } + //if there are several segments, then we need to compute the polygone. + if(segments.size() > 1) + { + std::list tmp; + fill_segments_infos(segments,tmp, tr); + Poly res; + res.reserve(4); + for(const auto& p : tmp) + res.push_back(p); + return result_type(std::forward(res)); + } + //else it must be adjacent to an vertex, so we return the point + else if(segments.size() == 1) + { + //adjacency to an edge, return resulting segment. + return result_type(std::forward(segments.front())); + } + else + { + //no segment = adjacency to an vertex or an edge : return result point + return result_type(std::forward(points.front())); + + } + } + break; + case 1: + case 2: + { + //tricky cases + Inter_type intersections[4]; + std::vector points; + std::vector segments; + for(std::size_t i = 0; i < 4; ++i) + { + const typename K::Triangle_3 triangle(tet.vertex((i+1)%4), + tet.vertex((i+2)%4), + tet.vertex((i+3)%4)); + intersections[i] = typename K::Intersect_3()(tr, triangle); + if(intersections[i]){ + if(const Triangle_3* t = boost::get(&*intersections[i])) + { + Triangle_3 res = *t; + return result_type(std::forward(res)); + } + //get segs and pts to construct poly + else if( const Segment_3* s + = boost::get(&*intersections[i])) + { + segments.push_back(*s); + } + else if( const typename K::Point_3* p + = boost::get(&*intersections[i])) + { + points.push_back(*p); + } + //if poly : then the input is in a supporting plane of a face, return the poly. + else if( const Poly* p + = boost::get(&*intersections[i])) + { + Poly res = *p; + return result_type(std::forward(res)); + } + } + } + if(segments.empty()) + { + //then there is only one point of contact. Return it: + return result_type(std::forward(points.front())); + } + + if(segments.size() > 1) + { + std::vector filtered; + filter_segments(segments, filtered); + segments = filtered; + } + + switch(segments.size()) + { + case 1: + { + bool return_solo_seg = true; + //only one intersection, a triangle edge is one of the tet edges, and + //the 3rd point is outside. This is the only intersection. + for(const auto& p : inside_points) + { + if(!tet.has_on_boundary(p)) + { + return_solo_seg = false; + break; + } + } + if(return_solo_seg) + { + return result_type(std::forward(segments.front())); + } + + if(inside_points.size() == 1) + { + Triangle_3 res(inside_points.front(), segments.front().source(), + segments.front().target()); + return result_type(std::forward(res)); + } + else //size 2 + { + Poly res(4); + res[0] = inside_points.front(); + res[1] = inside_points.back(); + if((inside_points.front() - inside_points.back()) * + (segments.front().source() - segments.front().target()) > 0) + { + res[2] = segments.front().target(); + res[3] = segments.front().source(); + } + else + { + res[3] = segments.front().target(); + res[2] = segments.front().source(); + } + return result_type(std::forward(res)); + } + } + break; + case 2: + case 3: + { + std::list segs(segments.begin(), segments.end()); + std::list tmp; + fill_points_list(segs, tmp); + if(inside_points.size() == 1) + { + Poly res; + res.reserve(4); + res.push_back(inside_points.front()); + for(const auto& p : tmp) + res.push_back(p); + return result_type(std::forward(res)); + } + else //size 2 + { + Poly res; + res.reserve(5); + typename K::Compute_scalar_product_3 scalar = + k.compute_scalar_product_3_object(); + typename K::Construct_vector_3 construct_vector = + k.construct_vector_3_object(); + + if(scalar(construct_vector(inside_points.front(), inside_points.back()), + construct_vector(tmp.front(), tmp.back())) > 0) + { + res.push_back(inside_points.front()); + } + res.push_back(inside_points.back()); + for(const auto& p : tmp) + res.push_back(p); + return result_type(std::forward(res)); + } + } + break; + default: + //3 faces max if a point or more are inside tetrahedron + break; + } + } + break; + + case 3: + { + //triangle entirely inside tetra : return input triangle + return result_type(tr); + } + break; + default: + //never happens (only 3 pts in a tr) + break; + } + return result_type(); +} + +template +typename Intersection_traits::result_type +intersection( + const typename K::Triangle_3 &pl, + const typename K::Tetrahedron_3 &tet, + const K& k) +{ + return intersection(tet, pl, k); +} + +}}} +#endif // CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_3_TRIANGLE_3_INTERSECTIONS_H diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h index 65433008969..17358b81ea5 100644 --- a/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h @@ -128,7 +128,7 @@ struct Triangle_Line_visitor { result_type operator()(const typename K::Point_3& p, const typename K::Segment_3& s) const { - if ( s.has_on(p) ) + if ( typename K::Has_on_3()(s,p) ) return intersection_return(p); else return result_type(); diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/intersection_3_1_impl.h b/Intersections_3/include/CGAL/Intersections_3/internal/intersection_3_1_impl.h index 35636c76f9e..f836dbd6aea 100644 --- a/Intersections_3/include/CGAL/Intersections_3/internal/intersection_3_1_impl.h +++ b/Intersections_3/include/CGAL/Intersections_3/internal/intersection_3_1_impl.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace CGAL { @@ -1243,6 +1244,37 @@ intersection(const Bbox_3 &box, return intersection(seg, box, k); } +template +inline +typename Intersection_traits::result_type +intersection(const Bbox_3 &a, + const Bbox_3 &b, + const K&) +{ + return CGAL::intersection(a, b); +} + +template +typename Intersection_traits::result_type +intersection(const CGAL::Bbox_3 &box, + const typename K::Iso_cuboid_3 &cub, + const K&) +{ + typename K::Iso_cuboid_3 iso_cub(box); + return intersection(iso_cub, cub); +} + + +template +inline +typename Intersection_traits::result_type +intersection(const typename K::Iso_cuboid_3 &cub, + const CGAL::Bbox_3 &box, + const K& k) +{ + return intersection(box,cub, k); +} + template typename Intersection_traits::result_type @@ -1567,7 +1599,6 @@ do_intersect(const Iso_cuboid_3 &j, const Line_3 &l, const R&) { return bool(CGAL::intersection(l, j)); } - } // namespace internal } // namespace Intersections } // namespace CGAL diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/tetrahedron_intersection_helpers.h b/Intersections_3/include/CGAL/Intersections_3/internal/tetrahedron_intersection_helpers.h new file mode 100644 index 00000000000..b227f9e3918 --- /dev/null +++ b/Intersections_3/include/CGAL/Intersections_3/internal/tetrahedron_intersection_helpers.h @@ -0,0 +1,261 @@ +// Copyright (c) 2019 GeometryFactory(France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Maxime Gimeno +// + +#ifndef CGAL_INTERNAL_TETRAHEDRON_INTERSECTION_HELPERS_H +#define CGAL_INTERNAL_TETRAHEDRON_INTERSECTION_HELPERS_H + +#include +#include +#include + +namespace CGAL { +namespace Intersections { +namespace internal { + +template +void filter_segments(const std::vector& input, + std::vector& output) +{ + std::list tmp(input.begin(), input.end()); + + do + { + Segment s = tmp.back(); + tmp.pop_back(); + for(auto s_it = tmp.begin(); s_it != tmp.end();) + { + if(s == *s_it || s == s_it->opposite()) + { + s_it = tmp.erase(s_it); + } + else { + ++s_it; + } + } + output.push_back(s); + }while (!tmp.empty()); +} + +template +void fill_segments_infos(std::vector& segments, + std::list& points, const Triangle& input_tr) +{ + struct Wrapped_segment + { + Segment segment; + bool s_dangling; + bool t_dangling; + std::size_t s_neighbor; + std::size_t t_neighbor; + Wrapped_segment(const Segment& s) + :segment(s), s_dangling(true), + t_dangling(true), s_neighbor(0), + t_neighbor(0){} + }; + + std::vector wrapped_segments; + + for(const Segment& s:segments) + wrapped_segments.push_back(Wrapped_segment(s)); + + std::vector bis = segments; + while(!bis.empty()) + { + Segment s = bis.back(); + bis.pop_back(); + Wrapped_segment& w_s = wrapped_segments.back(); + if(!w_s.s_dangling && ! w_s.t_dangling) + continue; + for(std::size_t i = 0; i< bis.size(); ++i) + { + const Segment& s2 = bis[i]; + if(s2.target() == s.source()) + { + w_s.s_dangling = false; + w_s.s_neighbor = i; + //same i because we empty from the bottom + wrapped_segments[i].t_dangling = false; + wrapped_segments[i].t_neighbor = bis.size(); + } + else if(s2.target() == s.target()) + { + w_s.t_dangling = false; + w_s.t_neighbor = i; + wrapped_segments[i].s_dangling = false; + wrapped_segments[i].s_neighbor = bis.size(); + wrapped_segments[i].segment = wrapped_segments[i].segment.opposite(); //also orient the structure + + } + else if(s2.source() == s.source()) + { + w_s.s_dangling = false; + w_s.s_neighbor = i; + wrapped_segments[i].t_dangling = false; + wrapped_segments[i].t_neighbor = bis.size(); + wrapped_segments[i].segment = wrapped_segments[i].segment.opposite(); + } + else if(s2.source() == s.target()) + { + w_s.t_dangling = false; + w_s.t_neighbor = i; + wrapped_segments[i].s_dangling = false; + wrapped_segments[i].s_neighbor = bis.size(); + } + } + + //fill dangling extremities using triangle edges + if(w_s.s_dangling) + { + for(int e_id = 0; e_id < 3; ++e_id) + { + Segment edge(input_tr.vertex(e_id), input_tr.vertex(e_id+1)); + if(!edge.has_on(s.source())) + { + continue; + } + for(std::size_t i = 0; i< bis.size(); ++i) + { + if(edge.has_on(bis[i].source())) + { + w_s.s_dangling = false; + w_s.s_neighbor = i; + //same i because we empty from the bottom + wrapped_segments[i].t_dangling = false; + wrapped_segments[i].t_neighbor = bis.size(); + wrapped_segments[i].segment = wrapped_segments[i].segment.opposite(); + } + else if(edge.has_on(bis[i].target())) + { + w_s.s_dangling = false; + w_s.s_neighbor = i; + //same i because we empty from the bottom + wrapped_segments[i].t_dangling = false; + wrapped_segments[i].t_neighbor = bis.size(); + } + } + } + } + if(w_s.t_dangling) + { + for(int e_id = 0; e_id < 3; ++e_id) + { + Segment edge(input_tr.vertex(e_id), input_tr.vertex(e_id+1)); + if(!edge.has_on(s.target())) + { + continue; + } + for(std::size_t i = 0; i< bis.size(); ++i) + { + if(edge.has_on(bis[i].source())) + { + w_s.t_dangling = false; + w_s.t_neighbor = i; + //same i because we empty from the bottom + wrapped_segments[i].s_dangling = false; + wrapped_segments[i].s_neighbor = bis.size(); + } + else if(edge.has_on(bis[i].target())) + { + w_s.t_dangling = false; + w_s.t_neighbor = i; + //same i because we empty from the bottom + wrapped_segments[i].s_dangling = false; + wrapped_segments[i].s_neighbor = bis.size(); + wrapped_segments[i].segment = wrapped_segments[i].segment.opposite(); + } + } + } + } + + if(w_s.s_dangling || w_s.t_dangling) + { + std::cerr<<"Error. Kernel must have exact constructions to compute this intersection."< +void fill_points_list(std::list& segments, std::list& points) +{ + CGAL_assertion(segments.size() > 1); + + Segment seg = segments.front(); + segments.pop_front(); + + points.push_back(seg.source()); + points.push_back(seg.target()); + + //find first seg with a point in common with seg.front = s2. + do + { + auto seg_it = segments.begin(); + bool found = false; + for(;seg_it != segments.end(); ++seg_it) + { + if(seg_it->source() == points.front()) + { + points.push_front(seg_it->target()); + found = true; + } + else if(seg_it->source() == points.back()) + { + points.push_back(seg_it->target()); + found = true; + } + else if(seg_it->target() == points.front()) + { + points.push_front(seg_it->source()); + found = true; + } + else if(seg_it->target() == points.back()) + { + points.push_back(seg_it->source()); + found = true; + } + if(found) + { + break; + } + } + if(!found) + { + std::cerr<<"Error. Kernel must have exact constructions to compute this intersection."< +#include +#include + +namespace CGAL { +namespace Intersections { +namespace internal { + +template +struct Tetrahedron_lines_intersection_3_base +{ + typedef typename Intersection_traits, + O>::result_type Result_type; + + typedef typename Intersection_traits, + O>::result_type Inter_type; + + const typename K::Tetrahedron_3& tet; + const O& o; + std::vector res_points; + Result_type output; + + Tetrahedron_lines_intersection_3_base(const typename K::Tetrahedron_3& tet, + const O& o):tet(tet), o(o) + {} + + bool all_inside_test() + { + return false; + } + + bool are_extremities_inside_test() + { + return false; + } + + void do_procede() + { + if(static_cast(this)->all_inside_test()) + return; + + int res_id = -1; + + Inter_type tr_seg[4]; + for(int i = 0; i < 4; ++i) + { + const typename K::Triangle_3 triangle(tet.vertex((i+1)%4), + tet.vertex((i+2)%4), + tet.vertex((i+3)%4)); + if(do_intersect(o, triangle)) + { + tr_seg[i] = typename K::Intersect_3()(o, triangle); + if( boost::get(&*tr_seg[i]) != nullptr) + { + res_id = i; + break; + } + } + } + + //if there is a segment in the intersections, then we return it + if(res_id !=-1) + { + output = tr_seg[res_id]; + return; + } + + //else if there is only 1 intersection + res_points.reserve(4); + res_id = -1; + + for(int i = 0; i< 4; ++i) + { + if(tr_seg[i]) + { + if (const typename K::Point_3* p = boost::get(&*tr_seg[i])) + { + if(res_points.empty()) + { + res_id = i; + } + else { + if(*p != res_points.front()) + { + res_id = -1; + } + } + res_points.push_back(*p); + } + } + } + + if(res_points.empty()) + { + return; + } + + if(res_id != -1) + { + if(static_cast(this)->are_extremities_inside_test()) + return; + //else point and segment entirely not inside or one intersection on boundary: + output = tr_seg[res_id]; + return; + } + + //else, we return a segment of the 2 intersection points (the most far away, in case of inexact) + typename K::FT max_dist = 0; + std::size_t res_id_2 = -1; + std::vector > sq_distances(res_points.size()); + + for(std::size_t i = 0; i< res_points.size(); ++i) + { + const auto& p1 = res_points[i]; + for(const auto& p2 : res_points) + { + sq_distances[i].push_back(CGAL::squared_distance(p1, p2)); + if(sq_distances[i].back() > max_dist) + { + res_id = static_cast(i); + res_id_2 = sq_distances[i].size()-1; + max_dist = sq_distances[i].back(); + } + } + } + + CGAL_assertion(res_id != -1); + CGAL_assertion(res_id_2 != static_cast(-1)); + CGAL_assertion(max_dist >0 ); + + typename K::Segment_3 res_seg(res_points[res_id], res_points[res_id_2]); + output = Result_type(std::forward(res_seg)); + return; + } +}; + +} +} +} //end namespaces +#endif // CGAL_INTERNAL_INTERSECTIONS_3_TETRAHEDRON_LINES_INTERSECTIONS_3_H diff --git a/Intersections_3/include/CGAL/intersection_3.h b/Intersections_3/include/CGAL/intersection_3.h index bf0b22dab87..a8717709cc4 100644 --- a/Intersections_3/include/CGAL/intersection_3.h +++ b/Intersections_3/include/CGAL/intersection_3.h @@ -83,6 +83,7 @@ #include #include +#include #include diff --git a/Intersections_3/test/Intersections_3/call_test.cpp b/Intersections_3/test/Intersections_3/call_test.cpp index 1f27833ad8d..34c45488e5b 100644 --- a/Intersections_3/test/Intersections_3/call_test.cpp +++ b/Intersections_3/test/Intersections_3/call_test.cpp @@ -19,6 +19,8 @@ typedef CGAL::Iso_cuboid_3< K > Cub; typedef CGAL::Sphere_3< K > Sph; typedef CGAL::Circle_3< K > C; +typedef CGAL::Tetrahedron_3< K > T; + typedef CGAL::Bbox_3 Bbox_3; template @@ -103,10 +105,16 @@ int main(int argc, char**) // call_intersection_global(Cub(), Tr()); call_intersection_global(Cub(), R()); call_intersection_global(Cub(), Cub()); +// call_intersection_global(Cub(), Bbox_3()); call_intersection_global(Bbox_3(), L()); call_intersection_global(Bbox_3(), S()); call_intersection_global(Bbox_3(), R()); + call_intersection_global(Bbox_3(), Cub()); + CGAL::intersection(Bbox_3(), Bbox_3()); + + call_intersection_global(T(), L()); + // with kernel diff --git a/Intersections_3/test/Intersections_3/test_intersections_3.cpp b/Intersections_3/test/Intersections_3/test_intersections_3.cpp index 0ab31a7d145..80913337543 100644 --- a/Intersections_3/test/Intersections_3/test_intersections_3.cpp +++ b/Intersections_3/test/Intersections_3/test_intersections_3.cpp @@ -47,7 +47,7 @@ randomint ri; inline double to_nt(int d) { - return double(d); + return double(d); } template < typename K > @@ -61,6 +61,7 @@ struct Test { typedef CGAL::Ray_3< K > R; typedef CGAL::Iso_cuboid_3< K > Cub; typedef CGAL::Sphere_3< K > Sph; + typedef CGAL::Tetrahedron_3< K > Tet; typedef CGAL::Bbox_3 Bbox; typedef std::vector

Pol; @@ -68,35 +69,35 @@ struct Test { template < typename Type > bool approx_equal_nt(const Type &t1, const Type &t2) { - if (t1 == t2) - return true; - if (CGAL::abs(t1 - t2) / (CGAL::max)(CGAL::abs(t1), CGAL::abs(t2)) < epsilon) - return true; - std::cout << " Approximate comparison failed between : " << t1 << " and " << t2 << "\n"; - return false; + if (t1 == t2) + return true; + if (CGAL::abs(t1 - t2) / (CGAL::max)(CGAL::abs(t1), CGAL::abs(t2)) < epsilon) + return true; + std::cout << " Approximate comparison failed between : " << t1 << " and " << t2 << "\n"; + return false; } template < typename Type > bool approx_equal(const Type&t1, const Type&t2) { - return t1 == t2; - // we need approx equal to check approx kernels, but maybe we should only test with exact kernels - // (approx kernels were useful before, when the text output was checked by diff ?) - // idea : test containment with intervals ? or use some "epsilon double"? - // I need to convert the text output to exact rationals in the source... - // Well, for now the current scheme works. + return t1 == t2; + // we need approx equal to check approx kernels, but maybe we should only test with exact kernels + // (approx kernels were useful before, when the text output was checked by diff ?) + // idea : test containment with intervals ? or use some "epsilon double"? + // I need to convert the text output to exact rationals in the source... + // Well, for now the current scheme works. } bool approx_equal(const P & p, const P & q) { - return approx_equal_nt(p.x(), q.x()) && - approx_equal_nt(p.y(), q.y()) && - approx_equal_nt(p.z(), q.z()); + return approx_equal_nt(p.x(), q.x()) && + approx_equal_nt(p.y(), q.y()) && + approx_equal_nt(p.z(), q.z()); } bool approx_equal(const S & p, const S & q) { - return approx_equal(p.source(), q.source()) && approx_equal(p.target(), q.target()); + return approx_equal(p.source(), q.source()) && approx_equal(p.target(), q.target()); } bool approx_equal(const Pol & p, const Pol & q) @@ -114,32 +115,32 @@ struct Test { template < typename O1, typename O2> void check_no_intersection(const O1& o1, const O2& o2) { - assert(!CGAL::do_intersect(o1, o2)); - assert(!CGAL::do_intersect(o2, o1)); + assert(!CGAL::do_intersect(o1, o2)); + assert(!CGAL::do_intersect(o2, o1)); } template < typename Res, typename O1, typename O2 > void check_intersection(const O1& o1, const O2& o2) { - Res tmp; - assert(CGAL::do_intersect(o1, o2)); - assert(CGAL::assign(tmp, CGAL::intersection(o1, o2))); - assert(CGAL::do_intersect(o2, o1)); - assert(CGAL::assign(tmp, CGAL::intersection(o2, o1))); + Res tmp; + assert(CGAL::do_intersect(o1, o2)); + assert(CGAL::assign(tmp, CGAL::intersection(o1, o2))); + assert(CGAL::do_intersect(o2, o1)); + assert(CGAL::assign(tmp, CGAL::intersection(o2, o1))); } template < typename Res, typename O1, typename O2 > void check_intersection(const O1& o1, const O2& o2, const Res& result, bool do_opposite = true) { - Res tmp; - assert(CGAL::do_intersect(o1, o2)); - assert(CGAL::assign(tmp, CGAL::intersection(o1, o2))); - assert(approx_equal(tmp, result)); - if (do_opposite) { - assert(CGAL::do_intersect(o2, o1)); - assert(CGAL::assign(tmp, CGAL::intersection(o2, o1))); - assert(approx_equal(tmp, result)); - } + Res tmp; + assert(CGAL::do_intersect(o1, o2)); + assert(CGAL::assign(tmp, CGAL::intersection(o1, o2))); + assert(approx_equal(tmp, result)); + if (do_opposite) { + assert(CGAL::do_intersect(o2, o1)); + assert(CGAL::assign(tmp, CGAL::intersection(o2, o1))); + assert(approx_equal(tmp, result)); + } } @@ -565,6 +566,419 @@ struct Test { << do_intersect_counter << "\n"; } // end function Bbox_Tr + void Tet_L(bool is_exact) + { + std::cout << "Tetrahedron_3 - Line_3\n"; + + Tet tet(P(0,0,0), P(0,1,0), P(1,0,0), P(0,0,1)); + if(is_exact) + { + check_no_intersection (tet, L(P(5,0,0), P(5,1,0))); + //on edge + check_intersection (tet, L(P(0,2,0), P(0,3,0)), S(P(0,0,0), P(0,1,0))); + //through vertex and out from face + check_intersection (tet, L(P(0,1,0), P(0.25,0,0.25)), S(P(0,1,0), P(0.25,0,0.25))); + //through a vertex only + check_intersection (tet, L(P(1,1,0), P(-1,1,0)), P(0,1,0)); + //through 2 faces + check_intersection (tet, L(P(0.25,0.25,0), P(0.25,0,0.25)), S(P(0.25,0,0.25), P(0.25,0.25,0))); + //through one edge + check_intersection (tet, L(P(0.5,-0.5,0.5), P(0.5,0.5,0.5)), P(0.5,0,0.5)); + //in a single face through 2 edges + check_intersection (tet, L(P(0.0,0.5,0.0), P(0.5,0.5,0.0)), S(P(0.0,0.5,0.0), P(0.5, 0.5, 0.0) )); + //in a single face through 1 vertex and 1 edge + check_intersection (tet, L(P(0.0,0.5,0.0), P(1.0,0.0,0.0)), S(P(0.0,0.5,0.0), P(1.0, 0.0, 0.0) )); + } + + } + + void Tet_S(bool is_exact) + { + std::cout << "Tetrahedron_3 - Segment_3\n"; + + Tet tet(P(0,0,0), P(0,1,0), P(1,0,0), P(0,0,1)); + if(is_exact) + { + check_no_intersection (tet, S(P(5,0,0), P(5,1,0))); + check_intersection (tet, S(P(0,2,0), P(0,-2,0)), S(P(0,1,0), P(0,0,0))); + check_intersection (tet, S(P(0,1,0), P(0.25,0,0.25)), S(P(0,1,0), P(0.25,0,0.25))); + check_intersection (tet, S(P(2,1,0), P(-2,1,0)), P(0,1,0)); + + check_intersection (tet, S(P(0.1,0.1,0.1), P(0.2,0.2,0.2)), S(P(0.1,0.1,0.1), P(0.2,0.2,0.2))); + typename K::FT coord = 1.0/3.0; + check_intersection (tet, S(P(0.25,0.25,0.25), P(2,2,2)), S(P(0.25,0.25,0.25), P(coord, coord, coord))); + } + else + { + CGAL::intersection (tet, S(P(0,2,0), P(0,-2,0))); + CGAL::intersection (tet, S(P(0,1,0), P(0.25,0,0.25))); + CGAL::intersection (tet, S(P(2,1,0), P(-2,1,0))); + CGAL::intersection (tet, S(P(0.1,0.1,0.1), P(0.2,0.2,0.2))); + CGAL::intersection (tet, S(P(0.25,0.25,0.25), P(2,2,2))); + } + + } + + void Tet_R(bool is_exact) + { + std::cout << "Tetrahedron_3 - Ray_3\n"; + + Tet tet(P(0,0,0), P(0,1,0), P(1,0,0), P(0,0,1)); + if(is_exact) + { + check_no_intersection (tet, R(P(5,0,0), P(5,1,0))); + + check_intersection (tet, R(P(0,2,0), P(0,-2,0)), S(P(0,1,0), P(0,0,0))); + check_intersection (tet, R(P(0,1,0), P(0.25,0,0.25)), S(P(0,1,0), P(0.25,0,0.25))); + check_intersection (tet, R(P(2,1,0), P(-2,1,0)), P(0,1,0)); + + typename K::FT coord = 1.0/3.0; + check_intersection (tet, R(P(0.1,0.1,0.1), P(0.2,0.2,0.2)), S(P(0.1,0.1,0.1), P(coord, coord, coord))); + check_intersection (tet, R(P(0.25,0.25,0.25), P(2,2,2)), S(P(0.25,0.25,0.25), P(coord, coord, coord))); + } + else + { + CGAL::intersection (tet, R(P(0,2,0), P(0,-2,0))); + CGAL::intersection (tet, R(P(0,1,0), P(0.25,0,0.25))); + CGAL::intersection (tet, R(P(2,1,0), P(-2,1,0))); + CGAL::intersection (tet, R(P(0.1,0.1,0.1), P(0.2,0.2,0.2))); + CGAL::intersection (tet, R(P(0.25,0.25,0.25), P(2,2,2))); + } + } + + void Tet_Pl(bool is_exact) + { + std::cout << "Tetrahedron_3 - Plane_3\n"; + + Tet tet(P(0,0,0), P(0,1,0), P(1,0,0), P(0,0,1)); + if(is_exact) + { + check_no_intersection (tet, Pl(P(2,0,0), + P(2,1,0), + P(2,0,1))); + + check_intersection (tet, Pl(P(0,2,0), P(0,0,0), P(0,0,1)), + Tr(P(0,0,0), P(0,1,0), P(0,0,1))); + + check_intersection (tet, Pl(P(0,1,0), P(1,0,0), P(0.5,0,-0.5)), + S(P(0,1,0), P(1,0,0))); + + check_intersection (tet, Pl(P(-1,1,12), P(0,1,-50), P(0.5,1,-0.5)), + P(0,1,0)); + + check_intersection (tet, Pl(P(0,0.5,0), P(1,0.5,-5), P(0.5,0.5,0.5)), + Tr(P(0.5,0.5,0), P(0,0.5,0), P(0,0.5,0.5))); + Pl pl(P(0,0.9,0), P(0.9,0,0), P(0.9,0.01,0.06)); + + typedef typename CGAL::Intersection_traits::result_type Res; + + + //Don't have the right values to test further. + Res res = CGAL::intersection (tet, pl); + + const std::vector

* poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + } + else + { + CGAL::intersection (tet, Pl(P(0,2,0), P(0,0,0), P(0,0,1))); + CGAL::intersection (tet, Pl(P(0,1,0), P(1,0,0), P(0.5,0,-0.5))); + CGAL::intersection (tet, Pl(P(-1,1,12), P(0,1,-50), P(0.5,1,-0.5))); + CGAL::intersection (tet, Pl(P(0,0.5,0), P(1,0.5,-5), P(0.5,0.5,0.5))); + } + + } + + void Tet_Tr(bool is_exact) + { + std::cout << "Tetrahedron_3 - Triangle_3\n"; + + typedef typename CGAL::Intersection_traits::result_type Res; + + Tet tet(P(0,0,0), P(0,1,0), P(1,0,0), P(0,0,1)); + if(is_exact) + { + check_no_intersection (tet, Tr(P(2,0,0), + P(2,1,0), + P(2,0,1))); + + //tr inside a face + check_intersection (tet, Tr(P(0,0.9,0), P(0,0.1,0), P(0,0,0.9)), + Tr(P(0,0.9,0), P(0,0.1,0), P(0,0,0.9))); + //face inside tr + check_intersection (tet, Tr(P(0,2,0), P(0,-2,0), P(0,0,2)), + Tr(P(0,0,1), P(0,0,0), P(0,1,0))); + + Tr tr(P(-2,2,0), P(2,2,0), P(0.25,0.25,0)); + + Res res = CGAL::intersection(tet, tr); + const std::vector

* poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + for(auto& p : *poly) + { + assert(tet.has_on_boundary(p) && tr.has_on(p)); + } + + //only one edge intersecting + check_intersection (tet, Tr(P(-2, 0.5, 0.5), P(-2,0.75,1), P(1.5, 0.5, 0.5)), + P(0,0.5,0.5)); + + + //edge shared, 3rd point outside + check_intersection (tet, Tr(P(0,1,0), P(1,0,0), P(0.5,0,-100)), + S(P(0,1,0), P(1,0,0))); + + //shared edge, 3rd point inside + check_intersection (tet, Tr(P(0,1,0), P(1,0,0), P(0.25,0.25,0.25)), + Tr(P(0,1,0), P(1,0,0), P(0.25,0.25,0.25))); + + //tr adjacent to a vertex, outside + check_intersection (tet, Tr(P(-1,1,12), P(0,1,-50), P(0.5,1,-0.5)), + P(0,1,0)); + + //tr adjacent to an edge, outside + check_intersection (tet, Tr(P(-0.6, 1, 0.6), + P(0.5, 1.20, -0.5), + P(0, -0.5, 0)), + S(P(0,0,0), P(0,1,0))); + + + //tr share a vertex, inside + check_intersection (tet, Tr(P(0,1,0), P(0.1,0.1,0), P(0.5,0.1,0)), + Tr(P(0,1,0), P(0.1,0.1,0), P(0.5,0.1,0))); + + //tr edge adjacent to a vertex + check_intersection (tet, Tr(P(-1,1,0), P(3,1,0), P(0,3,0)), + P(0,1,0)); + + //tr vertex adjacent to a vertex + check_intersection (tet, Tr(P(0,1,0), P(3,1,0), P(0,3,0)), + P(0,1,0)); + + + //traversing triangles + tr = Tr(P(-2, 0.5, 0.25), + P(-2, 0.75, 0.6), + P(1.5, 0.5, 0.25)); + + res = CGAL::intersection(tet, tr); + + + std::vector

* inter = boost::get >(&*res); + assert(inter != nullptr); + assert(inter->size() == 4); + for(auto& p : *inter) + { + assert(tet.has_on_boundary(p) && tr.has_on(p)); + } + + tr = Tr(P(-2, 0.25, 0), + P(-2, 0.75, 0), + P(1.5, 0.5, 0)); + + res = CGAL::intersection(tet, tr); + + + inter = boost::get >(&*res); + assert(inter != nullptr); + assert(inter->size() == 4); + for(auto& p : *inter) + { + assert(tet.has_on_boundary(p) && tr.has_on(p)); + } + + tr = Tr(P(0.2, 0.15, 0.3), + P(-2, 0.6, 0.15), + P(-2, 0.12, 0.15)); + + res = CGAL::intersection(tet, tr); + Tr* res_tr = boost::get(&*res); + assert(res_tr != nullptr); + + tr = Tr(P(0.2, 0.15, 0.3), + P(-1, 0.15, -2), + P(-1, 0.15, 2)); + + res = CGAL::intersection(tet, tr); + + + inter = boost::get >(&*res); + assert(inter != nullptr); + assert(inter->size() == 4); + + tr = Tr(P(0.45, 0.20, 0.1), + P(0.1, 0.20, 0.5), + P(-0.5, 0.25, -0.5)); + + res = CGAL::intersection(tet, tr); + + + inter = boost::get >(&*res); + assert(inter != nullptr); + assert(inter->size() == 4); + + for(auto& p : *inter) + { + assert( + (tet.has_on_bounded_side(p) || tet.has_on_boundary(p)) + && tr.has_on(p)); + } + + //tr share a vertex, through + tr = Tr(P(0,1,0), P(0.1,0.1,0), P(0.9,0.1,0)); + res = CGAL::intersection(tet, tr); + res_tr = boost::get(&*res); + assert(res_tr != nullptr); + + + tr = Tr(P(0.1, 0.5, 0.1), P(0.3,0.1,0.1), P(4,0.3,0.9)); + res = CGAL::intersection(tet, tr); + inter = boost::get >(&*res); + assert(inter != nullptr); + assert(inter->size() == 4); + } + else + { + //tr inside a face + CGAL::intersection(tet, Tr(P(0,0.9,0), P(0,0.1,0), P(0,0,0.9))); + //face inside tr + CGAL::intersection(tet, Tr(P(0,2,0), P(0,-2,0), P(0,0,2))); + + //only one edge intersecting + CGAL::intersection(tet, Tr(P(-2, 0.5, 0.5), P(-2,0.75,1), P(1.5, 0.5, 0.5))); + + //edge shared, 3rd point outside + CGAL::intersection(tet, Tr(P(0,1,0), P(1,0,0), P(0.5,0,-100))); + + //shared edge, 3rd point inside + CGAL::intersection(tet, Tr(P(0,1,0), P(1,0,0), P(0.25,0.25,0.25))); + + //tr adjacent to a vertex, outside + CGAL::intersection(tet, Tr(P(-1,1,12), P(0,1,-50), P(0.5,1,-0.5))); + + //tr share a vertex, inside + CGAL::intersection(tet, Tr(P(0,1,0), P(0.1,0.1,0), P(0.5,0.1,0))); + + //tr edge adjacent to a vertex + CGAL::intersection(tet, Tr(P(-1,1,0), P(3,1,0), P(0,3,0))); + + //tr vertex adjacent to a vertex + CGAL::intersection(tet, Tr(P(0,1,0), P(3,1,0), P(0,3,0))); + + typedef typename CGAL::Intersection_traits::result_type Res; + //traversing triangles + Tr tr(P(-2, 0.5, 0.25), + P(-2, 0.75, 0.6), + P(1.5, 0.5, 0.25)); + + Res res = CGAL::intersection(tet, tr); + + //tr share a vertex, through + tr = Tr(P(0,1,0), P(0.1,0.1,0), P(0.9,0.1,0)); + res = CGAL::intersection(tet, tr); + } + + } + + void Pl_Cub(bool is_exact) + { + std::cout << "Plane_3 - Cuboid_3\n"; + + Cub cub(P(1,1,1), P(2,2,2)); + + if(is_exact) + { + check_no_intersection (cub, Pl(P(3,0,0), + P(3,1,0), + P(3,0,1))); + + //edge + check_intersection (cub, Pl(P(1,1,1), P(1,2,1), P(1.5,0,0)), + S(P(1,2,1), P(1,1,1))); + + + //face + typedef typename CGAL::Intersection_traits::result_type Res; + Res res = CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(1,2,2))); + + const std::vector

* poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + for(auto& p : *poly) + { + assert(p.x() == 1); + } + res = CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(2,2,2))); + poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + for(auto& p : *poly) + { + assert(cub.has_on_boundary(p)); + } + + //vertex + check_intersection (cub, Pl(0.5, -0.5, -0.5, 0), + P(2,1,1)); + //triangle + + check_intersection (cub, Pl(P(2, 1.66, 2), + P(1.66,2,2), + P(2,2,1.66)), + Tr(P(2, 2, 1.66), + P(1.66,2,2), + P(2,1.66,2))); + + //other edge + Pl pl(P(1,1,1), P(1,2,1), P(1.5, 1, 2)); + res = CGAL::intersection(cub, pl); + poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + for(auto& p : *poly) + { + assert(pl.has_on(p) && cub.has_on_boundary(p)); + } + + //random + pl = Pl(0.265189, 0.902464, 0.33946, -2.47551); + res = CGAL::intersection(cub, pl); + poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 5); + for(auto& p : *poly) + { + assert(pl.has_on(p) && cub.has_on_boundary(p)); + } + } + else + { + CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(1.5,0,0))); + //face + typedef typename CGAL::Intersection_traits::result_type Res; + Res res = CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(1,2,2))); + res = CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(2,2,2))); + CGAL::intersection (cub, Pl(0.5, -0.5, -0.5, 0)); + CGAL::intersection (cub, Pl(P(2, 1.66, 2), + P(1.66,2,2), + P(2,2,1.66))); + Pl pl(0.265189, 0.902464, 0.33946, -2.47551); + res = CGAL::intersection(cub, pl); + } + } + void Cub_Tr(bool is_exact) { typedef typename CGAL::Intersection_traits::result_type Res; @@ -667,6 +1081,144 @@ struct Test { } } + void Pl_Box(bool is_exact) + { + std::cout << "Plane_3 - Bbox_3\n"; + + Bbox cub(1,1,1,2,2,2); + + if(is_exact) + { + check_no_intersection (cub, Pl(P(3,0,0), + P(3,1,0), + P(3,0,1))); + + //edge + check_intersection (cub, Pl(P(1,1,1), P(1,2,1), P(1.5,0,0)), + S(P(1,2,1), P(1,1,1))); + //face + typedef typename CGAL::Intersection_traits::result_type Res; + Res res = CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(1,2,2))); + + const std::vector

* poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + for(auto& p : *poly) + { + assert(p.x() == 1); + } + res = CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(2,2,2))); + poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + //vertex + check_intersection (cub, Pl(0.5, -0.5, -0.5, 0), + P(2,1,1)); + //triangle + + check_intersection (cub, Pl(P(2, 1.66, 2), + P(1.66,2,2), + P(2,2,1.66)), + Tr(P(2, 2, 1.66), + P(1.66,2,2), + P(2,1.66,2))); + + //random + Pl pl(0.265189, 0.902464, 0.33946, -2.47551); + res = CGAL::intersection(cub, pl); + poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 5); + for(auto& p : *poly) + { + assert(pl.has_on(p)); + } + } + else + { + CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(1.5,0,0))); + //face + typedef typename CGAL::Intersection_traits::result_type Res; + Res res = CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(1,2,2))); + res = CGAL::intersection(cub, Pl(P(1,1,1), P(1,2,1), P(2,2,2))); + CGAL::intersection (cub, Pl(0.5, -0.5, -0.5, 0)); + CGAL::intersection (cub, Pl(P(2, 1.66, 2), + P(1.66,2,2), + P(2,2,1.66))); + Pl pl(0.265189, 0.902464, 0.33946, -2.47551); + res = CGAL::intersection(cub, pl); + } + } + + void Box_Tr(bool is_exact) + { + std::cout << "Triangle_3 - Cuboid_3\n"; + + Cub cub(P(1,1,1), P(2,2,2)); + + check_no_intersection(cub, Tr(P(1.1, 2,0), + P(2, 3, 1), + P(4, 5, 6))); + + //tr in a face + check_intersection(cub, Tr(P(1,1.1,1), + P(1,1.5,1), + P(1,1,1.1)), + Tr(P(1,1.1,1), + P(1,1.5,1), + P(1,1,1.1)) + ); + //face in a tr + + typedef typename CGAL::Intersection_traits::result_type Res; + Tr tr(P(-3, -3, 1), P(3,-3,1), P(1.5,6,1)); + Res res = CGAL::intersection(cub, tr); + if(is_exact) + { + std::vector

* poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + for(auto& p : *poly) + { + assert(tr.has_on(p) && cub.has_on_boundary(p)); + } + } + + //tr adj to a point + + check_intersection (cub, Tr(P(1, 0.5, 0.5), + P(3, 2, 1), + P(3, 1, 2)), + P(2,1,1)); + //tr adj to an edge + check_intersection (cub, Tr(P(1,0,4), P(2,1,0), P(4,3,0)), + S(P(2,1,2), P(2,1,1))); + //tr inside + check_intersection (cub, Tr(P(1.1,1.1,1.1), P(1.8,1.8,1.8), P(1.5,1.8,1.1)), + Tr(P(1.1,1.1,1.1), P(1.8,1.8,1.8), P(1.5,1.8,1.1))); + //tr through + tr = Tr(P(2, 4, 2), + P(1, 3.5, -0.5), + P(1, -1, 1)); + res = CGAL::intersection(cub, tr); + if(is_exact) + { + std::vector

* poly = boost::get >(&*res); + assert(poly != nullptr); + assert(poly->size() == 4); + for(auto& p : *poly) + { + assert(tr.has_on(p) && cub.has_on_boundary(p)); + } + } + } + void run(bool is_exact = false) { std::cout << "3D Intersection tests\n"; @@ -686,11 +1238,24 @@ struct Test { R_L(); R_S(); R_R(); + + //If not exact, only check that it doesn't crash + //@{ + Tet_L(is_exact); + Tet_S(is_exact); + Tet_R(is_exact); + Tet_Pl(is_exact); + Tet_Tr(is_exact); + Pl_Cub(is_exact); + Cub_Tr(is_exact); + Pl_Box(is_exact); + Box_Tr(is_exact); + //@} + Bbox_L(); Bbox_R(); Bbox_Tr(); } - }; int main() @@ -707,4 +1272,3 @@ int main() std::cout << " |||||||| Test CGAL::Homogeneous ||||||||" << std::endl; Test< CGAL::Homogeneous >().run(true); } - diff --git a/Kernel_23/doc/Kernel_23/CGAL/intersections.h b/Kernel_23/doc/Kernel_23/CGAL/intersections.h index fdd2b7c5ab9..47155df7898 100644 --- a/Kernel_23/doc/Kernel_23/CGAL/intersections.h +++ b/Kernel_23/doc/Kernel_23/CGAL/intersections.h @@ -47,23 +47,15 @@ Also, `Type1` and `Type2` can be both of type In three-dimensional space, the types `Type1` and `Type2` can be any of the following: +- `Bbox_3`. - `Point_3` - `Plane_3` - `Line_3` - `Ray_3` - `Segment_3` +- `Sphere_3` - `Triangle_3`. -- `Bbox_3`. - -Also, `Type1` and `Type2` can be respectively of types - -- `Triangle_3` and `Tetrahedron_3` -- `Plane_3` and `Sphere_3` (or the contrary) -- `Sphere_3` and `Sphere_3` -- `Line_3` and `Iso_cuboid_3` -- `Ray_3` and `Iso_cuboid_3` -- `Segment_3` and `Iso_cuboid_3` -- `Iso_cuboid_3` and `Iso_cuboid_3`. +- `Tetrahedron_3`. */ bool do_intersect(Type1 obj1, Type2 obj2); /// @} @@ -193,6 +185,9 @@ It is equivalent to `boost::optional< boost::variant< T... > >`, the last column Additional overloads are provided for the type `Point_2` combined with any other type with the result type being `boost::optional< boost::variant< Point_2 > >`. +Overloads are also provided for the type `Bbox_2`, for all +intersections existing with the type `Iso_rectangle_2`. Note that the return type for `Bbox_2` - `Bbox_2` + is `Bbox_2` and not `Iso_rectangle_2`. \cgalHeading{3D Intersections} @@ -230,6 +225,16 @@ It is equivalent to `boost::optional< boost::variant< T... > >`, the last column Triangle_3 Point_3, or Segment_3 + + Line_3 + Tetrahedron_3 + Point_3, or Segment_3 + + + Line_3 + Iso_cuboid_3 + Point_3, or Segment_3 + Plane_3 Plane_3 @@ -255,6 +260,16 @@ It is equivalent to `boost::optional< boost::variant< T... > >`, the last column Triangle_3 Point_3, or Segment_3, or Triangle_3 + + Plane_3 + Tetrahedron_3 + Point_3, or Segment_3, or Triangle_3, or std::vector + + + Plane_3 + Iso_cuboid_3 + Point_3, or Segment_3, or Triangle_3, or std::vector + Ray_3 Ray_3 @@ -268,7 +283,17 @@ It is equivalent to `boost::optional< boost::variant< T... > >`, the last column Ray_3 Triangle_3 -p Point_3, or Segment_3 + Point_3, or Segment_3 + + + Ray_3 + Tetrahedron_3 + Point_3, or Segment_3 + + + Ray_3 + Iso_cuboid_3 + Point_3, or Segment_3 Segment_3 @@ -280,6 +305,16 @@ p Point_3, or Segment_3 Triangle_3 Point_3, or Segment_3 + + Segment_3 + Tetrahedron_3 + Point_3, or Segment_3 + + + Segment_3 + Iso_cuboid_3 + Point_3, or Segment_3 + Sphere_3 Sphere_3 @@ -290,11 +325,23 @@ p Point_3, or Segment_3 Triangle_3 Point_3, or Segment_3, or Triangle_3, or std::vector < Point_3 > + + Triangle_3 + Tetrahedron_3 + Point_3, or Segment_3, or Triangle_3, or std::vector < Point_3 > + + + Triangle_3 + Iso_cuboid_3 + Point_3, or Segment_3, or Triangle_3, or std::vector < Point_3 > + Additional overloads are provided for the type `Point_3` combined with any other type with the result type being -`boost::optional< boost::variant< Point_3 > >`. +`boost::optional< boost::variant< Point_3 > >`. Overloads are also provided for the type `Bbox_3`, for all +intersections existing with the type `Iso_cuboid_3`. Note that the return type for `Bbox_3` - `Bbox_3` + is `Bbox_3` and not `Iso_cuboid_3`. \cgalHeading{Examples} diff --git a/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h b/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h index 3fdc4cf0b0b..df7a76cb2c2 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h +++ b/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h @@ -444,7 +444,14 @@ private: if(dim == 0) msg << "corner ("; else msg << "point ("; msg << p << ")"; +#if CGAL_MESH_3_PROTECTION_DEBUG & 4 + CGAL_error_msg(([this, str = msg.str()](){ + dump_c3t3(this->c3t3_, "dump-bug"); + return str.c_str(); + }())); +#else // not CGAL_MESH_3_PROTECTION_DEBUG & 4 CGAL_error_msg(msg.str().c_str()); +#endif } return s; } @@ -730,7 +737,7 @@ smart_insert_point(const Bare_point& p, Weight w, int dim, const Index& index, Vertex_handle nearest_vh = tr.nearest_power_vertex(p, ch); FT sq_d = sq_distance(p, cp(tr.point(nearest_vh))); -#if CGAL_MESH_3_PROTECTION_DEBUG & 2 +#if CGAL_MESH_3_PROTECTION_DEBUG & 16 std::cerr << "Nearest power vertex of (" << p << ") is " << &*nearest_vh << " (" << c3t3_.triangulation().point(nearest_vh) << ") " << "at distance: " << sq_d << std::endl; @@ -1247,8 +1254,17 @@ insert_balls(const Vertex_handle& vp, norm_step_size = step_size; } else { CGAL_assertion_code(using boost::math::float_prior); - CGAL_assertion(n==0 || - dleft_frac >= float_prior(float_prior(1.))); +#if CGAL_MESH_3_PROTECTION_DEBUG & 4 + CGAL_assertion_msg(n==0 || + dleft_frac >= float_prior(float_prior(1.)), + ([this](){ + CGAL_USE(this); + dump_c3t3(this->c3t3_, "dump-bug"); + return "the sampling of protecting balls is not possible"; + }())); +#else + CGAL_assertion(n==0 || dleft_frac >= float_prior(float_prior(1.))); +#endif } // Launch balls @@ -1304,7 +1320,7 @@ void Protect_edges_sizing_field:: refine_balls() { -#if CGAL_MESH_3_PROTECTION_DEBUG & 4 +#if CGAL_MESH_3_PROTECTION_DEBUG & 8 dump_c3t3(c3t3_, "dump-before-refine_balls"); dump_c3t3_edges(c3t3_, "dump-before-refine_balls"); #endif @@ -1418,7 +1434,7 @@ refine_balls() } } -#if CGAL_MESH_3_PROTECTION_DEBUG & 4 +#if CGAL_MESH_3_PROTECTION_DEBUG & 8 dump_c3t3(c3t3_, "dump-before-check_and_repopulate_edges"); dump_c3t3_edges(c3t3_, "dump-before-check_and_repopulate_edges"); #endif diff --git a/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h b/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h index 37287ee95c9..b7d7e050735 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h @@ -434,6 +434,7 @@ public: && !defined(CGAL_MESH_3_USE_LAZY_UNSORTED_REFINEMENT_QUEUE) this->remove_element(c); #endif + CGAL_USE(c); } // Parallel: it's always lazy, so do nothing void remove_element_from_refinement_queue(Cell_handle, Parallel_tag) {} diff --git a/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h b/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h index 38f066ef049..b7b90c2e4ed 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h @@ -38,6 +38,7 @@ #ifdef CGAL_MESH_3_PROFILING #include #endif +#include #include #include @@ -570,6 +571,7 @@ protected: Facet canonical_facet = this->canonical_facet(facet); this->remove_element(canonical_facet); #endif + CGAL_USE(facet); } #ifdef CGAL_LINKED_WITH_TBB /// Removes facet from refinement queue @@ -846,8 +848,12 @@ public: if( zone.locate_type == Tr::VERTEX ) { std::stringstream sstr; + sstr.precision(17); sstr << "(" << p << ") is already inserted on surface.\n"; - CGAL_error_msg(sstr.str().c_str()); + CGAL_error_msg(([this,str=sstr.str()] { + dump_c3t3(this->r_c3t3_, "dump-bug"); + return str.c_str(); + }())); return CONFLICT_AND_ELEMENT_SHOULD_BE_DROPPED; } else diff --git a/Mesh_3/include/CGAL/Mesh_3/experimental/Facet_topological_criterion_with_adjacency.h b/Mesh_3/include/CGAL/Mesh_3/experimental/Facet_topological_criterion_with_adjacency.h index 498e2eb689b..2e3f3e313bb 100644 --- a/Mesh_3/include/CGAL/Mesh_3/experimental/Facet_topological_criterion_with_adjacency.h +++ b/Mesh_3/include/CGAL/Mesh_3/experimental/Facet_topological_criterion_with_adjacency.h @@ -87,8 +87,16 @@ protected: Index_set set; domain->get_corner_incidences(corner_id, std::back_inserter(set)); - if(std::find(set.begin(), set.end(), patch_index) == set.end()) + if(std::find(set.begin(), set.end(), patch_index) == set.end()) { +#ifdef CGAL_MESH_3_DEBUG_FACET_CRITERIA + std::cerr << "Bad facet " + "(Facet_topological_criterion_with_adjacency: corner #" + << corner_id << ", point " << v->point() + << ", is not incident to patch #" << patch_index << ")" + << std::endl; +#endif return Is_bad(Quality(1)); // bad! + } } break; case 1: @@ -98,13 +106,28 @@ protected: domain->curve_index(v->index()); Index_set set; domain->get_incidences(curve_id, std::back_inserter(set)); - if(std::find(set.begin(), set.end(), patch_index) == set.end()) + if(std::find(set.begin(), set.end(), patch_index) == set.end()) { +#ifdef CGAL_MESH_3_DEBUG_FACET_CRITERIA + std::cerr << "Bad facet " + "(Facet_topological_criterion_with_adjacency: curve #" + << curve_id << ", at point " << v->point() + << ", is not incident to patch #" << patch_index << ")" + << std::endl; +#endif return Is_bad(Quality(1)); // bad! + } } break; case 2: - if(domain->surface_patch_index(v->index()) != patch_index) + if(domain->surface_patch_index(v->index()) != patch_index) { +#ifdef CGAL_MESH_3_DEBUG_FACET_CRITERIA + std::cerr << "Bad facet (Facet_topological_criterion_with_adjacency: " + "vertex at point " + << v->point() << " is not on patch #" << patch_index << ")" + << std::endl; +#endif return Is_bad(Quality(1)); // bad! + } break; default: return Is_bad(Quality(1)); @@ -112,6 +135,10 @@ protected: } } if(nb_vertices_on_curves == 3) { +#ifdef CGAL_MESH_3_DEBUG_FACET_CRITERIA + std::cerr << "Bad facet (Facet_topological_criterion_with_adjacency: " + "three points on a curve)\n"; +#endif return Is_bad(Quality(1)); // bad! // All vertices are on curves. That means that the facet could be on // several different patches. Let's disallow that. diff --git a/Mesh_3/include/CGAL/Mesh_3/mesh_standard_facet_criteria.h b/Mesh_3/include/CGAL/Mesh_3/mesh_standard_facet_criteria.h index 1b7c5e44334..71498b35bfc 100644 --- a/Mesh_3/include/CGAL/Mesh_3/mesh_standard_facet_criteria.h +++ b/Mesh_3/include/CGAL/Mesh_3/mesh_standard_facet_criteria.h @@ -565,6 +565,10 @@ protected: { if ( !(v2->index() == index) ) { +#ifdef CGAL_MESH_3_DEBUG_FACET_CRITERIA + std::cerr << "Bad facet (on same surface criterion: " + << v2->index() << " != " << index << ")" << std::endl; +#endif return Is_bad(Quality(1)); } } @@ -579,6 +583,10 @@ protected: { if ( is_index_initialized && !(v3->index() == index) ) { +#ifdef CGAL_MESH_3_DEBUG_FACET_CRITERIA + std::cerr << "Bad facet (on same surface criterion: " + << v3->index() << " != " << index << ")" << std::endl; +#endif return Is_bad(Quality(1)); } } diff --git a/Mesh_3/include/CGAL/Mesh_3/polylines_to_protect.h b/Mesh_3/include/CGAL/Mesh_3/polylines_to_protect.h index 96c049491b1..1b1285a577a 100644 --- a/Mesh_3/include/CGAL/Mesh_3/polylines_to_protect.h +++ b/Mesh_3/include/CGAL/Mesh_3/polylines_to_protect.h @@ -264,7 +264,7 @@ struct Polyline_visitor ~Polyline_visitor() {//DEBUG -#if CGAL_MESH_3_PROTECTION_DEBUG > 1 +#if CGAL_MESH_3_PROTECTION_DEBUG & 2 std::ofstream og("polylines_graph.polylines.txt"); og.precision(17); for(const std::vector

& poly : polylines) @@ -274,7 +274,7 @@ struct Polyline_visitor og << p << " "; og << std::endl; } -#endif // CGAL_MESH_3_PROTECTION_DEBUG > 1 +#endif // CGAL_MESH_3_PROTECTION_DEBUG & 2 } void start_new_polyline() diff --git a/Mesh_3/include/CGAL/Mesh_domain_with_polyline_features_3.h b/Mesh_3/include/CGAL/Mesh_domain_with_polyline_features_3.h index 066ce70f0cc..9a35bf41a70 100644 --- a/Mesh_3/include/CGAL/Mesh_domain_with_polyline_features_3.h +++ b/Mesh_3/include/CGAL/Mesh_domain_with_polyline_features_3.h @@ -1165,6 +1165,13 @@ add_features_and_incidences(InputIterator first, InputIterator end, Curve_index curve_id = insert_edge(polyline.begin(), polyline.end()); edges_incidences_[curve_id].insert(patches_ids.begin(), patches_ids.end()); +#if CGAL_MESH_3_PROTECTION_DEBUG & 1 + std::cerr << "Curve #" << curve_id << " is incident to the following patches: {"; + for(auto id: patches_ids) { + std::cerr << " " << id; + } + std::cerr << "}\n"; +#endif // CGAL_MESH_3_PROTECTION_DEBUG & 1 *indices_out++ = curve_id; } @@ -1410,7 +1417,7 @@ compute_corners_incidences() std::inserter(incidences, incidences.begin())); } -#ifdef CGAL_MESH_3_PROTECTION_DEBUG +#if CGAL_MESH_3_PROTECTION_DEBUG & 1 display_corner_incidences(std::cerr, cit->first, id); #endif // CGAL_MESH_3_PROTECTION_DEBUG diff --git a/Mesh_3/include/CGAL/Polyhedral_complex_mesh_domain_3.h b/Mesh_3/include/CGAL/Polyhedral_complex_mesh_domain_3.h index 9421e883a45..de1599a0fe0 100644 --- a/Mesh_3/include/CGAL/Polyhedral_complex_mesh_domain_3.h +++ b/Mesh_3/include/CGAL/Polyhedral_complex_mesh_domain_3.h @@ -989,7 +989,7 @@ add_featured_edges_to_graph(const Polyhedron_type& p, } } -#if CGAL_MESH_3_PROTECTION_DEBUG > 1 +#if CGAL_MESH_3_PROTECTION_DEBUG & 2 {// DEBUG Mesh_3::internal::dump_graph_edges("edges-graph.polylines.txt", g_copy); } diff --git a/Mesh_3/include/CGAL/Polyhedral_mesh_domain_with_features_3.h b/Mesh_3/include/CGAL/Polyhedral_mesh_domain_with_features_3.h index ea92e1341ea..5f41be7e828 100644 --- a/Mesh_3/include/CGAL/Polyhedral_mesh_domain_with_features_3.h +++ b/Mesh_3/include/CGAL/Polyhedral_mesh_domain_with_features_3.h @@ -306,7 +306,7 @@ void dump_graph_edges(std::ostream& out, const Graph& g) typedef typename boost::graph_traits::edge_descriptor edge_descriptor; out.precision(17); - for(edge_descriptor e : edges(g)) + for(edge_descriptor e : make_range(edges(g))) { vertex_descriptor s = source(e, g); vertex_descriptor t = target(e, g); @@ -400,7 +400,7 @@ add_features_from_split_graph_into_polylines(Featured_edges_copy_graph& g_copy) this->add_features_with_context(polylines.begin(), polylines.end()); -#if CGAL_MESH_3_PROTECTION_DEBUG > 1 +#if CGAL_MESH_3_PROTECTION_DEBUG & 2 {//DEBUG std::ofstream og("polylines_graph.polylines.txt"); og.precision(17); @@ -413,7 +413,7 @@ add_features_from_split_graph_into_polylines(Featured_edges_copy_graph& g_copy) } og.close(); } -#endif // CGAL_MESH_3_PROTECTION_DEBUG > 1 +#endif // CGAL_MESH_3_PROTECTION_DEBUG & 2 } @@ -472,7 +472,7 @@ add_featured_edges_to_graph(const Polyhedron& p, } } -#if CGAL_MESH_3_PROTECTION_DEBUG > 1 +#if CGAL_MESH_3_PROTECTION_DEBUG & 2 {// DEBUG dump_graph_edges("edges-graph.polylines.txt", g_copy); } diff --git a/Mesher_level/include/CGAL/Double_map.h b/Mesher_level/include/CGAL/Double_map.h index a59d8f99bd1..927324a69a1 100644 --- a/Mesher_level/include/CGAL/Double_map.h +++ b/Mesher_level/include/CGAL/Double_map.h @@ -129,7 +129,6 @@ public : size_type size() const { - CGAL_assertion(is_valid()); return direct_func().size(); } diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index b875258f867..36f6844cdfb 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -437,6 +437,108 @@ flips to recover good shapes, unless specified otherwise (see below). Default: `true` \cgalNPEnd +\cgalNPBegin{do_orientation_tests} \anchor PMP_do_orientation_tests +Parameter used in `volume_connected_components()` to indicate if a surface orientation test +must be performed to decide whether a surface connected component belong to a volume or not. +\n +\b Type : `bool` \n +\b Default value is `true` +\cgalNPEnd + +\cgalNPBegin{do_self_intersection_tests} \anchor PMP_do_self_intersection_tests +Parameter used in `volume_connected_components()` to indicate if the input triangle mesh +might contains self-intersections that must be taken into account while detecting volume +connected components. +\n +\b Type : `bool` \n +\b Default value is `false` +\cgalNPEnd + +\cgalNPBegin{error_codes} \anchor PMP_error_codes +a reference to an object that must be a model of the `BackInsertionSequence` concept, +holding the classification of a volume connected component identified by its id. +The size of the container is exactly the number of volume connected components and the +object at position `k` in the container corresponds to the classification of the volume connected component +assigned the id `k`. +\n +\b Type: a `reference_wrapper` (either from `boost` or the standard library) + over an object of type `container` \n +\b Default: None +\cgalNPEnd + +\cgalNPBegin{face_connected_component_map} \anchor PMP_face_connected_component_map +a property map that will contain for each face the id +of its surface connected component in the range +`[0, number of connected components - 1[`. +\n +Type: a class model of `WritablePropertyMap` with +`boost::graph_traits::%face_descriptor` as key type and `std::size_t` as value type\n +\b Default: None +\cgalNPEnd + +\cgalNPBegin{volume_inclusions} \anchor PMP_volume_inclusions +a reference to a container holding a container of id of surface connected components. +The container must be a model of the `BackInsertionSequence` concept, +with a value type being a model of `BackInsertionSequence` of ids, +both types having the functions `reserve()` and `push_back()`. +The size of the container is exactly the number of surface connected components of the triangle mesh considered +The container at position `k` contains the id of each surface connected components +that is the first intersected by a ray with source on the surface connected component `k` +and directed outside the finite volume enclosed by the surface connected component `k`. +\n +\b Type: a `reference_wrapper` (either from `boost` or the standard library) + over an object of type `container< container >` \n +\b Default: None +\cgalNPEnd + +\cgalParamBegin{connected_component_id_to_volume_id} \anchor PMP_connected_component_id_to_volume_id +a `reference_wrapper` (either from `boost` or the standard library) containing +a reference to an object that must be a model of the `BackInsertionSequence` concept, +with a value_type being `std::size_t`. +The size of the container is exactly the number of connected components. +For each connected component identified using its id `ccid`, the id of the volume it contributes +to is the value at the position `ccid` in the parameter container. +\n +\b Type: a `reference_wrapper` (either from `boost` or the standard library) + over an object of type `container` \n +\b Default: None +\cgalParamEnd + +\cgalParamBegin{nesting_levels} \anchor PMP_nesting_levels +a `reference_wrapper` (either from `boost` or the standard library) containing +a reference to an object that must be a model of the `BackInsertionSequence` concept, +with a value_type being `std::size_t`. +The size of the container is exactly the number of connected components. +For each connected component identified using its id `ccid`, the container contains the number of +connected components containing on their bounded side this component. +\n +\b Type: a `reference_wrapper` (either from `boost` or the standard library) + over an object of type `container` \n +\b Default: None +\cgalParamEnd + +\cgalParamBegin{is_cc_outward_oriented} \anchor PMP_is_cc_outward_oriented +a `reference_wrapper` (either from `boost` or the standard library) containing +a reference to an object that must be a model of the `BackInsertionSequence` concept, +with a value_type being `bool`. +The size of the container is exactly the number of connected components. +For each connected component identified using its id `ccid`, the output of `is_outward_oriented` +called on the submesh corresponding to this connected component +is the value at the position `ccid` in the parameter container. +\n +\b Type: a `reference_wrapper` (either from `boost` or the standard library) + over an object of type `container` \n +\b Default: None +\cgalParamEnd + +\cgalParamBegin{intersecting_volume_pairs_output_iterator} \anchor PMP_intersecting_volume_pairs_output_iterator +Output iterator into which pairs of ids (id must be convertible to `std::size_t`) can be put. +Each pair of connected components intersecting will be reported using their ids. +If `do_self_intersection_tests` named parameter is set to `false`, nothing will be reported. +\b Type : a model of `OutputIterator` \n +\b Default : `#CGAL::Emptyset_iterator` +\cgalParamEnd + \cgalNPBegin{use_Delaunay_flips} \anchor PMP_use_Delaunay_flips Parameter used in the function `smooth_mesh()` to indicate if Delaunay-based flips should be used after area-based smoothing has been performed. A user wishing to preserve combinatorial information diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt index aa033ce874f..c0faeaa18a4 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt @@ -136,7 +136,9 @@ and provides a list of the parameters that are used in this package. \cgalCRPSection{Orientation Functions} - `CGAL::Polygon_mesh_processing::orient_polygon_soup()` - `CGAL::Polygon_mesh_processing::orient()` +- `CGAL::Polygon_mesh_processing::does_bound_a_volume()` - `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()` +- `CGAL::Polygon_mesh_processing::volume_connected_components()` - `CGAL::Polygon_mesh_processing::is_outward_oriented()` - `CGAL::Polygon_mesh_processing::reverse_face_orientations()` @@ -183,7 +185,6 @@ and provides a list of the parameters that are used in this package. - `CGAL::Polygon_mesh_processing::surface_intersection()` - `CGAL::Polygon_mesh_processing::clip()` - `CGAL::Polygon_mesh_processing::split()` -- `CGAL::Polygon_mesh_processing::does_bound_a_volume()` \cgalCRPSection{Geometric Measure Functions} - \link measure_grp `CGAL::Polygon_mesh_processing::face_area()` \endlink diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt index b038beef155..d356437bacc 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt @@ -598,6 +598,10 @@ outside of the domain bounded by the input polygon mesh. of halfedges around faces. As a consequence, the normal computed for each face (see Section \ref PMPNormalComp) is also reversed. +- The function `CGAL::Polygon_mesh_processing::volume_connected_components()` provides information about +the 3D arrangement of the surface connected components in a given triangle mesh. It comes with many +named parameter options making it also a more general version of `is_outward_oriented()`. + \subsection PolygonSoupExample Orientation Example @@ -790,6 +794,10 @@ enable the user to keep and remove only a selection of connected components, provided either as a range of faces that belong to the desired connected components or as a range of connected component ids (one or more per connected component). +When a triangle mesh has no boundary, it partitions the 3D space in different volumes. +The function `CGAL::Polygon_mesh_processing::volume_connected_components()` can be used to assign to +each face an id per volume defined by the surface connected components. + Finally, it can be useful to quickly remove some connected components, for example for noisy data where small connected components should be discarded in favor of major connected components. The function `CGAL::Polygon_mesh_processing::keep_largest_connected_components()` @@ -806,7 +814,6 @@ Also, the function `CGAL::Polygon_mesh_processing::split_connected_components()` enables the user to split the connected components of a polygon mesh in as many polygon meshes. - \subsection CCExample Connected Components Example The first example shows how to record the connected @@ -823,7 +830,6 @@ which enables to treat one or several connected components as a face graph. \cgalExample{Polygon_mesh_processing/face_filtered_graph_example.cpp} - \section PMPDistance Approximate Hausdorff Distance This package provides methods to compute (approximate) distances between meshes and point sets. diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt index 194199ba808..f50f3512ede 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt @@ -22,6 +22,7 @@ \example Polygon_mesh_processing/corefinement_mesh_union_and_intersection.cpp \example Polygon_mesh_processing/corefinement_consecutive_bool_op.cpp \example Polygon_mesh_processing/detect_features_example.cpp +\example Polygon_mesh_processing/volume_connected_components.cpp \example Polygon_mesh_processing/manifoldness_repair_example.cpp \example Polygon_mesh_processing/repair_polygon_soup_example.cpp \example Polygon_mesh_processing/mesh_smoothing_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index f529d52bee1..8aad160bacb 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -83,6 +83,7 @@ create_single_source_cgal_program( "corefinement_polyhedron_union.cpp" ) create_single_source_cgal_program( "random_perturbation_SM_example.cpp" ) create_single_source_cgal_program( "corefinement_LCC.cpp") create_single_source_cgal_program( "detect_features_example.cpp" ) +create_single_source_cgal_program( "volume_connected_components.cpp" ) create_single_source_cgal_program( "manifoldness_repair_example.cpp" ) create_single_source_cgal_program( "repair_polygon_soup_example.cpp" ) create_single_source_cgal_program( "mesh_smoothing_example.cpp") @@ -91,6 +92,7 @@ create_single_source_cgal_program( "locate_example.cpp") #create_single_source_cgal_program( "snapping_example.cpp") #create_single_source_cgal_program( "mesh_self_intersection_removal_example.cpp") + if(OpenMesh_FOUND) create_single_source_cgal_program( "compute_normals_example_OM.cpp" ) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/volume_connected_components.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/volume_connected_components.cpp new file mode 100644 index 00000000000..948706a87df --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/volume_connected_components.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef CGAL::Surface_mesh Surface_mesh; + +namespace PMP = CGAL::Polygon_mesh_processing; +namespace params = CGAL::parameters; + +int main(int argc, char** argv) +{ + const char* filename = (argc > 1) ? argv[1] : "data/blobby.off"; + std::ifstream input(filename); + assert(input); + Surface_mesh sm; + input >> sm; + + // property map to assign a volume id for each face + Surface_mesh::Property_map vol_id_map = + sm.add_property_map().first; + + std::vector err_codes; + // fill the volume id map + std::size_t nb_vol = + PMP::volume_connected_components(sm, + vol_id_map, + params::error_codes(std::ref(err_codes))); + + std::cout << "Found " << nb_vol << " volumes\n"; + + // write each volume in an OFF file + typedef CGAL::Face_filtered_graph Filtered_graph; + Filtered_graph vol_mesh(sm, 0, vol_id_map); + for(std::size_t id = 0; id < nb_vol; ++id) + { + if (err_codes[id] != PMP::VALID_VOLUME) + std::cerr << "There is an issue with volume #" << id << "\n"; + if(id > 0) + vol_mesh.set_selected_faces(id, vol_id_map); + Surface_mesh out; + CGAL::copy_face_graph(vol_mesh, out); + std::ostringstream oss; + oss << "vol_" << id <<".off"; + std::ofstream os(oss.str().data()); + os << out; + } +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index c6d7f87e1b1..e0e8c27de2f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -35,96 +35,6 @@ using Polygon_mesh_processing::Corefinement::Self_intersection_exception; namespace Polygon_mesh_processing { -namespace internal { - -template -bool recursive_does_bound_a_volume(const TriangleMesh& tm, - Vpm& vpm, - Fid_map fid_map, - const std::vector& xtrm_vertices, - boost::dynamic_bitset<>& cc_handled, - const std::vector& face_cc, - std::size_t xtrm_cc_id, - bool is_parent_outward_oriented) -{ - typedef boost::graph_traits Graph_traits; - typedef typename Graph_traits::face_descriptor face_descriptor; - typedef Side_of_triangle_mesh Side_of_tm; - -// first check that the orientation of the current cc is consistant with its -// parent cc containing it - bool new_is_parent_outward_oriented = internal::is_outward_oriented( - xtrm_vertices[xtrm_cc_id], tm, parameters::vertex_point_map(vpm)); - if (new_is_parent_outward_oriented==is_parent_outward_oriented) - return false; - cc_handled.set(xtrm_cc_id); - - std::size_t nb_cc = cc_handled.size(); - -// get all cc that are inside xtrm_cc_id - std::vector cc_faces; - for(face_descriptor fd : faces(tm)) - { - if(face_cc[get(fid_map, fd)]==xtrm_cc_id) - cc_faces.push_back(fd); - } - - typename Side_of_tm::AABB_tree aabb_tree(cc_faces.begin(), cc_faces.end(), - tm, vpm); - Side_of_tm side_of_cc(aabb_tree); - - std::vector cc_inside; - for(std::size_t id=0; id new_cc_handled(nb_cc,0); - new_cc_handled.set(); - new_cc_handled.reset(new_xtrm_cc_id); - cc_handled.set(new_xtrm_cc_id); - - std::size_t nb_candidates = cc_inside.size(); - for (std::size_t i=1;i - get(vpm,xtrm_vertices[new_xtrm_cc_id]).z()) new_xtrm_cc_id=candidate; - new_cc_handled.reset(candidate); - cc_handled.set(candidate); - } - - if ( !internal::recursive_does_bound_a_volume( - tm, vpm, fid_map, xtrm_vertices, new_cc_handled, face_cc, - new_xtrm_cc_id, new_is_parent_outward_oriented) ) return false; - } - -// now explore remaining cc included in the same cc as xtrm_cc_id - boost::dynamic_bitset<> cc_not_handled = ~cc_handled; - std::size_t new_xtrm_cc_id = cc_not_handled.find_first(); - if (new_xtrm_cc_id == cc_not_handled.npos) return true; - - for (std::size_t candidate = cc_not_handled.find_next(new_xtrm_cc_id); - candidate < cc_not_handled.npos; - candidate = cc_not_handled.find_next(candidate)) - { - if(get(vpm,xtrm_vertices[candidate]).z() > get(vpm,xtrm_vertices[new_xtrm_cc_id]).z()) - new_xtrm_cc_id = candidate; - } - - return internal::recursive_does_bound_a_volume( - tm, vpm, fid_map, xtrm_vertices, cc_handled, face_cc, - new_xtrm_cc_id, is_parent_outward_oriented); -} - -} //end of namespace internal - namespace Corefinement { /** \ingroup PMP_corefinement_grp @@ -145,99 +55,6 @@ enum Boolean_operation_type {UNION = 0, INTERSECTION=1, #endif } -/** \ingroup PMP_corefinement_grp - * - * indicates if `tm` bounds a volume. - * See \ref coref_def_subsec for details. - * - * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. - * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" - * - * @param tm a closed triangulated surface mesh - * @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below - * - * @pre `CGAL::is_closed(tm)` - * - * \cgalNamedParamsBegin - * \cgalParamBegin{vertex_point_map} - * the property map with the points associated to the vertices of `tm`. - * If this parameter is omitted, an internal property map for - * `CGAL::vertex_point_t` must be available in `TriangleMesh` - * \cgalParamEnd - * \cgalParamBegin{face_index_map} - * a property map containing the index of each face of `tm`. - * \cgalParamEnd - * \cgalNamedParamsEnd - * - * \see `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()` - */ -template -bool does_bound_a_volume(const TriangleMesh& tm, - const NamedParameters& np) -{ - typedef boost::graph_traits Graph_traits; - typedef typename Graph_traits::vertex_descriptor vertex_descriptor; - typedef typename GetVertexPointMap::const_type Vpm; - typedef typename boost::property_traits::value_type Point; - typedef typename Kernel_traits::Kernel Kernel; - - if (!is_closed(tm)) return false; - if (!is_triangle_mesh(tm)) return false; - - Vpm vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), - get_const_property_map(boost::vertex_point, tm)); - - typedef typename GetInitializedFaceIndexMap::const_type Fid_map; - Fid_map fid_map = get_initialized_face_index_map(tm, np); - - std::vector face_cc(num_faces(tm), std::size_t(-1)); - - // set the connected component id of each face - std::size_t nb_cc = connected_components(tm, - bind_property_maps(fid_map,make_property_map(face_cc)), - parameters::face_index_map(fid_map)); - - if (nb_cc == 1) - return true; - - boost::dynamic_bitset<> cc_handled(nb_cc, 0); - - // extract a vertex with max z coordinate for each connected component - std::vector xtrm_vertices(nb_cc, Graph_traits::null_vertex()); - for(vertex_descriptor vd : vertices(tm)) - { - std::size_t cc_id = face_cc[get(fid_map, face(halfedge(vd, tm), tm))]; - if (xtrm_vertices[cc_id] == Graph_traits::null_vertex()) - xtrm_vertices[cc_id]=vd; - else - if (get(vpm, vd).z()>get(vpm,xtrm_vertices[cc_id]).z()) - xtrm_vertices[cc_id]=vd; - } - - //extract a vertex with max z amongst all components - std::size_t xtrm_cc_id=0; - for(std::size_t id=1; idget(vpm,xtrm_vertices[xtrm_cc_id]).z()) - xtrm_cc_id=id; - - bool is_parent_outward_oriented = - !internal::is_outward_oriented(xtrm_vertices[xtrm_cc_id], tm, np); - - return internal::recursive_does_bound_a_volume(tm, vpm, fid_map, - xtrm_vertices, - cc_handled, - face_cc, - xtrm_cc_id, - is_parent_outward_oriented); -} - -/// \cond SKIP_IN_MANUAL -template -bool does_bound_a_volume(const TriangleMesh& tm) -{ - return does_bound_a_volume(tm, parameters::all_default()); -} -/// \endcond #define CGAL_COREF_SET_OUTPUT_EDGE_MARK_MAP(I) \ typedef typename internal_np::Lookup_named_param_def < \ diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h index 602ae2a2fde..325a3693d5b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Face_graph_output_builder.h @@ -463,11 +463,19 @@ public: Intersection_edge_map& intersection_edges2 = mesh_to_intersection_edges[&tm2]; // The property map must be either writable or well-initialized - if(!BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1))) + if( CGAL::internal::Is_writable_property_map::value && + !BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1)) ) + { BGL::internal::initialize_face_index_map(fids1, tm1); + } + CGAL_assertion(BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1))); - if(!BGL::internal::is_index_map_valid(fids2, num_faces(tm2), faces(tm2))) + if( CGAL::internal::Is_writable_property_map::value && + !BGL::internal::is_index_map_valid(fids2, num_faces(tm2), faces(tm2)) ) + { BGL::internal::initialize_face_index_map(fids2, tm2); + } + CGAL_assertion(BGL::internal::is_index_map_valid(fids2, num_faces(tm2), faces(tm2))); // bitset to identify coplanar faces boost::dynamic_bitset<> tm1_coplanar_faces(num_faces(tm1), 0); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Output_builder_for_autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Output_builder_for_autorefinement.h index 031d23a1c36..db57fbee53a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Output_builder_for_autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Output_builder_for_autorefinement.h @@ -237,8 +237,12 @@ public: } // The property map must be either writable or well-initialized - if(!BGL::internal::is_index_map_valid(fids, num_faces(tm), faces(tm))) + if( CGAL::internal::Is_writable_property_map::value && + !BGL::internal::is_index_map_valid(fids, num_faces(tm), faces(tm)) ) + { BGL::internal::initialize_face_index_map(fids, tm); + } + CGAL_assertion(BGL::internal::is_index_map_valid(fids, num_faces(tm), faces(tm))); // bitset to identify coplanar faces boost::dynamic_bitset<> tm_coplanar_faces(num_faces(tm), 0); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_impl.h index ab52338fcab..72c94c605d7 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/intersection_impl.h @@ -1268,6 +1268,8 @@ public: : nodes(tm1, tm2, vpm1, vpm2) , visitor(v) { + CGAL_precondition(is_triangle_mesh(tm1)); + CGAL_precondition(is_triangle_mesh(tm2)); CGAL_assertion_code( doing_autorefinement=false; ) } @@ -1278,6 +1280,7 @@ public: : nodes(tm, tm, vpm, vpm) , visitor(v) { + CGAL_precondition(is_triangle_mesh(tm)); CGAL_assertion_code( doing_autorefinement=true; ) } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index da6fb04d54e..8fa56070cb1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -22,8 +22,11 @@ #include #include -#include -#include +#include +#include +#include +#include +#include namespace CGAL { @@ -58,6 +61,87 @@ struct Less_on_point_of_target const VertexPointMap& vpm; }; +// Given a container of vectors of halfedges whose target are geometrically indentical, +// check that the intervals described by these pairs are either disjoint or nested. +// This is done to ensure valid combinatorics when we merge the vertices. +// If incompatible (overlapping) intervals are found, the pair representating the longest +// interval (arbitrary choice) is removed from the candidate list. +template +void sanitize_candidates(const std::vector::halfedge_descriptor, std::size_t> >& cycle_hedges, + std::vector >& candidate_hedges_with_id, + const VPM vpm, + const PolygonMesh& pm) +{ + if(candidate_hedges_with_id.empty()) + return; + + std::size_t nm_vertices_n = candidate_hedges_with_id.size(); + for(std::size_t fr_id=0, fr_end=nm_vertices_n-1; fr_id& first_candidates = candidate_hedges_with_id[fr_id]; + CGAL_assertion(first_candidates.size() >= 2); + + for(std::size_t i=0, ie=first_candidates.size()-1; i& second_candidates = candidate_hedges_with_id[sr_id]; + CGAL_assertion(second_candidates.size() >= 2); + + for(std::size_t j=0, je=second_candidates.size()-1; j >::iterator to_remove_iter = candidate_hedges_with_id.begin(); + if(first_candidates_range > second_candidates_range) + std::advance(to_remove_iter, fr_id); + else + std::advance(to_remove_iter, sr_id); + + candidate_hedges_with_id.erase(to_remove_iter); + + // restart the whole thing + return sanitize_candidates(cycle_hedges, candidate_hedges_with_id, vpm, pm); + } + } // entries of the second range + } // second range + } // entries of the first range + } // first range +} // warning: cycle_hedges will be altered (sorted) template @@ -75,26 +159,23 @@ void detect_identical_mergeable_vertices( std::size_t nbv=cycle_hedges.size(); std::size_t i=1; - std::set< std::pair > intervals; + // IDs of cycle_hedges + std::vector > candidate_hedges_with_id; - while(i!=nbv) + while(i != nbv) { - if ( get(vpm, target(cycle_hedges[i].first, pm)) == - get(vpm, target(cycle_hedges[i-1].first, pm)) ) + if(get(vpm, target(cycle_hedges[i].first, pm)) == + get(vpm, target(cycle_hedges[i-1].first, pm)) ) { - hedges_with_identical_point_target.push_back( std::vector() ); - hedges_with_identical_point_target.back().push_back(cycle_hedges[i-1].first); - hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first); - intervals.insert( std::make_pair(cycle_hedges[i-1].second, cycle_hedges[i].second) ); - std::size_t previous = cycle_hedges[i].second; - while(++i!=nbv) + candidate_hedges_with_id.resize(candidate_hedges_with_id.size() + 1); + candidate_hedges_with_id.back().push_back(i-1); + candidate_hedges_with_id.back().push_back(i); + while(++i != nbv) { - if ( get(vpm, target(cycle_hedges[i].first, pm)) == - get(vpm, target(cycle_hedges[i-1].first, pm)) ) + if(get(vpm, target(cycle_hedges[i].first, pm)) == + get(vpm, target(cycle_hedges[i-1].first, pm))) { - hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first); - intervals.insert( std::make_pair(previous, cycle_hedges[i].second) ); - previous = cycle_hedges[i].second; + candidate_hedges_with_id.back().push_back(i); } else { @@ -104,29 +185,22 @@ void detect_identical_mergeable_vertices( } } else + { ++i; + } } - // check that intervals are disjoint or strictly nested - // if there is only one issue we drop the whole cycle. - // @todo shall we try to be more conservative? - if (hedges_with_identical_point_target.empty()) return; - std::set< std::pair >::iterator it1 = intervals.begin(), - end2 = intervals.end(), - end1 = std::prev(end2), - it2; - for (; it1!=end1; ++it1) - for(it2=std::next(it1); it2!= end2; ++it2 ) + // Check that intervals are disjoint or strictly nested + sanitize_candidates(cycle_hedges, candidate_hedges_with_id, vpm, pm); + + for(const std::vector& candidates : candidate_hedges_with_id) + { + hedges_with_identical_point_target.resize(hedges_with_identical_point_target.size() + 1); + for(const std::size_t hid : candidates) { - CGAL_assertion(it1->firstfirst); - CGAL_assertion(it1->first < it1->second && it2->first < it2->second); - if (it1->second > it2->first && it2->second > it1->second) - { - std::cerr << "Merging is skipt to avoid bad cycle connections\n"; - hedges_with_identical_point_target.clear(); - return; - } + hedges_with_identical_point_target.back().push_back(cycle_hedges[hid].first); } + } } // \ingroup PMP_repairing_grp diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orientation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orientation.h index 03651b15e83..447d0d0f069 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orientation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orientation.h @@ -8,7 +8,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // -// Author(s) : Ilker O. Yaz +// Author(s) : Sebastien Loriot and Ilker O. Yaz #ifndef CGAL_ORIENT_POLYGON_MESH_H @@ -22,13 +22,18 @@ #include #include #include +#include #include #include #include #include +#include #include #include +#include + +#include namespace CGAL { namespace Polygon_mesh_processing { @@ -152,12 +157,17 @@ namespace internal{ * @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin - * \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh` \cgalParamEnd + * \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh` + * If this parameter is omitted, an internal property map for + * `CGAL::vertex_point_t` must be available in `TriangleMesh` + * \cgalParamEnd * \cgalParamBegin{geom_traits} a geometric traits class instance \cgalParamEnd * \cgalNamedParamsEnd * - * \todo code : The following only handles polyhedron with one connected component - * the code, the sample example and the plugin must be updated. + * \note This function is only doing an orientation test for one connected component of `pmesh`. + * For performance reasons, it is left to the user to call the function `does_bound_a_volume()` + * on a triangulated version of `pmesh` to ensure the result returned is relevant. + * For advanced usages, the function `volume_connected_components()` should be used instead. * * \sa `CGAL::Polygon_mesh_processing::reverse_face_orientations()` */ @@ -176,6 +186,7 @@ bool is_outward_oriented(const PolygonMesh& pmesh, if (faces(pmesh).first == faces(pmesh).second) return true; + using parameters::choose_parameter; using parameters::get_parameter; @@ -340,97 +351,6 @@ void reverse_face_orientations(const FaceRange& face_range, PolygonMesh& pmesh) } } } -namespace internal { - -template -void recursive_orient_volume_ccs( TriangleMesh& tm, - Vpm& vpm, - Fid_map& fid_map, - const std::vector& xtrm_vertices, - boost::dynamic_bitset<>& cc_handled, - const std::vector& face_cc, - std::size_t xtrm_cc_id, - bool is_parent_outward_oriented) -{ - typedef boost::graph_traits Graph_traits; - typedef typename Graph_traits::face_descriptor face_descriptor; - typedef Side_of_triangle_mesh Side_of_tm; - std::vector cc_faces; - for(face_descriptor fd : faces(tm)) - { - if(face_cc[get(fid_map, fd)]==xtrm_cc_id) - cc_faces.push_back(fd); - } -// first check that the orientation of the current cc is consistant with its -// parent cc containing it - bool new_is_parent_outward_oriented = internal::is_outward_oriented( - xtrm_vertices[xtrm_cc_id], tm, parameters::vertex_point_map(vpm)); - if (new_is_parent_outward_oriented==is_parent_outward_oriented) - { - Polygon_mesh_processing::reverse_face_orientations(cc_faces, tm); - new_is_parent_outward_oriented = !new_is_parent_outward_oriented; - } - cc_handled.set(xtrm_cc_id); - - std::size_t nb_cc = cc_handled.size(); - -// get all cc that are inside xtrm_cc_id - - typename Side_of_tm::AABB_tree aabb_tree(cc_faces.begin(), cc_faces.end(), - tm, vpm); - Side_of_tm side_of_cc(aabb_tree); - - std::vector cc_inside; - for(std::size_t id=0; id new_cc_handled(nb_cc,0); - new_cc_handled.set(); - new_cc_handled.reset(new_xtrm_cc_id); - cc_handled.set(new_xtrm_cc_id); - - std::size_t nb_candidates = cc_inside.size(); - for (std::size_t i=1;i - get(vpm,xtrm_vertices[new_xtrm_cc_id]).z()) new_xtrm_cc_id=candidate; - new_cc_handled.reset(candidate); - cc_handled.set(candidate); - } - - internal::recursive_orient_volume_ccs( - tm, vpm, fid_map, xtrm_vertices, new_cc_handled, face_cc, - new_xtrm_cc_id, new_is_parent_outward_oriented); - } - -// now explore remaining cc included in the same cc as xtrm_cc_id - boost::dynamic_bitset<> cc_not_handled = ~cc_handled; - std::size_t new_xtrm_cc_id = cc_not_handled.find_first(); - if (new_xtrm_cc_id == cc_not_handled.npos) return ; - - for (std::size_t candidate = cc_not_handled.find_next(new_xtrm_cc_id); - candidate < cc_not_handled.npos; - candidate = cc_not_handled.find_next(candidate)) - { - if(get(vpm,xtrm_vertices[candidate]).z() > get(vpm,xtrm_vertices[new_xtrm_cc_id]).z()) - new_xtrm_cc_id = candidate; - } - - internal::recursive_orient_volume_ccs( - tm, vpm, fid_map, xtrm_vertices, cc_handled, face_cc, - new_xtrm_cc_id, is_parent_outward_oriented); -} - -}//end internal /** * \ingroup PMP_orientation_grp @@ -443,6 +363,8 @@ void recursive_orient_volume_ccs( TriangleMesh& tm, * @param tm a closed triangulated surface mesh * @param np optional sequence of \ref pmp_namedparameters among the ones listed below * +* \pre `CGAL::is_closed(tm)` +* * \cgalNamedParamsBegin * \cgalParamBegin{vertex_point_map} * the property map with the points associated to the vertices of `tm`. @@ -531,6 +453,789 @@ void orient(TriangleMesh& tm) orient(tm, parameters::all_default()); } +/*! + * \ingroup PMP_orientation_grp + * Enumeration type used to indicate the status of a set of faces + * classified by the function `volume_connected_components()`. + * The set of faces defines either a volume connected connected component + * in the case of `VALID_VOLUME` or a surface connected component otherwise. + */ +enum Volume_error_code { VALID_VOLUME, ///< The set of faces bounds a volume + SURFACE_WITH_SELF_INTERSECTIONS, ///< The set of faces is self-intersecting + VOLUME_INTERSECTION, ///< The set of faces intersects another surface connected component + INCOMPATIBLE_ORIENTATION ///< The set of faces is included in a volume but has an incompatible orientation + }; +namespace internal { + +// helper function to copy data +template +void copy_container_content( + const std::vector& vec, + RefToContainer ref_wrapper) +{ + ref_wrapper.get().reserve(vec.size()); + for(const T& t : vec) + { + ref_wrapper.get().push_back(t); + } +} + +template +void copy_container_content( + std::vector& vec, + std::reference_wrapper > ref_wrapper) +{ + vec.swap(ref_wrapper.get()); +} + +template +void copy_container_content( + std::vector& vec, + boost::reference_wrapper > ref_wrapper) +{ + vec.swap(ref_wrapper.get()); +} + +template +inline +void copy_container_content( + std::vector&, + internal_np::Param_not_found) +{} + +template +void copy_nested_parents( + const std::vector< std::vector >& nested_parents, + RefToContainer ref_to_vector) +{ + typedef typename RefToContainer::type Container; + typedef typename Container::value_type Container_value; + + ref_to_vector.get().reserve(nested_parents.size()); + for(const auto& t : nested_parents) + { + Container_value c; + c.reserve(t.size()); + for(const std::size_t& val : t) + c.push_back(val); + ref_to_vector.get().push_back(c); + } +} + +inline +void copy_nested_parents( + std::vector< std::vector >& nested_parents, + std::reference_wrapper > > ref_to_vector) +{ + nested_parents.swap(ref_to_vector.get()); +} + +inline +void copy_nested_parents( + std::vector< std::vector >& nested_parents, + boost::reference_wrapper > > ref_to_vector) +{ + nested_parents.swap(ref_to_vector.get()); +} + +inline +void copy_nested_parents( + std::vector< std::vector >&, + internal_np::Param_not_found) +{} + + +// helper function for setting id maps +template +void set_f_cc_id( + const std::vector& f_cc, + FaceIndexMap face_index_map, + FaceCCIdMap face_cc_map, + const TriangleMesh& tm) +{ + for(typename boost::graph_traits::face_descriptor fd : faces(tm)) + put(face_cc_map, fd, f_cc[ get(face_index_map, fd) ]); +} + +template +void set_f_cc_id( + const std::vector&, + FaceIndexMap, + internal_np::Param_not_found, + const TriangleMesh&) +{} + +template +void copy_cc_to_volume_id( + std::vector& cc_volume_ids, + RefToVector ref_to_vector) +{ + ref_to_vector.get().swap( cc_volume_ids ); +} + +inline +void copy_cc_to_volume_id( + std::vector&, + internal_np::Param_not_found) +{} + +template +void copy_nesting_levels( + std::vector& nesting_levels, + RefToVector ref_to_vector) +{ + ref_to_vector.get().swap( nesting_levels ); +} + +inline +void copy_nesting_levels( + std::vector&, + internal_np::Param_not_found) +{} + +template +void copy_orientation_bitset( + const std::vector& is_cc_outward_oriented, + RefToBitset ref_to_bs) +{ + ref_to_bs.get() = is_cc_outward_oriented; +} + +inline +void copy_orientation_bitset( + const std::vector&, + internal_np::Param_not_found) +{} + +template +void set_cc_intersecting_pairs( + const std::set< std::pair >& self_intersecting_cc, + OutputIterator out) +{ + for (const std::pair& p : self_intersecting_cc) + *out++=p; +} + +inline +void set_cc_intersecting_pairs( + const std::set< std::pair >&, + internal_np::Param_not_found) +{} + +} // internal + + +/*! + * \ingroup PMP_orientation_grp + * assigns to each face of `tm` an id corresponding to the volume connected component + * it contributes to. + * + * Using the adjacency relation of two faces along an edge, a triangle mesh can be split + * into connected components (*surface components* in the following). + * A surface component without boundary separates the 3D space into an infinite and + * a finite volume. We say that the finite volume is enclosed by this surface + * component. + * + * The volume connected components (*volume components* in the following) are defined as follows: + * Each surface component `S` that is outside any volume enclosed by + * another surface component defines the *outer boundary* of a volume component. + * Each surface component that is inside the volume enclosed by `S` + * defines a *hole* if it is included in no other volume enclosed by a surface component + * but `S`. Ignoring the identified volume component, the same procedure is recursively + * repeated for all surface components in each hole. + * + * There are some special cases: + * - a non-closed surface component is reported as a volume component ignoring any inclusion test + * - a self-intersecting surface component is reported as a volume component ignoring any inclusion test + * - a surface component intersecting another surface component + * is reported as a volume component, and so are all the surface components inside its + * enclosed volume + * - if `do_orientation_tests` is set to `true`, if the holes are not all equally oriented + * (all inward or all outward) or if the holes and the outer boundary are equally + * oriented, each surface component is reported as a volume component, + * and so are all the surface components inside the corresponding enclosed volumes + * - If `do_orientation_tests` is set to `true` and the surface components that are + * outside all enclosed volumes are inward oriented, they are then considered as holes + * of the unbounded volume (that has no outer boundary) + * + * A property map for `CGAL::vertex_point_t` must be either available as an internal property map + * of `tm` or provided as one of the \ref pmp_namedparameters "Named Parameters". + * + * @tparam TriangleMesh a model of `FaceListGraph` + * @tparam VolumeFaceIndexMap a model of `WritablePropertyMap` with + * `boost::graph_traits::%face_descriptor` as key type and + * `boost::graph_traits::%faces_size_type` as value type. + * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" + * + * @param tm the input triangle mesh + * @param volume_id_map the property map filled by this function with indices of volume components associated to the faces of `tm` + * @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below + * + * @pre `CGAL::is_closed(tm)` + * + * \cgalNamedParamsBegin + * \cgalParamBegin{vertex_point_map} + * the property map with the points associated to the vertices of `tm`. + * If this parameter is omitted, an internal property map for + * `CGAL::vertex_point_t` must be available in `TriangleMesh` + * \cgalParamEnd + * \cgalParamBegin{face_index_map} + * a property map containing a unique id per face of `tm`, in the range `[0, num_faces(tm)[` + * \cgalParamEnd + * \cgalParamBegin{face_connected_component_map} + * a property map filled by this function and that will contain for each face the id + * of its surface component in the range `[0, number of surface components - 1[` + * \cgalParamEnd + * \cgalParamBegin{volume_inclusions} + * a `reference_wrapper` (either from `boost` or the standard library) containing + * a reference to an object that must be a model of the `BackInsertionSequence` concept, + * with a value type being a model of `BackInsertionSequence` of `std::size_t`, + * both types having the functions `reserve()` and `push_back()`. + * The size of the container is exactly the number of surface components of `tm`. + * The container at position `k` contains the ids of all the + * surface components that are the first intersected by any ray with source on + * the surface component `k` and directed outside the volume enclosed by the + * surface component `k`. There is only one such id but when some surface components intersect. + * \cgalParamEnd + * \cgalParamBegin{do_orientation_tests} + * if `true` (the default value), the orientation of the faces of + * each surface components will be taken into account for the definition of the volume. + * If `false`, the face orientation is ignored and the volumes are defined only by the + * nesting of surface components. + * \cgalParamEnd + * \cgalParamBegin{error_codes} + * a `reference_wrapper` (either from `boost` or the standard library) containing + * a reference to a container that must be a model of the `BackInsertionSequence` concept, + * with a `value_type` being \link PMP_orientation_grp `Volume_error_code` \endlink + * The size of the container is exactly the number of volume components. + * The container indicates the status of a volume assigned to a set of faces. + * The description of the value type is given in the documentation of the enumeration type. + * \cgalParamEnd + * \cgalParamBegin{do_self_intersection_tests} + * If `false` (the default value), it is assumed that `tm` does not contains any self-intersections. + * if `true`, the input might contain some self-intersections and a test is done + * prior to the volume decomposition. + * \cgalParamEnd + * \cgalParamBegin{connected_component_id_to_volume_id} + * a `reference_wrapper` (either from `boost` or the standard library) containing + * a reference to a container that must be a model of the `BackInsertionSequence` concept, + * with a value_type being `std::size_t`. + * The size of the container is exactly the number of connected components. + * For each connected component identified using its id `ccid`, the id of the volume it contributes + * to describe is the value at the position `ccid` in the container. + * \cgalParamEnd + * \cgalParamBegin{nesting_levels} + * a `reference_wrapper` (either from `boost` or the standard library) containing + * a reference to a container that must be a model of the `BackInsertionSequence` concept, + * with a `value_type` being `std::size_t`. + * The size of the container is exactly the number of connected components. + * For each connected component identified using its id `ccid`, the container contains the number of + * connected components containing on its bounded side this component. + * \cgalParamEnd + * \cgalParamBegin{is_cc_outward_oriented} + * a `reference_wrapper` (either from `boost` or the standard library) containing + * a reference to a container that must be a model of the `BackInsertionSequence` concept, + * with a `value_type` being `bool`. + * The size of the container is exactly the number of connected components. + * For each connected component identified using its id `ccid`, the output of `is_outward_oriented()` + * called on the triangle mesh corresponding to this connected component + * is the value at the position `ccid` in the container. + * \cgalParamEnd + * \cgalParamBegin{intersecting_volume_pairs_output_iterator} + * Output iterator into which pairs of ids (id must be convertible to `std::size_t`) can be put. + * Each pair of connected components intersecting will be reported using their ids. + * If `do_self_intersection_tests` named parameter is set to `false`, nothing will be reported. + * \cgalParamEnd + * + * \cgalNamedParamsEnd + * + * \return the number of volume components defined by `tm` + */ +template +std::size_t +volume_connected_components(const TriangleMesh& tm, + VolumeFaceIndexMap volume_id_map, + const NamedParameters& np) +{ + CGAL_precondition(is_triangle_mesh(tm)); + CGAL_precondition(is_closed(tm)); + + typedef boost::graph_traits GT; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename GT::face_descriptor face_descriptor; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GetVertexPointMap::const_type Vpm; + typedef typename GetInitializedFaceIndexMap::type FaceIndexMap; + + typedef typename Kernel_traits< + typename boost::property_traits::value_type >::Kernel Kernel; + + Vpm vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), + get_const_property_map(boost::vertex_point, tm)); + + FaceIndexMap fid_map = CGAL::get_initialized_face_index_map(tm, np); + + std::vector face_cc(num_faces(tm), std::size_t(-1)); + +// set the connected component id of each face + const std::size_t nb_cc = connected_components(tm, + bind_property_maps(fid_map,make_property_map(face_cc)), + parameters::face_index_map(fid_map)); + + // contains for each CC the CC that are in its bounded side + std::vector > nested_cc_per_cc(nb_cc); + + // copy cc-id info + internal::set_f_cc_id(face_cc, fid_map, parameters::get_parameter(np, internal_np::face_connected_component_map), tm); + + const bool do_self_intersection_tests = + parameters::choose_parameter(parameters::get_parameter(np, internal_np::do_self_intersection_tests), + false); + const bool ignore_orientation_of_cc = + !parameters::choose_parameter(parameters::get_parameter(np, internal_np::do_orientation_tests), + true); + + const bool used_as_a_predicate = + parameters::choose_parameter(parameters::get_parameter(np, internal_np::i_used_as_a_predicate), + false); // indicate if the function is called by does_bound_a_volume + + CGAL_assertion(!used_as_a_predicate || !ignore_orientation_of_cc); + + std::vector error_codes; + std::vector is_cc_outward_oriented; + std::vector cc_volume_ids(nb_cc, -1); + std::vector < std::size_t > nesting_levels(nb_cc, 0); // indicates for each CC its nesting level + + boost::dynamic_bitset<> cc_handled(nb_cc, 0); + std::size_t next_volume_id = 0; + +// Handle self-intersecting connected components + typedef std::pair Face_pair; + std::vector si_faces; + std::set< std::pair > self_intersecting_cc; // due to self-intersections + if (do_self_intersection_tests) + self_intersections(tm, std::back_inserter(si_faces)); + std::vector is_involved_in_self_intersection(nb_cc, false); + + if (!si_faces.empty() && used_as_a_predicate) + return 0; + + for(const Face_pair& fp : si_faces) + { + std::size_t first_cc_id = face_cc[ get(fid_map, fp.first) ]; + std::size_t second_cc_id = face_cc[ get(fid_map, fp.second) ]; + + if (first_cc_id==second_cc_id) + { + if ( !cc_handled.test(first_cc_id) ) + { + cc_handled.set(first_cc_id); + cc_volume_ids[first_cc_id]=next_volume_id++; + error_codes.push_back(SURFACE_WITH_SELF_INTERSECTIONS); + } + } + else + { + is_involved_in_self_intersection[first_cc_id] = true; + is_involved_in_self_intersection[second_cc_id] = true; + self_intersecting_cc.insert( make_sorted_pair(first_cc_id, second_cc_id) ); + } + } + + std::vector< std::vector > nesting_parents(nb_cc); + if (!cc_handled.all()) + { + // extract a vertex with max z coordinate for each connected component + std::vector xtrm_vertices(nb_cc, GT::null_vertex()); + for(vertex_descriptor vd : vertices(tm)) + { + halfedge_descriptor h = halfedge(vd, tm); + if (is_border(h, tm)) h = opposite(h, tm); + std::size_t cc_id = face_cc[get(fid_map, face(h, tm))]; + if (cc_handled.test(cc_id)) continue; + if (xtrm_vertices[cc_id]==GT::null_vertex()) + xtrm_vertices[cc_id]=vd; + else + if (get(vpm, vd).z()>get(vpm,xtrm_vertices[cc_id]).z()) + xtrm_vertices[cc_id]=vd; + } + + // fill orientation vector for each surface CC + if (!ignore_orientation_of_cc) + { + is_cc_outward_oriented.resize(nb_cc); + for(std::size_t cc_id=0; cc_id > faces_per_cc(nb_cc); + std::vector< std::size_t > nb_faces_per_cc(nb_cc, 0); + for(face_descriptor fd : faces(tm)) + { + std::size_t cc_id = face_cc[ get(fid_map, fd) ]; + ++nb_faces_per_cc[ cc_id ]; + } + for (std::size_t i=0; i > nested_cc_per_cc_shared(nb_cc); + std::vector < boost::dynamic_bitset<> > level_k_nestings; // container containing CCs in the same volume (one bitset per volume) at level k + level_k_nestings.push_back( ~cc_handled ); + + // the following loop is exploring the nesting level by level (0 -> max_level) + std::size_t k = 0; + while (!level_k_nestings.empty()) + { + std::vector < boost::dynamic_bitset<> > level_k_plus_1_nestings; + for(boost::dynamic_bitset<> cc_to_handle : level_k_nestings) + { + CGAL_assertion( cc_to_handle.any() ); + while(cc_to_handle.any()) + { + //extract a vertex with max z amongst all components + std::size_t xtrm_cc_id=cc_to_handle.find_first(); + for(std::size_t id = cc_to_handle.find_next(xtrm_cc_id); + id != cc_to_handle.npos; + id = cc_to_handle.find_next(id)) + { + if (get(vpm, xtrm_vertices[id]).z()>get(vpm,xtrm_vertices[xtrm_cc_id]).z()) + xtrm_cc_id=id; + } + cc_to_handle.reset(xtrm_cc_id); + nesting_levels[xtrm_cc_id] = k; + + // collect id inside xtrm_cc_id CC + typedef Side_of_triangle_mesh Side_of_tm; + typename Side_of_tm::AABB_tree aabb_tree(faces_per_cc[xtrm_cc_id].begin(), + faces_per_cc[xtrm_cc_id].end(), + tm, vpm); + Side_of_tm side_of_cc(aabb_tree); + + std::vector cc_intersecting; // contains id of CC intersecting xtrm_cc_id + + boost::dynamic_bitset<> nested_cc_to_handle(nb_cc, 0); + for(std::size_t id = cc_to_handle.find_first(); + id != cc_to_handle.npos; + id = cc_to_handle.find_next(id)) + { + if (self_intersecting_cc.count( make_sorted_pair(xtrm_cc_id, id) )!= 0) + { + cc_intersecting.push_back(id); + continue; // to not dot inclusion test for intersecting CCs + } + + if (side_of_cc(get(vpm,xtrm_vertices[id]))==ON_BOUNDED_SIDE) + { + nested_cc_per_cc[xtrm_cc_id].push_back(id); + // mark nested CC as handle and collect them for the handling of the next level + nested_cc_to_handle.set(id); + cc_to_handle.reset(id); + } + } + + //for each CC intersecting xtrm_cc_id, find the CCs included in both + for(std::size_t id : cc_intersecting) + { + typename Side_of_tm::AABB_tree aabb_tree(faces_per_cc[id].begin(), + faces_per_cc[id].end(), + tm, vpm); + Side_of_tm side_of_cc(aabb_tree); + for(std::size_t ncc_id : nested_cc_per_cc[xtrm_cc_id]) + { + if (self_intersecting_cc.count( make_sorted_pair(ncc_id, id) )!= 0) + continue; + if (side_of_cc(get(vpm,xtrm_vertices[ncc_id]))==ON_BOUNDED_SIDE) + nested_cc_per_cc_shared[id].push_back(ncc_id); + } + } + + if ( nested_cc_per_cc[xtrm_cc_id].empty() ) continue; + level_k_plus_1_nestings.push_back(nested_cc_to_handle); + } + } + ++k; + level_k_nestings.swap(level_k_plus_1_nestings); + } + + // early return for orient_to_bound_a_volume + if (parameters::choose_parameter(parameters::get_parameter(np, internal_np::i_used_for_volume_orientation),false)) + { + internal::copy_container_content(nesting_levels, parameters::get_parameter(np, internal_np::nesting_levels)); + internal::copy_container_content(is_cc_outward_oriented, parameters::get_parameter(np, internal_np::is_cc_outward_oriented)); + return 0; + } + + // detect inconsistencies of the orientation at the level 0 + // and check if all CC at level 0 are in the same volume + std::size_t ref_cc_id = nb_cc; + std::size_t FIRST_LEVEL = 0; // used to know if even or odd nesting is the top level + if(!ignore_orientation_of_cc) + { + for(std::size_t cc_id=0; cc_id`. + * The size of the vector is exactly the number of connected components. + * For each connected component identified using its id `ccid`, the output of `is_outward_oriented` + * called on the submesh corresponding to this connected component + * is the value at the position `ccid` in the parameter vector. + * \cgalParamEnd + * \cgalNamedParamsEnd + * + * \see `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()` + */ +template +bool does_bound_a_volume(const TriangleMesh& tm, const NamedParameters& np) +{ + typedef boost::graph_traits GT; + typedef typename GT::face_descriptor face_descriptor; + + CGAL_precondition(is_closed(tm)); + CGAL_precondition(is_triangle_mesh(tm)); + + Static_property_map vidmap(0); // dummy map not used + std::size_t res = + volume_connected_components(tm, vidmap, np.do_orientation_tests(true) + .i_used_as_a_predicate(true)); + CGAL_assertion(res==0 || res==1); + + return res!=0; +} + +/// \cond SKIP_IN_MANUAL +template +bool does_bound_a_volume(const TriangleMesh& tm) +{ + return does_bound_a_volume(tm, parameters::all_default()); +} + +template +std::size_t volume_connected_components(const TriangleMesh& tm, VolumeFaceIndexMap volume_id_map) +{ + return volume_connected_components(tm, volume_id_map, parameters::all_default()); +} +/// \endcond + /** \ingroup PMP_orientation_grp * @@ -543,6 +1248,8 @@ void orient(TriangleMesh& tm) * @param tm a closed triangulated surface mesh * @param np optional sequence of \ref pmp_namedparameters among the ones listed below * + * @pre `CGAL::is_closed(tm)` + * * \cgalNamedParamsBegin * \cgalParamBegin{vertex_point_map} * the property map with the points associated to the vertices of `tm`. @@ -566,16 +1273,13 @@ void orient_to_bound_a_volume(TriangleMesh& tm, const NamedParameters& np) { typedef boost::graph_traits Graph_traits; - typedef typename Graph_traits::vertex_descriptor vertex_descriptor; + typedef typename Graph_traits::face_descriptor face_descriptor; typedef typename GetVertexPointMap::const_type Vpm; - typedef typename boost::property_traits::value_type Point; - typedef typename Kernel_traits::Kernel Kernel; - typedef typename GetInitializedFaceIndexMap::type FaceIndexMap; - if (!is_closed(tm)) return; - if (!is_triangle_mesh(tm)) return; + CGAL_assertion(is_closed(tm)); + CGAL_assertion(is_triangle_mesh(tm)); using parameters::choose_parameter; using parameters::get_parameter; @@ -588,50 +1292,44 @@ void orient_to_bound_a_volume(TriangleMesh& tm, FaceIndexMap fid_map = CGAL::get_initialized_face_index_map(tm, np); std::vector face_cc(num_faces(tm), std::size_t(-1)); + std::vector nesting_levels; + std::vector is_cc_outward_oriented; + Static_property_map vidmap(0); // dummy map not used + volume_connected_components(tm, vidmap, + parameters::vertex_point_map(vpm) + .nesting_levels(boost::ref(nesting_levels)) + .face_connected_component_map(bind_property_maps(fid_map,make_property_map(face_cc))) + .i_used_for_volume_orientation(true) + .do_orientation_tests(true) + .is_cc_outward_oriented(boost::ref(is_cc_outward_oriented)) + ); // set the connected component id of each face - std::size_t nb_cc = connected_components(tm, - bind_property_maps(fid_map,make_property_map(face_cc)), - parameters::face_index_map(fid_map)); - if (nb_cc == 1) + + if (nesting_levels.empty()) //case 1 cc { - if( orient_outward != is_outward_oriented(tm)) + if( orient_outward != is_cc_outward_oriented[0]) reverse_face_orientations(faces(tm), tm); return ; } - - - boost::dynamic_bitset<> cc_handled(nb_cc, 0); - - // extract a vertex with max z coordinate for each connected component - std::vector xtrm_vertices(nb_cc, Graph_traits::null_vertex()); - for(vertex_descriptor vd : vertices(tm)) + std::size_t nb_cc = nesting_levels.size(); + boost::dynamic_bitset<> cc_to_reverse(nb_cc, 0); + for(std::size_t i=0; iget(vpm,xtrm_vertices[cc_id]).z()) - xtrm_vertices[cc_id]=vd; + if ( ((nesting_levels[i]%2==0) == orient_outward) != is_cc_outward_oriented[i] ) + { + cc_to_reverse.set(i); + } } - //extract a vertex with max z amongst all components - std::size_t xtrm_cc_id=0; - for(std::size_t id=1; idget(vpm,xtrm_vertices[xtrm_cc_id]).z()) - xtrm_cc_id=id; + std::vector faces_to_reverse; + for (face_descriptor f : faces(tm)) + if ( cc_to_reverse.test( face_cc[get(fid_map, f)] ) ) + faces_to_reverse.push_back(f); - bool is_parent_outward_oriented = - ! orient_outward; - - internal::recursive_orient_volume_ccs(tm, vpm, fid_map, - xtrm_vertices, - cc_handled, - face_cc, - xtrm_cc_id, - is_parent_outward_oriented); + reverse_face_orientations(faces_to_reverse, tm); } template diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index 846f0d1d556..6620bfa4b80 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -89,6 +89,7 @@ endif() create_single_source_cgal_program("test_orient_cc.cpp") create_single_source_cgal_program("test_pmp_transform.cpp") create_single_source_cgal_program("extrude_test.cpp") + create_single_source_cgal_program("test_split_volume.cpp") create_single_source_cgal_program("test_merging_border_vertices.cpp") create_single_source_cgal_program("test_repair_polygon_soup.cpp") create_single_source_cgal_program("test_shape_predicates.cpp") diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points_2.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points_2.off new file mode 100644 index 00000000000..899498241eb --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points_2.off @@ -0,0 +1,148 @@ +OFF +69 77 0 +2734.3676346525872 3434.3504459479127 1442.9335140122612 +2719.3403506595987 3453.4161721323617 1460.6868219157659 +2709.1200036965829 3441.0937601908799 1443.3605847576555 +2707.2898207580311 3427.6843473822037 1462.6824351011669 +2722.1846572494605 3416.4192046623757 1441.5944682235963 +2742.8716613249344 3402.490623086861 1440.3198836496226 +2753.3364495634642 3427.6689952491442 1442.5723811823786 +2677.1902323292707 3388.8980441619497 1468.2608674769649 +2682.9890456558351 3394.0201967527873 1469.405566697666 +2675.442865180702 3393.1462806017598 1467.5442633199923 +2686.8980753431397 3380.7421806328939 1443.2374713937929 +2716.5786976037834 3400.3200157594829 1451.4765490779578 +2685.7236893195068 3384.3586152364051 1464.1503953788588 +2684.8536689831963 3407.8756005874329 1468.3014418028488 +2697.5519534461582 3412.3935343507283 1472.1757125168774 +2671.8706491766616 3440.1432641534166 1468.4737378177533 +2690.720700628156 3395.1959881699113 1469.5153182281392 +2629.6983323962659 3429.4843967878969 1472.5423339454806 +2594.4066161078717 3450.0106258471924 1477.7666258487839 +2611.1701258541689 3412.7742988800183 1469.5336661012821 +2578.8472625124887 3405.1212073127308 1451.316062559159 +2597.9102056517986 3407.5801335061378 1455.8513406758216 +2595.5942479980158 3336.8714115436887 1450.4555995156179 +2651.1411353571648 3268.7347854042091 1448.2437478852689 +2642.0007558087773 3340.8524168296035 1471.6775773740933 +2684.6578030762194 3284.3494943162532 1444.8361338629848 +2746.6435546875 3281.73828125 1464.1597900390625 +2740.7837371075884 3314.211365505269 1439.3263783429693 +2733.017717935536 3268.5602714994648 1441.9623977045908 +2809.9441752754037 3300.5772357138785 1435.0251730088157 +2773.6435271344671 3279.0727549248313 1437.6208090620771 +2800.655517578125 3280.3955078125 1435.466064453125 +2614.6291234451946 3386.7314361481222 1449.0219180273643 +2588.0364408767973 3377.0482872259145 1462.3690172263191 +2827.7365690371798 3283.0041660240058 1433.753734396882 +2825.802734375 3295.31640625 1451.844482421875 +2635.3136822443203 3449.3019266923911 1473.2683784698279 +2648.414115329484 3487.3622759119589 1479.7258550820666 +2823.434618860947 3319.5472154122308 1435.5915969990174 +2853.7975913909549 3332.2754306138595 1438.0417630460538 +2807.2569703176523 3348.2627066182322 1437.2224669894972 +2694.882230800019 3316.5825583479455 1442.8415459717917 +2685.0408118635805 3352.1126936768487 1444.1628949826604 +2669.277004428076 3401.8710698669383 1463.7753003388066 +2628.353515625 3411.537841796875 1453.240966796875 +2647.3840165088322 3387.4930258137474 1446.4389282539896 +2832.307535839569 3381.235958495593 1441.9428539616388 +2786.0634765625 3373.05712890625 1453.71337890625 +2838.2438104016237 3417.000586555514 1465.3140587573271 +2718.1522711267571 3264.9136200035673 1457.5973059275025 +2704.2383913459889 3256.7073475650695 1444.8385837253218 +2720.8878481688253 3350.5889280456677 1459.9354608396256 +2730.6231271484994 3356.6350443986285 1440.5864729182235 +2724.5318986025195 3383.7888818757892 1445.0939258740668 +2668.8563414775472 3369.7017848427331 1445.3383453307138 +2723.925537109375 3293.966796875 1470.58935546875 +2755.482421875 3367.92919921875 1455.7613525390625 +2781.495849609375 3361.289306640625 1454.6324462890625 +2773.2406815180684 3369.0781148672268 1438.1137239370505 +2762.7962173755814 3381.5269947752195 1439.1176815674298 +2777.5338287310433 3450.4593918498058 1445.8167897288913 +2746.2389104526192 3344.8938903067665 1439.2729644592046 +2716.5786976037834 3400.3200157594829 1451.4765490779578 +2686.8980753431397 3380.7421806328939 1443.2374713937929 +2740.7837371075884 3314.211365505269 1439.3263783429693 +2733.017717935536 3268.5602714994648 1441.9623977045908 +2669.277004428076 3401.8710698669383 1463.7753003388066 +2730.6231271484994 3356.6350443986285 1440.5864729182235 +2762.7962173755814 3381.5269947752195 1439.1176815674298 +3 0 1 2 +3 3 4 2 +3 2 4 0 +3 5 6 0 +3 7 8 9 +3 63 11 12 +3 13 14 15 +3 16 8 12 +3 17 18 19 +3 18 20 21 +3 22 23 24 +3 24 23 25 +3 26 27 28 +3 29 30 31 +3 22 32 33 +3 34 29 31 +3 29 34 35 +3 36 15 37 +3 29 35 38 +3 62 4 3 +3 35 39 38 +3 17 36 18 +3 5 0 4 +3 17 15 36 +3 29 38 40 +3 24 41 42 +3 38 39 40 +3 2 1 15 +3 66 44 45 +3 39 46 40 +3 15 43 13 +3 47 40 46 +3 48 47 46 +3 15 1 37 +3 49 25 50 +3 9 8 13 +3 41 51 42 +3 10 52 53 +3 41 64 51 +3 13 8 16 +3 57 47 58 +3 25 41 24 +3 33 21 20 +3 59 47 6 +3 17 43 15 +3 60 6 47 +3 42 51 52 +3 58 61 57 +3 56 58 59 +3 42 52 10 +3 67 27 61 +3 21 33 32 +3 44 21 32 +3 14 13 16 +3 56 61 58 +3 67 61 56 +3 27 40 61 +3 27 26 30 +3 40 57 61 +3 32 45 44 +3 29 27 30 +3 28 30 26 +3 47 48 60 +3 27 29 40 +3 65 25 49 +3 6 5 59 +3 13 43 9 +3 47 59 58 +3 25 65 55 +3 25 55 41 +3 22 24 32 +3 12 8 7 +3 41 55 64 +3 16 12 11 +3 42 10 54 +3 53 52 68 +3 19 18 21 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp index 4ae670175e3..08e76756edf 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp @@ -43,6 +43,7 @@ int main(int argc, char** argv) if (argc==1) { test_merge_duplicated_vertices_in_boundary_cycles("data/merge_points.off", 43); + test_merge_duplicated_vertices_in_boundary_cycles("data/merge_points_2.off", 62); } else { diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_orient_cc.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_orient_cc.cpp index e5acb1dcf7e..b790acc0202 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_orient_cc.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_orient_cc.cpp @@ -4,15 +4,24 @@ #include #include #include - +#include #include #include +#define TEST_ALL_ORIENTATIONS 1 + namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef CGAL::Surface_mesh SMesh; +template +struct deque_wrapper : public std::deque +{ + template + void reserve(const U&){} +}; + template bool test_orientation(const TriangleMesh& tm, bool is_positive, const NamedParameters& np) { @@ -78,12 +87,13 @@ int main() std::ifstream input("data-coref/nested_cubes_invalid_volume.off"); assert(input); - SMesh sm1, sm2, sm3, sm4, volume; + SMesh sm1, sm2, sm3, sm4, volume, volume_copy; input >> sm1; sm2 = sm1; sm3 = sm1; sm4 = sm1; volume = sm1; + volume_copy = volume; PMP::orient(sm1); if(!test_orientation(sm1, true, PMP::parameters::all_default())) return 1; @@ -121,6 +131,7 @@ int main() return 1; } + assert(!PMP::does_bound_a_volume(volume)); PMP::orient_to_bound_a_volume(volume); if( !PMP::does_bound_a_volume(volume)) { @@ -128,6 +139,84 @@ int main() return 1; } - std::cout << "Done!" << std::endl; + PMP::orient_to_bound_a_volume(volume); + if( !PMP::does_bound_a_volume(volume)) + { + std::cerr << "ERROR for test5\n"; + return 1; + } + + PMP::orient_to_bound_a_volume(volume_copy, CGAL::parameters::outward_orientation(false)); + if( !PMP::does_bound_a_volume(volume_copy) ) + { + std::cerr << "ERROR for test6\n"; + return 1; + } + +#ifdef TEST_ALL_ORIENTATIONS //takes around 2 hours + std::cout<<"testing ALL orientations..."< fccmap = + sm1.add_property_map("f:CC").first; + std::vector is_cc_o_or; + PMP::orient_to_bound_a_volume(sm1); + PMP::does_bound_a_volume(sm1, + CGAL::parameters::is_cc_outward_oriented(std::ref(is_cc_o_or))); + + std::size_t nb_ccs = PMP::connected_components(sm1, fccmap); + + std::vector< std::vector > faces_per_cc(nb_ccs); + for(SMesh::Face_index fd : faces(sm1)) + { + std::size_t cc_id = get(fccmap, fd); + faces_per_cc[cc_id].push_back(fd); + } + //double total_length = 1<<20; + CGAL::Timer timer; + timer.start(); + //for(std::size_t i=1; i>=1; + ++cc; + } + deque_wrapper is_loop_o_o; + PMP::orient_to_bound_a_volume(loop_m); + + if( !PMP::does_bound_a_volume(loop_m, + CGAL::parameters::is_cc_outward_oriented(std::ref(is_loop_o_o))) ) + { + std::cerr << "ERROR for test7\n"; + return 1; + } + if(is_loop_o_o.empty()) + return 1; + for(std::size_t k = 0; k< is_loop_o_o.size(); ++k){ + if(is_loop_o_o[k]!= is_cc_o_or[k]) + { + std::cerr << "ERROR for test7\n"; + return 1; + } + } + if(i%1000 == 0){ + timer.stop(); + double remaining = ((1<<20) -i)* timer.time()/1000.0 ; + std::cout< +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef CGAL::Surface_mesh Surface_mesh; +typedef Kernel::Aff_transformation_3 Trsfrm; + +namespace PMP = CGAL::Polygon_mesh_processing; +namespace params = PMP::parameters; +int main() +{ + Surface_mesh base_cube; + std::ifstream input("data-coref/cube.off"); + input >> base_cube; + + Surface_mesh input_mesh; + Surface_mesh::Property_map vol_id_map = + input_mesh.add_property_map().first; + + // add large cube + input_mesh.join(base_cube); + + // add middle cube + Surface_mesh middle_cube = base_cube; + PMP::transform(Trsfrm(CGAL::SCALING, 0.5), middle_cube); + PMP::transform(Trsfrm(CGAL::TRANSLATION, Kernel::Vector_3(0.25,0.25,0.25)), middle_cube); + input_mesh.join(middle_cube); + + // add central cube + Surface_mesh small_cube = base_cube; + PMP::transform(Trsfrm(CGAL::SCALING, 0.1), small_cube); + PMP::transform(Trsfrm(CGAL::TRANSLATION, Kernel::Vector_3(0.5,0.5,0.5)), small_cube); + input_mesh.join(small_cube); + + // test using orientation + std::vector expected_result; + expected_result.push_back(PMP::VALID_VOLUME); + expected_result.push_back(PMP::INCOMPATIBLE_ORIENTATION); + expected_result.push_back(PMP::INCOMPATIBLE_ORIENTATION); + std::vector error_codes; + std::size_t nb_vol = PMP::volume_connected_components(input_mesh, vol_id_map, + params::error_codes(boost::ref(error_codes))); + assert(nb_vol==3); + std::sort(error_codes.begin(), error_codes.end()); + assert( error_codes==expected_result ); + + // test ignoring orientation + nb_vol = PMP::volume_connected_components(input_mesh, vol_id_map, + params::do_orientation_tests(false) + .error_codes(boost::ref(error_codes))); + assert(nb_vol==2); + expected_result.clear(); + expected_result.resize(2, PMP::VALID_VOLUME); + assert( error_codes==expected_result ); + + + // test surface component self-intersection + { + Surface_mesh tmp = input_mesh; + Surface_mesh::Property_map tmp_vol_id_map = + tmp.add_property_map().first; + // create a self-intersection + Surface_mesh::Halfedge_index h = *tmp.halfedges().begin(); + h = CGAL::Euler::split_edge(h, tmp); + tmp.point( target(h, tmp) ) = tmp.point( target( tmp.next(h), tmp) ); + CGAL::Euler::split_face(h, next( next(h, tmp), tmp), tmp); + h = opposite(h, tmp); + CGAL::Euler::split_face(h, next( next(h, tmp), tmp), tmp); + + nb_vol = PMP::volume_connected_components(tmp, tmp_vol_id_map, + params::do_orientation_tests(false) + .error_codes(boost::ref(error_codes)) + .do_self_intersection_tests(true)); + + assert(nb_vol==2); + expected_result.clear(); + expected_result.push_back(PMP::VALID_VOLUME); + expected_result.push_back(PMP::SURFACE_WITH_SELF_INTERSECTIONS); + std::sort(error_codes.begin(), error_codes.end()); + assert( error_codes==expected_result ); + } + + // test single cc + { + Surface_mesh tmp = base_cube; + Surface_mesh::Property_map tmp_vol_id_map = + tmp.add_property_map().first; + + nb_vol = PMP::volume_connected_components(tmp, tmp_vol_id_map, + params::do_orientation_tests(false) + .error_codes(boost::ref(error_codes)) + .do_self_intersection_tests(true)); + + assert(nb_vol==1); + expected_result.clear(); + expected_result.push_back(PMP::VALID_VOLUME); + assert( error_codes==expected_result ); + } + + // test intersection between cc + { + Surface_mesh tmp = input_mesh; + // create surface intersection + tmp.join(base_cube); + Surface_mesh::Property_map tmp_vol_id_map = + tmp.add_property_map().first; + + nb_vol = PMP::volume_connected_components(tmp, tmp_vol_id_map, + params::do_orientation_tests(false) + .error_codes(boost::ref(error_codes)) + .do_self_intersection_tests(true)); + + assert(nb_vol==4); + expected_result.clear(); + expected_result.resize(4, PMP::VOLUME_INTERSECTION); + assert( error_codes==expected_result ); + } + { + Surface_mesh tmp = input_mesh; + // create surface intersection + tmp.join(middle_cube); + tmp.join(middle_cube); + Surface_mesh::Property_map fccmap = + tmp.add_property_map("f:CC").first; + + Surface_mesh::Property_map tmp_vol_id_map = + tmp.add_property_map().first; + std::vector< std::vector > nested_cc_per_cc; + + nb_vol = PMP::volume_connected_components(tmp, tmp_vol_id_map, + params::do_orientation_tests(false) + .error_codes(boost::ref(error_codes)) + .face_connected_component_map(fccmap) + .do_self_intersection_tests(true) + .volume_inclusions(boost::ref(nested_cc_per_cc))); + + assert(nb_vol==5); + expected_result.clear(); + expected_result.push_back(PMP::VALID_VOLUME); + expected_result.resize(5, PMP::VOLUME_INTERSECTION); + std::sort(error_codes.begin(), error_codes.end()); + assert( error_codes==expected_result ); + std::size_t sum=0; + for (int i=0; i<5; ++i) + { + sum+=nested_cc_per_cc[i].size(); + assert(nested_cc_per_cc[i].size()==1 || + nested_cc_per_cc[i].size()==3 || + nested_cc_per_cc[i].size()==0); + } + assert(sum == 0+3*1+3); + } + + // test level 0 orientation + { + Surface_mesh tmp = base_cube; + PMP::transform(Trsfrm(CGAL::TRANSLATION, Kernel::Vector_3(2,0,0)), tmp); + tmp.join(base_cube); + PMP::reverse_face_orientations(tmp); + Surface_mesh::Property_map tmp_vol_id_map = + tmp.add_property_map().first; + // create a self-intersection + nb_vol = PMP::volume_connected_components(tmp, tmp_vol_id_map, + params::do_orientation_tests(false)); + + assert(nb_vol==2); + nb_vol = PMP::volume_connected_components(tmp, tmp_vol_id_map, + params::do_orientation_tests(true)); + + assert(nb_vol==1); + } + // debug code + /* + std::cout << " found " << nb_vol << " volumes\n"; + typedef CGAL::Face_filtered_graph Filtered_graph; + Filtered_graph vol_mesh(sm, 0, vol_id_map); + for(std::size_t id = 0; id < nb_vol; ++id) + { + if(id > 0) + vol_mesh.set_selected_faces(id, vol_id_map); + Surface_mesh out; + CGAL::copy_face_graph(vol_mesh, out); + std::ostringstream oss; + oss << "vol_" << id <<".off"; + std::ofstream os(oss.str().data()); + os << out; + } + */ +} diff --git a/Polyhedron/demo/Polyhedron/C3t3_type.h b/Polyhedron/demo/Polyhedron/C3t3_type.h index 245c8c95fdc..d37bf5306f7 100644 --- a/Polyhedron/demo/Polyhedron/C3t3_type.h +++ b/Polyhedron/demo/Polyhedron/C3t3_type.h @@ -1,6 +1,9 @@ #ifndef CGAL_DEMO_MESH_3_C3T3_TYPE_H #define CGAL_DEMO_MESH_3_C3T3_TYPE_H +#include "config.h" +#include "Plugins/Mesh_3/config_mesh_3.h" + // include this to get #define BOOST_PARAMETER_MAX_ARITY 12 // as otherwise it gets set via inclusion of Polyhedron_3.h #include diff --git a/Polyhedron/demo/Polyhedron/CMakeLists.txt b/Polyhedron/demo/Polyhedron/CMakeLists.txt index 6a60e678a8b..9e00808a516 100644 --- a/Polyhedron/demo/Polyhedron/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/CMakeLists.txt @@ -122,9 +122,6 @@ else( CGAL_ACTIVATE_CONCURRENT_MESH_3 OR ENV{CGAL_ACTIVATE_CONCURRENT_MESH_3} ) endif( LINK_WITH_TBB ) endif() - - - if(CGAL_Qt5_FOUND AND Qt5_FOUND) include(${CGAL_USE_FILE}) @@ -254,6 +251,10 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND) if(TBB_FOUND) CGAL_target_use_TBB(scene_c3t3_item) endif() + if(COMMAND target_precompile_headers) + # Support for precompiled headers, for Mesh_3 (since CMake 3.16) + target_precompile_headers(scene_c3t3_item PUBLIC [["C3t3_type.h"]]) + endif() add_item(scene_transform_item Plugins/PCA/Scene_facegraph_transform_item.cpp ) add_item(scene_edit_box_item Plugins/PCA/Scene_edit_box_item.cpp ) add_item(scene_image_item Scene_image_item.cpp) diff --git a/Polyhedron/demo/Polyhedron/Color_map.h b/Polyhedron/demo/Polyhedron/Color_map.h index 46164c649cb..2e04a3ee5bd 100644 --- a/Polyhedron/demo/Polyhedron/Color_map.h +++ b/Polyhedron/demo/Polyhedron/Color_map.h @@ -8,7 +8,7 @@ inline QColor generate_color(double h, double s_min = 0.35) std::size_t s_max=255; if(h >0.8 && h < 0.95) //span of ugly pink, desaturates make it less garish IMO s_max = 160; - std::size_t s = std::rand() % (s_max-static_cast(s_min*255)) +s_min*255; + std::size_t s = std::rand() % (s_max-static_cast(s_min*255)) + static_cast(s_min*255); return QColor::fromHsvF(h,s/255.0,1.0); } diff --git a/Polyhedron/demo/Polyhedron/MainWindow.cpp b/Polyhedron/demo/Polyhedron/MainWindow.cpp index e05962776a6..38fa9d16ea2 100644 --- a/Polyhedron/demo/Polyhedron/MainWindow.cpp +++ b/Polyhedron/demo/Polyhedron/MainWindow.cpp @@ -2504,8 +2504,8 @@ void MainWindow::viewerShowObject() CGAL::qglviewer::Vec min((float)bbox.xmin()+viewer->offset().x, (float)bbox.ymin()+viewer->offset().y, (float)bbox.zmin()+viewer->offset().z), max((float)bbox.xmax()+viewer->offset().x, (float)bbox.ymax()+viewer->offset().y, (float)bbox.zmax()+viewer->offset().z); viewer->setSceneBoundingBox(min, max); - viewerShow(min.x, min.y, min.z, - max.x, max.y, max.z); + viewerShow((float)min.x, (float)min.y, (float)min.z, + (float)max.x, (float)max.y, (float)max.z); } } /* to check diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/C3t3_rib_exporter_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/C3t3_rib_exporter_plugin.cpp index 7e11d62f6a8..f622cfa4862 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/C3t3_rib_exporter_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/C3t3_rib_exporter_plugin.cpp @@ -755,8 +755,8 @@ write_surface_cells(const C3t3& c3t3, const Plane& /* plane */, std::ofstream& o //const int TRANSPARENCY_ALPHA_VALUE = 100; CGAL::Bbox_3 bbox = c3t3.bbox(); - float relPos = (c->weighted_circumcenter().x() - bbox.xmin()) - / (bbox.xmax() - bbox.xmin()); + float relPos = static_cast((c->weighted_circumcenter().x() - bbox.xmin()) + / (bbox.xmax() - bbox.xmin())); float TRANSPARENCY_ALPHA_VALUE = 1.f - (relPos < 0.25f ? @@ -842,7 +842,7 @@ write_surface_cells(const C3t3& c3t3, const Plane& /* plane */, std::ofstream& o { if (i == last_2D_vertex_index) { - edgecolor.setAlpha(TRANSPARENCY_ALPHA_VALUE); + edgecolor.setAlphaF(TRANSPARENCY_ALPHA_VALUE); } else { diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/CMakeLists.txt index a2af303f16c..8f1468406ac 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/CMakeLists.txt @@ -10,6 +10,7 @@ polyhedron_demo_plugin(mesh_3_plugin Mesh_3_plugin ${meshingUI_FILES} KEYWORDS Mesh_3) target_link_libraries(mesh_3_plugin PUBLIC scene_polygon_soup_item scene_polylines_item scene_implicit_function_item scene_image_item scene_surface_mesh_item scene_c3t3_item ${OPENGL_gl_LIBRARY} ) +target_include_directories(mesh_3_plugin PRIVATE include) find_package(VTK QUIET COMPONENTS vtkImagingGeneral vtkIOImage NO_MODULE) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp index 8a6f182fa17..2eb04fb34d3 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin.cpp @@ -19,6 +19,16 @@ #include #include +#include + +// Small addition from GSL v2.0.0: +template +auto make_not_null(T&& t) { + return gsl::not_null>>{std::forward(t)}; +} + +#include +#include #include "Scene_polylines_item.h" #ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS @@ -26,6 +36,7 @@ #endif #ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES #include "Scene_image_item.h" +#include "Image_type.h" #endif #include "Meshing_thread.h" @@ -39,6 +50,7 @@ const QColor default_mesh_color(45,169,70); #include "Mesh_3_plugin_cgal_code.h" // declare functions `cgal_code_mesh_3` #include "split_polylines.h" +#include class Mesh_3_plugin : public QObject, @@ -48,6 +60,25 @@ class Mesh_3_plugin : Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0" FILE "mesh_3_plugin.json") + Q_PROPERTY(double angle READ get_angle WRITE set_angle); + Q_PROPERTY(double sharp_edges_angle_bound + READ get_sharp_edges_angle_bound + WRITE set_sharp_edges_angle_bound); + Q_PROPERTY(double edges_sizing READ get_edges_sizing WRITE set_edges_sizing); + Q_PROPERTY(double facets_sizing READ get_facets_sizing WRITE set_facets_sizing); + Q_PROPERTY(double approx READ get_approx WRITE set_approx); + Q_PROPERTY(double tets_sizing READ get_tets_sizing WRITE set_tets_sizing); + Q_PROPERTY(double tets_shape READ get_tets_shape WRITE set_tets_shape); + Q_PROPERTY(bool protect_features READ get_protect_features WRITE set_protect_features); + Q_PROPERTY(bool protect_borders READ get_protect_borders WRITE set_protect_borders); + Q_PROPERTY(bool manifold_criterion READ get_manifold_criterion WRITE set_manifold_criterion); + + typedef CGAL::Mesh_facet_topology Mesh_facet_topology; + Q_ENUMS(Mesh_facet_topology) + Q_PROPERTY(Mesh_facet_topology facet_topology + READ get_facet_topology + WRITE set_facet_topology) + public: void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, @@ -91,37 +122,61 @@ public: return qobject_cast (scene->item(scene->mainSelectionIndex())) != nullptr; } -#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS - if(qobject_cast - (scene->item(scene->mainSelectionIndex())) != nullptr) { - return true; - } -#endif -#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES - if( qobject_cast - (scene->item(scene->mainSelectionIndex())) != nullptr ) { - return true; - } -#endif - for(int ind: scene->selectionIndices()){ - Scene_surface_mesh_item* sm_item - = qobject_cast(scene->item(ind)); - if(nullptr == sm_item) - return false; - } - return true; + return !get_items_or_return_error_string(); } public Q_SLOTS: + boost::optional get_items_or_return_error_string() const; + void set_defaults(); void mesh_3_volume(); void mesh_3_surface(); - void mesh_3_surface_with_defaults() { mesh_3(true, true); } + void mesh_3_surface_with_defaults() { + mesh_3(Mesh_type::SURFACE_ONLY, Dialog_choice::NO_DIALOG); + } + void mesh_3_volume_with_defaults() { + mesh_3(Mesh_type::VOLUME, Dialog_choice::NO_DIALOG); + } + void mesh_3(bool with_dialog) { // compatibility with old Qt Scripts + return mesh_3( + Mesh_type::VOLUME, + with_dialog ? Dialog_choice::DIALOG : Dialog_choice::NO_DIALOG); + } void splitPolylines(); void meshing_done(Meshing_thread* t); void status_report(QString str); +public Q_SLOTS: + void set_angle(const double v) { angle = v; }; + void set_sharp_edges_angle_bound(const double v) { + sharp_edges_angle_bound = v; + } + void set_edges_sizing(const double v) { edges_sizing = v; }; + void set_facets_sizing(const double v) { facets_sizing = v; }; + void set_approx(const double v) { approx = v; }; + void set_tets_sizing(const double v) { tets_sizing = v; }; + void set_tets_shape(const double v) { tets_shape = v; }; + void set_manifold_criterion(const bool v) { manifold_criterion = v; } + void set_facet_topology(const CGAL::Mesh_facet_topology v) { facet_topology = v; } + void set_protect_features(const bool v) { protect_features = v; }; + void set_protect_borders(const bool v) { protect_borders = v; }; + + double get_angle() { return angle; }; + double get_sharp_edges_angle_bound() { return sharp_edges_angle_bound; } + double get_edges_sizing() { return edges_sizing; }; + double get_facets_sizing() { return facets_sizing; }; + double get_approx() { return approx; }; + double get_tets_sizing() { return tets_sizing; }; + double get_tets_shape() { return tets_shape; }; + bool get_manifold_criterion() { return manifold_criterion; }; + CGAL::Mesh_facet_topology get_facet_topology() { return facet_topology; }; + bool get_protect_features() { return protect_features; }; + bool get_protect_borders() { return protect_borders; }; + + private: - void mesh_3(const bool surface_only, const bool use_defaults = false); + enum class Mesh_type : bool { VOLUME, SURFACE_ONLY }; + enum class Dialog_choice : bool { NO_DIALOG, DIALOG }; + void mesh_3(const Mesh_type mesh_type, const Dialog_choice dialog = Dialog_choice::DIALOG); void launch_thread(Meshing_thread* mesh_thread); void treat_result(Scene_item& source_item, Scene_c3t3_item* result_item) const; @@ -136,6 +191,47 @@ private: CGAL::Three::Scene_interface* scene; QMainWindow* mw; bool as_facegraph; + + double angle; + double sharp_edges_angle_bound; + int sizing_decimals; + double approx; + int approx_decimals; + double edges_sizing; + double facets_sizing; + double tets_sizing; + double tets_shape; + bool manifold_criterion; + CGAL::Mesh_facet_topology facet_topology; + bool protect_features; + bool protect_borders; + + struct Polyhedral_mesh_items { + Polyhedral_mesh_items() noexcept + : sm_items{}, bounding_sm_item(nullptr), polylines_item(nullptr) {} + QList> sm_items; + Scene_surface_mesh_item* bounding_sm_item; + Scene_polylines_item* polylines_item; + }; + struct Image_mesh_items { + gsl::not_null image_item; + Scene_polylines_item* polylines_item = nullptr; + }; + struct Implicit_mesh_items { + gsl::not_null function_item; + }; + enum Item_types { + POLYHEDRAL_MESH_ITEMS, + IMAGE_MESH_ITEMS, + IMPLICIT_MESH_ITEMS + }; + mutable boost::optional> + items; + mutable bool features_protection_available = false; + mutable Scene_item* item = nullptr; + mutable CGAL::Three::Scene_interface::Bbox bbox = {}; }; // end class Mesh_3_plugin double @@ -168,124 +264,181 @@ void Mesh_3_plugin::splitPolylines() { void Mesh_3_plugin::mesh_3_surface() { - mesh_3(true); + mesh_3(Mesh_type::SURFACE_ONLY); } void Mesh_3_plugin::mesh_3_volume() { - mesh_3(false); + mesh_3(Mesh_type::VOLUME); } -void Mesh_3_plugin::mesh_3(const bool surface_only, const bool use_defaults) -{ - QList sm_items; - Scene_surface_mesh_item* bounding_sm_item = nullptr; - Scene_implicit_function_item* function_item = nullptr; - Scene_image_item* image_item = nullptr; - Scene_polylines_item* polylines_item = nullptr; - - for(int ind: scene->selectionIndices()) { - Scene_surface_mesh_item* sm_item = - qobject_cast(scene->item(ind)); - if(sm_item) { - sm_items.push_back(sm_item); - if(is_closed(*sm_item->polyhedron())) { - bounding_sm_item = sm_item; +boost::optional Mesh_3_plugin::get_items_or_return_error_string() const { + using boost::get; + items = {}; + features_protection_available = false; + item = nullptr; + for (int ind : scene->selectionIndices()) { + try { + if (auto sm_item = + qobject_cast(scene->item(ind))) { + if (!items) items = Polyhedral_mesh_items{}; + auto& poly_items = get(*items); + auto& sm_items = poly_items.sm_items; + sm_items.push_back(make_not_null(sm_item)); + if (is_closed(*sm_item->polyhedron())) { + poly_items.bounding_sm_item = sm_item; + } + } +# ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS + else if (auto function_item = qobject_cast( + scene->item(ind))) { + if (!items) + items = Implicit_mesh_items{make_not_null(function_item)}; + else + return tr( + "An implicit function cannot be mixed with other items type"); + } +# endif +# ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES + else if (auto image_item = + qobject_cast(scene->item(ind))) { + if (!items) + items = Image_mesh_items{make_not_null(image_item)}; + else + return tr("An image items cannot be mixed with other items type"); + } +# endif + else if (auto polylines_item = + qobject_cast(scene->item(ind))) { + if (!items) items = Polyhedral_mesh_items{}; + auto poly_items_ptr = get(&*items); + if(poly_items_ptr) { + if (poly_items_ptr->polylines_item) { + return tr("Only one polyline item is accepted"); + } else { + poly_items_ptr->polylines_item = polylines_item; + } + } else { + auto image_items = get(*items); + if (image_items.polylines_item) { + return tr("Only one polyline item is accepted"); + } else { + image_items.polylines_item = polylines_item; + } + } + } else { + return tr("Wrong selection of items"); + } + } catch (const boost::bad_get&) { return tr("Wrong selection of items"); } + } // end for loop on selected items + if (!items) { return tr("Selected objects can't be meshed"); } + item = nullptr; + features_protection_available = false; + if (auto poly_items = get(&*items)) { + auto& sm_items = poly_items->sm_items; + for (auto sm_item : sm_items) { + if (nullptr == sm_item->polyhedron()) { + return tr("ERROR: no data in selected item %1").arg(sm_item->name()); + } + if (!is_triangle_mesh(*sm_item->polyhedron())) { + return tr("Selected Scene_surface_mesh_item %1 is not triangulated.") + .arg(sm_item->name()); + } + if (sm_item->getNbIsolatedvertices() != 0) { + return tr("ERROR: there are isolated vertices in this mesh."); } } -#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS - else if(function_item == nullptr && - nullptr != - (function_item = qobject_cast(scene->item(ind)))) - {} -#endif -#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES - else if(image_item == nullptr && - nullptr != (image_item = qobject_cast(scene->item(ind)))) - {} -#endif - else if(polylines_item == nullptr && - nullptr != (polylines_item = qobject_cast(scene->item(ind)))) - {} - else { - QMessageBox::warning(mw, tr("Mesh_3 plugin"), - tr("Wrong selection of items")); - return; - } - } - Scene_item* item = nullptr; - const bool more_than_one_item = sm_items.size() > 1; - bool features_protection_available = false; - if(!sm_items.empty()) - { - for(auto sm_item : sm_items) { - if(nullptr == sm_item->polyhedron()) { - QApplication::restoreOverrideCursor(); - QMessageBox::critical(mw, tr("Mesh_3 plugin"), - tr("ERROR: no data in selected item %1").arg(sm_item->name())); - return; - } - if (!is_triangle_mesh(*sm_item->polyhedron())) - { - QApplication::restoreOverrideCursor(); - QMessageBox::warning(mw, tr("Mesh_3 plugin"), - tr("Selected Scene_surface_mesh_item %1 is not triangulated.") - .arg(sm_item->name())); - return; - } - if(sm_item->getNbIsolatedvertices() != 0) - { - QApplication::restoreOverrideCursor(); - QMessageBox::critical(mw, tr(""), tr("ERROR: there are isolated vertices in this mesh.")); - return; - } - } - item = sm_items.front(); + if (!sm_items.empty()) item = sm_items.front(); features_protection_available = true; } -#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS - else if (nullptr != function_item) { item = function_item; } -#endif -#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES - else if (nullptr != image_item) - { +# ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS + else if (auto implicit_mesh_items = get(&*items)) { + item = implicit_mesh_items->function_item; + } +# endif +# ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES + else if (auto image_mesh_items = get(&*items)) { + auto& image_item = image_mesh_items->image_item; item = image_item; features_protection_available = true; bool fit_wrdtp = true; std::size_t img_wdim = image_item->image()->image()->wdim; WORD_KIND img_wordKind = image_item->image()->image()->wordKind; - //check if the word type fits the hardcoded values in the plugin - if(image_item->isGray()) - { - if(img_wordKind != WK_FLOAT) + // check if the word type fits the hardcoded values in the plugin + if (image_item->isGray()) { + if (img_wordKind != WK_FLOAT) fit_wrdtp = false; - else - if(img_wdim != 4) - fit_wrdtp = false; - } - else - { - if(img_wordKind != WK_FIXED) + else if (img_wdim != 4) + fit_wrdtp = false; + } else { + if (img_wordKind != WK_FIXED) + fit_wrdtp = false; + else if (img_wdim != 1) fit_wrdtp = false; - else - if(img_wdim != 1) - fit_wrdtp = false; } - if(!fit_wrdtp) - { - QMessageBox::warning(mw, tr(""), - tr("Selected object can't be meshed because the image's word type is not supported by this plugin.")); - return; + if (!fit_wrdtp) { + return tr( + "Selected object can't be meshed because the image's word type is " + "not supported by this plugin."); } } -#endif +# endif - if (nullptr == item) - { - QMessageBox::warning(mw, tr(""), - tr("Selected object can't be meshed")); + if(item) { + bbox = item->bbox(); + if (auto poly_items = get(&*items)) { + for (auto it : poly_items->sm_items) { + bbox = bbox + it->bbox(); + } + if (poly_items->polylines_item) + bbox = bbox + poly_items->polylines_item->bbox(); + } + } + return {}; +} + +void Mesh_3_plugin::set_defaults() { + auto error = get_items_or_return_error_string(); + if(error) return; + double diag = CGAL::sqrt((bbox.xmax()-bbox.xmin())*(bbox.xmax()-bbox.xmin()) + (bbox.ymax()-bbox.ymin())*(bbox.ymax()-bbox.ymin()) + (bbox.zmax()-bbox.zmin())*(bbox.zmax()-bbox.zmin())); + facets_sizing = get_approximate(diag * 0.05, 2, sizing_decimals); + edges_sizing = facets_sizing; + tets_sizing = facets_sizing; + angle = 25.; + sharp_edges_angle_bound = 60.; + approx = get_approximate(diag * 0.005, 2, approx_decimals); +} + +void Mesh_3_plugin::mesh_3(const Mesh_type mesh_type, + const Dialog_choice dialog_choice) { + CGAL_assertion(static_cast(items)); + auto error_string = get_items_or_return_error_string(); + if (error_string) { + QApplication::restoreOverrideCursor(); + QMessageBox::warning(mw, tr("Mesh_3 plugin"), *error_string); return; } + using boost::get; + const bool more_than_one_item = + get(&*items) && + (get(&*items)->sm_items.size() > 1); + + Scene_image_item* image_item = + get(&*items) + ? get(&*items)->image_item.get() + : nullptr; + Scene_surface_mesh_item* bounding_sm_item = + get(&*items) + ? get(&*items)->bounding_sm_item + : nullptr; + Scene_polylines_item* polylines_item = + get(&*items) + ? get(&*items)->polylines_item + : nullptr; + Scene_implicit_function_item* function_item = + get(&*items) + ? get(&*items)->function_item.get() + : nullptr; // ----------------------------------- // Create Mesh dialog // ----------------------------------- @@ -305,126 +458,132 @@ void Mesh_3_plugin::mesh_3(const bool surface_only, const bool use_defaults) &QLabel::linkActivated, &QDesktopServices::openUrl); - dialog.setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint|Qt::WindowCloseButtonHint); - connect(ui.buttonBox, SIGNAL(accepted()), - &dialog, SLOT(accept())); - connect(ui.buttonBox, SIGNAL(rejected()), - &dialog, SLOT(reject())); + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | + Qt::WindowCloseButtonHint); + connect(ui.buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(ui.buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); // Connect checkboxes to spinboxes - connect(ui.noApprox, SIGNAL(toggled(bool)), - ui.approx, SLOT(setEnabled(bool))); + connect( + ui.noApprox, SIGNAL(toggled(bool)), ui.approx, SLOT(setEnabled(bool))); - connect(ui.noFacetSizing, SIGNAL(toggled(bool)), - ui.facetSizing, SLOT(setEnabled(bool))); + connect(ui.noFacetSizing, + SIGNAL(toggled(bool)), + ui.facetSizing, + SLOT(setEnabled(bool))); - connect(ui.noAngle, SIGNAL(toggled(bool)), - ui.facetAngle, SLOT(setEnabled(bool))); + connect( + ui.noAngle, SIGNAL(toggled(bool)), ui.facetAngle, SLOT(setEnabled(bool))); - connect(ui.noTetSizing, SIGNAL(toggled(bool)), - ui.tetSizing, SLOT(setEnabled(bool))); + connect(ui.noTetSizing, + SIGNAL(toggled(bool)), + ui.tetSizing, + SLOT(setEnabled(bool))); - connect(ui.noTetShape, SIGNAL(toggled(bool)), - ui.tetShape, SLOT(setEnabled(bool))); + connect(ui.noTetShape, + SIGNAL(toggled(bool)), + ui.tetShape, + SLOT(setEnabled(bool))); - connect(ui.protect, SIGNAL(toggled(bool)), - ui.noEdgeSizing, SLOT(setEnabled(bool))); + connect(ui.protect, + SIGNAL(toggled(bool)), + ui.noEdgeSizing, + SLOT(setEnabled(bool))); - connect(ui.protect, SIGNAL(toggled(bool)), - ui.noEdgeSizing, SLOT(setChecked(bool))); + connect(ui.protect, + SIGNAL(toggled(bool)), + ui.noEdgeSizing, + SLOT(setChecked(bool))); - connect(ui.noEdgeSizing, SIGNAL(toggled(bool)), - ui.edgeLabel, SLOT(setEnabled(bool))); + connect(ui.noEdgeSizing, + SIGNAL(toggled(bool)), + ui.edgeLabel, + SLOT(setEnabled(bool))); - connect(ui.noEdgeSizing, SIGNAL(toggled(bool)), - ui.edgeSizing, SLOT(setEnabled(bool))); + connect(ui.noEdgeSizing, + SIGNAL(toggled(bool)), + ui.edgeSizing, + SLOT(setEnabled(bool))); - connect(ui.protect, SIGNAL(toggled(bool)), - ui.sharpEdgesAngle, SLOT(setEnabled(bool))); + connect(ui.protect, + SIGNAL(toggled(bool)), + ui.sharpEdgesAngle, + SLOT(setEnabled(bool))); - connect(ui.protect, SIGNAL(toggled(bool)), - ui.sharpEdgesAngleLabel, SLOT(setEnabled(bool))); + connect(ui.protect, + SIGNAL(toggled(bool)), + ui.sharpEdgesAngleLabel, + SLOT(setEnabled(bool))); - connect(ui.protect, SIGNAL(toggled(bool)), - ui.protectEdges, SLOT(setEnabled(bool))); + connect(ui.protect, + SIGNAL(toggled(bool)), + ui.protectEdges, + SLOT(setEnabled(bool))); - QString item_name = more_than_one_item ? - QString("%1...").arg(item->name()) : - item->name(); + QString item_name = + more_than_one_item ? QString("%1...").arg(item->name()) : item->name(); - // Set default parameters - CGAL::Three::Scene_interface::Bbox bbox = item->bbox(); - if(more_than_one_item) { - for(auto it: sm_items) { - bbox = bbox + it->bbox(); - } - } ui.objectName->setText(item_name); ui.objectNameSize->setText(tr("Object bbox size (w,h,d): %1, %2, %3") .arg(bbox.xmax() - bbox.xmin(),0,'g',3) .arg(bbox.ymax() - bbox.ymin(),0,'g',3) .arg(bbox.zmax() - bbox.zmin(),0,'g',3) ); + set_defaults(); double diag = CGAL::sqrt((bbox.xmax()-bbox.xmin())*(bbox.xmax()-bbox.xmin()) + (bbox.ymax()-bbox.ymin())*(bbox.ymax()-bbox.ymin()) + (bbox.zmax()-bbox.zmin())*(bbox.zmax()-bbox.zmin())); - int decimals = 0; - double sizing_default = get_approximate(diag * 0.05, 2, decimals); ui.facetSizing->setRange(diag * 10e-6, // min diag); // max - ui.facetSizing->setValue(sizing_default); // default value - ui.edgeSizing->setValue(sizing_default); + ui.facetSizing->setValue(facets_sizing); + ui.edgeSizing->setValue(edges_sizing); ui.tetSizing->setRange(diag * 10e-6, // min - diag); // max - ui.tetSizing->setValue(sizing_default); // default value + diag); // max + ui.tetSizing->setValue(tets_sizing); // default value - double approx_default = get_approximate(diag * 0.005, 2, decimals); ui.approx->setRange(diag * 10e-7, // min - diag); // max - ui.approx->setValue(approx_default); + diag); // max + ui.approx->setValue(approx); ui.protect->setEnabled(features_protection_available); ui.protect->setChecked(features_protection_available); ui.protectEdges->setEnabled(features_protection_available); - ui.facegraphCheckBox->setVisible(surface_only); - ui.initializationGroup->setVisible(image_item != nullptr && !image_item->isGray()); + ui.facegraphCheckBox->setVisible(mesh_type == Mesh_type::SURFACE_ONLY); + ui.initializationGroup->setVisible(image_item != nullptr && + !image_item->isGray()); ui.grayImgGroup->setVisible(image_item != nullptr && image_item->isGray()); - if (!sm_items.empty()) - ui.volumeGroup->setVisible(!surface_only && nullptr != bounding_sm_item); + if (items->which() == POLYHEDRAL_MESH_ITEMS) + ui.volumeGroup->setVisible(mesh_type == Mesh_type::VOLUME && + nullptr != bounding_sm_item); else - ui.volumeGroup->setVisible(!surface_only); - if ((!sm_items.empty())|| polylines_item != nullptr) { + ui.volumeGroup->setVisible(mesh_type == Mesh_type::VOLUME); + ui.sharpEdgesAngle->setValue(sharp_edges_angle_bound); + if (items->which() != POLYHEDRAL_MESH_ITEMS || polylines_item != nullptr) { ui.sharpEdgesAngleLabel->setVisible(false); ui.sharpEdgesAngle->setVisible(false); ui.facetTopology->setEnabled(false); - ui.facetTopology->setToolTip(tr("Notice: " - "This option is only available with a" - " polyhedron or a surface mesh, when features are detected" - " automatically")); + ui.facetTopology->setToolTip( + tr("Notice: " + "This option is only available with a" + " polyhedron or a surface mesh, when features are detected" + " automatically")); } ui.noEdgeSizing->setChecked(ui.protect->isChecked()); ui.edgeLabel->setEnabled(ui.noEdgeSizing->isChecked()); ui.edgeSizing->setEnabled(ui.noEdgeSizing->isChecked()); - if (features_protection_available) - { - if (!sm_items.empty()) - { - if (surface_only) - { + if (features_protection_available) { + if (items->which() == POLYHEDRAL_MESH_ITEMS) { + if (mesh_type == Mesh_type::SURFACE_ONLY) { ui.protectEdges->addItem(QString("Sharp and Boundary edges")); ui.protectEdges->addItem(QString("Boundary edges only")); - } - else + } else ui.protectEdges->addItem(QString("Sharp edges")); - } - else if(nullptr != image_item) - { - if(polylines_item != nullptr) + } else if (items->which() == IMAGE_MESH_ITEMS) { + if (polylines_item != nullptr) ui.protectEdges->addItem(QString("Input polylines")); - else - { + else { ui.protectEdges->addItem(QString("Polylines on cube")); } } @@ -433,38 +592,47 @@ void Mesh_3_plugin::mesh_3(const bool surface_only, const bool use_defaults) // Get values // ----------------------------------- - //reset cursor from the code for the scripts + // reset cursor from the code for the scripts QApplication::restoreOverrideCursor(); - if(!use_defaults) { + if (dialog_choice == Dialog_choice::DIALOG) { int i = dialog.exec(); - if( i == QDialog::Rejected ) { return; } + if (i == QDialog::Rejected) { return; } } // 0 means parameter is not considered - const double angle = !ui.noAngle->isChecked() ? 0 : ui.facetAngle->value(); - const double approx = !ui.noApprox->isChecked() ? 0 : ui.approx->value(); - const double facet_sizing = !ui.noFacetSizing->isChecked() ? 0 : ui.facetSizing->value(); - const double radius_edge = !ui.noTetShape->isChecked() ? 0 : ui.tetShape->value(); - const double tet_sizing = !ui.noTetSizing->isChecked() ? 0 : ui.tetSizing->value(); - const double edge_size = !ui.noEdgeSizing->isChecked() ? DBL_MAX : ui.edgeSizing->value(); - const bool protect_features = ui.protect->isChecked() && (ui.protectEdges->currentIndex() == 0); - const bool protect_borders = ui.protect->isChecked() && (ui.protectEdges->currentIndex() == 1); - const double sharp_edges_angle = ui.sharpEdgesAngle->value(); + angle = !ui.noAngle->isChecked() ? 0 : ui.facetAngle->value(); + sharp_edges_angle_bound = ui.sharpEdgesAngle->value(); + std::cerr << "sharp_edges_angle_bound: " << sharp_edges_angle_bound << '\n'; + edges_sizing = + !ui.noEdgeSizing->isChecked() ? DBL_MAX : ui.edgeSizing->value(); + facets_sizing = !ui.noFacetSizing->isChecked() ? 0 : ui.facetSizing->value(); + approx = !ui.noApprox->isChecked() ? 0 : ui.approx->value(); + tets_shape = !ui.noTetShape->isChecked() ? 0 : ui.tetShape->value(); + tets_sizing = !ui.noTetSizing->isChecked() ? 0 : ui.tetSizing->value(); + protect_features = + ui.protect->isChecked() && (ui.protectEdges->currentIndex() == 0); + protect_borders = + ui.protect->isChecked() && (ui.protectEdges->currentIndex() == 1); const bool detect_connected_components = ui.detectComponents->isChecked(); - const int manifold = - (ui.manifoldCheckBox->isChecked() ? 1 : 0) - + (ui.facetTopology->isChecked() ? 2 : 0); + const int manifold = (ui.manifoldCheckBox->isChecked() ? 1 : 0) + + (ui.facetTopology->isChecked() ? 2 : 0); const float iso_value = float(ui.iso_value_spinBox->value()); const float value_outside = float(ui.value_outside_spinBox->value()); - const float inside_is_less = float(ui.inside_is_less_checkBox->isChecked()); - as_facegraph = surface_only ? ui.facegraphCheckBox->isChecked() : false; + const bool inside_is_less = ui.inside_is_less_checkBox->isChecked(); + as_facegraph = (mesh_type == Mesh_type::SURFACE_ONLY) + ? ui.facegraphCheckBox->isChecked() + : false; Meshing_thread* thread = nullptr; - if (!sm_items.empty()) - { + switch (items->which()) { + case POLYHEDRAL_MESH_ITEMS: { + auto& poly_items = get(*items); + auto& sm_items = poly_items.sm_items; + const auto bounding_sm_item = poly_items.bounding_sm_item; + const auto polylines_item = poly_items.polylines_item; QList polyhedrons; - if(!surface_only) { - sm_items.removeAll(bounding_sm_item); + if(mesh_type != Mesh_type::SURFACE_ONLY) { + sm_items.removeAll(make_not_null(bounding_sm_item)); } std::transform(sm_items.begin(), sm_items.end(), std::back_inserter(polyhedrons), @@ -472,81 +640,87 @@ void Mesh_3_plugin::mesh_3(const bool surface_only, const bool use_defaults) return item->polyhedron(); }); Scene_polylines_item::Polylines_container plc; - SMesh *bounding_polyhedron = - (bounding_sm_item == nullptr) ? nullptr : bounding_sm_item->polyhedron(); + SMesh* bounding_polyhedron = (bounding_sm_item == nullptr) + ? nullptr + : bounding_sm_item->polyhedron(); - thread = cgal_code_mesh_3(polyhedrons, - (polylines_item == nullptr)?plc:polylines_item->polylines, - bounding_polyhedron, - item_name, - angle, - facet_sizing, - approx, - tet_sizing, - edge_size, - radius_edge, - protect_features, - protect_borders, - sharp_edges_angle, - manifold, - surface_only); + thread = cgal_code_mesh_3( + polyhedrons, + (polylines_item == nullptr) ? plc : polylines_item->polylines, + bounding_polyhedron, + item_name, + angle, + facets_sizing, + approx, + tets_sizing, + edges_sizing, + tets_shape, + protect_features, + protect_borders, + sharp_edges_angle_bound, + manifold, + mesh_type == Mesh_type::SURFACE_ONLY); + break; } // Image -#ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS - else if (nullptr != function_item) - { +# ifdef CGAL_MESH_3_DEMO_ACTIVATE_IMPLICIT_FUNCTIONS + case IMPLICIT_MESH_ITEMS: { const Implicit_function_interface* pFunction = function_item->function(); - if (nullptr == pFunction) - { + if (nullptr == pFunction) { QMessageBox::critical(mw, tr(""), tr("ERROR: no data in selected item")); return; } - thread = cgal_code_mesh_3(pFunction, - angle, - facet_sizing, - approx, - tet_sizing, - edge_size, - radius_edge, - manifold, - surface_only); + thread = cgal_code_mesh_3(pFunction, + angle, + facets_sizing, + approx, + tets_sizing, + edges_sizing, + tets_shape, + manifold, + mesh_type == Mesh_type::SURFACE_ONLY); + break; } -#endif -#ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES - else if (nullptr != image_item) - { +# endif +# ifdef CGAL_MESH_3_DEMO_ACTIVATE_SEGMENTED_IMAGES + case IMAGE_MESH_ITEMS: { const Image* pImage = image_item->image(); - if (nullptr == pImage) - { + if (nullptr == pImage) { QMessageBox::critical(mw, tr(""), tr("ERROR: no data in selected item")); return; } Scene_polylines_item::Polylines_container plc; - thread = cgal_code_mesh_3(pImage, - (polylines_item == nullptr)?plc:polylines_item->polylines, - angle, - facet_sizing, - approx, - tet_sizing, - edge_size, - radius_edge, - protect_features, - manifold, - surface_only, - detect_connected_components, - image_item->isGray(), - iso_value, - value_outside, - inside_is_less); + thread = cgal_code_mesh_3( + pImage, + (polylines_item == nullptr) ? plc : polylines_item->polylines, + angle, + facets_sizing, + approx, + tets_sizing, + edges_sizing, + tets_shape, + protect_features, + manifold, + mesh_type == Mesh_type::SURFACE_ONLY, + detect_connected_components, + image_item->isGray(), + iso_value, + value_outside, + inside_is_less); + break; } -#endif + default: + CGAL::Three::Three::error(tr("Mesh_3 plugin"), + tr("This type of item is not handled!")); + return; + } // end switch +# endif - if ( nullptr == thread ) - { - QMessageBox::critical(mw,tr(""),tr("ERROR: no thread created")); + if (nullptr == thread) { + QMessageBox::critical(mw, tr(""), tr("ERROR: no thread created")); return; } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp index 3425d41f357..5761dc4dab9 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Mesh_3_plugin_cgal_code.cpp @@ -161,6 +161,9 @@ Meshing_thread* cgal_code_mesh_3(const Implicit_function_interface* pfunction, param.tet_shape = tet_shape; param.edge_sizing = edge_size; param.manifold = manifold; + param.detect_connected_components = false; // to avoid random values + // in the debug displays + typedef ::Mesh_function Mesh_function; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Optimization_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Optimization_plugin.cpp index 869e1a6acf3..85cb6ccd1a1 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Optimization_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Optimization_plugin.cpp @@ -16,7 +16,6 @@ #include "ui_Local_optimizers_dialog.h" #include "Scene_c3t3_item.h" -#include "C3t3_type.h" #include "Optimizer_thread.h" @@ -521,9 +520,9 @@ treat_result(Scene_c3t3_item& source_item, if ( &source_item != &result_item) { const Scene_item::Bbox& bbox = result_item.bbox(); - result_item.setPosition((bbox.xmin() + bbox.xmax())/2.f, - (bbox.ymin() + bbox.ymax())/2.f, - (bbox.zmin() + bbox.zmax())/2.f); + result_item.setPosition(static_cast(bbox.xmin() + bbox.xmax())/2.f, + static_cast(bbox.ymin() + bbox.ymax())/2.f, + static_cast(bbox.zmin() + bbox.zmax())/2.f); result_item.setColor(QColor(59,74,226)); result_item.setRenderingMode(source_item.renderingMode()); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/config_mesh_3.h b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/config_mesh_3.h index f551629a445..50aa90773b8 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/config_mesh_3.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/config_mesh_3.h @@ -3,6 +3,15 @@ #define BOOST_PARAMETER_MAX_ARITY 12 +// CGAL_MESH_3_PROTECTION_DEBUG: +// - 1 : display debug messages +// - 2 : dump file `polylines_graph.polylines.txt` and +// `edges-graph.polylines.txt` from polyhedral and image domains +// - 4 : dump c3t3 in case of a bug +// - 8 : dump c3t3 at various stages of the protection +// - 16 : more precise debug messages +//#define CGAL_MESH_3_PROTECTION_DEBUG 255 + //#define CGAL_PROFILE @@ -31,7 +40,7 @@ # define CGAL_POLYHEDRON_DEMO_USE_SURFACE_MESHER #endif -#define CGAL_MESH_3_DEMO_BIGGER_HISTOGRAM_WITH_WHITE_BACKGROUNG +//#define CGAL_MESH_3_DEMO_BIGGER_HISTOGRAM_WITH_WHITE_BACKGROUNG // If you define this, implicit function and segmented images won't be available //#define CGAL_MESH_3_DEMO_ACTIVATE_SHARP_FEATURES_IN_POLYHEDRAL_DOMAIN diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/include/gsl/gsl_assert b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/include/gsl/gsl_assert new file mode 100644 index 00000000000..3c952e6e01a --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/include/gsl/gsl_assert @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_CONTRACTS_H +#define GSL_CONTRACTS_H + +#include +#include // for logic_error + +// +// make suppress attributes parse for some compilers +// Hopefully temporary until suppresion standardization occurs +// +#if defined (_MSC_VER) +#define GSL_SUPPRESS(x) [[gsl::suppress(x)]] +#else +#if defined(__clang__) +#define GSL_SUPPRESS(x) [[gsl::suppress("x")]] +#else +#define GSL_SUPPRESS(x) +#endif // __clang__ +#endif // _MSC_VER + +// +// Temporary until MSVC STL supports no-exceptions mode. +// Currently terminate is a no-op in this mode, so we add termination behavior back +// +#if defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS +#define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND +#include +#define RANGE_CHECKS_FAILURE 0 +#endif + +// +// There are three configuration options for this GSL implementation's behavior +// when pre/post conditions on the GSL types are violated: +// +// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default) +// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown +// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens +// +#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) || defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) || \ + defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)) +#define GSL_TERMINATE_ON_CONTRACT_VIOLATION +#endif + +#define GSL_STRINGIFY_DETAIL(x) #x +#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) + +#if defined(__clang__) || defined(__GNUC__) +#define GSL_LIKELY(x) __builtin_expect(!!(x), 1) +#define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define GSL_LIKELY(x) (!!(x)) +#define GSL_UNLIKELY(x) (!!(x)) +#endif + +// +// GSL_ASSUME(cond) +// +// Tell the optimizer that the predicate cond must hold. It is unspecified +// whether or not cond is actually evaluated. +// +#ifdef _MSC_VER +#define GSL_ASSUME(cond) __assume(cond) +#elif defined(__GNUC__) +#define GSL_ASSUME(cond) ((cond) ? static_cast(0) : __builtin_unreachable()) +#else +#define GSL_ASSUME(cond) static_cast((cond) ? 0 : 0) +#endif + +// +// GSL.assert: assertions +// + +namespace gsl +{ +struct fail_fast : public std::logic_error +{ + explicit fail_fast(char const* const message) : std::logic_error(message) {} +}; + +namespace details +{ +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + + typedef void (__cdecl *terminate_handler)(); + + GSL_SUPPRESS(f.6) // NO-FORMAT: attribute + [[noreturn]] inline void __cdecl default_terminate_handler() + { + __fastfail(RANGE_CHECKS_FAILURE); + } + + inline gsl::details::terminate_handler& get_terminate_handler() noexcept + { + static terminate_handler handler = &default_terminate_handler; + return handler; + } + +#endif + + [[noreturn]] inline void terminate() noexcept + { +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + (*gsl::details::get_terminate_handler())(); +#else + std::terminate(); +#endif + } + +#if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) + + template + [[noreturn]] void throw_exception(Exception&&) noexcept + { + gsl::details::terminate(); + } + +#else + + template + [[noreturn]] void throw_exception(Exception&& exception) + { + throw std::forward(exception); + } + +#endif + +} // namespace details +} // namespace gsl + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) \ + (GSL_LIKELY(cond) ? static_cast(0) \ + : gsl::details::throw_exception(gsl::fail_fast( \ + "GSL: " type " failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)))) + +#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) \ + (GSL_LIKELY(cond) ? static_cast(0) : gsl::details::terminate()) + +#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) GSL_ASSUME(cond) + +#endif + +#define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond) +#define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond) + +#endif // GSL_CONTRACTS_H diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/include/gsl/pointers b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/include/gsl/pointers new file mode 100644 index 00000000000..a3388561184 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/include/gsl/pointers @@ -0,0 +1,198 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_POINTERS_H +#define GSL_POINTERS_H + +#include // for Ensures, Expects + +#include // for forward +#include // for ptrdiff_t, nullptr_t, ostream, size_t +#include // for shared_ptr, unique_ptr +#include // for hash +#include // for enable_if_t, is_convertible, is_assignable + +#if defined(_MSC_VER) && _MSC_VER < 1910 +#pragma push_macro("constexpr") +#define constexpr /*constexpr*/ + +#endif // defined(_MSC_VER) && _MSC_VER < 1910 + +namespace gsl +{ + +// +// GSL.owner: ownership pointers +// +using std::unique_ptr; +using std::shared_ptr; + +// +// owner +// +// owner is designed as a bridge for code that must deal directly with owning pointers for some reason +// +// T must be a pointer type +// - disallow construction from any type other than pointer type +// +template ::value>> +using owner = T; + +// +// not_null +// +// Restricts a pointer or smart pointer to only hold non-null values. +// +// Has zero size overhead over T. +// +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* +// - disallow construction from nullptr_t +// - disallow default construction +// - ensure construction from null U* fails +// - allow implicit conversion to U* +// +template +class not_null +{ +public: + static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); + + template ::value>> + constexpr explicit not_null(U&& u) : ptr_(std::forward(u)) + { + Expects(ptr_ != nullptr); + } + + template ::value>> + constexpr explicit not_null(T u) : ptr_(u) + { + Expects(ptr_ != nullptr); + } + + template ::value>> + constexpr not_null(const not_null& other) : not_null(other.get()) + { + } + + not_null(not_null&& other) = default; + not_null(const not_null& other) = default; + not_null& operator=(const not_null& other) = default; + + constexpr T get() const + { + Ensures(ptr_ != nullptr); + return ptr_; + } + + constexpr operator T() const { return get(); } + constexpr T operator->() const { return get(); } + constexpr decltype(auto) operator*() const { return *get(); } + + // prevents compilation when someone attempts to assign a null pointer constant + not_null(std::nullptr_t) = delete; + not_null& operator=(std::nullptr_t) = delete; + + // unwanted operators...pointers only point to single objects! + not_null& operator++() = delete; + not_null& operator--() = delete; + not_null operator++(int) = delete; + not_null operator--(int) = delete; + not_null& operator+=(std::ptrdiff_t) = delete; + not_null& operator-=(std::ptrdiff_t) = delete; + void operator[](std::ptrdiff_t) const = delete; + +private: + T ptr_; +}; + +template +auto make_not_null(T&& t) { + return gsl::not_null>>{std::forward(t)}; +} + +template +std::ostream& operator<<(std::ostream& os, const not_null& val) +{ + os << val.get(); + return os; +} + +template +auto operator==(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() == rhs.get()) +{ + return lhs.get() == rhs.get(); +} + +template +auto operator!=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() != rhs.get()) +{ + return lhs.get() != rhs.get(); +} + +template +auto operator<(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() < rhs.get()) +{ + return lhs.get() < rhs.get(); +} + +template +auto operator<=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() <= rhs.get()) +{ + return lhs.get() <= rhs.get(); +} + +template +auto operator>(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() > rhs.get()) +{ + return lhs.get() > rhs.get(); +} + +template +auto operator>=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() >= rhs.get()) +{ + return lhs.get() >= rhs.get(); +} + +// more unwanted operators +template +std::ptrdiff_t operator-(const not_null&, const not_null&) = delete; +template +not_null operator-(const not_null&, std::ptrdiff_t) = delete; +template +not_null operator+(const not_null&, std::ptrdiff_t) = delete; +template +not_null operator+(std::ptrdiff_t, const not_null&) = delete; + +} // namespace gsl + +namespace std +{ +template +struct hash> +{ + std::size_t operator()(const gsl::not_null& value) const { return hash{}(value); } +}; + +} // namespace std + +#if defined(_MSC_VER) && _MSC_VER < 1910 +#undef constexpr +#pragma pop_macro("constexpr") + +#endif // defined(_MSC_VER) && _MSC_VER < 1910 + +#endif // GSL_POINTERS_H diff --git a/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp index caddceb3a2e..184770f685b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp @@ -777,7 +777,8 @@ void Basic_generator_plugin::generateGrid() QString points_text; Point extrema[2]; - typename boost::graph_traits::vertices_size_type nb_cells[2]; + using size_type = typename boost::graph_traits::vertices_size_type; + size_type nb_cells[2]; bool triangulated = dock_widget->grid_checkBox->isChecked(); points_text= dock_widget->grid_lineEdit->text(); @@ -806,8 +807,8 @@ void Basic_generator_plugin::generateGrid() } extrema[0] = Point(coords[0], coords[1], coords[2]); extrema[1] = Point(coords[3], coords[4], coords[5]); - nb_cells[0] = static_cast(dock_widget->gridX_spinBox->value()); - nb_cells[1] = static_cast(dock_widget->gridY_spinBox->value()); + nb_cells[0] = static_cast(dock_widget->gridX_spinBox->value()); + nb_cells[1] = static_cast(dock_widget->gridY_spinBox->value()); //nb_points = nb_cells+1 Point_generator point_gen(nb_cells[0]+1, nb_cells[1]+1, extrema[0], extrema[1]); diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp index 6a251cb6464..34daa1ade0c 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp @@ -11,14 +11,17 @@ #include #include #include -#include #include #include #include #include +#include #include "ui_RemoveNeedlesDialog.h" +#include +#include +#include using namespace CGAL::Three; class Polyhedron_demo_repair_polyhedron_plugin : @@ -44,6 +47,7 @@ public: actionRemoveSelfIntersections = new QAction(tr("Remove Self-Intersections"), mw); actionStitchCloseBorderHalfedges = new QAction(tr("Stitch Close Border Halfedges"), mw); actionDuplicateNMVertices = new QAction(tr("Duplicate Non-Manifold Vertices"), mw); + actionMergeDuplicatedVerticesOnBoundaryCycles = new QAction(tr("Merge Duplicated Vertices on Boundary Cycles"), mw); actionAutorefine = new QAction(tr("Autorefine Mesh"), mw); actionAutorefineAndRMSelfIntersections = new QAction(tr("Autorefine and Remove Self-Intersections"), mw); actionRemoveNeedlesAndCaps = new QAction(tr("Remove Needles And Caps")); @@ -53,6 +57,7 @@ public: actionRemoveSelfIntersections->setObjectName("actionRemoveSelfIntersections"); actionStitchCloseBorderHalfedges->setObjectName("actionStitchCloseBorderHalfedges"); actionDuplicateNMVertices->setObjectName("actionDuplicateNMVertices"); + actionMergeDuplicatedVerticesOnBoundaryCycles->setObjectName("actionMergeDuplicatedVerticesOnBoundaryCycles"); actionAutorefine->setObjectName("actionAutorefine"); actionAutorefineAndRMSelfIntersections->setObjectName("actionAutorefineAndRMSelfIntersections"); actionRemoveNeedlesAndCaps->setObjectName("actionRemoveNeedlesAndCaps"); @@ -62,6 +67,7 @@ public: actionRemoveSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental"); actionRemoveIsolatedVertices->setProperty("subMenuName", "Polygon Mesh Processing/Repair"); actionDuplicateNMVertices->setProperty("subMenuName", "Polygon Mesh Processing/Repair"); + actionMergeDuplicatedVerticesOnBoundaryCycles->setProperty("subMenuName", "Polygon Mesh Processing/Repair"); actionAutorefine->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental"); actionAutorefineAndRMSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental"); actionRemoveNeedlesAndCaps->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental"); @@ -76,6 +82,7 @@ public: << actionRemoveSelfIntersections << actionStitchCloseBorderHalfedges << actionDuplicateNMVertices + << actionMergeDuplicatedVerticesOnBoundaryCycles << actionAutorefine << actionAutorefineAndRMSelfIntersections << actionRemoveNeedlesAndCaps; @@ -97,6 +104,8 @@ public: template void on_actionDuplicateNMVertices_triggered(Scene_interface::Item_id index); template + void on_actionMergeDuplicatedVerticesOnBoundaryCycles_triggered(Scene_interface::Item_id index); + template void on_actionAutorefine_triggered(Scene_interface::Item_id index); template void on_actionAutorefineAndRMSelfIntersections_triggered(Scene_interface::Item_id index); @@ -107,6 +116,7 @@ public Q_SLOTS: void on_actionRemoveSelfIntersections_triggered(); void on_actionStitchCloseBorderHalfedges_triggered(); void on_actionDuplicateNMVertices_triggered(); + void on_actionMergeDuplicatedVerticesOnBoundaryCycles_triggered(); void on_actionAutorefine_triggered(); void on_actionAutorefineAndRMSelfIntersections_triggered(); void on_actionRemoveNeedlesAndCaps_triggered(); @@ -117,6 +127,7 @@ private: QAction* actionRemoveSelfIntersections; QAction* actionStitchCloseBorderHalfedges; QAction* actionDuplicateNMVertices; + QAction* actionMergeDuplicatedVerticesOnBoundaryCycles; QAction* actionAutorefine; QAction* actionAutorefineAndRMSelfIntersections; QAction* actionRemoveNeedlesAndCaps; @@ -343,4 +354,31 @@ void Polyhedron_demo_repair_polyhedron_plugin::on_actionDuplicateNMVertices_trig QApplication::restoreOverrideCursor(); } +template +void Polyhedron_demo_repair_polyhedron_plugin::on_actionMergeDuplicatedVerticesOnBoundaryCycles_triggered(Scene_interface::Item_id index) +{ + namespace PMP = CGAL::Polygon_mesh_processing; + + if(Item* poly_item = qobject_cast(scene->item(index))) + { + const std::size_t old_nv = vertices(*poly_item->polyhedron()).size(); + PMP::merge_duplicated_vertices_in_boundary_cycles(*poly_item->polyhedron()); + const std::size_t new_nv = vertices(*poly_item->polyhedron()).size(); + CGAL::Three::Three::information(tr(" %1 vertices merged").arg(old_nv - new_nv)); + if(old_nv != new_nv) + { + poly_item->invalidateOpenGLBuffers(); + Q_EMIT poly_item->itemChanged(); + } + } +} + +void Polyhedron_demo_repair_polyhedron_plugin::on_actionMergeDuplicatedVerticesOnBoundaryCycles_triggered() +{ + QApplication::setOverrideCursor(Qt::WaitCursor); + const Scene_interface::Item_id index = scene->mainSelectionIndex(); + on_actionMergeDuplicatedVerticesOnBoundaryCycles_triggered(index); + QApplication::restoreOverrideCursor(); +} + #include "Repair_polyhedron_plugin.moc" diff --git a/Polyhedron/demo/Polyhedron/Scene.cpp b/Polyhedron/demo/Polyhedron/Scene.cpp index a1d107a15de..d664cb9d707 100644 --- a/Polyhedron/demo/Polyhedron/Scene.cpp +++ b/Polyhedron/demo/Polyhedron/Scene.cpp @@ -826,9 +826,9 @@ Scene::draw_aux(bool with_names, CGAL::Three::Viewer_interface* viewer) //blending program.bind(); vaos[viewer]->bind(); - viewer->glClearColor(background.redF(), - background.greenF(), - background.blueF(), + viewer->glClearColor((GLclampf)background.redF(), + (GLclampf)background.greenF(), + (GLclampf)background.blueF(), 0.0f); viewer->glDisable(GL_DEPTH_TEST); viewer->glClear(GL_COLOR_BUFFER_BIT); diff --git a/Polyhedron/demo/Polyhedron/Scene_c3t3_item.cpp b/Polyhedron/demo/Polyhedron/Scene_c3t3_item.cpp index 16b82dd2ef4..85d79582dd3 100644 --- a/Polyhedron/demo/Polyhedron/Scene_c3t3_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_c3t3_item.cpp @@ -50,6 +50,14 @@ typedef Edge_container Ec; typedef Point_container Pc; typedef Viewer_interface Vi; +QVector4D cgal_plane_to_vector4d(EPICK::Plane_3 plane) { + return { + static_cast(-plane.a()), + static_cast(-plane.b()), + static_cast(-plane.c()), + static_cast(-plane.d()) }; +} + // The special Scene_item only for triangles class Scene_intersection_item : public CGAL::Three::Scene_item_rendering_helper { @@ -148,7 +156,7 @@ public : viewer->makeCurrent(); const EPICK::Plane_3& plane = qobject_cast(this->parent())->plane(); float shrink_factor = qobject_cast(this->parent())->getShrinkFactor(); - QVector4D cp(-plane.a(), -plane.b(), -plane.c(), -plane.d()); + QVector4D cp = cgal_plane_to_vector4d(plane); getTriangleContainer(0)->setPlane(cp); getTriangleContainer(0)->setShrinkFactor(shrink_factor); // positions_poly is also used for the faces in the cut plane @@ -161,7 +169,7 @@ public : if(is_fast) return; const EPICK::Plane_3& plane = qobject_cast(this->parent())->plane(); - QVector4D cp(-plane.a(), -plane.b(), -plane.c(), -plane.d()); + QVector4D cp = cgal_plane_to_vector4d(plane); getEdgeContainer(0)->setPlane(cp); getEdgeContainer(0)->setColor(QColor(Qt::black)); getEdgeContainer(0)->draw(viewer, true); @@ -180,48 +188,38 @@ public : Geom_traits::Vector_3 n = cross_product(pb - pa, pc - pa); n = n / CGAL::sqrt(n*n); + auto push_normal = [this](auto n) { + normals->push_back(static_cast(n.x())); + normals->push_back(static_cast(n.y())); + normals->push_back(static_cast(n.z())); + }; + + auto push_vertex = [this, &offset](const auto& p) { + this->vertices->push_back(static_cast(p.x()+offset.x)); + this->vertices->push_back(static_cast(p.y()+offset.y)); + this->vertices->push_back(static_cast(p.z()+offset.z)); + }; + + auto push_edge = [this, &offset](const auto& pa, const auto& pb) { + this->edges->push_back(static_cast(pa.x()+offset.x)); + this->edges->push_back(static_cast(pa.y()+offset.y)); + this->edges->push_back(static_cast(pa.z()+offset.z)); + this->edges->push_back(static_cast(pb.x()+offset.x)); + this->edges->push_back(static_cast(pb.y()+offset.y)); + this->edges->push_back(static_cast(pb.z()+offset.z)); + }; + for (int i = 0; i<3; i++) { - normals->push_back(n.x()); - normals->push_back(n.y()); - normals->push_back(n.z()); + push_normal(n); } + push_vertex(pa); + push_vertex(pb); + push_vertex(pc); - vertices->push_back(pa.x()+offset.x); - vertices->push_back(pa.y()+offset.y); - vertices->push_back(pa.z()+offset.z); - - vertices->push_back(pb.x()+offset.x); - vertices->push_back(pb.y()+offset.y); - vertices->push_back(pb.z()+offset.z); - - vertices->push_back(pc.x()+offset.x); - vertices->push_back(pc.y()+offset.y); - vertices->push_back(pc.z()+offset.z); - - edges->push_back(pa.x()+offset.x); - edges->push_back(pa.y()+offset.y); - edges->push_back(pa.z()+offset.z); - - edges->push_back(pb.x()+offset.x); - edges->push_back(pb.y()+offset.y); - edges->push_back(pb.z()+offset.z); - - edges->push_back(pb.x()+offset.x); - edges->push_back(pb.y()+offset.y); - edges->push_back(pb.z()+offset.z); - - edges->push_back(pc.x()+offset.x); - edges->push_back(pc.y()+offset.y); - edges->push_back(pc.z()+offset.z); - - edges->push_back(pc.x()+offset.x); - edges->push_back(pc.y()+offset.y); - edges->push_back(pc.z()+offset.z); - - edges->push_back(pa.x()+offset.x); - edges->push_back(pa.y()+offset.y); - edges->push_back(pa.z()+offset.z); + push_edge(pa, pb); + push_edge(pb, pc); + push_edge(pc, pa); for(int i=0; i<3; i++) { @@ -229,9 +227,9 @@ public : colors->push_back((float)color.green()/255); colors->push_back((float)color.blue()/255); - barycenters->push_back((pa[0]+pb[0]+pc[0])/3.0 + offset.x); - barycenters->push_back((pa[1]+pb[1]+pc[1])/3.0 + offset.y); - barycenters->push_back((pa[2]+pb[2]+pc[2])/3.0 + offset.z); + barycenters->push_back(static_cast((pa[0]+pb[0]+pc[0])/3.0 + offset.x)); + barycenters->push_back(static_cast((pa[1]+pb[1]+pc[1])/3.0 + offset.y)); + barycenters->push_back(static_cast((pa[2]+pb[2]+pc[2])/3.0 + offset.z)); } } @@ -503,6 +501,28 @@ struct Scene_c3t3_item_priv { bool is_valid; bool is_surface; bool last_intersection; + + void push_normal(std::vector& normals, const EPICK::Vector_3& n) const + { + normals.push_back(static_cast(n.x())); + normals.push_back(static_cast(n.y())); + normals.push_back(static_cast(n.z())); + } + void push_point(std::vector& points, const EPICK::Point_3& p, + const CGAL::qglviewer::Vec& offset) const + { + points.push_back(static_cast(p.x()+offset.x)); + points.push_back(static_cast(p.y()+offset.y)); + points.push_back(static_cast(p.z()+offset.z)); + } + void push_edge(std::vector& edges, + const EPICK::Point_3& pa, + const EPICK::Point_3& pb, + const CGAL::qglviewer::Vec& offset) const + { + push_point(edges, pa, offset); + push_point(edges, pb, offset); + } }; struct Set_show_tetrahedra { @@ -748,8 +768,8 @@ Scene_c3t3_item::build_histogram() const int text_margin = 3; const int text_height = 34; - histogram_ = QPixmap(width, height + text_height); - histogram_.fill(QColor(255, 255, 255)); + d->histogram_ = QPixmap(width, height + text_height); + d->histogram_.fill(QColor(255, 255, 255)); #else // Create an histogram_ and display it const int height = 140; @@ -933,7 +953,7 @@ void Scene_c3t3_item::draw(CGAL::Three::Viewer_interface* viewer) const { if(renderingMode() == Flat || renderingMode() == FlatPlusEdges) { - QVector4D cp(this->plane().a(),this->plane().b(),this->plane().c(),this->plane().d()); + QVector4D cp = cgal_plane_to_vector4d(this->plane()); getTriangleContainer(C3t3_faces)->setPlane(cp); float shrink_factor = getShrinkFactor(); getTriangleContainer(C3t3_faces)->setShrinkFactor(shrink_factor); @@ -968,7 +988,7 @@ void Scene_c3t3_item::draw(CGAL::Three::Viewer_interface* viewer) const { getEdgeContainer(Grid_edges)->setColor(QColor(Qt::black)); QMatrix4x4 f_mat; for (int i = 0; i<16; i++) - f_mat.data()[i] = d->frame->matrix()[i]; + f_mat.data()[i] = static_cast(d->frame->matrix()[i]); getEdgeContainer(Grid_edges)->setFrameMatrix(f_mat); getEdgeContainer(Grid_edges)->draw(viewer, true); } @@ -1005,12 +1025,12 @@ void Scene_c3t3_item::drawEdges(CGAL::Three::Viewer_interface* viewer) const { getEdgeContainer(Grid_edges)->setColor(QColor(Qt::black)); QMatrix4x4 f_mat; for (int i = 0; i<16; i++) - f_mat.data()[i] = d->frame->matrix()[i]; + f_mat.data()[i] = static_cast(d->frame->matrix()[i]); getEdgeContainer(Grid_edges)->setFrameMatrix(f_mat); getEdgeContainer(Grid_edges)->draw(viewer, true); } - QVector4D cp(this->plane().a(),this->plane().b(),this->plane().c(),this->plane().d()); + QVector4D cp = cgal_plane_to_vector4d(this->plane()); getEdgeContainer(C3t3_edges)->setPlane(cp); getEdgeContainer(C3t3_edges)->setIsSurface(d->is_surface); getEdgeContainer(C3t3_edges)->setColor(QColor(Qt::black)); @@ -1060,7 +1080,7 @@ void Scene_c3t3_item::drawPoints(CGAL::Three::Viewer_interface * viewer) const } - QVector4D cp(this->plane().a(),this->plane().b(),this->plane().c(),this->plane().d()); + QVector4D cp = cgal_plane_to_vector4d(this->plane()); getPointContainer(C3t3_points)->setPlane(cp); getPointContainer(C3t3_points)->setIsSurface(d->is_surface); getPointContainer(C3t3_points)->setColor(this->color()); @@ -1071,7 +1091,7 @@ void Scene_c3t3_item::drawPoints(CGAL::Three::Viewer_interface * viewer) const getEdgeContainer(Grid_edges)->setColor(QColor(Qt::black)); QMatrix4x4 f_mat; for (int i = 0; i<16; i++) - f_mat.data()[i] = d->frame->matrix()[i]; + f_mat.data()[i] = static_cast(d->frame->matrix()[i]); getEdgeContainer(Grid_edges)->setFrameMatrix(f_mat); getEdgeContainer(Grid_edges)->draw(viewer, true); } @@ -1086,100 +1106,41 @@ void Scene_c3t3_item_priv::draw_triangle(const Tr::Bare_point& pa, const Tr::Bare_point& pb, const Tr::Bare_point& pc) const { -#undef darker Geom_traits::Vector_3 n = cross_product(pb - pa, pc - pa); n = n / CGAL::sqrt(n*n); const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset(); for (int i = 0; i<3; i++) { - normals.push_back(n.x()); - normals.push_back(n.y()); - normals.push_back(n.z()); + push_normal(normals, n); } - positions_poly.push_back(pa.x()+offset.x); - positions_poly.push_back(pa.y()+offset.y); - positions_poly.push_back(pa.z()+offset.z); - - positions_poly.push_back(pb.x()+offset.x); - positions_poly.push_back(pb.y()+offset.y); - positions_poly.push_back(pb.z()+offset.z); - - positions_poly.push_back(pc.x()+offset.x); - positions_poly.push_back(pc.y()+offset.y); - positions_poly.push_back(pc.z()+offset.z); + push_point(positions_poly, pa, offset); + push_point(positions_poly, pb, offset); + push_point(positions_poly, pc, offset); for(int i=0; i<3; ++i) { - positions_barycenter.push_back((pa[0]+pb[0]+pc[0])/3.0 + offset.x); - positions_barycenter.push_back((pa[1]+pb[1]+pc[1])/3.0 + offset.y); - positions_barycenter.push_back((pa[2]+pb[2]+pc[2])/3.0 + offset.z); + push_point(positions_barycenter, CGAL::centroid(pa, pb, pc), offset); } - - } void Scene_c3t3_item_priv::draw_triangle_edges(const Tr::Bare_point& pa, const Tr::Bare_point& pb, const Tr::Bare_point& pc) const { -#undef darker const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset(); - positions_lines.push_back(pa.x()+offset.x); - positions_lines.push_back(pa.y()+offset.y); - positions_lines.push_back(pa.z()+offset.z); - - positions_lines.push_back(pb.x()+offset.x); - positions_lines.push_back(pb.y()+offset.y); - positions_lines.push_back(pb.z()+offset.z); - - positions_lines.push_back(pb.x()+offset.x); - positions_lines.push_back(pb.y()+offset.y); - positions_lines.push_back(pb.z()+offset.z); - - positions_lines.push_back(pc.x()+offset.x); - positions_lines.push_back(pc.y()+offset.y); - positions_lines.push_back(pc.z()+offset.z); - - positions_lines.push_back(pc.x()+offset.x); - positions_lines.push_back(pc.y()+offset.y); - positions_lines.push_back(pc.z()+offset.z); - - positions_lines.push_back(pa.x()+offset.x); - positions_lines.push_back(pa.y()+offset.y); - positions_lines.push_back(pa.z()+offset.z); - + push_edge(positions_lines, pa, pb, offset); + push_edge(positions_lines, pb, pc, offset); + push_edge(positions_lines, pc, pa, offset); } void Scene_c3t3_item_priv::draw_triangle_edges_cnc(const Tr::Bare_point& pa, const Tr::Bare_point& pb, const Tr::Bare_point& pc) const { -#undef darker const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset(); - positions_lines_not_in_complex.push_back(pa.x()+offset.x); - positions_lines_not_in_complex.push_back(pa.y()+offset.y); - positions_lines_not_in_complex.push_back(pa.z()+offset.z); - - positions_lines_not_in_complex.push_back(pb.x()+offset.x); - positions_lines_not_in_complex.push_back(pb.y()+offset.y); - positions_lines_not_in_complex.push_back(pb.z()+offset.z); - - positions_lines_not_in_complex.push_back(pb.x()+offset.x); - positions_lines_not_in_complex.push_back(pb.y()+offset.y); - positions_lines_not_in_complex.push_back(pb.z()+offset.z); - - positions_lines_not_in_complex.push_back(pc.x()+offset.x); - positions_lines_not_in_complex.push_back(pc.y()+offset.y); - positions_lines_not_in_complex.push_back(pc.z()+offset.z); - - positions_lines_not_in_complex.push_back(pc.x()+offset.x); - positions_lines_not_in_complex.push_back(pc.y()+offset.y); - positions_lines_not_in_complex.push_back(pc.z()+offset.z); - - positions_lines_not_in_complex.push_back(pa.x()+offset.x); - positions_lines_not_in_complex.push_back(pa.y()+offset.y); - positions_lines_not_in_complex.push_back(pa.z()+offset.z); - + push_edge(positions_lines_not_in_complex, pa, pb, offset); + push_edge(positions_lines_not_in_complex, pb, pc, offset); + push_edge(positions_lines_not_in_complex, pc, pa, offset); } double Scene_c3t3_item_priv::complex_diag() const { @@ -1435,7 +1396,7 @@ void Scene_c3t3_item_priv::computeSpheres() Tr::Bare_point center(wp2p(vit->point()).x() + offset.x, wp2p(vit->point()).y() + offset.y, wp2p(vit->point()).z() + offset.z); - float radius = vit->point().weight() ; + double radius = vit->point().weight() ; typedef unsigned char UC; tr_vertices.push_back(*vit); spheres->add_sphere(Geom_traits::Sphere_3(center, radius),s_id++, @@ -1468,29 +1429,29 @@ void Scene_c3t3_item_priv::computeElements() //The grid { - float x = (2 * (float)complex_diag()) / 10.0; - float y = (2 * (float)complex_diag()) / 10.0; + float x = (2 * (float)complex_diag()) / 10.0f; + float y = (2 * (float)complex_diag()) / 10.0f; for (float u = 0; u < 11; u += 1.f) { positions_grid.push_back(-(float)complex_diag() + x* u); positions_grid.push_back(-(float)complex_diag()); - positions_grid.push_back(0.0); + positions_grid.push_back(0.0f); positions_grid.push_back(-(float)complex_diag() + x* u); positions_grid.push_back((float)complex_diag()); - positions_grid.push_back(0.0); + positions_grid.push_back(0.0f); } for (float v = 0; v<11; v += 1.f) { positions_grid.push_back(-(float)complex_diag()); positions_grid.push_back(-(float)complex_diag() + v * y); - positions_grid.push_back(0.0); + positions_grid.push_back(0.0f); positions_grid.push_back((float)complex_diag()); positions_grid.push_back(-(float)complex_diag() + v * y); - positions_grid.push_back(0.0); + positions_grid.push_back(0.0f); } } @@ -1511,9 +1472,9 @@ void Scene_c3t3_item_priv::computeElements() const Tr::Bare_point& pc = wp2p(cell->vertex((index + 3) & 3)->point()); QColor color = colors[cell->surface_patch_index(index)]; - f_colors.push_back(color.redF());f_colors.push_back(color.greenF());f_colors.push_back(color.blueF()); - f_colors.push_back(color.redF());f_colors.push_back(color.greenF());f_colors.push_back(color.blueF()); - f_colors.push_back(color.redF());f_colors.push_back(color.greenF());f_colors.push_back(color.blueF()); + f_colors.push_back((float)color.redF());f_colors.push_back((float)color.greenF());f_colors.push_back((float)color.blueF()); + f_colors.push_back((float)color.redF());f_colors.push_back((float)color.greenF());f_colors.push_back((float)color.blueF()); + f_colors.push_back((float)color.redF());f_colors.push_back((float)color.greenF());f_colors.push_back((float)color.blueF()); if ((index % 2 == 1) == c3t3.is_in_complex(cell)) draw_triangle(pb, pa, pc); else draw_triangle(pa, pb, pc); @@ -1784,10 +1745,10 @@ QString Scene_c3t3_item::computeStats(int type) if(!d->computed_stats) { - float nb_edges = 0; - float total_edges = 0; - float nb_angle = 0; - float total_angle = 0; + double nb_edges = 0; + double total_edges = 0; + double nb_angle = 0; + double total_angle = 0; for (C3t3::Facet_iterator fit = d->c3t3.facets_begin(), @@ -1799,19 +1760,19 @@ QString Scene_c3t3_item::computeStats(int type) const Tr::Bare_point& pa = wp2p(cell->vertex((index + 1) & 3)->point()); const Tr::Bare_point& pb = wp2p(cell->vertex((index + 2) & 3)->point()); const Tr::Bare_point& pc = wp2p(cell->vertex((index + 3) & 3)->point()); - float edges[3]; + double edges[3]; edges[0]=(std::sqrt(CGAL::squared_distance(pa, pb))); edges[1]=(std::sqrt(CGAL::squared_distance(pa, pc))); edges[2]=(std::sqrt(CGAL::squared_distance(pb, pc))); for(int i=0; i<3; ++i) { - if(edges[i] < d->min_edges_length){ d->min_edges_length = edges[i]; } - if(edges[i] > d->max_edges_length){ d->max_edges_length = edges[i]; } + if(edges[i] < d->min_edges_length){ d->min_edges_length = static_cast(edges[i]); } + if(edges[i] > d->max_edges_length){ d->max_edges_length = static_cast(edges[i]); } total_edges+=edges[i]; ++nb_edges; } } - d->mean_edges_length = total_edges/(float)nb_edges; + d->mean_edges_length = static_cast(total_edges/nb_edges); for(Tr::Finite_vertices_iterator vit = d->c3t3.triangulation().finite_vertices_begin(), end = d->c3t3.triangulation().finite_vertices_end(); @@ -1859,10 +1820,10 @@ QString Scene_c3t3_item::computeStats(int type) const Tr::Bare_point& p1 = wp2p(cit->vertex(1)->point()); const Tr::Bare_point& p2 = wp2p(cit->vertex(2)->point()); const Tr::Bare_point& p3 = wp2p(cit->vertex(3)->point()); - float v = std::abs(CGAL::volume(p0, p1, p2, p3)); - float circumradius = std::sqrt(CGAL::squared_radius(p0, p1, p2, p3)); + double v = std::abs(CGAL::volume(p0, p1, p2, p3)); + double circumradius = std::sqrt(CGAL::squared_radius(p0, p1, p2, p3)); //find smallest edge - float edges[6]; + double edges[6]; edges[0] = std::sqrt(CGAL::squared_distance(p0, p1)); edges[1] = std::sqrt(CGAL::squared_distance(p0, p2)); edges[2] = std::sqrt(CGAL::squared_distance(p0, p3)); @@ -1870,60 +1831,59 @@ QString Scene_c3t3_item::computeStats(int type) edges[4] = std::sqrt(CGAL::squared_distance(p2, p3)); edges[5] = std::sqrt(CGAL::squared_distance(p1, p3)); - float min_edge = edges[0]; + double min_edge = edges[0]; for(int i=1; i<6; ++i) { if(edges[i]smallest_edge_radius) - d->smallest_edge_radius = smallest_edge_radius; + d->smallest_edge_radius = static_cast(smallest_edge_radius); if(smallest_radius_radius < d->smallest_radius_radius) - d->smallest_radius_radius = smallest_radius_radius; + d->smallest_radius_radius = static_cast(smallest_radius_radius); if(biggest_v_sma_cube > d->biggest_v_sma_cube) - d->biggest_v_sma_cube = biggest_v_sma_cube; + d->biggest_v_sma_cube = static_cast(biggest_v_sma_cube); + + auto update_min_max_dihedral_angle = [this](double a) { + if(a < this->d->min_dihedral_angle) { this->d->min_dihedral_angle = static_cast(a); } + if(a > this->d->max_dihedral_angle) { this->d->max_dihedral_angle = static_cast(a); } + }; double a = CGAL::to_double(CGAL::abs(approx_dihedral_angle(p0, p1, p2, p3))); - if(a < d->min_dihedral_angle) { d->min_dihedral_angle = a; } - if(a > d->max_dihedral_angle) { d->max_dihedral_angle = a; } + update_min_max_dihedral_angle(a); total_angle+=a; ++nb_angle; a = CGAL::to_double(CGAL::abs(approx_dihedral_angle(p0, p2, p1, p3))); - if(a < d->min_dihedral_angle) { d->min_dihedral_angle = a; } - if(a > d->max_dihedral_angle) { d->max_dihedral_angle = a; } + update_min_max_dihedral_angle(a); total_angle+=a; ++nb_angle; a = CGAL::to_double(CGAL::abs(approx_dihedral_angle(p0, p3, p1, p2))); - if(a < d->min_dihedral_angle) { d->min_dihedral_angle = a; } - if(a > d->max_dihedral_angle) { d->max_dihedral_angle = a; } + update_min_max_dihedral_angle(a); total_angle+=a; ++nb_angle; a = CGAL::to_double(CGAL::abs(approx_dihedral_angle(p1, p2, p0, p3))); - if(a < d->min_dihedral_angle) { d->min_dihedral_angle = a; } - if(a > d->max_dihedral_angle) { d->max_dihedral_angle = a; } + update_min_max_dihedral_angle(a); total_angle+=a; ++nb_angle; a = CGAL::to_double(CGAL::abs(approx_dihedral_angle(p1, p3, p0, p2))); - if(a < d->min_dihedral_angle) { d->min_dihedral_angle = a; } - if(a > d->max_dihedral_angle) { d->max_dihedral_angle = a; } + update_min_max_dihedral_angle(a); total_angle+=a; ++nb_angle; a = CGAL::to_double(CGAL::abs(approx_dihedral_angle(p2, p3, p0, p1))); - if(a < d->min_dihedral_angle) { d->min_dihedral_angle = a; } - if(a > d->max_dihedral_angle) { d->max_dihedral_angle = a; } + update_min_max_dihedral_angle(a); total_angle+=a; ++nb_angle; } - d->mean_dihedral_angle = total_angle/(float)nb_angle; + d->mean_dihedral_angle = static_cast(total_angle/nb_angle); d->nb_subdomains = sub_ids.size(); d->nb_vertices = d->c3t3.number_of_vertices_in_complex(); d->nb_tets = d->c3t3.number_of_cells(); diff --git a/Polyhedron/demo/Polyhedron/Scene_implicit_function_item.cpp b/Polyhedron/demo/Polyhedron/Scene_implicit_function_item.cpp index ebe5a7066c8..0066720cd5e 100644 --- a/Polyhedron/demo/Polyhedron/Scene_implicit_function_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_implicit_function_item.cpp @@ -102,9 +102,9 @@ void Scene_implicit_function_item_priv::compute_vertices_and_texmap(void) const CGAL::Three::Scene_item::Bbox& b = item->bbox(); float x,y,z; - z = (b.zmax()+b.zmin())/2.0; - x = (b.xmax()+b.xmin())/2.0; - y = (b.ymax()+b.ymin())/2.0; + z = static_cast(b.zmax()+b.zmin())/2.0f; + x = static_cast(b.xmax()+b.xmin())/2.0f; + y = static_cast(b.ymax()+b.ymin())/2.0f; // The Quad { diff --git a/Polyhedron/demo/Polyhedron/Three.cpp b/Polyhedron/demo/Polyhedron/Three.cpp index f3e50064776..53489e63f03 100644 --- a/Polyhedron/demo/Polyhedron/Three.cpp +++ b/Polyhedron/demo/Polyhedron/Three.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "Messages_interface.h" using namespace CGAL::Three; @@ -175,6 +176,18 @@ void Three::error(QString s) { qobject_cast(mainWindow())->message_error(s); } +void Three::information(QString title, QString s) +{ + QMessageBox::information(mainWindow(), title, s); +} +void Three::warning(QString title, QString s) +{ + QMessageBox::warning(mainWindow(), title, s); +} +void Three::error(QString title, QString s) +{ + QMessageBox::critical(mainWindow(), title, s); +} RenderingMode Three::defaultSurfaceMeshRenderingMode() { return s_defaultSMRM; diff --git a/Polyhedron/demo/Polyhedron/Viewer.cpp b/Polyhedron/demo/Polyhedron/Viewer.cpp index dbb3e5855e0..ecadeba815a 100644 --- a/Polyhedron/demo/Polyhedron/Viewer.cpp +++ b/Polyhedron/demo/Polyhedron/Viewer.cpp @@ -140,23 +140,23 @@ public: .arg(d->position.y()) .arg(d->position.z())); QPalette palette; - ambient=QColor(255*d->ambient.x(), - 255*d->ambient.y(), - 255*d->ambient.z()); + ambient=QColor::fromRgbF(d->ambient.x(), + d->ambient.y(), + d->ambient.z()); palette.setColor(QPalette::Button,ambient); ambientButton->setPalette(palette); ambientButton->setStyle(QStyleFactory::create("Fusion")); - diffuse=QColor(255*d->diffuse.x(), - 255*d->diffuse.y(), - 255*d->diffuse.z()); + diffuse=QColor::fromRgbF(d->diffuse.x(), + d->diffuse.y(), + d->diffuse.z()); palette.setColor(QPalette::Button,diffuse); diffuseButton->setPalette(palette); diffuseButton->setStyle(QStyleFactory::create("Fusion")); - specular=QColor(255*d->specular.x(), - 255*d->specular.y(), - 255*d->specular.z()); + specular=QColor::fromRgbF(d->specular.x(), + d->specular.y(), + d->specular.z()); palette.setColor(QPalette::Button,specular); specularButton->setPalette(palette); specularButton->setStyle(QStyleFactory::create("Fusion")); @@ -268,14 +268,14 @@ void Viewer::doBindings() 1.0f); QString front_color = viewer_settings.value("front_color", QString("1.0,0.0,0.0")).toString(); - d->front_color= QColor(255*front_color.split(",").at(0).toFloat(), - 255*front_color.split(",").at(1).toFloat(), - 255*front_color.split(",").at(2).toFloat(), + d->front_color= QColor::fromRgbF(front_color.split(",").at(0).toFloat(), + front_color.split(",").at(1).toFloat(), + front_color.split(",").at(2).toFloat(), 1.0f); QString back_color = viewer_settings.value("back_color", QString("0.0,0.0,1.0")).toString(); - d->back_color= QColor( 255*back_color.split(",").at(0).toFloat(), - 255*back_color.split(",").at(1).toFloat(), - 255*back_color.split(",").at(2).toFloat(), + d->back_color= QColor::fromRgbF( back_color.split(",").at(0).toFloat(), + back_color.split(",").at(1).toFloat(), + back_color.split(",").at(2).toFloat(), 1.0f); d->spec_power = viewer_settings.value("spec_power", 51.8).toFloat(); d->scene = 0; @@ -1784,7 +1784,7 @@ void Viewer::setLighting() msgBox->exec(); return; } - double coords[3]; + float coords[3]; for(int j=0; j<3; ++j) { bool ok; @@ -1806,9 +1806,9 @@ void Viewer::setLighting() //set ambient connect(dialog, &LightingDialog::s_ambient_changed, [this, dialog](){ - d->ambient=QVector4D(dialog->ambient.redF(), - dialog->ambient.greenF(), - dialog->ambient.blueF(), + d->ambient=QVector4D((float)dialog->ambient.redF(), + (float)dialog->ambient.greenF(), + (float)dialog->ambient.blueF(), 1.0f); update(); }); @@ -1816,18 +1816,18 @@ void Viewer::setLighting() //set diffuse connect(dialog, &LightingDialog::s_diffuse_changed, [this, dialog](){ - d->diffuse=QVector4D(dialog->diffuse.redF(), - dialog->diffuse.greenF(), - dialog->diffuse.blueF(), + d->diffuse=QVector4D((float)dialog->diffuse.redF(), + (float)dialog->diffuse.greenF(), + (float)dialog->diffuse.blueF(), 1.0f); update(); }); //set specular connect(dialog, &LightingDialog::s_specular_changed, [this, dialog](){ - d->specular=QVector4D(dialog->specular.redF() , - dialog->specular.greenF(), - dialog->specular.blueF() , + d->specular=QVector4D((float)dialog->specular.redF(), + (float)dialog->specular.greenF(), + (float)dialog->specular.blueF(), 1.0f); update(); @@ -1837,8 +1837,8 @@ void Viewer::setLighting() connect(dialog->buttonBox->button(QDialogButtonBox::StandardButton::RestoreDefaults), &QPushButton::clicked, [this](){ d->position = QVector4D(0,0,1,1); - d->ambient=QVector4D(77.0/255,77.0/255,77.0/255, 1.0); - d->diffuse=QVector4D(204.0/255,204.0/255,204.0/255,1.0); + d->ambient=QVector4D(77.0f/255,77.0f/255,77.0f/255, 1.0); + d->diffuse=QVector4D(204.0f/255,204.0f/255,204.0f/255,1.0); d->specular=QVector4D(0,0,0,1.0); d->spec_power = 51; update(); diff --git a/Polyline_simplification_2/package_info/Polyline_simplification_2/dependencies b/Polyline_simplification_2/package_info/Polyline_simplification_2/dependencies index ba5bb8645f3..46b60927a44 100644 --- a/Polyline_simplification_2/package_info/Polyline_simplification_2/dependencies +++ b/Polyline_simplification_2/package_info/Polyline_simplification_2/dependencies @@ -1,5 +1,6 @@ Algebraic_foundations Arithmetic_kernel +BGL Cartesian_kernel Circulator Distance_2 diff --git a/Three/include/CGAL/Three/Three.h b/Three/include/CGAL/Three/Three.h index 5966928a7f4..38b2ac8ea7d 100644 --- a/Three/include/CGAL/Three/Three.h +++ b/Three/include/CGAL/Three/Three.h @@ -73,15 +73,31 @@ public: * in the plugin. */ static void autoConnectActions(CGAL::Three::Polyhedron_demo_plugin_interface* plugin); + /*! + * Displays in the console a blue text preceded by the mention + * "INFO: ". + */ static void information(QString); /*! - * Displays a blue text preceded by the mention "WARNING :". + * Displays in the console an orange text preceded by the mention "WARNING: ". */ static void warning(QString); /*! - * Displays a red text preceded by the mention "ERROR :". + * Displays in the console a red text preceded by the mention "ERROR: ". */ static void error(QString); + /*! + * Displays an information popup. + */ + static void information(QString title, QString message); + /*! + * Displays a warning popup. + */ + static void warning(QString title, QString message); + /*! + * Displays an error popup. + */ + static void error(QString title, QString message); protected: static QMainWindow* s_mainwindow; static Viewer_interface* s_mainviewer; diff --git a/Triangulation_2/doc/Triangulation_2/CGAL/Constrained_triangulation_plus_2.h b/Triangulation_2/doc/Triangulation_2/CGAL/Constrained_triangulation_plus_2.h index 821d59c2d42..7f8eeddc547 100644 --- a/Triangulation_2/doc/Triangulation_2/CGAL/Constrained_triangulation_plus_2.h +++ b/Triangulation_2/doc/Triangulation_2/CGAL/Constrained_triangulation_plus_2.h @@ -293,6 +293,30 @@ std::size_t insert_constraints(PointIterator points_first, PointIterator points_ IndicesIterator indices_first, IndicesIterator indices_last); +/*! +splits into constraints the graph of subconstraints. + +Consider the graph `g={V,E}` where `V` is the set of vertices of the +triangulation and `E` is the set of all subconstraints of all +constraints of the triangulation. + +This function splits into polylines the graph `g` at vertices of +degree greater than 2 and at vertices for which +`is_terminal(v)==true`. + +Each computed polyline is stored as a constraint of the triangulation. + +\warning all existing constraints will be discarded. + +\param is_terminal An optional function returning `true` if the vertex +`v` of degree 2 is a polyline endpoint and `false` otherwise. If +omitted, a function always returning `false` will be used, that is no +degree 2 vertex will be considered as a polyline endpoint. + +\sa `split_graph_into_polylines()` +*/ +void split_subconstraint_graph_into_constraints(const std::function& is_terminal); + /*! removes the constraint `cid`, without removing the points from the triangulation. */ diff --git a/Triangulation_2/doc/Triangulation_2/Triangulation_2.txt b/Triangulation_2/doc/Triangulation_2/Triangulation_2.txt index 3c7e043b29a..90446677a4c 100644 --- a/Triangulation_2/doc/Triangulation_2/Triangulation_2.txt +++ b/Triangulation_2/doc/Triangulation_2/Triangulation_2.txt @@ -1124,6 +1124,22 @@ through the edge. \cgalExample{Triangulation_2/polylines_triangulation.cpp} +\subsection Triangulation_2ExampleBuildingFromSoup Example: Building a Triangulated Arrangement of Polylines from a Segment Soup + +The `Constrained_triangulation_plus_2` structure is initialized by a +set of polylines. As users may only be able to provide a disconnected +segment soup as input, a member function \link +Constrained_triangulation_plus_2::split_subconstraint_graph_into_constraints() +`split_subconstraint_graph_into_constraints()` \endlink is provided: +this function identifies the polylines by connecting segments until a +vertex whose degree is different from 2 is reached. + +The following code shows how a "blind" insertion of disconnected +segments can be processed into a set of well-defined polyline +constraints in a `Constrained_triangulation_plus_2`: + +\cgalExample{Triangulation_2/segment_soup_to_polylines.cpp} + \section Section_2D_Triangulations_Hierarchy The Triangulation Hierarchy The class `Triangulation_hierarchy_2` diff --git a/Triangulation_2/doc/Triangulation_2/dependencies b/Triangulation_2/doc/Triangulation_2/dependencies index 84dba85f07d..22ae69dcfef 100644 --- a/Triangulation_2/doc/Triangulation_2/dependencies +++ b/Triangulation_2/doc/Triangulation_2/dependencies @@ -4,6 +4,7 @@ STL_Extension Algebraic_foundations Circulator Stream_support +BGL TDS_2 Triangulation_3 Spatial_sorting diff --git a/Triangulation_2/doc/Triangulation_2/examples.txt b/Triangulation_2/doc/Triangulation_2/examples.txt index 45128d71309..7061593de13 100644 --- a/Triangulation_2/doc/Triangulation_2/examples.txt +++ b/Triangulation_2/doc/Triangulation_2/examples.txt @@ -19,5 +19,6 @@ \example Triangulation_2/voronoi.cpp \example Triangulation_2/copy_triangulation_2.cpp \example Triangulation_2/polylines_triangulation.cpp +\example Triangulation_2/segment_soup_to_polylines.cpp \example Triangulation_2/draw_triangulation_2.cpp */ diff --git a/Triangulation_2/examples/Triangulation_2/segment_soup_to_polylines.cpp b/Triangulation_2/examples/Triangulation_2/segment_soup_to_polylines.cpp new file mode 100644 index 00000000000..5eadd29090f --- /dev/null +++ b/Triangulation_2/examples/Triangulation_2/segment_soup_to_polylines.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +typedef CGAL::Exact_predicates_exact_constructions_kernel K; +typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; +typedef CGAL::Constrained_triangulation_plus_2 CDTP; + +typedef CDTP::Point Point; +typedef CDTP::Constraint_id Cid; +typedef CDTP::Vertex_handle Vertex_handle; + +int main() +{ + CDTP cdtp; + + // First polyline + cdtp.insert_constraint(Point(0,0), Point(1,1)); + cdtp.insert_constraint(Point(1,1), Point(2,2)); + cdtp.insert_constraint(Point(2,2), Point(3,3)); + + // Second polyline that cuts the first one in 2 + cdtp.insert_constraint(Point(1,1), Point(1,2)); + cdtp.insert_constraint(Point(1,2), Point(1,3)); + + // Third polyline + cdtp.insert_constraint(Point(10,10), Point(20,20)); + cdtp.insert_constraint(Point(20,20), Point(30,30)); + + // Fourth polyline + cdtp.insert_constraint(Point(100,100), Point(200,200)); + + // Segment soup of 8 segments as input + std::cout << "Input CDT+ has " << cdtp.number_of_constraints() << " constraints/subconstraints" << std::endl; + + std::cout << "Splitting subconstraints graph into constraints" << std::endl; + cdtp.split_subconstraint_graph_into_constraints(); + + // 5 polylines as output + std::cout << "Output CDT+ has " << cdtp.number_of_constraints() << " constraints:" << std::endl; + + for (CDTP::Constraint_id cid : cdtp.constraints()) + { + std::cout << " *"; + for (CDTP::Vertex_handle vh : cdtp.vertices_in_constraint(cid)) + std::cout << " (" << vh->point() << ")"; + std::cout << std::endl; + } + + return EXIT_SUCCESS; +} diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h index 51d291d1454..0b19a9a5698 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -315,6 +316,22 @@ public: return n; } */ + + void split_subconstraint_graph_into_constraints(const std::function& is_terminal + = std::function()) + { + internal::CTP2_graph_visitor visitor(*this); + if (is_terminal) + CGAL::split_graph_into_polylines (internal::CTP2_subconstraint_graph(*this), visitor, + [&is_terminal](Vertex_handle vh, + const internal::CTP2_subconstraint_graph&) -> bool + { + return is_terminal(vh); + }); + else + CGAL::split_graph_into_polylines (internal::CTP2_subconstraint_graph(*this), visitor); + } + Vertex_handle push_back(const Point& p) { return insert(p); diff --git a/Triangulation_2/include/CGAL/Triangulation_2/internal/CTP2_subconstraint_graph.h b/Triangulation_2/include/CGAL/Triangulation_2/internal/CTP2_subconstraint_graph.h new file mode 100644 index 00000000000..77e212b9f04 --- /dev/null +++ b/Triangulation_2/include/CGAL/Triangulation_2/internal/CTP2_subconstraint_graph.h @@ -0,0 +1,126 @@ +// Copyright (c) 2020 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Simon Giraudot + +#ifndef CGAL_CTP2_SUBCONSTRAINT_GRAPH_H +#define CGAL_CTP2_SUBCONSTRAINT_GRAPH_H + +#include + +#include +#include + +#include + +namespace CGAL +{ + +namespace internal +{ + +template +class CTP2_subconstraint_graph +{ + CTP2& ctp2; +public: + + typedef typename CTP2::Vertex_handle vertex_descriptor; + typedef typename CTP2::Subconstraint edge_descriptor; + typedef boost::undirected_tag directed_category; + typedef boost::disallow_parallel_edge_tag edge_parallel_category; + struct CTP2_graph_traversal_category : + public virtual boost::bidirectional_graph_tag, + public virtual boost::adjacency_graph_tag, + public virtual boost::edge_list_graph_tag, + public virtual boost::vertex_list_graph_tag + { }; + typedef CTP2_graph_traversal_category traversal_category; + typedef internal::Dereference_to_handle_enforcer< + CTP2, + typename CTP2::Finite_vertices_iterator, + vertex_descriptor> vertex_iterator; + + typedef typename CTP2::Subconstraint_iterator::value_type Subconstr_it_v_t; + typedef First_of_pair_property_map Subconstr_map; + typedef Property_map_to_unary_function Subconstr_uf; + typedef boost::transform_iterator edge_iterator; + + CTP2_subconstraint_graph (CTP2& ctp2) : ctp2(ctp2) { } + + friend Iterator_range vertices (const CTP2_subconstraint_graph& g) + { + return make_range (vertex_iterator(g.ctp2.finite_vertices_begin()), + vertex_iterator(g.ctp2.finite_vertices_end())); + } + + friend Iterator_range edges (const CTP2_subconstraint_graph& g) + { + return make_range (boost::make_transform_iterator(g.ctp2.subconstraints_begin(), Subconstr_uf(Subconstr_map())), + boost::make_transform_iterator(g.ctp2.subconstraints_end(), Subconstr_uf(Subconstr_map()))); + } + + friend vertex_descriptor source (edge_descriptor ed, const CTP2_subconstraint_graph&) + { + return ed.first; + } + + friend vertex_descriptor target (edge_descriptor ed, const CTP2_subconstraint_graph&) + { + return ed.second; + } +}; + + +template +class CTP2_graph_visitor +{ +private: + CTP2& ctp2; + std::vector to_remove; + typename CTP2::Constraint_id current; + typename CTP2::Vertex_handle latest_vertex; + +public: + + CTP2_graph_visitor (CTP2& ctp2) : ctp2 (ctp2) { } + + void start_new_polyline() + { + latest_vertex = typename CTP2::Vertex_handle(); + current = typename CTP2::Constraint_id(); + } + + void add_node (typename CTP2::Vertex_handle vh) + { + if (latest_vertex != typename CTP2::Vertex_handle()) + { + to_remove.push_back (ctp2.context(latest_vertex, vh).id()); + typename CTP2::Constraint_id cid = ctp2.insert_constraint(latest_vertex, vh); + if (current == typename CTP2::Constraint_id()) + current = cid; + else + current = ctp2.concatenate (current, cid); + } + latest_vertex = vh; + } + + void end_polyline() + { + for (typename CTP2::Constraint_id id : to_remove) + ctp2.remove_constraint(id); + to_remove.clear(); + } +}; + +} // namespace internal + +} // namespace CGAL + +#endif // CGAL_CTP2_SUBCONSTRAINT_GRAPH_H