///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2022 - Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RigConvexHull.h" #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigConvexHull::compute2d( const std::vector& points ) { // Sort our points from left to right std::vector sortedPoints = sortPoints( points ); // Find the lower half of the convex hull. std::vector lower; for ( auto it = sortedPoints.begin(); it != sortedPoints.end(); ++it ) { // Remove any points that does not make a convex angle with the current point removePointsWithoutConvexAngle( lower, *it ); lower.push_back( *it ); } // Find the upper half of the convex hull. std::vector upper; for ( auto it = sortedPoints.rbegin(); it != sortedPoints.rend(); ++it ) { // Remove any points that does not make a convex angle with the current point removePointsWithoutConvexAngle( upper, *it ); upper.push_back( *it ); } // Concatenation of the lower and upper hulls gives the convex hull. // Last point of each list is omitted because it is repeated at the beginning of the other list. std::vector hull; hull.insert( hull.end(), lower.begin(), lower.end() - 1 ); hull.insert( hull.end(), upper.begin(), upper.end() - 1 ); return hull; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigConvexHull::sortPoints( const std::vector& unsorted ) { // Returns true if a is left of b auto isLeftOf = []( const cvf::Vec3d& a, const cvf::Vec3d& b ) { return ( a.x() < b.x() || ( a.x() == b.x() && a.y() < b.y() ) ); }; std::vector sorted = unsorted; std::sort( sorted.begin(), sorted.end(), isLeftOf ); return sorted; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigConvexHull::removePointsWithoutConvexAngle( std::vector& points, const cvf::Vec3d& current ) { // 2D cross product of (a, b) and (a, c) vectors, i.e. z-component of their 3D cross product. // Returns a positive value, if c->(a,b) makes a counter-clockwise turn, // negative for clockwise turn, and zero if the points are collinear. auto counterClockWise = []( const cvf::Vec3d& a, const cvf::Vec3d& b, const cvf::Vec3d& c ) { return ( b.x() - a.x() ) * ( c.y() - a.y() ) - ( b.y() - a.y() ) * ( c.x() - a.x() ); }; // Remove all points that is not a counter-clockwise turn from current point while ( points.size() >= 2 && counterClockWise( *( points.rbegin() + 1 ), *( points.rbegin() ), current ) >= 0 ) { points.pop_back(); } }