使用 boost geometry 调整几何对象模型的进一步问题

Further problems in adapting a geometry object model using boost geometry

我想对以下由点、多边形(开放或封闭)和多边形域组成的不可变二维模型应用 boost::geometry 算法 class(具有任意数量的孔)class,分别如下图:

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/register/linestring.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/range.hpp>
#include <memory>
#include <vector>

namespace NGeometry {
   class Point2D {
   public:
      Point2D( double x, double y ) : m_x( x ), m_y( y ) {}
      double getX() const { return m_x; }
      double getY() const { return m_y; }
   private:
      double m_x, m_y;
   };
   using TyPoints2D = std::vector< Point2D >;

   // Either open (first point != last point) or closed (first point == last point) polygon
   class Polygon2D {
   public:
      Polygon2D( TyPoints2D points ) : m_points( std::move( points ) ) {}
      const TyPoints2D& getPoints() const { return m_points; }
   private:
      TyPoints2D m_points;
   };
   using TyPolygon2D = std::shared_ptr< Polygon2D >;
   using TyPolygons2D = std::vector< TyPolygon2D >;

   // Polygonal domain with outer cw oriented closed polygon and >= 0 ccw oriented inner polygons
   class PolygonalDomain2D {
   public:
      PolygonalDomain2D( TyPolygon2D outer, TyPolygons2D inners )
         : m_outer( std::move( outer ) ), m_inners( std::move( inners ) ) {}
      const TyPolygon2D& getOuter() const { return m_outer; }
      const TyPolygons2D& getInners() const { return m_inners; }
   private:
      TyPolygon2D m_outer;
      TyPolygons2D m_inners;
   };
} // namespace NGeometry


// Provide read only Boost.Range for Polygon2D
namespace boost {
   template<>
   struct range_const_iterator< NGeometry::Polygon2D >
   {
      typedef std::vector< NGeometry::Point2D >::const_iterator type;
   };
   template<>
   struct range_value< NGeometry::Polygon2D >
   {
      typedef NGeometry::Point2D type;
   };
} // namespace boost
inline std::vector< NGeometry::Point2D >::const_iterator range_begin( const NGeometry::Polygon2D polygon ) {
   return polygon.getPoints().cbegin();
}
inline std::vector< NGeometry::Point2D >::const_iterator range_end( const NGeometry::Polygon2D polygon ) {
   return polygon.getPoints().cend();
}

BOOST_GEOMETRY_REGISTER_POINT_2D_CONST( NGeometry::Point2D, double, cs::cartesian, getX(), getY() );
BOOST_GEOMETRY_REGISTER_LINESTRING( NGeometry::Polygon2D );
// How to register PolygonalDomain2D?

int main( int argc, char* argv[] )
{
   NGeometry::Point2D point( 0.0, 0.0 );
   auto outerPolygonSP = std::make_shared< NGeometry::Polygon2D >(
      NGeometry::TyPoints2D { { -2.0, -2.0 }, { -2.0, 2.0 }, { 2.0, 2.0 }, { 2.0, -2.0 }, { -2.0, -2.0 } } );
   auto innerPolygonSP = std::make_shared< NGeometry::Polygon2D >(
      NGeometry::TyPoints2D { { -1.0, -1.0 }, { 1.0, -1.0 }, { 1.0, 1.0 }, { -1.0, 1.0 }, { -1.0, -1.0 } } );
   NGeometry::PolygonalDomain2D domain( outerPolygonSP, { innerPolygonSP } );

   double length = boost::geometry::length( *outerPolygonSP );
   double area = boost::geometry::area( domain );
   bool isInside = boost::geometry::within( point, domain );

   return 0;
}

中描述的第一个步骤相比,classes 已被修改并移至 NGeometry 命名空间,这会导致编译器错误 NGeometry::Polygon2D 缺少开始和结束。此外,我不知道如何调整域。如果有任何帮助,我将不胜感激。

g++ 编译器输出:

In file included from /usr/include/boost/geometry/core/closure.hpp:22,
                 from /usr/include/boost/geometry/geometry.hpp:25,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/core/point_type.hpp: In instantiation of ‘struct boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>’:
