From 445aaf66c39e5ca67bf3b8810c8b22ec924402a3 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Wed, 30 Apr 2025 12:37:35 +0300 Subject: [PATCH 01/13] feat: Create polyhedral surface class Co-authored-by: Siddharth kumar --- doc/geometry.qbk | 1 + doc/html/index.html | 8 +- doc/imports.qbk | 1 + doc/make_qbk.py | 5 +- doc/quickref.xml | 34 ++++--- doc/reference.qbk | 22 +++-- example/Jamfile | 1 + include/boost/geometry/algorithms/clear.hpp | 17 ++++ include/boost/geometry/core/point_type.hpp | 15 ++- include/boost/geometry/core/ring_type.hpp | 24 ++++- include/boost/geometry/core/tags.hpp | 3 + .../geometry/geometries/concepts/check.hpp | 3 +- .../concepts/polyhedral_surface_concept.hpp | 77 +++++++++++++++ .../boost/geometry/geometries/geometries.hpp | 6 +- .../geometries/polyhedral_surface.hpp | 99 +++++++++++++++++++ .../boost/geometry/io/wkt/detail/prefix.hpp | 5 + include/boost/geometry/io/wkt/read.hpp | 38 +++++++ include/boost/geometry/io/wkt/write.hpp | 33 +++++++ test/geometries/CMakeLists.txt | 1 + test/geometries/Jamfile | 3 +- 20 files changed, 361 insertions(+), 35 deletions(-) create mode 100644 include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp create mode 100644 include/boost/geometry/geometries/polyhedral_surface.hpp diff --git a/doc/geometry.qbk b/doc/geometry.qbk index a48aa66226..c224c26e98 100644 --- a/doc/geometry.qbk +++ b/doc/geometry.qbk @@ -29,6 +29,7 @@ [def __0dim__ pointlike (e.g. point)] [def __1dim__ linear (e.g. linestring)] [def __2dim__ areal (e.g. polygon)] +[def __3dim__ volumetric (e.g. polyhedral_surface)] [def __single__ single (e.g. point, polygon)] [def __multi__ multiple (e.g. multi_point, multi_polygon)] [def __cart__ Cartesian] diff --git a/doc/html/index.html b/doc/html/index.html index d1e399c5a3..b5ae9640bc 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -3,7 +3,7 @@ Chapter 1. Geometry - + @@ -52,7 +52,7 @@

Table of Contents

-
+
Introduction
Compilation
Design Rationale
@@ -103,12 +103,12 @@

- Contributions + Contributions

Boost.Geometry contains contributions by:

