diff --git a/include/geode/geometry/is_point_inside.hpp b/include/geode/geometry/is_point_inside.hpp new file mode 100644 index 000000000..a928e9a38 --- /dev/null +++ b/include/geode/geometry/is_point_inside.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 - 2026 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#pragma once + +#include + +namespace geode +{ + FORWARD_DECLARATION_DIMENSION_CLASS( Point ); + FORWARD_DECLARATION_DIMENSION_CLASS( Polygon ); + ALIAS_2D( Point ); + ALIAS_2D( Polygon ); +} // namespace geode + +namespace geode +{ + /*! + * Find if point is inside a polygon. + * @param[in] point The point to rotate. + */ + [[nodiscard]] bool opengeode_geometry_api is_point_inside_polygon( + const Point2D& point, const Polygon2D& polygon ); + +} // namespace geode \ No newline at end of file diff --git a/src/geode/geometry/CMakeLists.txt b/src/geode/geometry/CMakeLists.txt index 973cc2456..b70775c6e 100644 --- a/src/geode/geometry/CMakeLists.txt +++ b/src/geode/geometry/CMakeLists.txt @@ -47,6 +47,7 @@ add_geode_library( "information.cpp" "intersection.cpp" "intersection_detection.cpp" + "is_point_inside.cpp" "mensuration.cpp" "nn_search.cpp" "normal_frame_transform.cpp" @@ -86,6 +87,7 @@ add_geode_library( "information.hpp" "intersection.hpp" "intersection_detection.hpp" + "is_point_inside.hpp" "mensuration.hpp" "nn_search.hpp" "normal_frame_transform.hpp" diff --git a/src/geode/geometry/is_point_inside.cpp b/src/geode/geometry/is_point_inside.cpp new file mode 100644 index 000000000..ac9db7c91 --- /dev/null +++ b/src/geode/geometry/is_point_inside.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 - 2026 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +#include +#include + +namespace +{ + bool is_left( const geode::Point2D& p0, + const geode::Point2D& p1, + const geode::Point2D& p2 ) + { + return ( p1.value( 0 ) - p0.value( 0 ) ) + * ( p2.value( 1 ) - p0.value( 1 ) ) + - ( p2.value( 0 ) - p0.value( 0 ) ) + * ( p1.value( 1 ) - p0.value( 1 ) ) + > 0; + } +} // namespace + +namespace geode +{ + bool is_point_inside_polygon( + const Point2D& point, const Polygon2D& polygon ) + { + double winding_number{ 0 }; + const auto& vertices = polygon.vertices(); + for( const auto polygon_vertex : geode::Range{ polygon.nb_vertices() } ) + { + const auto& v0 = vertices[polygon_vertex]; + const auto& next_polygon_vertex = + ( polygon_vertex + 1 ) % vertices.size(); + const auto& v1 = vertices[next_polygon_vertex]; + if( v0.get().value( 1 ) <= point.value( 1 ) ) + { + if( v1.get().value( 1 ) > point.value( 1 ) ) + { + if( is_left( v0, v1, point ) ) + { + winding_number++; + } + } + } + else + { + if( v1.get().value( 1 ) <= point.value( 1 ) ) + { + if( !is_left( v0, v1, point ) ) + { + winding_number--; + } + } + } + } + return winding_number != 0; + } +} // namespace geode \ No newline at end of file