/usr/include/boost/geometry/core/point_type.hpp:66:17:   required from ‘struct boost::geometry::core_dispatch::point_type<void, NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/coordinate_system.hpp:58:59:   required from ‘struct boost::geometry::core_dispatch::coordinate_system<void, NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/coordinate_system.hpp:93:17:   required from ‘struct boost::geometry::coordinate_system<NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/cs.hpp:244:17:   required from ‘struct boost::geometry::cs_tag<NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/strategies/area_result.hpp:59:8:   required from ‘struct boost::geometry::area_result<NGeometry::PolygonalDomain2D, boost::geometry::default_strategy>’
/usr/include/boost/geometry/algorithms/area.hpp:320:1:   required by substitution of ‘template<class Geometry> typename boost::geometry::area_result<Geometry, boost::geometry::default_strategy>::type boost::geometry::area(const Geometry&) [with Geometry = NGeometry::PolygonalDomain2D]’
main.cpp:78:48:   required from here
/usr/include/boost/geometry/core/point_type.hpp:45:5: error: could not convert ‘boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>::NOT_IMPLEMENTED_FOR_THIS_POINT_TYPE45::assert_arg()’ from ‘mpl_::failed************ (boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>::NOT_IMPLEMENTED_FOR_THIS_POINT_TYPE::************)(mpl_::assert_::types<NGeometry::PolygonalDomain2D, mpl_::na, mpl_::na, mpl_::na>)’ to ‘mpl_::assert<false>::type’ {aka ‘mpl_::assert<false>’}
   45 |     BOOST_MPL_ASSERT_MSG
      |     ^
      |     |
      |     mpl_::failed************ (boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>::NOT_IMPLEMENTED_FOR_THIS_POINT_TYPE::************)(mpl_::assert_::types<NGeometry::PolygonalDomain2D, mpl_::na, mpl_::na, mpl_::na>)
In file included from /usr/include/boost/geometry/core/coordinate_dimension.hpp:23,
                 from /usr/include/boost/geometry/geometry.hpp:26,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/core/point_type.hpp: In instantiation of ‘struct boost::geometry::core_dispatch::point_type<void, NGeometry::PolygonalDomain2D>’:
/usr/include/boost/geometry/core/coordinate_system.hpp:58:59:   required from ‘struct boost::geometry::core_dispatch::coordinate_system<void, NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/coordinate_system.hpp:93:17:   required from ‘struct boost::geometry::coordinate_system<NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/core/cs.hpp:244:17:   required from ‘struct boost::geometry::cs_tag<NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/strategies/area_result.hpp:59:8:   required from ‘struct boost::geometry::area_result<NGeometry::PolygonalDomain2D, boost::geometry::default_strategy>’
/usr/include/boost/geometry/algorithms/area.hpp:320:1:   required by substitution of ‘template<class Geometry> typename boost::geometry::area_result<Geometry, boost::geometry::default_strategy>::type boost::geometry::area(const Geometry&) [with Geometry = NGeometry::PolygonalDomain2D]’
main.cpp:78:48:   required from here
/usr/include/boost/geometry/core/point_type.hpp:66:17: error: no type named ‘type’ in ‘struct boost::geometry::traits::point_type<NGeometry::PolygonalDomain2D>’
   66 |         >::type type;
      |                 ^~~~
main.cpp: In function ‘int main(int, char**)’:
main.cpp:78:48: error: no matching function for call to ‘area(NGeometry::PolygonalDomain2D&)’
   78 |    double area = boost::geometry::area( domain );
      |                                                ^
In file included from /usr/include/boost/geometry/geometry.hpp:52,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/algorithms/area.hpp:320:1: note: candidate: ‘template<class Geometry> typename boost::geometry::area_result<Geometry, boost::geometry::default_strategy>::type boost::geometry::area(const Geometry&)’
  320 | area(Geometry const& geometry)
      | ^~~~
/usr/include/boost/geometry/algorithms/area.hpp:320:1: note:   substitution of deduced template arguments resulted in errors seen above
/usr/include/boost/geometry/algorithms/area.hpp:356:1: note: candidate: ‘template<class Geometry, class Strategy> typename boost::geometry::area_result<Geometry, Strategy>::type boost::geometry::area(const Geometry&, const Strategy&)’
  356 | area(Geometry const& geometry, Strategy const& strategy)
      | ^~~~