-
    +
    • Akira Takahashi (adaption of Boost.Fusion)
    • diff --git a/doc/imports.qbk b/doc/imports.qbk index 81b8122636..a868144ffb 100644 --- a/doc/imports.qbk +++ b/doc/imports.qbk @@ -120,6 +120,7 @@ [import src/examples/geometries/point_xyz.cpp] [import src/examples/geometries/point.cpp] [import src/examples/geometries/polygon.cpp] +[import src/examples/geometries/polyhedral_surface.cpp] [import src/examples/geometries/ring.cpp] [import src/examples/geometries/segment.cpp] diff --git a/doc/make_qbk.py b/doc/make_qbk.py index 7396a360b7..73e7adcda9 100755 --- a/doc/make_qbk.py +++ b/doc/make_qbk.py @@ -6,9 +6,10 @@ # Copyright (c) 2009-2012 Mateusz Loskot (mateusz@loskot.net), London, UK # Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland # -# Copyright (c) 2018-2021, Oracle and/or its affiliates. +# Copyright (c) 2018-2025, Oracle and/or its affiliates. # Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle # Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +# # Use, modification and distribution is subject to the Boost Software License, # Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) @@ -141,7 +142,7 @@ def cs_to_quickbook(section): , "ever_circling_iterator"] models = ["point", "linestring", "box" - , "polygon", "segment", "ring" + , "polygon", "segment", "ring", "polyhedral_surface" , "multi_linestring", "multi_point", "multi_polygon", "referring_segment"] srs = ["spheroid"] diff --git a/doc/quickref.xml b/doc/quickref.xml index 5fe37f0525..0029d27a43 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -25,7 +25,7 @@ --> - + @@ -64,6 +64,12 @@ MultiPolygon + + 3-dimensional + + PolyhedralSurface + + @@ -109,6 +115,12 @@ multi_polygon + + 3-dimensional + + polyhedral_surface + + @@ -144,10 +156,10 @@ BOOST_GEOMETRY_REGISTER_POINT_3D BOOST_GEOMETRY_REGISTER_POINT_3D_CONST BOOST_GEOMETRY_REGISTER_POINT_3D_GET_SET - + BOOST_GEOMETRY_REGISTER_MULTI_POINT BOOST_GEOMETRY_REGISTER_MULTI_POINT_TEMPLATED - + @@ -207,7 +219,7 @@ Access Functions - get set @@ -349,7 +361,7 @@ Area - area + area Assign @@ -366,7 +378,7 @@ Azimuth - azimuth + azimuth Buffer @@ -466,7 +478,7 @@ Similarity discrete_frechet_distance - discrete_hausdorff_distance + discrete_hausdorff_distance Simplify @@ -483,7 +495,7 @@ Unique unique - + @@ -737,7 +749,7 @@ - + @@ -770,7 +782,7 @@ rtree() rtree(parameters_type const &, indexable_getter const &, value_equal const &, allocator_type const &) - rtree(Iterator, Iterator) + rtree(Iterator, Iterator) rtree(Range const &) rtree(Iterator, Iterator, parameters_type const &, indexable_getter const &, value_equal const &, allocator_type const &, PackAlloc const &) rtree(Range const &, parameters_type const &, indexable_getter const &, value_equal const &, allocator_type const &, PackAlloc const &) @@ -790,7 +802,7 @@ operator=(const rtree &) operator=(rtree &&) - swap(rtree &) + swap(rtree &) insert(value_type const &) insert(Iterator, Iterator) insert(ConvertibleOrRange const &) diff --git a/doc/reference.qbk b/doc/reference.qbk index fe8b890635..705a779838 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -23,7 +23,7 @@ [section:access Access Functions] -[/ This section is not ordered alfabetically +[/ This section is not ordered alfabetically to have get/set first and then the rings] [section:get get] @@ -240,12 +240,13 @@ [section:arithmetic Arithmetic] [include generated/arithmetic.qbk] -[endsect] +[endsect] [section:concepts Concepts] [include concept/point.qbk] [include concept/linestring.qbk] [include concept/polygon.qbk] +[include concept/polyhedral_surface.qbk] [include concept/multi_point.qbk] [include concept/multi_linestring.qbk] [include concept/multi_polygon.qbk] @@ -257,7 +258,7 @@ [section:constants Constants] [include reference/core/min_corner.qbk] [include reference/core/max_corner.qbk] -[endsect] +[endsect] [section:cs Coordinate Systems] [include generated/cartesian.qbk] @@ -286,16 +287,16 @@ [include generated/de9im_mask.qbk] [include generated/de9im_matrix.qbk] [include generated/de9im_static_mask.qbk] -[endsect] +[endsect] [section:enumerations Enumerations] [include generated/enum.qbk] -[endsect] +[endsect] [section:exceptions Exceptions] [include generated/exception.qbk] [include generated/centroid_exception.qbk] -[endsect] +[endsect] [section:io IO (input/output)] @@ -310,14 +311,14 @@ [include generated/svg.qbk] [include generated/svg_mapper.qbk] [endsect] -[endsect] +[endsect] [section:iterators Iterators] [include generated/closing_iterator.qbk] [include generated/circular_iterator.qbk] [include generated/ever_circling_iterator.qbk] -[endsect] +[endsect] [section:models Models] @@ -327,6 +328,7 @@ [include generated/point_xyz.qbk] [include generated/linestring.qbk] [include generated/polygon.qbk] +[include generated/polyhedral_surface.qbk] [include generated/multi_point.qbk] [include generated/multi_linestring.qbk] [include generated/multi_polygon.qbk] @@ -411,7 +413,7 @@ [include generated/within_winding.qbk] [include generated/within_franklin.qbk] [include generated/within_crossings_multiply.qbk] -[endsect] +[endsect] [section:views Views] @@ -420,7 +422,7 @@ [include generated/closeable_view.qbk] [include generated/reversible_view.qbk] [include generated/identity_view.qbk] -[endsect] +[endsect] [endsect] [/reference] diff --git a/example/Jamfile b/example/Jamfile index c2d7f11512..2b29b5963f 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -25,6 +25,7 @@ exe 06_a_transformation_example : 06_a_transformation_example.cpp ; exe 06_b_transformation_example : 06_b_transformation_example.cpp ; exe 07_a_graph_route_example : 07_a_graph_route_example.cpp : /boost/graph//boost_graph ; exe 07_b_graph_route_example : 07_b_graph_route_example.cpp : /boost/graph//boost_graph ; +exe 08_polyhedralsurface_example : 08_polyhedralsurface_example.cpp ; exe c01_custom_point_example : c01_custom_point_example.cpp ; exe c02_custom_box_example : c02_custom_box_example.cpp ; diff --git a/include/boost/geometry/algorithms/clear.hpp b/include/boost/geometry/algorithms/clear.hpp index efaa93d184..f301e5bf3f 100644 --- a/include/boost/geometry/algorithms/clear.hpp +++ b/include/boost/geometry/algorithms/clear.hpp @@ -72,6 +72,18 @@ struct polygon_clear } }; +template +struct polyhedral_surface_clear +{ + static inline void apply(Polyhedral_surface& polyhedral_surface) + { + traits::clear + < + typename std::remove_reference::type + >::apply(polyhedral_surface); + } +}; + template struct no_action { @@ -122,6 +134,11 @@ struct clear : detail::clear::collection_clear {}; +// Clear for Polyhedral surface +template +struct clear + : detail::clear::polyhedral_surface_clear +{}; // Polygon can (indirectly) use std for clear template diff --git a/include/boost/geometry/core/point_type.hpp b/include/boost/geometry/core/point_type.hpp index 89c572c241..524aeea493 100644 --- a/include/boost/geometry/core/point_type.hpp +++ b/include/boost/geometry/core/point_type.hpp @@ -5,8 +5,9 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2024 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2020-2021. -// Modifications copyright (c) 2020-2021 Oracle and/or its affiliates. +// This file was modified by Oracle on 2020-2025. +// Modifications copyright (c) 2020-2025 Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library @@ -95,6 +96,16 @@ struct point_type using type = typename boost::range_value::type; }; +// Specialization for PolyhedralSurface: the point-type is the point-type of its polygon type +template +struct point_type +{ + using type = typename point_type + < + polygon_tag, + typename boost::range_value::type + >::type; +}; // Specialization for polygon: the point-type is the point-type of its rings template diff --git a/include/boost/geometry/core/ring_type.hpp b/include/boost/geometry/core/ring_type.hpp index e382c61172..f040f35fa9 100644 --- a/include/boost/geometry/core/ring_type.hpp +++ b/include/boost/geometry/core/ring_type.hpp @@ -24,7 +24,6 @@ #include #include - #include #include #include @@ -62,7 +61,6 @@ struct ring_mutable_type Geometry); }; - } // namespace traits @@ -88,6 +86,20 @@ struct ring_return_type typedef Ring& type; }; +template +struct ring_return_type +{ + using type = typename ring_return_type + < + polygon_tag, + std::conditional_t + < + std::is_const::value, + typename boost::range_value::type const, + typename boost::range_value::type + > + >::type; +}; template struct ring_return_type @@ -153,6 +165,14 @@ struct ring_type typedef Ring type; }; +template +struct ring_type +{ + using type = std::remove_reference_t + < + typename ring_return_type::type + >; +}; template struct ring_type diff --git a/include/boost/geometry/core/tags.hpp b/include/boost/geometry/core/tags.hpp index 88422e4e07..16c4445211 100644 --- a/include/boost/geometry/core/tags.hpp +++ b/include/boost/geometry/core/tags.hpp @@ -105,6 +105,9 @@ struct box_tag : single_tag, areal_tag {}; /// Convenience segment (2-points) identifying tag struct segment_tag : single_tag, linear_tag {}; +/// OGC Polyhedral surface identifying tag +struct polyhedral_surface_tag : single_tag, volumetric_tag {}; + /// OGC Multi point identifying tag struct multi_point_tag : multi_tag, pointlike_tag {}; diff --git a/include/boost/geometry/geometries/concepts/check.hpp b/include/boost/geometry/geometries/concepts/check.hpp index 34eb740329..c9368be9b9 100644 --- a/include/boost/geometry/geometries/concepts/check.hpp +++ b/include/boost/geometry/geometries/concepts/check.hpp @@ -36,7 +36,8 @@ #include #include #include - +#include +#include namespace boost { namespace geometry { namespace concepts { diff --git a/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp b/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp new file mode 100644 index 0000000000..bbbc6e1a7c --- /dev/null +++ b/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp @@ -0,0 +1,77 @@ +// Boost.Geometry + +// Copyright (c) 2025 Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_GEOMETRIES_CONCEPTS_POLYHEDRAL_SURFACE_CONCEPT_HPP +#define BOOST_GEOMETRY_GEOMETRIES_CONCEPTS_POLYHEDRAL_SURFACE_CONCEPT_HPP + +#include +#include + +#include +#include +#include + +namespace boost { namespace geometry { namespace concepts +{ + +template +class PolyhedralSurface +{ +#ifndef DOXYGEN_NO_CONCEPT_MEMBERS + using polygon_type = typename boost::range_value::type; + + BOOST_CONCEPT_ASSERT( (concepts::Polygon) ); + BOOST_CONCEPT_ASSERT( (boost::RandomAccessRangeConcept) ); + +public: + + BOOST_CONCEPT_USAGE(PolyhedralSurface) + { + Geometry* ps = 0; + traits::clear::apply(*ps); + traits::resize::apply(*ps, 0); + // The concept should support the second version of push_back, using && + polygon_type* poly = 0; + traits::push_back::apply(*ps, std::move(*poly)); + } +#endif +}; + +// polyhedral surface(constant version) +template +class ConstPolyhedralSurface +{ +#ifndef DOXYGEN_NO_CONCEPT_MEMBERS + using polygon_type = typename boost::range_value::type; + + BOOST_CONCEPT_ASSERT( (concepts::ConstPolygon) ); + BOOST_CONCEPT_ASSERT( (boost::RandomAccessRangeConcept) ); + +public: + + BOOST_CONCEPT_USAGE(ConstPolyhedralSurface) + {} + +#endif +}; + +template +struct concept_type +{ + using type = PolyhedralSurface; +}; + +template +struct concept_type +{ + using type = ConstPolyhedralSurface; +}; + +}}} // namespace boost::geometry::concepts +#endif // BOOST_GEOMETRY_GEOMETRIES_CONCEPTS_POLYHEDRAL_SURFACE_CONCEPT_HPP diff --git a/include/boost/geometry/geometries/geometries.hpp b/include/boost/geometry/geometries/geometries.hpp index 50e700f6f0..1d3e1c5eeb 100644 --- a/include/boost/geometry/geometries/geometries.hpp +++ b/include/boost/geometry/geometries/geometries.hpp @@ -4,8 +4,9 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2020-2021. -// Modifications copyright (c) 2020-2021, Oracle and/or its affiliates. +// This file was modified by Oracle on 2020-2025. +// Modifications copyright (c) 2020-2025, Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library @@ -28,6 +29,7 @@ #include #include #include +#include #include #include diff --git a/include/boost/geometry/geometries/polyhedral_surface.hpp b/include/boost/geometry/geometries/polyhedral_surface.hpp new file mode 100644 index 0000000000..779316eb29 --- /dev/null +++ b/include/boost/geometry/geometries/polyhedral_surface.hpp @@ -0,0 +1,99 @@ +// Boost.Geometry + +// Copyright (c) 2025 Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_GEOMETRY_GEOMETRIES_POLYHEDRAL_SURFACE_HPP +#define BOOST_GEOMETRY_GEOMETRIES_POLYHEDRAL_SURFACE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace geometry +{ +namespace model +{ + +/*! +\brief A Polyhedral Surface is a contiguous collection of polygons, + which share common boundary segments. +\ingroup geometries +\tparam Polygon polygon type +\tparam Container container type for polygons, + default std::vector +\tparam Allocator container-allocator-type, for the polygons + +\qbk{[include reference/geometries/polyhedral_surface.qbk]} +\qbk{before.synopsis, +[heading Model of] +[link geometry.reference.concepts.concept_polyhedral_surface PolyhedralSurface Concept] +} + +*/ +template +< + typename Polygon, + template class Container = std::vector, + template class Allocator = std::allocator + +> +class polyhedral_surface : public Container > +{ + BOOST_CONCEPT_ASSERT( (concepts::Polygon) ); + +public : + using polygon_type = Polygon; + using polygon_container = Container >; + + /// \constructor_default{polyhedron} + inline polyhedral_surface() + : polygon_container() + {} + + /// \constructor_initialized_list{polyhedron} + inline polyhedral_surface(std::initializer_list l) + : polygon_container(l.begin(), l.end()) + {} + +}; +} // namespace model + +#ifndef DOXYGEN_NO_TRAITS_SPECIALIZATIONS +namespace traits +{ + +template +< + typename Polygon, + template class Container, + template class Allocator +> +struct tag +< + model::polyhedral_surface + < + Polygon, + Container, Allocator + > +> +{ + using type = polyhedral_surface_tag; +}; + +} // namespace traits +#endif // DOXYGEN_NO_TRAITS_SPECIALIZATIONS + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_GEOMETRIES_POLYHEDRAL_SURFACE_HPP diff --git a/include/boost/geometry/io/wkt/detail/prefix.hpp b/include/boost/geometry/io/wkt/detail/prefix.hpp index a61b8ae0f6..0768121650 100644 --- a/include/boost/geometry/io/wkt/detail/prefix.hpp +++ b/include/boost/geometry/io/wkt/detail/prefix.hpp @@ -42,6 +42,11 @@ struct prefix_linestring static inline const char* apply() { return "LINESTRING"; } }; +struct prefix_polyhedral_surface +{ + static inline const char* apply() { return "POLYHEDRALSURFACE"; } +}; + struct prefix_multipoint { static inline const char* apply() { return "MULTIPOINT"; } diff --git a/include/boost/geometry/io/wkt/read.hpp b/include/boost/geometry/io/wkt/read.hpp index 9d96dffeb1..afab50b8f0 100644 --- a/include/boost/geometry/io/wkt/read.hpp +++ b/include/boost/geometry/io/wkt/read.hpp @@ -458,6 +458,35 @@ struct polygon_parser } }; +template +struct polyhderal_surface_parser +{ + using polygon_t = typename PolyhedralSurface::polygon_type; + + template + static inline void apply(TokenizerIterator& it, + TokenizerIterator const& end, + std::string const& wkt, + PolyhedralSurface& polyhedral) + { + handle_open_parenthesis(it, end, wkt); + + // Parse polygons + while (it != end && *it != ")") + { + traits::resize::apply(polyhedral, boost::size(polyhedral) + 1); + polygon_parser::apply(it, end, wkt, *(boost::end(polyhedral) - 1)); + if (it != end && *it == ",") + { + // Skip "," after multi-element is parsed + ++it; + } + } + + handle_close_parenthesis(it, end, wkt); + } +}; + template inline bool one_of(TokenizerIterator const& it, @@ -1063,6 +1092,15 @@ struct read_wkt > {}; +template +struct read_wkt + : detail::wkt::geometry_parser + < + Geometry, + detail::wkt::polyhderal_surface_parser, + detail::wkt::prefix_polyhedral_surface + > +{}; template struct read_wkt diff --git a/include/boost/geometry/io/wkt/write.hpp b/include/boost/geometry/io/wkt/write.hpp index e1e9a03ef4..b4368fbdbb 100644 --- a/include/boost/geometry/io/wkt/write.hpp +++ b/include/boost/geometry/io/wkt/write.hpp @@ -228,6 +228,30 @@ struct wkt_poly }; +template +struct wkt_polyhedral +{ + template + static inline void apply(std::basic_ostream& os, + Polyhedral_surface const& polyhedral, bool force_closure) + { + using polygon = typename Polyhedral_surface::polygon_type; + + os << PrefixPolicy::apply(); + + os << "("; + for (auto it = boost::begin(polyhedral); it != boost::end(polyhedral); ++it) + { + if (it != boost::begin(polyhedral)) + { + os << ","; + } + wkt_poly::apply(os, *it, force_closure); + } + os << ")"; + } +}; + template struct wkt_multi { @@ -396,6 +420,15 @@ struct wkt > {}; +template +struct wkt + : detail::wkt::wkt_polyhedral + < + Polyhedral_surface, + detail::wkt::prefix_polyhedral_surface + > +{}; + template struct wkt : detail::wkt::wkt_multi diff --git a/test/geometries/CMakeLists.txt b/test/geometries/CMakeLists.txt index ea88f035d4..8157cc1cc2 100644 --- a/test/geometries/CMakeLists.txt +++ b/test/geometries/CMakeLists.txt @@ -24,6 +24,7 @@ foreach(item IN ITEMS point_xy point_xyz polygon + polyhedral_surface ring segment infinite_line diff --git a/test/geometries/Jamfile b/test/geometries/Jamfile index b67d5c5336..69c872464a 100644 --- a/test/geometries/Jamfile +++ b/test/geometries/Jamfile @@ -14,7 +14,7 @@ test-suite boost-geometry-geometries [ run adapted.cpp : : : : geometries_adapted ] [ run boost_array_as_point.cpp : : : : geometries_boost_array_as_point ] [ run boost_fusion.cpp : : : : geometries_boost_fusion ] - [ run boost_polygon.cpp : : : : geometries_boost_polygon ] + [ run boost_polygon.cpp : : : : geometries_boost_polygon ] [ run boost_range.cpp : : : : geometries_boost_range ] [ run boost_tuple.cpp : : : : geometries_boost_tuple ] [ run box.cpp : : : : geometries_box ] @@ -34,6 +34,7 @@ test-suite boost-geometry-geometries [ run point_xy.cpp : : : : geometries_point_xy ] [ run point_xyz.cpp : : : : geometries_point_xyz ] [ run polygon.cpp : : : : geometries_polygon ] + [ run polyhedral_surface.cpp : : : : geometries_polyhedral_surface ] [ run ring.cpp : : : : geometries_ring ] [ run segment.cpp : : : : geometries_segment ] [ run infinite_line.cpp : : : : geometries_infinite_line ] From d48155b122eea374c70f3f5c8805451ce3f9b5fe Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Wed, 30 Apr 2025 12:47:45 +0300 Subject: [PATCH 02/13] doc: Polyhedral surface --- doc/concept/polyhedral_surface.qbk | 45 +++++++++++ .../geometries/polyhedral_surface.qbk | 14 ++++ .../geometries/polyhedral_surface.cpp | 77 +++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 doc/concept/polyhedral_surface.qbk create mode 100644 doc/reference/geometries/polyhedral_surface.qbk create mode 100644 doc/src/examples/geometries/polyhedral_surface.cpp diff --git a/doc/concept/polyhedral_surface.qbk b/doc/concept/polyhedral_surface.qbk new file mode 100644 index 0000000000..f944129ee6 --- /dev/null +++ b/doc/concept/polyhedral_surface.qbk @@ -0,0 +1,45 @@ +[/============================================================================ + Boost.Geometry (aka GGL, Generic Geometry Library) + + Copyright (c) 2025 Oracle and/or its affiliates. + Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + + Use, modification and distribution is subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) + +=============================================================================/] + +[section:concept_polyhedral_surface PolyhedralSurface Concept] + +[heading Description] +[concept PolyhedralSurface..polyhedral surface] + +[heading Concept Definition] + +The PolyhedralSurface Concept is defined as following: + +* There must be a specialization of the metafunction `traits::tag`, defining `polyhedral_surface_tag` as type +* It must behave like a Boost.Range Random Access Range +* The type defined by the metafunction `range_value<...>::type` must fulfill + the [link geometry.reference.concepts.concept_polygon Polygon Concept] + +[heading Rules] + +Besides the Concepts, which are checks on compile-time, there are +rules that valid PolyhedralSurfaces must fulfill. See the +[link geometry.reference.concepts.concept_polygon Polygon Concept] for more information +on the rules a polygon (and also a polyhedral surface) must fulfill. + +Additionally: + +* It is a contiguous collection of polygons, which share common boundary segments. +* For any two polygons that share a common boundary, the “top” of the polygon shall be consistent. + This means that when two LinearRings from these two Polygons traverse the common boundary segment, + they do so in opposite directions. + +[heading Available Models] +* [link geometry.reference.models.model_polyhedral_surface model::polyhedral_surface] + + +[endsect] diff --git a/doc/reference/geometries/polyhedral_surface.qbk b/doc/reference/geometries/polyhedral_surface.qbk new file mode 100644 index 0000000000..cd901592a3 --- /dev/null +++ b/doc/reference/geometries/polyhedral_surface.qbk @@ -0,0 +1,14 @@ +[/============================================================================ + Boost.Geometry (aka GGL, Generic Geometry Library) + + Copyright (c) 2025 Oracle and/or its affiliates. + Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + + Use, modification and distribution is subject to the Boost Software License, + Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================/] + +[heading Examples] +[polyhedral_surface] +[polyhedral_surface_output] diff --git a/doc/src/examples/geometries/polyhedral_surface.cpp b/doc/src/examples/geometries/polyhedral_surface.cpp new file mode 100644 index 0000000000..b5bc45c1b0 --- /dev/null +++ b/doc/src/examples/geometries/polyhedral_surface.cpp @@ -0,0 +1,77 @@ +// Boost.Geometry +// QuickBook Example + +// Copyright (c) 2025 Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +//[polyhedral_surface +//` Declaration and use of the Boost.Geometry model::polyhedral_surface, modelling the PolyhedralSurface Concept + +#include +#include +#include + +namespace bg = boost::geometry; + +int main() +{ + using point_t = bg::model::point; + using polygon_t = bg::model::polygon; + using polyhedral_t = bg::model::polyhedral_surface; + + polyhedral_t polyhedral0; /*< Initializing an empty polyhedral surface (default constructor) >*/ + + polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}},\ + {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}},\ + {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}},\ + {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}},\ + {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}},\ + {{{1, 1, 1}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}}}}; /*< Creating a polyhderal surface (cube) using standard initiallized list >*/ + + + polyhedral0.resize(4); + bg::append(polyhedral0[0].outer(), point_t(0.0, 0.0, 0.0)); + bg::append(polyhedral0[0].outer(), point_t(5.0, 0.0, 0.0)); + bg::append(polyhedral0[0].outer(), point_t(0.0, 5.0, 0.0)); + bg::append(polyhedral0[1].outer(), point_t(0.0, 0.0, 0.0)); + bg::append(polyhedral0[1].outer(), point_t(0.0, 5.0, 0.0)); + bg::append(polyhedral0[1].outer(), point_t(0.0, 0.0, 5.0)); + bg::append(polyhedral0[2].outer(), point_t(0.0, 0.0, 0.0)); + bg::append(polyhedral0[2].outer(), point_t(5.0, 0.0, 0.0)); + bg::append(polyhedral0[2].outer(), point_t(0.0, 0.0, 5.0)); + bg::append(polyhedral0[3].outer(), point_t(5.0, 0.0, 0.0)); + bg::append(polyhedral0[3].outer(), point_t(0.0, 5.0, 0.0)); + bg::append(polyhedral0[3].outer(), point_t(0.0, 0.0, 5.0)); /*< Creating a polyhderal surface (triangular pyramid) using append and resize >*/ + + polyhedral_t polyhedral2; + bg::read_wkt("POLYHEDRALSURFACE(((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\ + ((0 0 0, 0 1 0, 0 1 1, 0 0 1, 0 0 0)),\ + ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),\ + ((1 1 1, 1 0 1, 0 0 1, 0 1 1, 1 1 1)),\ + ((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1)),\ + ((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1)))", polyhedral2); /*< Initialize polyhedral surface (cube) with wkt >*/ + + std::cout << bg::wkt(polyhedral0) << std::endl; /*< Write polyhedral surface wkt >*/ + std::cout << bg::wkt(polyhedral1) << std::endl; + std::cout << bg::wkt(polyhedral2) << std::endl; + + return 0; +} + +//] + + +//[polyhedral_surface_output +/*` +Output: +[pre +POLYHEDRALSURFACE(((0 0 0,5 0 0,0 5 0,0 0 0)),((0 0 0,0 5 0,0 0 5,0 0 0)),((0 0 0,5 0 0,0 0 5,0 0 0)),((5 0 0,0 5 0,0 0 5,5 0 0))) +POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,0 1 0,0 1 1,0 0 1,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 1,1 0 1,0 0 1,0 1 1,1 1 1)),((1 1 1,1 0 1,1 0 0,1 1 0,1 1 1)),((1 1 1,1 1 0,0 1 0,0 1 1,1 1 1))) +POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,0 1 0,0 1 1,0 0 1,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 1,1 0 1,0 0 1,0 1 1,1 1 1)),((1 1 1,1 0 1,1 0 0,1 1 0,1 1 1)),((1 1 1,1 1 0,0 1 0,0 1 1,1 1 1))) +] +*/ +//] From 29bae4f8b3a2abda4fc2dca767af27b3ada1571a Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Wed, 30 Apr 2025 12:49:04 +0300 Subject: [PATCH 03/13] chore: Add polyhedral surface example --- example/08_polyhedralsurface_example.cpp | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 example/08_polyhedralsurface_example.cpp diff --git a/example/08_polyhedralsurface_example.cpp b/example/08_polyhedralsurface_example.cpp new file mode 100644 index 0000000000..f3216429bb --- /dev/null +++ b/example/08_polyhedralsurface_example.cpp @@ -0,0 +1,65 @@ +// Boost.Geometry + +// Copyright (c) 2025 Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Polyhedral surface example + +#include +#include + +namespace bg = boost::geometry; + +int main() +{ + using point_t = bg::model::point; + using polygon_t = bg::model::polygon; + using polyhedral_t = bg::model::polyhedral_surface; + + // intializing an empty polyhedral surface (default constructor) + polyhedral_t polyhedral0; + + // creating a polyhderal surface (cube) using standard initiallized list + polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}},\ + {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}},\ + {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}},\ + {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}},\ + {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}},\ + {{{1, 1, 1}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}}}}; + + // creating a polyhderal surface (triangular pyramid) using append + // and resize + polyhedral0.resize(4); + bg::append(polyhedral0[0].outer(), point_t(0.0, 0.0, 0.0)); + bg::append(polyhedral0[0].outer(), point_t(5.0, 0.0, 0.0)); + bg::append(polyhedral0[0].outer(), point_t(0.0, 5.0, 0.0)); + bg::append(polyhedral0[1].outer(), point_t(0.0, 0.0, 0.0)); + bg::append(polyhedral0[1].outer(), point_t(0.0, 5.0, 0.0)); + bg::append(polyhedral0[1].outer(), point_t(0.0, 0.0, 5.0)); + bg::append(polyhedral0[2].outer(), point_t(0.0, 0.0, 0.0)); + bg::append(polyhedral0[2].outer(), point_t(5.0, 0.0, 0.0)); + bg::append(polyhedral0[2].outer(), point_t(0.0, 0.0, 5.0)); + bg::append(polyhedral0[3].outer(), point_t(5.0, 0.0, 0.0)); + bg::append(polyhedral0[3].outer(), point_t(0.0, 5.0, 0.0)); + bg::append(polyhedral0[3].outer(), point_t(0.0, 0.0, 5.0)); + + // Initialize polyhedral surface (cube) with wkt + polyhedral_t polyhedral2; + bg::read_wkt("POLYHEDRALSURFACE(((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\ + ((0 0 0, 0 1 0, 0 1 1, 0 0 1, 0 0 0)),\ + ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),\ + ((1 1 1, 1 0 1, 0 0 1, 0 1 1, 1 1 1)),\ + ((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1)),\ + ((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1)))", polyhedral2); + + // Write polyhedral surface wkt + std::cout << bg::wkt(polyhedral0) << std::endl; + std::cout << bg::wkt(polyhedral1) << std::endl; + std::cout << bg::wkt(polyhedral2) << std::endl; + + return 0; +} From 38ac122cc23473b5338e8845d10aaff510153ba9 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Wed, 30 Apr 2025 12:50:11 +0300 Subject: [PATCH 04/13] test: Polyhedral surface --- test/geometries/polyhedral_surface.cpp | 86 ++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/geometries/polyhedral_surface.cpp diff --git a/test/geometries/polyhedral_surface.cpp b/test/geometries/polyhedral_surface.cpp new file mode 100644 index 0000000000..62c2b990cc --- /dev/null +++ b/test/geometries/polyhedral_surface.cpp @@ -0,0 +1,86 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2025 Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Polyhedral surface tests + +#include +#include + +#include + +#include +#include +#include +#include + +int test_main(int, char* []) +{ + namespace bg = boost::geometry; + using point_t = bg::model::point; + using polygon_t = bg::model::polygon; + using polyhedral_t = bg::model::polyhedral_surface; + using const_polyhedral_t = bg::model::polyhedral_surface const; + + // intializing an empty polyhedral surface (default constructor) + polyhedral_t polyhedral0; + BOOST_CHECK_EQUAL(boost::size(polyhedral0), 0); + + // creating a polyhderal surface using standard initiallized list + polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}},\ + {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}},\ + {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}},\ + {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}},\ + {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}},\ + {{{1, 1, 1}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}}}}; + + // Modify polyhedral1 by adding a new polygon + polygon_t new_polygon = {{{0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1}, {0, 0, 1}}}; + polyhedral1.push_back(new_polygon); + // Modify polyhedral1 by removing the last polygon + polyhedral1.pop_back(); + // Modify polyhedral1 by changing the first point of the first polygon + polyhedral1[0].outer()[0] = point_t(0, 2, 0); + + // Test that the polyhedral surface has 6 polygons + BOOST_CHECK_EQUAL(boost::size(polyhedral1), 6); + + // Test that each polygon has 5 points + for (const auto& polygon : polyhedral1) + { + BOOST_CHECK_EQUAL(boost::size(polygon.outer()), 5); + } + + // clear polyhedral surface + bg::clear(polyhedral1); + BOOST_CHECK_EQUAL(boost::size(polyhedral1), 0); + + // read/write polyhedral surface wkt + polyhedral_t polyhedral2; + std::string wkt = "POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,0 1 0,0 1 1,0 0 1,0 0 0)))"; + bg::read_wkt(wkt, polyhedral2); + std::ostringstream oss; + oss << bg::wkt(polyhedral2); + BOOST_CHECK_EQUAL(oss.str(), wkt); + + // Custom polyhedral surface + std::initializer_list IL = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}}}; + bg::model::polyhedral_surface ps1(IL); + std::ostringstream out; + out << bg::wkt(ps1); + BOOST_CHECK_EQUAL(out.str(), "POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)))"); + // Test that the polyhedral surface has 1 polygon + BOOST_CHECK_EQUAL(boost::size(ps1), 1); + + // Test concepts + BOOST_CONCEPT_ASSERT( (bg::concepts::PolyhedralSurface) ); + BOOST_CONCEPT_ASSERT( (bg::concepts::ConstPolyhedralSurface) ); + + return 0; +} From 72c43ae4f96818497009a4519a3d0e2b3019c155 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Wed, 30 Apr 2025 12:56:31 +0300 Subject: [PATCH 05/13] chore: Remove unused header --- include/boost/geometry/geometries/concepts/check.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/boost/geometry/geometries/concepts/check.hpp b/include/boost/geometry/geometries/concepts/check.hpp index c9368be9b9..08232ed7d4 100644 --- a/include/boost/geometry/geometries/concepts/check.hpp +++ b/include/boost/geometry/geometries/concepts/check.hpp @@ -37,7 +37,6 @@ #include #include #include -#include namespace boost { namespace geometry { namespace concepts { From 5652cbd2040992722ee69d39c09cdf3931af1af9 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Fri, 2 May 2025 12:08:23 +0300 Subject: [PATCH 06/13] chore: Fix typos, remove tabs, backslashes from initializations, rename structs and templates --- .../examples/geometries/polyhedral_surface.cpp | 14 +++++++------- example/08_polyhedralsurface_example.cpp | 16 ++++++++-------- .../concepts/polyhedral_surface_concept.hpp | 10 +++++----- .../geometry/geometries/polyhedral_surface.hpp | 11 +++++++---- include/boost/geometry/io/wkt/detail/prefix.hpp | 2 +- include/boost/geometry/io/wkt/read.hpp | 4 ++-- include/boost/geometry/io/wkt/write.hpp | 10 +++++----- test/geometries/polyhedral_surface.cpp | 14 +++++++------- 8 files changed, 42 insertions(+), 39 deletions(-) diff --git a/doc/src/examples/geometries/polyhedral_surface.cpp b/doc/src/examples/geometries/polyhedral_surface.cpp index b5bc45c1b0..c751887434 100644 --- a/doc/src/examples/geometries/polyhedral_surface.cpp +++ b/doc/src/examples/geometries/polyhedral_surface.cpp @@ -25,12 +25,12 @@ int main() polyhedral_t polyhedral0; /*< Initializing an empty polyhedral surface (default constructor) >*/ - polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}},\ - {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}},\ - {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}},\ - {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}},\ - {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}},\ - {{{1, 1, 1}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}}}}; /*< Creating a polyhderal surface (cube) using standard initiallized list >*/ + polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}}, + {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}}, + {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}}, + {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}}, + {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}}, + {{{1, 1, 1}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}}}}; /*< Creating a polyhedral surface (cube) using standard initialized list >*/ polyhedral0.resize(4); @@ -45,7 +45,7 @@ int main() bg::append(polyhedral0[2].outer(), point_t(0.0, 0.0, 5.0)); bg::append(polyhedral0[3].outer(), point_t(5.0, 0.0, 0.0)); bg::append(polyhedral0[3].outer(), point_t(0.0, 5.0, 0.0)); - bg::append(polyhedral0[3].outer(), point_t(0.0, 0.0, 5.0)); /*< Creating a polyhderal surface (triangular pyramid) using append and resize >*/ + bg::append(polyhedral0[3].outer(), point_t(0.0, 0.0, 5.0)); /*< Creating a polyhedral surface (triangular pyramid) using append and resize >*/ polyhedral_t polyhedral2; bg::read_wkt("POLYHEDRALSURFACE(((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\ diff --git a/example/08_polyhedralsurface_example.cpp b/example/08_polyhedralsurface_example.cpp index f3216429bb..9c17b9295b 100644 --- a/example/08_polyhedralsurface_example.cpp +++ b/example/08_polyhedralsurface_example.cpp @@ -20,18 +20,18 @@ int main() using polygon_t = bg::model::polygon; using polyhedral_t = bg::model::polyhedral_surface; - // intializing an empty polyhedral surface (default constructor) + // Intializing an empty polyhedral surface (default constructor) polyhedral_t polyhedral0; - // creating a polyhderal surface (cube) using standard initiallized list - polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}},\ - {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}},\ - {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}},\ - {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}},\ - {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}},\ + // Creating a polyhedral surface (cube) using a standard initialized list + polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}}, + {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}}, + {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}}, + {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}}, + {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}}, {{{1, 1, 1}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}}}}; - // creating a polyhderal surface (triangular pyramid) using append + // Creating a polyhedral surface (triangular pyramid) using append // and resize polyhedral0.resize(4); bg::append(polyhedral0[0].outer(), point_t(0.0, 0.0, 0.0)); diff --git a/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp b/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp index bbbc6e1a7c..a33653ebe0 100644 --- a/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp +++ b/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp @@ -26,7 +26,7 @@ class PolyhedralSurface #ifndef DOXYGEN_NO_CONCEPT_MEMBERS using polygon_type = typename boost::range_value::type; - BOOST_CONCEPT_ASSERT( (concepts::Polygon) ); + BOOST_CONCEPT_ASSERT( (concepts::Polygon) ); BOOST_CONCEPT_ASSERT( (boost::RandomAccessRangeConcept) ); public: @@ -48,15 +48,15 @@ template class ConstPolyhedralSurface { #ifndef DOXYGEN_NO_CONCEPT_MEMBERS - using polygon_type = typename boost::range_value::type; + using polygon_type = typename boost::range_value::type; - BOOST_CONCEPT_ASSERT( (concepts::ConstPolygon) ); + BOOST_CONCEPT_ASSERT( (concepts::ConstPolygon) ); BOOST_CONCEPT_ASSERT( (boost::RandomAccessRangeConcept) ); public: - BOOST_CONCEPT_USAGE(ConstPolyhedralSurface) - {} + BOOST_CONCEPT_USAGE(ConstPolyhedralSurface) + {} #endif }; diff --git a/include/boost/geometry/geometries/polyhedral_surface.hpp b/include/boost/geometry/geometries/polyhedral_surface.hpp index 779316eb29..683459f1bf 100644 --- a/include/boost/geometry/geometries/polyhedral_surface.hpp +++ b/include/boost/geometry/geometries/polyhedral_surface.hpp @@ -26,8 +26,9 @@ namespace model { /*! -\brief A Polyhedral Surface is a contiguous collection of polygons, - which share common boundary segments. +\brief A Polyhedral Surface is a contiguous collection of polygons in 3-dimensional space, + which share common boundary segments. The concepts and the constructors don't check if the + boundary segments are common but is_valid() does. \ingroup geometries \tparam Polygon polygon type \tparam Container container type for polygons, @@ -41,6 +42,8 @@ namespace model } */ + +//TODO: add is_valid() check template < typename Polygon, @@ -50,7 +53,7 @@ template > class polyhedral_surface : public Container > { - BOOST_CONCEPT_ASSERT( (concepts::Polygon) ); + BOOST_CONCEPT_ASSERT( (concepts::Polygon) ); public : using polygon_type = Polygon; @@ -63,7 +66,7 @@ public : /// \constructor_initialized_list{polyhedron} inline polyhedral_surface(std::initializer_list l) - : polygon_container(l.begin(), l.end()) + : polygon_container(l.begin(), l.end()) {} }; diff --git a/include/boost/geometry/io/wkt/detail/prefix.hpp b/include/boost/geometry/io/wkt/detail/prefix.hpp index 0768121650..fb4e139a37 100644 --- a/include/boost/geometry/io/wkt/detail/prefix.hpp +++ b/include/boost/geometry/io/wkt/detail/prefix.hpp @@ -44,7 +44,7 @@ struct prefix_linestring struct prefix_polyhedral_surface { - static inline const char* apply() { return "POLYHEDRALSURFACE"; } + static inline const char* apply() { return "POLYHEDRALSURFACE"; } }; struct prefix_multipoint diff --git a/include/boost/geometry/io/wkt/read.hpp b/include/boost/geometry/io/wkt/read.hpp index afab50b8f0..e8db2b464a 100644 --- a/include/boost/geometry/io/wkt/read.hpp +++ b/include/boost/geometry/io/wkt/read.hpp @@ -459,7 +459,7 @@ struct polygon_parser }; template -struct polyhderal_surface_parser +struct polyhedral_surface_parser { using polygon_t = typename PolyhedralSurface::polygon_type; @@ -1097,7 +1097,7 @@ struct read_wkt : detail::wkt::geometry_parser < Geometry, - detail::wkt::polyhderal_surface_parser, + detail::wkt::polyhedral_surface_parser, detail::wkt::prefix_polyhedral_surface > {}; diff --git a/include/boost/geometry/io/wkt/write.hpp b/include/boost/geometry/io/wkt/write.hpp index b4368fbdbb..db2412f25d 100644 --- a/include/boost/geometry/io/wkt/write.hpp +++ b/include/boost/geometry/io/wkt/write.hpp @@ -228,14 +228,14 @@ struct wkt_poly }; -template -struct wkt_polyhedral +template +struct wkt_polyhedral_surface { template static inline void apply(std::basic_ostream& os, - Polyhedral_surface const& polyhedral, bool force_closure) + PolyhedralSurface const& polyhedral, bool force_closure) { - using polygon = typename Polyhedral_surface::polygon_type; + using polygon = typename PolyhedralSurface::polygon_type; os << PrefixPolicy::apply(); @@ -422,7 +422,7 @@ struct wkt template struct wkt - : detail::wkt::wkt_polyhedral + : detail::wkt::wkt_polyhedral_surface < Polyhedral_surface, detail::wkt::prefix_polyhedral_surface diff --git a/test/geometries/polyhedral_surface.cpp b/test/geometries/polyhedral_surface.cpp index 62c2b990cc..3e775b17a0 100644 --- a/test/geometries/polyhedral_surface.cpp +++ b/test/geometries/polyhedral_surface.cpp @@ -28,16 +28,16 @@ int test_main(int, char* []) using polyhedral_t = bg::model::polyhedral_surface; using const_polyhedral_t = bg::model::polyhedral_surface const; - // intializing an empty polyhedral surface (default constructor) + // Intializing an empty polyhedral surface (default constructor) polyhedral_t polyhedral0; BOOST_CHECK_EQUAL(boost::size(polyhedral0), 0); - // creating a polyhderal surface using standard initiallized list - polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}},\ - {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}},\ - {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}},\ - {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}},\ - {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}},\ + // Creating a polyhedral surface using standard initialized list + polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}}, + {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}}, + {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}}, + {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}}, + {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}}, {{{1, 1, 1}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}}}}; // Modify polyhedral1 by adding a new polygon From 8c5cfd8a1491b456070e920e12786ad5ba00d1fc Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Fri, 2 May 2025 12:10:24 +0300 Subject: [PATCH 07/13] chore: Remove repeated polyhedral surface example --- example/08_polyhedralsurface_example.cpp | 65 ------------------------ 1 file changed, 65 deletions(-) delete mode 100644 example/08_polyhedralsurface_example.cpp diff --git a/example/08_polyhedralsurface_example.cpp b/example/08_polyhedralsurface_example.cpp deleted file mode 100644 index 9c17b9295b..0000000000 --- a/example/08_polyhedralsurface_example.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Boost.Geometry - -// Copyright (c) 2025 Oracle and/or its affiliates. -// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle - -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -// Polyhedral surface example - -#include -#include - -namespace bg = boost::geometry; - -int main() -{ - using point_t = bg::model::point; - using polygon_t = bg::model::polygon; - using polyhedral_t = bg::model::polyhedral_surface; - - // Intializing an empty polyhedral surface (default constructor) - polyhedral_t polyhedral0; - - // Creating a polyhedral surface (cube) using a standard initialized list - polyhedral_t polyhedral1 = {{{{0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}, {0, 0, 0}}}, - {{{0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {0, 0, 0}}}, - {{{0, 0, 0}, {1, 0, 0}, {1, 0, 1}, {0, 0, 1}, {0, 0, 0}}}, - {{{1, 1, 1}, {1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}}, - {{{1, 1, 1}, {1, 0, 1}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1}}}, - {{{1, 1, 1}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1}}}}; - - // Creating a polyhedral surface (triangular pyramid) using append - // and resize - polyhedral0.resize(4); - bg::append(polyhedral0[0].outer(), point_t(0.0, 0.0, 0.0)); - bg::append(polyhedral0[0].outer(), point_t(5.0, 0.0, 0.0)); - bg::append(polyhedral0[0].outer(), point_t(0.0, 5.0, 0.0)); - bg::append(polyhedral0[1].outer(), point_t(0.0, 0.0, 0.0)); - bg::append(polyhedral0[1].outer(), point_t(0.0, 5.0, 0.0)); - bg::append(polyhedral0[1].outer(), point_t(0.0, 0.0, 5.0)); - bg::append(polyhedral0[2].outer(), point_t(0.0, 0.0, 0.0)); - bg::append(polyhedral0[2].outer(), point_t(5.0, 0.0, 0.0)); - bg::append(polyhedral0[2].outer(), point_t(0.0, 0.0, 5.0)); - bg::append(polyhedral0[3].outer(), point_t(5.0, 0.0, 0.0)); - bg::append(polyhedral0[3].outer(), point_t(0.0, 5.0, 0.0)); - bg::append(polyhedral0[3].outer(), point_t(0.0, 0.0, 5.0)); - - // Initialize polyhedral surface (cube) with wkt - polyhedral_t polyhedral2; - bg::read_wkt("POLYHEDRALSURFACE(((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\ - ((0 0 0, 0 1 0, 0 1 1, 0 0 1, 0 0 0)),\ - ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),\ - ((1 1 1, 1 0 1, 0 0 1, 0 1 1, 1 1 1)),\ - ((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1)),\ - ((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1)))", polyhedral2); - - // Write polyhedral surface wkt - std::cout << bg::wkt(polyhedral0) << std::endl; - std::cout << bg::wkt(polyhedral1) << std::endl; - std::cout << bg::wkt(polyhedral2) << std::endl; - - return 0; -} From f60829ca229e893e765c7c97414f01d99d694bdd Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Fri, 2 May 2025 12:18:40 +0300 Subject: [PATCH 08/13] chore: Update copyright notes --- doc/concept/polyhedral_surface.qbk | 2 +- doc/src/examples/geometries/polyhedral_surface.cpp | 2 ++ .../geometry/geometries/concepts/polyhedral_surface_concept.hpp | 1 + include/boost/geometry/geometries/polyhedral_surface.hpp | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/concept/polyhedral_surface.qbk b/doc/concept/polyhedral_surface.qbk index f944129ee6..91a990ce0a 100644 --- a/doc/concept/polyhedral_surface.qbk +++ b/doc/concept/polyhedral_surface.qbk @@ -22,7 +22,7 @@ The PolyhedralSurface Concept is defined as following: * There must be a specialization of the metafunction `traits::tag`, defining `polyhedral_surface_tag` as type * It must behave like a Boost.Range Random Access Range * The type defined by the metafunction `range_value<...>::type` must fulfill - the [link geometry.reference.concepts.concept_polygon Polygon Concept] + the [link geometry.reference.concepts.concept_polygon Polygon Concept] [heading Rules] diff --git a/doc/src/examples/geometries/polyhedral_surface.cpp b/doc/src/examples/geometries/polyhedral_surface.cpp index c751887434..5e34738a2a 100644 --- a/doc/src/examples/geometries/polyhedral_surface.cpp +++ b/doc/src/examples/geometries/polyhedral_surface.cpp @@ -1,6 +1,8 @@ // Boost.Geometry // QuickBook Example +// Copyright (c) 2025 Siddharth Kumar, Roorkee, India. + // Copyright (c) 2025 Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle diff --git a/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp b/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp index a33653ebe0..a78403a609 100644 --- a/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp +++ b/include/boost/geometry/geometries/concepts/polyhedral_surface_concept.hpp @@ -1,5 +1,6 @@ // Boost.Geometry +// Copyright (c) 2025 Siddharth Kumar, Roorkee, India. // Copyright (c) 2025 Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle diff --git a/include/boost/geometry/geometries/polyhedral_surface.hpp b/include/boost/geometry/geometries/polyhedral_surface.hpp index 683459f1bf..0dc60819d5 100644 --- a/include/boost/geometry/geometries/polyhedral_surface.hpp +++ b/include/boost/geometry/geometries/polyhedral_surface.hpp @@ -1,5 +1,6 @@ // Boost.Geometry +// Copyright (c) 2025 Siddharth Kumar, Roorkee, India. // Copyright (c) 2025 Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle From 62839b6552f2cad97cdbebf95c57145104d5348b Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Fri, 2 May 2025 15:49:13 +0300 Subject: [PATCH 09/13] chore: Update examples Jamfile --- example/Jamfile | 1 - 1 file changed, 1 deletion(-) diff --git a/example/Jamfile b/example/Jamfile index 2b29b5963f..c2d7f11512 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -25,7 +25,6 @@ exe 06_a_transformation_example : 06_a_transformation_example.cpp ; exe 06_b_transformation_example : 06_b_transformation_example.cpp ; exe 07_a_graph_route_example : 07_a_graph_route_example.cpp : /boost/graph//boost_graph ; exe 07_b_graph_route_example : 07_b_graph_route_example.cpp : /boost/graph//boost_graph ; -exe 08_polyhedralsurface_example : 08_polyhedralsurface_example.cpp ; exe c01_custom_point_example : c01_custom_point_example.cpp ; exe c02_custom_box_example : c02_custom_box_example.cpp ; From 08cf5c14765f12b529d448455b235325e3116c84 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Mon, 5 May 2025 10:56:00 +0300 Subject: [PATCH 10/13] chore: replace template Polyhedral_surface by PolyhedralSurface --- include/boost/geometry/algorithms/clear.hpp | 6 +++--- include/boost/geometry/io/wkt/write.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/boost/geometry/algorithms/clear.hpp b/include/boost/geometry/algorithms/clear.hpp index f301e5bf3f..9a053edde9 100644 --- a/include/boost/geometry/algorithms/clear.hpp +++ b/include/boost/geometry/algorithms/clear.hpp @@ -72,14 +72,14 @@ struct polygon_clear } }; -template +template struct polyhedral_surface_clear { - static inline void apply(Polyhedral_surface& polyhedral_surface) + static inline void apply(PolyhedralSurface& polyhedral_surface) { traits::clear < - typename std::remove_reference::type + typename std::remove_reference::type >::apply(polyhedral_surface); } }; diff --git a/include/boost/geometry/io/wkt/write.hpp b/include/boost/geometry/io/wkt/write.hpp index db2412f25d..8326c29dc5 100644 --- a/include/boost/geometry/io/wkt/write.hpp +++ b/include/boost/geometry/io/wkt/write.hpp @@ -420,11 +420,11 @@ struct wkt > {}; -template -struct wkt +template +struct wkt : detail::wkt::wkt_polyhedral_surface < - Polyhedral_surface, + PolyhedralSurface, detail::wkt::prefix_polyhedral_surface > {}; From 870a2acd01d41968f45fe264138f1d12333bbb44 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Mon, 5 May 2025 11:06:16 +0300 Subject: [PATCH 11/13] doc: add polyhedral surface concept description and a link to OGC standard --- doc/concept/polyhedral_surface.qbk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/concept/polyhedral_surface.qbk b/doc/concept/polyhedral_surface.qbk index 91a990ce0a..1ac5055b28 100644 --- a/doc/concept/polyhedral_surface.qbk +++ b/doc/concept/polyhedral_surface.qbk @@ -15,6 +15,8 @@ [heading Description] [concept PolyhedralSurface..polyhedral surface] +['A PolyhedralSurface is a contiguous collection of polygons, which share common boundary segments] (__ogc_sf__). + [heading Concept Definition] The PolyhedralSurface Concept is defined as following: From adcede53e0cac406b96b7bc808ddca972a5a8a41 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Mon, 5 May 2025 11:09:27 +0300 Subject: [PATCH 12/13] doc: fix a typo in polygon concept --- doc/concept/polygon.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/concept/polygon.qbk b/doc/concept/polygon.qbk index 8cce35ff5f..89ab172d79 100644 --- a/doc/concept/polygon.qbk +++ b/doc/concept/polygon.qbk @@ -15,7 +15,7 @@ [heading Description] [concept Polygon..polygon] -A polygon is ['A polygon is a planar surface defined by one exterior boundary and zero or more interior boundaries] +['A polygon is a planar surface defined by one exterior boundary and zero or more interior boundaries] (__ogc_sf__). So the definition of a Boost.Geometry polygon differs a bit from e.g. Wiki, where a polygon does not have holes. A From 4ee255e5a049ef71c8c15da83997b4d3795953f4 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Mon, 5 May 2025 11:53:57 +0300 Subject: [PATCH 13/13] doc: add triangular pyramid picture --- doc/html/img/geometries/triangular_pyramid.png | Bin 0 -> 64537 bytes .../examples/geometries/polyhedral_surface.cpp | 5 ++++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 doc/html/img/geometries/triangular_pyramid.png diff --git a/doc/html/img/geometries/triangular_pyramid.png b/doc/html/img/geometries/triangular_pyramid.png new file mode 100644 index 0000000000000000000000000000000000000000..b69999205ba9937add56bfea5e5caaf902941546 GIT binary patch literal 64537 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%0A~VB$}hv3AL4sC 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 literal 0 HcmV?d00001 diff --git a/doc/src/examples/geometries/polyhedral_surface.cpp b/doc/src/examples/geometries/polyhedral_surface.cpp index 5e34738a2a..e568d50ad6 100644 --- a/doc/src/examples/geometries/polyhedral_surface.cpp +++ b/doc/src/examples/geometries/polyhedral_surface.cpp @@ -47,7 +47,7 @@ int main() bg::append(polyhedral0[2].outer(), point_t(0.0, 0.0, 5.0)); bg::append(polyhedral0[3].outer(), point_t(5.0, 0.0, 0.0)); bg::append(polyhedral0[3].outer(), point_t(0.0, 5.0, 0.0)); - bg::append(polyhedral0[3].outer(), point_t(0.0, 0.0, 5.0)); /*< Creating a polyhedral surface (triangular pyramid) using append and resize >*/ + bg::append(polyhedral0[3].outer(), point_t(0.0, 0.0, 5.0)); /*< Creating a polyhedral surface (triangular pyramid) using append and resize. See figure below.>*/ polyhedral_t polyhedral2; bg::read_wkt("POLYHEDRALSURFACE(((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\ @@ -75,5 +75,8 @@ POLYHEDRALSURFACE(((0 0 0,5 0 0,0 5 0,0 0 0)),((0 0 0,0 5 0,0 0 5,0 0 0)),((0 0 POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,0 1 0,0 1 1,0 0 1,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 1,1 0 1,0 0 1,0 1 1,1 1 1)),((1 1 1,1 0 1,1 0 0,1 1 0,1 1 1)),((1 1 1,1 1 0,0 1 0,0 1 1,1 1 1))) POLYHEDRALSURFACE(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,0 1 0,0 1 1,0 0 1,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 1,1 0 1,0 0 1,0 1 1,1 1 1)),((1 1 1,1 0 1,1 0 0,1 1 0,1 1 1)),((1 1 1,1 1 0,0 1 0,0 1 1,1 1 1))) ] + +[$img/geometries/triangular_pyramid.png] + */ //]