Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-20 08:44:11

0001 // Boost.Geometry
0002 
0003 // Copyright (c) 2025 Oracle and/or its affiliates.
0004 // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
0005 
0006 // Use, modification and distribution is subject to the Boost Software License,
0007 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0008 // http://www.boost.org/LICENSE_1_0.txt)
0009 
0010 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYHEDRAL_SURFACE_HPP
0011 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYHEDRAL_SURFACE_HPP
0012 
0013 #include <boost/geometry/algorithms/buffer.hpp>
0014 #include <boost/geometry/algorithms/correct.hpp>
0015 #include <boost/geometry/algorithms/detail/is_valid/interface.hpp>
0016 #include <boost/geometry/algorithms/detail/is_valid/polygon.hpp>
0017 #include <boost/geometry/algorithms/detail/calculate_point_order.hpp>
0018 #include <boost/geometry/algorithms/distance.hpp> //needed for rtree
0019 #include <boost/geometry/index/predicates.hpp>
0020 #include <boost/geometry/index/rtree.hpp>
0021 #include <boost/geometry/io/wkt/wkt.hpp>
0022 #include <boost/geometry/iterators/point_reverse_iterator.hpp>
0023 
0024 #include <vector>
0025 #include <stack>
0026 
0027 namespace boost { namespace geometry
0028 {
0029 
0030 #ifndef DOXYGEN_NO_DETAIL
0031 namespace detail { namespace is_valid
0032 {
0033 
0034 struct polygon_frontier_type
0035 {
0036     polygon_frontier_type(std::size_t size)
0037         : m_stack(), m_visited(size, false)
0038     {}
0039 
0040     void push(std::size_t index)
0041     {
0042         if (!m_visited[index])
0043         {
0044             m_stack.push(index);
0045             m_visited[index] = true;
0046         }
0047     }
0048     bool empty()
0049     {
0050         return m_stack.empty();
0051     }
0052     std::size_t top()
0053     {
0054         return m_stack.top();
0055     }
0056     void pop()
0057     {
0058         m_stack.pop();
0059     }
0060 private :
0061     std::stack<std::size_t> m_stack;
0062     std::vector<bool> m_visited;
0063 };
0064 
0065 // Implementation of the polygon segment intersection algorithm
0066 // This function checks if a segment (s1, s2) intersects with a 2D polygon in 3D space.
0067 // proj1 and proj2 are the indices of the coordinate axes used to determine the sign of three points
0068 // of the polygon after projection.
0069 // It returns:
0070 // - 0 if the segment does not intersect the polygon
0071 // - 1 if the segment intersects the polygon in the interior
0072 // - 2 if the segment intersects the polygon at a vertex
0073 // - 3 if the segment intersects the polygon at an edge
0074 // Reference: R.J.Segura, F.R.Feito - An algorithm for determining intersection segment-polygon in
0075 // 3D, Computers & Graphics 1998
0076 template <typename Point, typename Polygon, typename Strategy>
0077 int polygon_segment_intersection(Point const& s1,
0078                                  Point const& s2,
0079                                  Polygon const& polygon,
0080                                  int proj1,
0081                                  int proj2,
0082                                  Strategy const& strategy)
0083 {
0084     BOOST_GEOMETRY_ASSERT(geometry::dimension<Point>::value == 3);
0085     BOOST_GEOMETRY_ASSERT(geometry::dimension<Polygon>::value == 3);
0086 
0087     auto const pit_a = points_begin(polygon);
0088     auto const& p_a = *pit_a;
0089 
0090     int cut = 0;
0091 
0092     for (auto pit = boost::next(pit_a); pit != boost::prior(points_end(polygon)); ++pit)
0093     {
0094         auto const& p_b = *pit;
0095         auto const& p_c = *boost::next(pit);
0096 
0097         int const sign = strategy.side().apply(p_a, p_b, p_c, proj1, proj2);
0098 
0099         int const sign1 = strategy.side3().apply(s1, p_a, s2, p_b);
0100         int const sign2 = strategy.side3().apply(s1, p_c, p_b, s2);
0101         int const sign3 = strategy.side3().apply(s1, p_a, p_c, s2);
0102 
0103         // Insects at p_b or p_a or p_c -> vertex
0104         if ((sign1 == 0 && sign2 == 0) || (sign1 == 0 && sign3 == 0) || (sign2 == 0 && sign3 == 0))
0105         {
0106             return 2;
0107         }
0108 
0109         // Intersects at edge p_a, p_c -> internal edge
0110         if (sign1 == 0 && sign2 == sign3) cut += sign;
0111 
0112         // Intersects at edge p_b, p_c -> external edge
0113         if (sign2 == 0 && sign1 == sign3) return 3;
0114 
0115         // Intersects at edge p_a, p_b -> internal edge
0116         if (sign3 == 0 && sign1 == sign2) cut += sign;
0117 
0118         // Intersects inside p_a, p_b, p_c -> inside
0119         if (sign1 == sign2 && sign2 == sign3) cut += 2 * sign;
0120     }
0121 
0122     return (cut == 2) ? 1 : 0;
0123 }
0124 
0125 template <typename PolyhedralSurface>
0126 struct valid_intersection
0127 {
0128     valid_intersection(std::size_t num_of_polygons)
0129         : m_polygon_projections(num_of_polygons)
0130     {}
0131 
0132     using coordinate_type = typename coordinate_type<PolyhedralSurface>::type;
0133     using polygon_3d = typename boost::range_value<PolyhedralSurface>::type;
0134     using ring_3d = typename boost::geometry::ring_type<polygon_3d>::type;
0135     using point_3d = typename boost::geometry::point_type<ring_3d>::type;
0136     using segment_3d = typename boost::geometry::model::segment<point_3d>;
0137     using box_3d = typename boost::geometry::model::box<point_3d>;
0138 
0139     using point_2d = boost::geometry::model::point
0140         <coordinate_type, 2, boost::geometry::cs::cartesian>;
0141     using linestring_2d = typename boost::geometry::model::linestring<point_2d>;
0142     using polygon_2d = typename std::conditional
0143         <
0144             boost::geometry::point_order<polygon_3d>::value == boost::geometry::counterclockwise,
0145             typename boost::geometry::model::polygon<point_2d, true>,
0146             typename boost::geometry::model::polygon<point_2d, false>
0147         >::type;
0148     using ring_2d = typename boost::geometry::ring_type<polygon_2d>::type;
0149 
0150 private:
0151 
0152     enum class intersection_type
0153     {
0154         none,
0155         valid,
0156         invalid
0157     };
0158 
0159     struct polygon_projection
0160     {
0161         polygon_2d projected_polygon;
0162         int p1_idx;
0163         int p2_idx;
0164         int p3_idx;
0165         int proj1;
0166         int proj2;
0167         int dir;
0168     };
0169 
0170     template <typename Strategy>
0171     static std::tuple<int, int, int> affinely_independent(point_3d const& p1,
0172                                                           point_3d const& p2,
0173                                                           point_3d const& p3,
0174                                                           Strategy const& strategy)
0175     {
0176         int side = strategy.side().apply(p1, p2, p3, 0, 1);
0177         if (side != 0) return {0, 1, side};
0178 
0179         side = strategy.side().apply(p1, p2, p3, 0, 2);
0180         if (side != 0) return {0, 2, side};
0181 
0182         side = strategy.side().apply(p1, p2, p3, 1, 2);
0183         if (side != 0) return {1, 2, side};
0184 
0185         return {-1, -1, 0}; // All points are collinear
0186     }
0187 
0188     template <typename Strategy>
0189     static std::tuple<int, int, int> find_non_collinear_points(polygon_3d const& polygon,
0190                                                                int& p1_index,
0191                                                                int& p2_index,
0192                                                                int& p3_index,
0193                                                                Strategy const& strategy)
0194     {
0195         for (auto pit1 = points_begin(polygon); pit1 != points_end(polygon); ++pit1)
0196         {
0197             for (auto pit2 = boost::next(pit1); pit2 != points_end(polygon); ++pit2)
0198             {
0199                 for (auto pit3 = boost::next(pit2); pit3 != points_end(polygon); ++pit3)
0200                 {
0201                     // A set of 3 independent points is sufficient to construct a plane.
0202                     auto res = affinely_independent(*pit1, *pit2, *pit3, strategy);
0203                     if (std::get<0>(res) != -1)
0204                     {
0205                         p1_index = std::distance(points_begin(polygon), pit1);
0206                         p2_index = std::distance(points_begin(polygon), pit2);
0207                         p3_index = std::distance(points_begin(polygon), pit3);
0208                         return res;
0209                     }
0210                 }
0211             }
0212         }
0213         return {-1, -1, 0}; // All points are collinear
0214     }
0215 
0216     template <typename VisitPolicy>
0217     static bool has_correct_intersection_orientation(polygon_2d const& polygon,
0218                                                      point_2d const& segment_point1,
0219                                                      point_2d const& segment_point2,
0220                                                      VisitPolicy& visitor)
0221     {
0222         if (boost::geometry::equals(segment_point1, segment_point2))
0223         {
0224             visitor.template apply<failure_duplicate_points>();
0225             return false;
0226         }
0227 
0228         boost::geometry::closing_iterator<ring_2d> it(boost::geometry::exterior_ring(polygon));
0229         auto begin = it;
0230         boost::geometry::closing_iterator<ring_2d> end(boost::geometry::exterior_ring(polygon), true);
0231 
0232         for (; it != end - 1; ++it)
0233         {
0234             if (boost::geometry::equals(*it, segment_point1))
0235             {
0236                 auto prev = (it == begin) ? end - 1 : boost::prior(it);
0237 
0238                 if (boost::geometry::equals(segment_point2, *prev))
0239                 {
0240                     // valid intersection and consistent orientation
0241                     return true;
0242                 }
0243 
0244                 auto next = (it == end) ? begin : boost::next(it);
0245 
0246                 if (boost::geometry::equals(segment_point2, *next))
0247                 {
0248                     // valid intersection but inconsistent orientation
0249                     visitor.template apply<failure_inconsistent_orientation>();
0250                     return false;
0251                 }
0252             }
0253         }
0254 
0255         visitor.template apply<failure_invalid_intersection>();
0256 
0257         return false;
0258     }
0259 
0260     point_2d project_point_to_coordinate_plane(point_3d const& point, int proj1, int proj2)
0261     {
0262         if (proj1 == 1 && proj2 == 2) return point_2d{geometry::get<1>(point), geometry::get<2>(point)};
0263         if (proj1 == 0 && proj2 == 2) return point_2d{geometry::get<0>(point), geometry::get<2>(point)};
0264         if (proj1 == 0 && proj2 == 1) return point_2d{geometry::get<0>(point), geometry::get<1>(point)};
0265         return point_2d{};
0266     }
0267 
0268     // Check if a segment intersects a polygon in 3D space
0269     template <typename VisitPolicy, typename Strategy>
0270     intersection_type check_polygon_segment_intersection(point_3d const& segment_point1,
0271                                                          point_3d const& segment_point2,
0272                                                          polygon_3d const& polygon,
0273                                                          std::size_t const polygon_index,
0274                                                          VisitPolicy& visitor,
0275                                                          Strategy const& strategy)
0276     {
0277         auto const& projection = m_polygon_projections[polygon_index];
0278         auto const& projected_polygon = projection.projected_polygon;
0279 
0280         auto const& p1 = polygon.outer()[projection.p1_idx];
0281         auto const& p2 = polygon.outer()[projection.p2_idx];
0282         auto const& p3 = polygon.outer()[projection.p3_idx];
0283 
0284         int proj1 = projection.proj1;
0285         int proj2 = projection.proj2;
0286         int dir = projection.dir;
0287 
0288         // Check if the segment intersects the plane of the polygon
0289         auto sign1 = strategy.side3().apply(p1, p2, p3, segment_point1);
0290         auto sign2 = strategy.side3().apply(p1, p2, p3, segment_point2);
0291 
0292         if ((sign1 > 0 && sign2 > 0) || (sign1 < 0 && sign2 < 0))
0293         {
0294             return intersection_type::none; // No intersection, this is valid
0295         }
0296 
0297         // Check if the segment is on the same plane as the polygon
0298         if (sign1 == 0 && sign2 == 0)
0299         {
0300             auto const& projected_p1 = project_point_to_coordinate_plane(segment_point1, proj1, proj2);
0301             auto const& projected_p2 = project_point_to_coordinate_plane(segment_point2, proj1, proj2);
0302 
0303             linestring_2d projected_segment = {projected_p1, projected_p2};
0304 
0305             // Check if the projected segment intersects the polygon
0306             std::tuple
0307             <
0308                 boost::geometry::model::multi_point<point_2d>,
0309                 boost::geometry::model::multi_linestring<linestring_2d>> intersections;
0310             boost::geometry::intersection(projected_segment, projected_polygon, intersections);
0311 
0312             auto const& points = std::get<0>(intersections);
0313             auto const& linestrings = std::get<1>(intersections);
0314 
0315             if (boost::geometry::is_empty(points) && boost::geometry::is_empty(linestrings))
0316             {
0317                 return intersection_type::none; // No intersection, this is valid
0318             }
0319 
0320             if (!points.empty())
0321             {
0322                 if (points.size() == 1 &&
0323                     (boost::geometry::equals(points.front(), projected_p1) ||
0324                     boost::geometry::equals(points.front(), projected_p2)))
0325                 {
0326                     return intersection_type::none; // Valid empty intersection
0327                 }
0328                 visitor.template apply<failure_invalid_intersection>();
0329                 return intersection_type::invalid; // Invalid intersection
0330             }
0331 
0332             if (linestrings.size() == 1)
0333             {
0334                 if (boost::geometry::equals(linestrings.front(), projected_segment))
0335                 {
0336                     auto const& pp1 = dir < 0 ? projected_p2 : projected_p1;
0337                     auto const& pp2 = dir < 0 ? projected_p1 : projected_p2;
0338                     if (!has_correct_intersection_orientation(projected_polygon, pp1, pp2, visitor))
0339                     {
0340                         return intersection_type::invalid;
0341                     }
0342                     return intersection_type::valid; // The only case of valid non empty intersection
0343                 }
0344             }
0345             visitor.template apply<failure_invalid_intersection>();
0346             return intersection_type::invalid;
0347         }
0348 
0349         int inter = polygon_segment_intersection(segment_point1, segment_point2, polygon, proj1,
0350             proj2, strategy);
0351 
0352         if ((inter == 1 || inter == 3) || (inter == 2 && sign1 !=0 && sign2 != 0))
0353         {
0354             visitor.template apply<failure_invalid_intersection>();
0355             return intersection_type::invalid;
0356         }
0357 
0358         return intersection_type::none;
0359     }
0360 
0361     // Helper function to check if any segment of a ring intersects a polygon
0362     template <typename VisitPolicy, typename Strategy>
0363     intersection_type check_polygon_polygon_intersection(polygon_3d const& polygon1,
0364                                                          polygon_3d const& polygon2,
0365                                                          std::size_t const& index2,
0366                                                          VisitPolicy& visitor,
0367                                                          Strategy const& strategy)
0368     {
0369         bool valid_intersection = false;
0370         auto const& ring1 = boost::geometry::exterior_ring(polygon1);
0371 
0372         for (auto it = boost::begin(ring1); it + 1 != boost::end(ring1); ++it)
0373         {
0374             auto result = check_polygon_segment_intersection(*it, *(it + 1), polygon2, index2,
0375                 visitor, strategy);
0376 
0377             if (result == intersection_type::invalid)
0378             {
0379                 return intersection_type::invalid; // Found invalid intersection
0380             }
0381 
0382             if (result == intersection_type::valid)
0383             {
0384                 valid_intersection = true; // Found a valid intersection
0385             }
0386         }
0387         // Either no intersection or valid intersection
0388         return valid_intersection ? intersection_type::valid : intersection_type::none;
0389     }
0390 
0391  public:
0392 
0393     template <typename VisitPolicy, typename Strategy>
0394     intersection_type apply(polygon_3d const& polygon1,
0395                             polygon_3d const& polygon2,
0396                             std::size_t const& index1,
0397                             std::size_t const& index2,
0398                             VisitPolicy& visitor,
0399                             Strategy const& strategy)
0400     {
0401         auto intersection1 = check_polygon_polygon_intersection(polygon1, polygon2, index2,
0402             visitor, strategy);
0403 
0404         if (intersection1 == intersection_type::invalid)
0405         {
0406             return intersection_type::invalid; // Found invalid intersection
0407         }
0408 
0409         auto intersection2 = check_polygon_polygon_intersection(polygon2, polygon1, index1,
0410             visitor, strategy);
0411 
0412         if (intersection2 == intersection_type::invalid)
0413         {
0414             return intersection_type::invalid; // Found invalid intersection
0415         }
0416 
0417         if (intersection1 == intersection_type::none && intersection2 == intersection_type::none)
0418         {
0419             return intersection_type::none; // No intersection found
0420         }
0421 
0422         return intersection_type::valid;
0423     }
0424 
0425     template <typename VisitPolicy, typename Strategy>
0426     intersection_type initialize(polygon_3d const& polygon,
0427                                  std::size_t const polygon_index,
0428                                  VisitPolicy& visitor,
0429                                  Strategy const& strategy)
0430     {
0431         // TODO: Support interior rings
0432         ring_3d const& ring = boost::geometry::exterior_ring(polygon);
0433 
0434         if (boost::size(ring) < detail::minimum_ring_size<ring_3d>::value)
0435         {
0436             visitor.template apply<failure_few_points_on_face>();
0437             return intersection_type::invalid;
0438         }
0439 
0440         // Get three non-collinear points from the polygon to define the plane
0441         int p1_idx = 0;
0442         int p2_idx = 0;
0443         int p3_idx = 0;
0444 
0445         auto res = find_non_collinear_points(polygon, p1_idx, p2_idx, p3_idx, strategy);
0446 
0447         if (std::get<0>(res) == -1)
0448         {
0449             visitor.template apply<failure_collinear_points_on_face>();
0450             return intersection_type::invalid;
0451         }
0452 
0453         int proj1 = std::get<0>(res);
0454         int proj2 = std::get<1>(res);
0455         int dir = std::get<2>(res);
0456 
0457         auto const& p1 = polygon.outer()[p1_idx];
0458         auto const& p2 = polygon.outer()[p2_idx];
0459         auto const& p3 = polygon.outer()[p3_idx];
0460 
0461         // Check for non-coplanar points
0462         for (auto pit = points_begin(polygon); pit != points_end(polygon); ++pit)
0463         {
0464             if (strategy.side3().apply(p1, p2, p3, *pit) != 0)
0465             {
0466                 visitor.template apply<failure_non_coplanar_points_on_face>();
0467                 return intersection_type::invalid;
0468             }
0469         }
0470 
0471         // Project the polygon
0472         polygon_2d projected_polygon;
0473 
0474         auto add_projected_points = [&](auto begin_it, auto end_it)
0475         {
0476             for (auto it = begin_it; it != end_it; ++it)
0477             {
0478                 projected_polygon.outer().emplace_back(
0479                     project_point_to_coordinate_plane(*it, proj1, proj2));
0480             }
0481         };
0482 
0483         if (dir > 0) {
0484             add_projected_points(points_begin(ring), points_end(ring));
0485         } else {
0486             add_projected_points(points_rbegin(ring), points_rend(ring));
0487         }
0488 
0489         // Check if the projected polygon is valid
0490         if (!resolve_dynamic::is_valid<polygon_2d>::apply(projected_polygon, visitor, strategy))
0491         {
0492             return intersection_type::invalid;
0493         }
0494 
0495         // Cache the projection for future use
0496         m_polygon_projections[polygon_index] = polygon_projection{std::move(projected_polygon),
0497             p1_idx, p2_idx, p3_idx, proj1, proj2, dir};
0498 
0499         return intersection_type::valid;
0500     }
0501 
0502 private:
0503     std::vector<polygon_projection> m_polygon_projections;
0504 };
0505 
0506 template <typename PolyhedralSurface>
0507 class is_valid_polyhedral_surface
0508 {
0509 public:
0510     template <typename VisitPolicy, typename Strategy>
0511     static inline bool apply(PolyhedralSurface const& surface,
0512                              VisitPolicy& visitor,
0513                              Strategy const& strategy)
0514     {
0515         boost::ignore_unused(strategy);
0516 
0517         valid_intersection<PolyhedralSurface> valid_intersection(surface.size());
0518 
0519         // Check for colinear/coplanar points and compute projections
0520         for (std::size_t i = 0; i < surface.size(); ++i)
0521         {
0522             auto intersection = valid_intersection.initialize(surface[i], i, visitor, strategy);
0523 
0524             using intersection_type = decltype(intersection);
0525 
0526             if (intersection == intersection_type::invalid)
0527             {
0528                 return false;
0529             }
0530         }
0531 
0532         // Create an rtree to use it for polygon pair intersections
0533         using box_3d = typename boost::geometry::detail::is_valid::valid_intersection
0534             <PolyhedralSurface>::box_3d;
0535         using value = std::pair<box_3d, std::size_t>;
0536 
0537         boost::geometry::index::rtree<value, boost::geometry::index::rstar<4>> rtree;
0538         std::vector<std::unique_ptr<box_3d>> boxes(surface.size());
0539 
0540         for (std::size_t i = 0; i < surface.size(); ++i)
0541         {
0542             auto b = boost::geometry::return_envelope<box_3d>(surface[i], strategy);
0543             boxes[i] = std::make_unique<box_3d>(b);
0544             rtree.insert(std::make_pair(b, i)); // use balancing algorithm
0545         }
0546 
0547         // We check pairs of polygons for having a valid intersection.
0548         // We iterate in a BFS manner to check connectivity of the surface.
0549 
0550         auto it1 = boost::begin(surface);
0551 
0552         polygon_frontier_type polygon_frontier(surface.size());
0553         polygon_frontier.push(0);
0554 
0555         std::vector<std::size_t> remaining_polygons(surface.size());
0556         std::vector<std::size_t> positions(surface.size());
0557 
0558         for (std::size_t i = 0; i < surface.size(); ++i)
0559         {
0560             remaining_polygons[i] = i;
0561             positions[i] = i;
0562         }
0563 
0564         int remaining_count = surface.size();
0565 
0566         while (!polygon_frontier.empty())
0567         {
0568             auto index1 = polygon_frontier.top();
0569             polygon_frontier.pop();
0570 
0571             it1 = boost::begin(surface) + index1;
0572 
0573             // Remove index from remaining polygons and update positions
0574             auto moving_index = remaining_polygons[remaining_count - 1];
0575             remaining_polygons[positions[index1]] = moving_index;
0576             positions[moving_index] = positions[index1];
0577 
0578             // Query all polygons that intersect with the current polygon.
0579             // We use a buffer to avoid precision issues with the rtree query.
0580             std::vector<value> intersecting_boxes;
0581             auto const& b_query = *boxes[index1];
0582             auto const& b_query_buf = boost::geometry::return_buffer<box_3d>(b_query, 0.0001);
0583 
0584             rtree.query(boost::geometry::index::intersects(b_query_buf),
0585                         std::back_inserter(intersecting_boxes));
0586 
0587             for (int i = 0; i < remaining_count - 1; ++i)
0588             {
0589                 auto index2 = remaining_polygons[i];
0590 
0591                 // Skip if index2 is not in the intersecting boxes
0592                 if (std::find_if(intersecting_boxes.begin(), intersecting_boxes.end(),
0593                     [index2](const value& v) { return v.second == index2; }
0594                 ) == intersecting_boxes.end())
0595                 {
0596                     continue;
0597                 }
0598 
0599                 auto intersection = valid_intersection.apply(*it1, surface[index2], index1, index2,
0600                     visitor, strategy);
0601 
0602                 using intersection_type = decltype(intersection);
0603 
0604                 if (intersection == intersection_type::invalid)
0605                 {
0606                     return false; //  Invalid intersection found (including inconsistent orientation)
0607                 }
0608                 else if (intersection == intersection_type::valid)
0609                 {
0610                     polygon_frontier.push(index2);
0611                 }
0612             }
0613             remaining_count--;
0614         }
0615 
0616         if (remaining_count > 0)
0617         {
0618             visitor.template apply<failure_disconnected_surface>();
0619             return false; // Not all polygons were visited, hence disconnected
0620         }
0621 
0622         return true;
0623     }
0624 };
0625 
0626 }} // namespace detail::is_valid
0627 #endif // DOXYGEN_NO_DETAIL
0628 
0629 #ifndef DOXYGEN_NO_DISPATCH
0630 namespace dispatch
0631 {
0632 
0633 
0634 // Reference (for validity of Polyhedral Surfaces): OGC
0635 // OpenGISĀ® Implementation Standard for Geographic
0636 // information - Simple feature access - Part 1: Common
0637 // architecture: 6.1.12
0638 template <typename PolyhedralSurface, bool AllowEmptyMultiGeometries>
0639 struct is_valid
0640     <
0641         PolyhedralSurface, polyhedral_surface_tag, AllowEmptyMultiGeometries
0642     > : detail::is_valid::is_valid_polyhedral_surface<PolyhedralSurface>
0643 {};
0644 
0645 
0646 } // namespace dispatch
0647 #endif // DOXYGEN_NO_DISPATCH
0648 
0649 }} // namespace boost::geometry
0650 
0651 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_POLYHEDRAL_SURFACE_HPP