/usr/include/boost/geometry/algorithms/area.hpp:356:1: note:   template argument deduction/substitution failed:
main.cpp:78:48: note:   candidate expects 2 arguments, 1 provided
   78 |    double area = boost::geometry::area( domain );
      |                                                ^
In file included from /usr/include/boost/geometry/core/closure.hpp:22,
                 from /usr/include/boost/geometry/geometry.hpp:25,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/algorithms/not_implemented.hpp: In instantiation of ‘struct boost::geometry::nyi::not_implemented_error<void, void, void>’:
/usr/include/boost/geometry/algorithms/not_implemented.hpp:108:8:   required from ‘struct boost::geometry::not_implemented<void, void, void>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:69:8:   required from ‘struct boost::geometry::dispatch::check<const NGeometry::PolygonalDomain2D, void, true>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:196:8:   required from ‘struct boost::geometry::concepts::detail::checker<const NGeometry::PolygonalDomain2D>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:219:31:   required from ‘void boost::geometry::concepts::check() [with Geometry = const NGeometry::PolygonalDomain2D]’
/usr/include/boost/geometry/algorithms/detail/within/interface.hpp:108:41:   required from ‘static bool boost::geometry::resolve_variant::within<Geometry1, Geometry2>::apply(const Geometry1&, const Geometry2&, const Strategy&) [with Strategy = boost::geometry::default_strategy; Geometry1 = NGeometry::Point2D; Geometry2 = NGeometry::PolygonalDomain2D]’
/usr/include/boost/geometry/algorithms/detail/within/interface.hpp:255:17:   required from ‘bool boost::geometry::within(const Geometry1&, const Geometry2&) [with Geometry1 = NGeometry::Point2D; Geometry2 = NGeometry::PolygonalDomain2D]’
main.cpp:79:59:   required from here
/usr/include/boost/geometry/algorithms/not_implemented.hpp:69:5: error: could not convert ‘boost::geometry::nyi::not_implemented_error<void, void, void>::THIS_OPERATION_IS_NOT_OR_NOT_YET_IMPLEMENTED69::assert_arg()’ from ‘mpl_::failed************ (boost::geometry::nyi::not_implemented_error<void, void, void>::THIS_OPERATION_IS_NOT_OR_NOT_YET_IMPLEMENTED::************)(mpl_::assert_::types<void, void, void, mpl_::na>)’ to ‘mpl_::assert<false>::type’ {aka ‘mpl_::assert<false>’}
   69 |     BOOST_MPL_ASSERT_MSG
      |     ^
      |     |
      |     mpl_::failed************ (boost::geometry::nyi::not_implemented_error<void, void, void>::THIS_OPERATION_IS_NOT_OR_NOT_YET_IMPLEMENTED::************)(mpl_::assert_::types<void, void, void, mpl_::na>)
In file included from /usr/include/boost/geometry/core/coordinate_dimension.hpp:21,
                 from /usr/include/boost/geometry/geometry.hpp:26,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/geometry/core/coordinate_dimension.hpp: In instantiation of ‘void boost::geometry::assert_dimension_equal() [with G1 = NGeometry::Point2D; G2 = NGeometry::PolygonalDomain2D]’:
/usr/include/boost/geometry/algorithms/detail/within/interface.hpp:109:53:   required from ‘static bool boost::geometry::resolve_variant::within<Geometry1, Geometry2>::apply(const Geometry1&, const Geometry2&, const Strategy&) [with Strategy = boost::geometry::default_strategy; Geometry1 = NGeometry::Point2D; Geometry2 = NGeometry::PolygonalDomain2D]’
/usr/include/boost/geometry/algorithms/detail/within/interface.hpp:255:17:   required from ‘bool boost::geometry::within(const Geometry1&, const Geometry2&) [with Geometry1 = NGeometry::Point2D; Geometry2 = NGeometry::PolygonalDomain2D]’
main.cpp:79:59:   required from here
/usr/include/boost/geometry/core/coordinate_dimension.hpp:122:5: error: no type named ‘type’ in ‘struct boost::geometry::dimension<NGeometry::PolygonalDomain2D>’
  122 |     BOOST_STATIC_ASSERT(( static_cast<size_t>(dimension<G1>::type::value) == static_cast<size_t>(dimension<G2>::type::value) ));
      |     ^~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/range/functions.hpp:18,
                 from /usr/include/boost/range/iterator_range_core.hpp:38,
                 from /usr/include/boost/lexical_cast.hpp:30,
                 from /usr/include/boost/math/tools/convert_from_string.hpp:15,
                 from /usr/include/boost/math/constants/constants.hpp:13,
                 from /usr/include/boost/geometry/util/math.hpp:29,
                 from /usr/include/boost/geometry/core/radian_access.hpp:33,
                 from /usr/include/boost/geometry/geometry.hpp:42,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/range/begin.hpp: In instantiation of ‘constexpr typename boost::range_iterator<T>::type boost::range_detail::range_begin(C&) [with C = const NGeometry::Polygon2D; typename boost::range_iterator<T>::type = __gnu_cxx::__normal_iterator<const NGeometry::Point2D*, std::vector<NGeometry::Point2D> >]’:
/usr/include/boost/range/begin.hpp:119:23:   required from ‘void boost::SinglePassRangeConcept<T>::const_constraints(const Rng&) [with T = const NGeometry::Polygon2D; boost::SinglePassRangeConcept<T>::Rng = const NGeometry::Polygon2D]’
/usr/include/boost/range/concepts.hpp:295:13:   required from ‘boost::SinglePassRangeConcept<T>::~SinglePassRangeConcept() [with T = const NGeometry::Polygon2D]’
/usr/include/boost/range/concepts.hpp:318:12:   required from ‘static void boost::concepts::requirement<boost::concepts::failed************ Model::************>::failed() [with Model = boost::ForwardRangeConcept<const NGeometry::Polygon2D>]’
/usr/include/boost/geometry/geometries/concepts/linestring_concept.hpp:111:5:   required from ‘class boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D>’
/usr/include/boost/concept/detail/has_constraints.hpp:32:62:   required by substitution of ‘template<class Model> boost::concepts::detail::yes boost::concepts::detail::has_constraints_(Model*, boost::concepts::detail::wrap_constraints<Model, (& Model::constraints)>*) [with Model = boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D>]’
/usr/include/boost/concept/detail/has_constraints.hpp:42:5:   [ skipping 3 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/boost/concept_check.hpp:50:7:   required from ‘class boost::geometry::detail::concept_check::check<boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D> >’
/usr/include/boost/geometry/geometries/concepts/check.hpp:86:8:   required from ‘struct boost::geometry::dispatch::check<const NGeometry::Polygon2D, boost::geometry::linestring_tag, true>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:196:8:   required from ‘struct boost::geometry::concepts::detail::checker<const NGeometry::Polygon2D>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:219:31:   required from ‘void boost::geometry::concepts::check() [with Geometry = const NGeometry::Polygon2D]’
/usr/include/boost/geometry/algorithms/length.hpp:282:36:   required from ‘typename boost::geometry::default_length_result<Geometry>::type boost::geometry::length(const Geometry&) [with Geometry = NGeometry::Polygon2D; typename boost::geometry::default_length_result<Geometry>::type = long double]’
main.cpp:77:61:   required from here
/usr/include/boost/range/concepts.hpp:301:46:   in ‘constexpr’ expansion of ‘boost::range_adl_barrier::begin<NGeometry::Polygon2D>((* & const_range))’
/usr/include/boost/range/begin.hpp:49:18: error: ‘const class NGeometry::Polygon2D’ has no member named ‘begin’
   49 |         return c.begin();
      |                ~~^~~~~
In file included from /usr/include/boost/range/functions.hpp:19,
                 from /usr/include/boost/range/iterator_range_core.hpp:38,
                 from /usr/include/boost/lexical_cast.hpp:30,
                 from /usr/include/boost/math/tools/convert_from_string.hpp:15,
                 from /usr/include/boost/math/constants/constants.hpp:13,
                 from /usr/include/boost/geometry/util/math.hpp:29,
                 from /usr/include/boost/geometry/core/radian_access.hpp:33,
                 from /usr/include/boost/geometry/geometry.hpp:42,
                 from /usr/include/boost/geometry.hpp:17,
                 from main.cpp:1:
/usr/include/boost/range/end.hpp: In instantiation of ‘constexpr typename boost::range_iterator<T>::type boost::range_detail::range_end(C&) [with C = const NGeometry::Polygon2D; typename boost::range_iterator<T>::type = __gnu_cxx::__normal_iterator<const NGeometry::Point2D*, std::vector<NGeometry::Point2D> >]’:
/usr/include/boost/range/end.hpp:113:21:   required from ‘void boost::SinglePassRangeConcept<T>::const_constraints(const Rng&) [with T = const NGeometry::Polygon2D; boost::SinglePassRangeConcept<T>::Rng = const NGeometry::Polygon2D]’
/usr/include/boost/range/concepts.hpp:295:13:   required from ‘boost::SinglePassRangeConcept<T>::~SinglePassRangeConcept() [with T = const NGeometry::Polygon2D]’
/usr/include/boost/range/concepts.hpp:318:12:   required from ‘static void boost::concepts::requirement<boost::concepts::failed************ Model::************>::failed() [with Model = boost::ForwardRangeConcept<const NGeometry::Polygon2D>]’
/usr/include/boost/geometry/geometries/concepts/linestring_concept.hpp:111:5:   required from ‘class boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D>’
/usr/include/boost/concept/detail/has_constraints.hpp:32:62:   required by substitution of ‘template<class Model> boost::concepts::detail::yes boost::concepts::detail::has_constraints_(Model*, boost::concepts::detail::wrap_constraints<Model, (& Model::constraints)>*) [with Model = boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D>]’
/usr/include/boost/concept/detail/has_constraints.hpp:42:5:   [ skipping 3 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/boost/concept_check.hpp:50:7:   required from ‘class boost::geometry::detail::concept_check::check<boost::geometry::concepts::ConstLinestring<const NGeometry::Polygon2D> >’
/usr/include/boost/geometry/geometries/concepts/check.hpp:86:8:   required from ‘struct boost::geometry::dispatch::check<const NGeometry::Polygon2D, boost::geometry::linestring_tag, true>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:196:8:   required from ‘struct boost::geometry::concepts::detail::checker<const NGeometry::Polygon2D>’
/usr/include/boost/geometry/geometries/concepts/check.hpp:219:31:   required from ‘void boost::geometry::concepts::check() [with Geometry = const NGeometry::Polygon2D]’
/usr/include/boost/geometry/algorithms/length.hpp:282:36:   required from ‘typename boost::geometry::default_length_result<Geometry>::type boost::geometry::length(const Geometry&) [with Geometry = NGeometry::Polygon2D; typename boost::geometry::default_length_result<Geometry>::type = long double]’
main.cpp:77:61:   required from here
/usr/include/boost/range/concepts.hpp:302:44:   in ‘constexpr’ expansion of ‘boost::range_adl_barrier::end<NGeometry::Polygon2D>((* & const_range))’
/usr/include/boost/range/end.hpp:50:22: error: ‘const class NGeometry::Polygon2D’ has no member named ‘end’
   50 |             return c.end();
      |                    ~~^~~

首先,让我们修复 Undefined Behaviour,因为您在 range_beginrange_end 中按值获取了参数。这意味着,根据定义,您将迭代器返回到临时对象中,这些临时对象在您使用它们之前就已经消失了。

另外,根据 Boost Range 设计启用 ADL,方法是将这些重载放在其范围类型的声明命名空间中:

namespace NGeometry {
    inline std::vector<NGeometry::Point2D>::const_iterator
    range_begin(NGeometry::Polygon2D const& polygon) {
        return polygon.getPoints().cbegin();
    }
    inline std::vector<NGeometry::Point2D>::const_iterator
    range_end(NGeometry::Polygon2D const& polygon) {
        return polygon.getPoints().cend();
    }
}

好的,哇。现在它不会在我们 运行 发生任何事情时立即崩溃(或者更糟的是,不要崩溃并导致大量法律费用)。

戒指概念

接下来,您将多边形类型注册为... LINESTRING。这是行不通的,因为它不是多边形概念所需要的:Ring and Polygon.

解决这个问题

BOOST_GEOMETRY_REGISTER_RING(NGeometry::Polygon2D)

让你更进一步:

namespace bg = boost::geometry;
auto outer =
    std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
        {-2.0, -2.0}, {-2.0, 2.0}, {2.0, 2.0}, {2.0, -2.0}, {-2.0, -2.0}});

std::cout << bg::wkt(*outer) << "\n";
std::cout << bg::dsv(*outer) << "\n";
std::cout << "Length: " << bg::length(*outer) << "\n";
std::cout << "Area: " << bg::area(*outer) << "\n";

版画

POLYGON((-2 -2,-2 2,2 2,2 -2,-2 -2))
((-2, -2), (-2, 2), (2, 2), (2, -2), (-2, -2))
Length: 0
Area: 16

扩展到 PolygonalDomain2D

您的“域”就是 OGC 所知道的多边形。它有一个外圈和可选的多个内圈。幸运的是,OGC 标准还要求反转内环的点顺序。

但是,您不是 直接 而是 shared_ptr 聚合环,这让事情变得有点复杂。我认为您 可能 也经历了将 shared_ptr 改编为合适的戒指的麻烦。我将在下面向您展示我所做的。

如何注册多边形

没有“REGISTER_XXX”工具。您必须按照记录的 Concept Requirements.

The documentation is not 100% in sync with reality, I've figured this out a while ago:

如果你去写出相关的特征:

template <> struct tag<NGeometry::PolygonalDomain2D> {
    using type = polygon_tag;
};
template <> struct ring_mutable_type<NGeometry::PolygonalDomain2D> {
    using type = NGeometry::Polygon2D;
};
template <> struct ring_const_type<NGeometry::PolygonalDomain2D> {
    using type = NGeometry::Polygon2D const;
};
template <> struct exterior_ring<NGeometry::PolygonalDomain2D> {
    static decltype(auto) get(NGeometry::PolygonalDomain2D &v) {
        return *v.getOuter();
    }
    static decltype(auto) get(NGeometry::PolygonalDomain2D const &v) {
        return *v.getOuter();
    }
};

现在它变得越来越辣了,因为我们已经到达了你的内部环,它不是作为 Ring 模型的简单容器存储的,而是作为指向它们的共享指针的容器存储的。

我要站在巨人的肩膀上,用boost::adaptors::indirected合成一个范围,隐藏那层间接

template <> struct interior_rings<NGeometry::PolygonalDomain2D> {
    static decltype(auto) get(NGeometry::PolygonalDomain2D &v) {
        return v.getInners() | boost::adaptors::indirected;
    }
    static decltype(auto) get(NGeometry::PolygonalDomain2D const &v) {
        return v.getInners() | boost::adaptors::indirected;
    }
};

接下来,我将类型特征重新排序到该实现之后,这样我就可以使用类型推导而不是拼出实现类型名称:

template <> struct interior_mutable_type<NGeometry::PolygonalDomain2D> {
    using type = decltype(interior_rings<NGeometry::PolygonalDomain2D>::get(
        std::declval<NGeometry::PolygonalDomain2D>()));
};
template <> struct interior_const_type<NGeometry::PolygonalDomain2D> {
    using type = decltype(interior_rings<NGeometry::PolygonalDomain2D>::get(
        std::declval<NGeometry::PolygonalDomain2D>())) const;
};

有了这个技巧,我们可以测试:

int main() {
    auto report = [](auto heading, auto &g) {
        std::cout << " == " << heading << " ========================\n";
        check_poly(g);
        std::cout << "WKT:       " << bg::wkt(g)       << "\n";
        std::cout << "DSV:       " << bg::dsv(g)       << "\n";
        std::cout << "Area:      " << bg::area(g)      << "\n";
        std::cout << "Perimeter: " << bg::perimeter(g) << "\n";

        if constexpr (std::is_same_v<bg::polygon_tag,
                                     typename bg::traits::tag<
                                         std::decay_t<decltype(g)>>::type>)
        {
            std::cout << "Outer Perimeter: "
                      << bg::perimeter(bg::exterior_ring(g)) << "\n";
        }
    };

    auto outer =
        std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
            {-3.0, -3.0}, {-3.0, 3.0}, {3.0, 3.0}, {3.0, -3.0}, {-3.0, -3.0}});
    auto inner =
        std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
            {-2.0, -2.0}, {2.0, -2.0}, {2.0, 2.0}, {-2.0, 2.0}, {-2.0, -2.0}});

    NGeometry::PolygonalDomain2D domain(outer, {inner});

    report("Outer", *outer);
    report("Inner", *inner);
    report("Domain", domain);

    std::cout << " == Within Check ========================\n";
    std::cout << "Point (0,0) within domain? " << std::boolalpha
              << bg::within(NGeometry::Point2D(0.0, 0.0), domain) << "\n";
}

打印

 == Outer ========================
WKT:       POLYGON((-3 -3,-3 3,3 3,3 -3,-3 -3))
DSV:       ((-3, -3), (-3, 3), (3, 3), (3, -3), (-3, -3))
Area:      36
Perimeter: 24
 == Inner ========================
Warning: Geometry has wrong orientation
WKT:       POLYGON((-2 -2,2 -2,2 2,-2 2,-2 -2))
DSV:       ((-2, -2), (2, -2), (2, 2), (-2, 2), (-2, -2))
Area:      -16
Perimeter: 16
 == Domain ========================
WKT:       POLYGON((-3 -3,-3 3,3 3,3 -3,-3 -3),(-2 -2,2 -2,2 2,-2 2,-2 -2))
DSV:       (((-3, -3), (-3, 3), (3, 3), (3, -3), (-3, -3)), ((-2, -2), (2, -2), (2, 2), (-2, 2
), (-2, -2)))
Area:      20
Perimeter: 40
Outer Perimeter: 24
 == Within Check ========================
Point (0,0) within domain? false

Note

The Warning: Geometry has wrong orientation is exactly what you want due the concept requirements that interior rings have opposite point order. So, the check confirms what we wanted to see.

完整演示 Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/register/ring.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <memory>
#include <vector>
#include <iostream>

namespace NGeometry {
class Point2D {
  public:
    Point2D(double x, double y) : m_x(x) , m_y(y) { }
    [[nodiscard]] double getX() const { return m_x; }
    [[nodiscard]] double getY() const { return m_y; }

  private:
    double m_x, m_y;
};
using TyPoints2D = std::vector<Point2D>;

// Either open (first point != last point) or closed (first point == last
// point) polygon
class Polygon2D {
  public:
    Polygon2D(TyPoints2D points) : m_points(std::move(points)) {}
    [[nodiscard]] const TyPoints2D &getPoints() const { return m_points; }

  private:
    TyPoints2D m_points;
};

using SharedPolygon2D = std::shared_ptr<Polygon2D>;
using TyPolygons2D = std::vector<SharedPolygon2D>;

// Polygonal domain with outer cw oriented closed
// polygon and >= 0 ccw oriented inner polygons
class PolygonalDomain2D {
  public:
    PolygonalDomain2D(SharedPolygon2D outer, TyPolygons2D inners)
        : m_outer(std::move(outer)), m_inners(std::move(inners)) {}
    [[nodiscard]] const SharedPolygon2D &getOuter() const { return m_outer; }
    [[nodiscard]] const TyPolygons2D &getInners() const { return m_inners; }

  private:
    SharedPolygon2D m_outer;
    TyPolygons2D m_inners;
};
} // namespace NGeometry

// Provide read only Boost.Range for Polygon2D
namespace NGeometry {
    inline std::vector<NGeometry::Point2D>::const_iterator
    range_begin(NGeometry::Polygon2D const& polygon) {
        return polygon.getPoints().cbegin();
    }
    inline std::vector<NGeometry::Point2D>::const_iterator
    range_end(NGeometry::Polygon2D const& polygon) {
        return polygon.getPoints().cend();
    }

    inline std::vector<NGeometry::Point2D>::const_iterator
    range_begin(NGeometry::Polygon2D& polygon) {
        return polygon.getPoints().cbegin();
    }
    inline std::vector<NGeometry::Point2D>::const_iterator
    range_end(NGeometry::Polygon2D& polygon) {
        return polygon.getPoints().cend();
    }
}

namespace boost {
    template <> struct range_iterator<NGeometry::Polygon2D> {
        using type = std::vector<NGeometry::Point2D>::const_iterator;
    };
    template <> struct range_const_iterator<NGeometry::Polygon2D> {
        using type = std::vector<NGeometry::Point2D>::const_iterator;
    };
    template <> struct range_value<NGeometry::Polygon2D> {
        using type = NGeometry::Point2D;
    };
} // namespace boost

BOOST_GEOMETRY_REGISTER_POINT_2D_CONST(NGeometry::Point2D, double,
                                       cs::cartesian, getX(), getY())
BOOST_GEOMETRY_REGISTER_RING(NGeometry::Polygon2D)

// How to register PolygonalDomain2D?
namespace boost::geometry::traits {
    template <> struct tag<NGeometry::PolygonalDomain2D> {
        using type = polygon_tag;
    };
    template <> struct ring_mutable_type<NGeometry::PolygonalDomain2D> {
        using type = NGeometry::Polygon2D;
    };
    template <> struct ring_const_type<NGeometry::PolygonalDomain2D> {
        using type = NGeometry::Polygon2D const;
    };
    template <> struct exterior_ring<NGeometry::PolygonalDomain2D> {
        static decltype(auto) get(NGeometry::PolygonalDomain2D &v) {
            return *v.getOuter();
        }
        static decltype(auto) get(NGeometry::PolygonalDomain2D const &v) {
            return *v.getOuter();
        }
    };
    template <> struct interior_rings<NGeometry::PolygonalDomain2D> {
        static decltype(auto) get(NGeometry::PolygonalDomain2D &v) {
            return v.getInners() | boost::adaptors::indirected;
        }
        static decltype(auto) get(NGeometry::PolygonalDomain2D const &v) {
            return v.getInners() | boost::adaptors::indirected;
        }
    };
    template <> struct interior_mutable_type<NGeometry::PolygonalDomain2D> {
        using type = decltype(interior_rings<NGeometry::PolygonalDomain2D>::get(
            std::declval<NGeometry::PolygonalDomain2D>()));
    };
    template <> struct interior_const_type<NGeometry::PolygonalDomain2D> {
        using type = decltype(interior_rings<NGeometry::PolygonalDomain2D>::get(
            std::declval<NGeometry::PolygonalDomain2D>())) const;
    };
}

namespace bg = boost::geometry;

template <typename G>
void check_poly(G& g) {
    if constexpr (1) {
        bg::model::polygon<bg::model::d2::point_xy<double>> copy;
        bg::convert(g, copy);
        std::string reason;
        while (!bg::is_valid(copy, reason)) {
            std::cout << "Warning: " << reason << "\n";
            bg::correct(copy);
        }
    }
}

int main() {
    auto report = [](auto heading, auto &g) {
        std::cout << " == " << heading << " ========================\n";
        check_poly(g);
        std::cout << "WKT:       " << bg::wkt(g)       << "\n";
        std::cout << "DSV:       " << bg::dsv(g)       << "\n";
        std::cout << "Area:      " << bg::area(g)      << "\n";
        std::cout << "Perimeter: " << bg::perimeter(g) << "\n";

        if constexpr (std::is_same_v<bg::polygon_tag,
                                     typename bg::traits::tag<
                                         std::decay_t<decltype(g)>>::type>)
        {
            std::cout << "Outer Perimeter: "
                      << bg::perimeter(bg::exterior_ring(g)) << "\n";
        }
    };

    auto outer =
        std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
            {-3.0, -3.0}, {-3.0, 3.0}, {3.0, 3.0}, {3.0, -3.0}, {-3.0, -3.0}});
    auto inner =
        std::make_shared<NGeometry::Polygon2D>(NGeometry::TyPoints2D{
            {-2.0, -2.0}, {2.0, -2.0}, {2.0, 2.0}, {-2.0, 2.0}, {-2.0, -2.0}});

    NGeometry::PolygonalDomain2D domain(outer, {inner});

    report("Outer", *outer);
    report("Inner", *inner);
    report("Domain", domain);

    std::cout << " == Within Check ========================\n";
    std::cout << "Point (0,0) within domain? " << std::boolalpha
              << bg::within(NGeometry::Point2D(0.0, 0.0), domain) << "\n";
}