///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2017 Statoil 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 "RigCellGeometryTools.h" #include "cvfGeometryTools.h" #include "cvfStructGrid.h" #include "cafHexGridIntersectionTools/cafHexGridIntersectionTools.h" #include "cvfBoundingBox.h" #include "cvfMatrix3.h" #include "clipper/clipper.hpp" #include #include #include //-------------------------------------------------------------------------------------------------- /// Efficient Computation of Volume of Hexahedral Cells /// Jeffrey Grandy, Lawrence Livermore National Laboratory /// https://www.osti.gov/servlets/purl/632793/ /// /// Note that in the paper the following vertex numbering is used /// 6---------7 /// /| /| |k /// / | / | | /j /// 4---------5 | |/ /// | 2------|--3 *---i /// | / | / /// |/ |/ /// 0---------1 /// /// While in ResInsight, this is the numbering. Thus we need to swap 2<->3, 6<->7 in the equations. /// Note the negative k! This causes an additional set of 0<->4, 1<->5, etc. index swaps. /// 7---------6 /// /| /| |-k /// / | / | | /j /// 4---------5 | |/ /// | 3------|--2 *---i /// | / | / /// |/ |/ /// 0---------1 //-------------------------------------------------------------------------------------------------- double RigCellGeometryTools::calculateCellVolume( const std::array& x ) { // 6 * 3 flops = 18 flops // Perform index swap when retrieving corners but keep indices in variable names matching paper. cvf::Vec3d x3mx0 = x[6] - x[4]; // Swap 3->2, then negate z by 2->6 and 0->4 cvf::Vec3d x5mx0 = x[1] - x[4]; // Negate z by Swap 5->1 and 0->4 cvf::Vec3d x6mx0 = x[3] - x[4]; // Swap 6->7, then negate z by 7->3 and 0->4 cvf::Vec3d x7mx1 = x[2] - x[5]; // Swap 7->6, then negate z by 6->2 and 1->5 cvf::Vec3d x7mx2 = x[2] - x[7]; // Swap 7->6, 2->3, then negate z by 6->2 and 3->7 cvf::Vec3d x7mx4 = x[2] - x[0]; // Swap 7->6 then negate z by 6->2 and 4->0 // 3 flops for summation + 5 for dot product + 9 flops for cross product = 17 flops double det1 = ( x7mx1 + x6mx0 ) * ( x7mx2 ^ x3mx0 ); // 3 flops for summation + 5 for dot product + 9 flops for cross product = 17 flops double det2 = x6mx0 * ( ( x7mx2 + x5mx0 ) ^ x7mx4 ); // 3 flops for summation + 5 for dot product + 9 flops for cross product = 17 flops double det3 = x7mx1 * ( x5mx0 ^ ( x7mx4 + x3mx0 ) ); // 2 flops for summation + 1 for division = 3 flops double volume = ( det1 + det2 + det3 ) / 12.0; // In order for this to work in any rotation of the cell, we need the absolute value. 1 flop. return std::abs( volume ); // Altogether 18 + 3*17 + 3 + 1 flops = 73 flops. } //-------------------------------------------------------------------------------------------------- /// A reasonable approximation to the overlap volume //-------------------------------------------------------------------------------------------------- bool RigCellGeometryTools::estimateHexOverlapWithBoundingBox( const std::array& hexCorners, const cvf::BoundingBox& boundingBox, std::array* overlapElement, cvf::BoundingBox* overlapBoundingBox ) { CVF_ASSERT( overlapElement && overlapBoundingBox ); *overlapBoundingBox = cvf::BoundingBox(); std::vector uniqueTopPoints = { hexCorners[0], hexCorners[1], hexCorners[2], hexCorners[3] }; auto uniqueTopEnd = std::unique( uniqueTopPoints.begin(), uniqueTopPoints.end() ); if ( uniqueTopEnd - uniqueTopPoints.begin() < 3u ) return false; cvf::Plane topPlane; if ( !topPlane.setFromPoints( uniqueTopPoints[0], uniqueTopPoints[1], uniqueTopPoints[2] ) ) return false; std::vector uniqueBottomPoints = { hexCorners[4], hexCorners[5], hexCorners[6], hexCorners[7] }; auto uniqueBottomEnd = std::unique( uniqueBottomPoints.begin(), uniqueBottomPoints.end() ); if ( uniqueBottomEnd - uniqueBottomPoints.begin() < 3u ) return false; cvf::Plane bottomPlane; if ( !bottomPlane.setFromPoints( uniqueBottomPoints[0], uniqueBottomPoints[1], uniqueBottomPoints[2] ) ) return false; const cvf::Vec3d& boundingMin = boundingBox.min(); const cvf::Vec3d& boundingMax = boundingBox.max(); for ( size_t i = 0; i < 4; ++i ) { const cvf::Vec3d& hexCorner = hexCorners[i]; double x = std::clamp( hexCorner.x(), boundingMin.x(), boundingMax.x() ); double y = std::clamp( hexCorner.y(), boundingMin.y(), boundingMax.y() ); cvf::Vec3d corner; cvf::Vec3d maxZCorner( x, y, boundingMax.z() ); cvf::Vec3d minZCorner( x, y, boundingMin.z() ); if ( topPlane.intersect( minZCorner, maxZCorner, &corner ) ) { overlapBoundingBox->add( corner ); std::swap( ( *overlapElement )[i], corner ); } else { double z = std::clamp( hexCorner.z(), boundingMin.z(), boundingMax.z() ); cvf::Vec3d clampedCorner( x, y, z ); overlapBoundingBox->add( clampedCorner ); ( *overlapElement )[i] = clampedCorner; } } for ( size_t i = 4; i < 8; ++i ) { const cvf::Vec3d& hexCorner = hexCorners[i]; double x = std::clamp( hexCorner.x(), boundingMin.x(), boundingMax.x() ); double y = std::clamp( hexCorner.y(), boundingMin.y(), boundingMax.y() ); cvf::Vec3d corner; cvf::Vec3d maxZCorner( x, y, boundingMax.z() ); cvf::Vec3d minZCorner( x, y, boundingMin.z() ); if ( bottomPlane.intersect( minZCorner, maxZCorner, &corner ) ) { overlapBoundingBox->add( corner ); std::swap( ( *overlapElement )[i], corner ); } else { double z = std::clamp( hexCorner.z(), boundingMin.z(), boundingMax.z() ); cvf::Vec3d clampedCorner( x, y, z ); overlapBoundingBox->add( clampedCorner ); ( *overlapElement )[i] = clampedCorner; } } return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RigCellGeometryTools::createPolygonFromLineSegments( std::list>& intersectionLineSegments, std::vector>& polygons, double tolerance ) { bool startNewPolygon = true; while ( !intersectionLineSegments.empty() ) { if ( startNewPolygon ) { std::vector polygon; // Add first line segments to polygon and remove from list std::pair linesegment = intersectionLineSegments.front(); polygon.push_back( linesegment.first ); polygon.push_back( linesegment.second ); intersectionLineSegments.remove( linesegment ); polygons.push_back( polygon ); startNewPolygon = false; } std::vector& polygon = polygons.back(); // Search remaining list for next point... bool isFound = false; for ( std::list>::iterator lIt = intersectionLineSegments.begin(); lIt != intersectionLineSegments.end(); lIt++ ) { cvf::Vec3d lineSegmentStart = lIt->first; cvf::Vec3d lineSegmentEnd = lIt->second; cvf::Vec3d polygonEnd = polygon.back(); double lineSegmentLength = ( lineSegmentStart - lineSegmentEnd ).lengthSquared(); if ( lineSegmentLength < tolerance * tolerance ) { intersectionLineSegments.erase( lIt ); isFound = true; break; } double lineSegmentStartDiff = ( lineSegmentStart - polygonEnd ).lengthSquared(); if ( lineSegmentStartDiff < tolerance * tolerance ) { polygon.push_back( lIt->second ); intersectionLineSegments.erase( lIt ); isFound = true; break; } double lineSegmentEndDiff = ( lineSegmentEnd - polygonEnd ).lengthSquared(); if ( lineSegmentEndDiff < tolerance * tolerance ) { polygon.push_back( lIt->first ); intersectionLineSegments.erase( lIt ); isFound = true; break; } } if ( isFound ) { continue; } else { startNewPolygon = true; } } } //-------------------------------------------------------------------------------------------------- /// Ramer-Douglas-Peucker simplification algorithm /// /// https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm //-------------------------------------------------------------------------------------------------- void RigCellGeometryTools::simplifyPolygon( std::vector* vertices, double epsilon ) { CVF_ASSERT( vertices ); if ( vertices->size() < 3 ) return; std::pair maxDistPoint( 0u, 0.0 ); for ( size_t i = 1; i < vertices->size() - 1; ++i ) { cvf::Vec3d v = vertices->at( i ); double u; cvf::Vec3d v_proj = cvf::GeometryTools::projectPointOnLine( vertices->front(), vertices->back(), v, &u ); double distance = ( v_proj - v ).length(); if ( distance > maxDistPoint.second ) { maxDistPoint = std::make_pair( i, distance ); } } if ( maxDistPoint.second > epsilon ) { std::vector newVertices1( vertices->begin(), vertices->begin() + maxDistPoint.first + 1 ); std::vector newVertices2( vertices->begin() + maxDistPoint.first, vertices->end() ); // Recurse simplifyPolygon( &newVertices1, epsilon ); simplifyPolygon( &newVertices2, epsilon ); std::vector newVertices( newVertices1.begin(), newVertices1.end() - 1 ); newVertices.insert( newVertices.end(), newVertices2.begin(), newVertices2.end() ); *vertices = newVertices; } else { std::vector newVertices = { vertices->front(), vertices->back() }; *vertices = newVertices; } } //================================================================================================== /// //================================================================================================== void RigCellGeometryTools::findCellLocalXYZ( const std::array& hexCorners, cvf::Vec3d& localXdirection, cvf::Vec3d& localYdirection, cvf::Vec3d& localZdirection ) { cvf::ubyte faceVertexIndices[4]; cvf::StructGridInterface::FaceEnum face; face = cvf::StructGridInterface::NEG_I; cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices ); cvf::Vec3d faceCenterNegI = cvf::GeometryTools::computeFaceCenter( hexCorners[faceVertexIndices[0]], hexCorners[faceVertexIndices[1]], hexCorners[faceVertexIndices[2]], hexCorners[faceVertexIndices[3]] ); // TODO: Should we use face centroids instead of face centers? face = cvf::StructGridInterface::POS_I; cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices ); cvf::Vec3d faceCenterPosI = cvf::GeometryTools::computeFaceCenter( hexCorners[faceVertexIndices[0]], hexCorners[faceVertexIndices[1]], hexCorners[faceVertexIndices[2]], hexCorners[faceVertexIndices[3]] ); face = cvf::StructGridInterface::NEG_J; cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices ); cvf::Vec3d faceCenterNegJ = cvf::GeometryTools::computeFaceCenter( hexCorners[faceVertexIndices[0]], hexCorners[faceVertexIndices[1]], hexCorners[faceVertexIndices[2]], hexCorners[faceVertexIndices[3]] ); face = cvf::StructGridInterface::POS_J; cvf::StructGridInterface::cellFaceVertexIndices( face, faceVertexIndices ); cvf::Vec3d faceCenterPosJ = cvf::GeometryTools::computeFaceCenter( hexCorners[faceVertexIndices[0]], hexCorners[faceVertexIndices[1]], hexCorners[faceVertexIndices[2]], hexCorners[faceVertexIndices[3]] ); cvf::Vec3d faceCenterCenterVectorI = faceCenterPosI - faceCenterNegI; cvf::Vec3d faceCenterCenterVectorJ = faceCenterPosJ - faceCenterNegJ; localZdirection.cross( faceCenterCenterVectorI, faceCenterCenterVectorJ ); localZdirection.normalize(); cvf::Vec3d crossPoductJZ; crossPoductJZ.cross( faceCenterCenterVectorJ, localZdirection ); localXdirection = faceCenterCenterVectorI + crossPoductJZ; localXdirection.normalize(); cvf::Vec3d crossPoductIZ; crossPoductIZ.cross( faceCenterCenterVectorI, localZdirection ); localYdirection = faceCenterCenterVectorJ - crossPoductIZ; localYdirection.normalize(); // TODO: Check if we end up with 0-vectors, and handle this case... } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RigCellGeometryTools::polygonLengthInLocalXdirWeightedByArea( const std::vector& polygonToCalcLengthOf ) { // Find bounding box cvf::BoundingBox polygonBBox; for ( cvf::Vec3d nodeCoord : polygonToCalcLengthOf ) polygonBBox.add( nodeCoord ); cvf::Vec3d bboxCorners[8]; polygonBBox.cornerVertices( bboxCorners ); // Split bounding box in multiple polygons (2D) int resolutionOfLengthCalc = 20; double widthOfPolygon = polygonBBox.extent().y() / resolutionOfLengthCalc; std::vector areasOfPolygonContributions; std::vector lengthOfPolygonContributions; cvf::Vec3d directionOfLength( 1, 0, 0 ); for ( int i = 0; i < resolutionOfLengthCalc; i++ ) { cvf::Vec3d pointOnLine1( bboxCorners[0].x(), bboxCorners[0].y() + i * widthOfPolygon, 0 ); cvf::Vec3d pointOnLine2( bboxCorners[0].x(), bboxCorners[0].y() + ( i + 1 ) * widthOfPolygon, 0 ); std::pair line1 = getLineThroughBoundingBox( directionOfLength, polygonBBox, pointOnLine1 ); std::pair line2 = getLineThroughBoundingBox( directionOfLength, polygonBBox, pointOnLine2 ); std::vector polygon; polygon.push_back( line1.first ); polygon.push_back( line1.second ); polygon.push_back( line2.second ); polygon.push_back( line2.first ); // Use clipper to find overlap between bbpolygon and fracture std::vector> clippedPolygons = intersectionWithPolygon( polygonToCalcLengthOf, polygon ); double area = 0; double length = 0; cvf::Vec3d areaVector = cvf::Vec3d::ZERO; // Calculate length (max-min) and area for ( std::vector clippedPolygon : clippedPolygons ) { areaVector = cvf::GeometryTools::polygonAreaNormal3D( clippedPolygon ); area += areaVector.length(); length += ( getLengthOfPolygonAlongLine( line1, clippedPolygon ) + getLengthOfPolygonAlongLine( line2, clippedPolygon ) ) / 2; } areasOfPolygonContributions.push_back( area ); lengthOfPolygonContributions.push_back( length ); } // Calculate area-weighted length average. double totalArea = 0.0; double totalAreaXlength = 0.0; for ( size_t i = 0; i < areasOfPolygonContributions.size(); i++ ) { totalArea += areasOfPolygonContributions[i]; totalAreaXlength += ( areasOfPolygonContributions[i] * lengthOfPolygonContributions[i] ); } double areaWeightedLength = totalAreaXlength / totalArea; return areaWeightedLength; } double clipperConversionFactor = 10000; // For transform to clipper int ClipperLib::IntPoint toClipperPoint( const cvf::Vec3d& cvfPoint ) { int xInt = cvfPoint.x() * clipperConversionFactor; int yInt = cvfPoint.y() * clipperConversionFactor; int zInt = cvfPoint.z() * clipperConversionFactor; return ClipperLib::IntPoint( xInt, yInt, zInt ); } cvf::Vec3d fromClipperPoint( const ClipperLib::IntPoint& clipPoint ) { double zDValue; if ( clipPoint.Z == std::numeric_limits::max() ) { zDValue = HUGE_VAL; } else { zDValue = clipPoint.Z; } return cvf::Vec3d( clipPoint.X, clipPoint.Y, zDValue ) / clipperConversionFactor; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector> RigCellGeometryTools::intersectionWithPolygons( const std::vector& polygon1, const std::vector>& polygonToIntersectWith ) { std::vector> clippedPolygons; // Convert to int for clipper library and store as clipper "path" ClipperLib::Clipper clpr; { ClipperLib::Path polygon1path; for ( const cvf::Vec3d& v : polygon1 ) { polygon1path.push_back( toClipperPoint( v ) ); } clpr.AddPath( polygon1path, ClipperLib::ptSubject, true ); } for ( const auto& path : polygonToIntersectWith ) { ClipperLib::Path polygon2path; for ( const auto& v : path ) { polygon2path.push_back( toClipperPoint( v ) ); } clpr.AddPath( polygon2path, ClipperLib::ptClip, true ); } ClipperLib::Paths solution; clpr.Execute( ClipperLib::ctIntersection, solution, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd ); // Convert back to std::vector > for ( ClipperLib::Path pathInSol : solution ) { std::vector clippedPolygon; for ( ClipperLib::IntPoint IntPosition : pathInSol ) { clippedPolygon.push_back( fromClipperPoint( IntPosition ) ); } clippedPolygons.push_back( clippedPolygon ); } return clippedPolygons; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector> RigCellGeometryTools::intersectionWithPolygon( const std::vector& polygon1, const std::vector& polygon2 ) { return intersectionWithPolygons( polygon1, { polygon2 } ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector> RigCellGeometryTools::subtractPolygons( const std::vector& sourcePolygon, const std::vector>& polygonsToSubtract ) { ClipperLib::Clipper clpr; { // Convert to int for clipper library and store as clipper "path" ClipperLib::Path polygon1path; for ( const auto& v : sourcePolygon ) { polygon1path.push_back( toClipperPoint( v ) ); } clpr.AddPath( polygon1path, ClipperLib::ptSubject, true ); } for ( const auto& path : polygonsToSubtract ) { ClipperLib::Path polygon2path; for ( const auto& v : path ) { polygon2path.push_back( toClipperPoint( v ) ); } clpr.AddPath( polygon2path, ClipperLib::ptClip, true ); } ClipperLib::Paths solution; clpr.Execute( ClipperLib::ctDifference, solution, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd ); std::vector> clippedPolygons; // Convert back to std::vector > for ( ClipperLib::Path pathInSol : solution ) { std::vector clippedPolygon; for ( ClipperLib::IntPoint IntPosition : pathInSol ) { clippedPolygon.push_back( fromClipperPoint( IntPosition ) ); } clippedPolygons.push_back( clippedPolygon ); } return clippedPolygons; } std::vector> RigCellGeometryTools::subtractPolygon( const std::vector& sourcePolygon, const std::vector& polygonToSubtract ) { return subtractPolygons( sourcePolygon, { polygonToSubtract } ); } //-------------------------------------------------------------------------------------------------- /// Note for cppcheck : First four parameter cannot be const to match the signature of the receiver //-------------------------------------------------------------------------------------------------- void fillInterpolatedSubjectZ( ClipperLib::IntPoint& e1bot, ClipperLib::IntPoint& e1top, ClipperLib::IntPoint& e2bot, ClipperLib::IntPoint& e2top, ClipperLib::IntPoint& pt ) { ClipperLib::IntPoint ePLbot; ClipperLib::IntPoint ePLtop; if ( e1top.Z == std::numeric_limits::max() ) { ePLtop = e2top; ePLbot = e2bot; } else { ePLtop = e1top; ePLbot = e1bot; } double ePLXRange = ( ePLtop.X - ePLbot.X ); double ePLYRange = ( ePLtop.Y - ePLbot.Y ); double ePLLength = sqrt( ePLXRange * ePLXRange + ePLYRange * ePLYRange ); if ( ePLLength <= 1 ) { pt.Z = ePLbot.Z; return; } double ePLBotPtXRange = pt.X - ePLbot.X; double ePLBotPtYRange = pt.Y - ePLbot.Y; double ePLBotPtLength = sqrt( ePLBotPtXRange * ePLBotPtXRange + ePLBotPtYRange * ePLBotPtYRange ); double fraction = ePLBotPtLength / ePLLength; pt.Z = std::nearbyint( ePLbot.Z + fraction * ( ePLtop.Z - ePLbot.Z ) ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void fillUndefinedZ( ClipperLib::IntPoint& e1bot, ClipperLib::IntPoint& e1top, ClipperLib::IntPoint& e2bot, ClipperLib::IntPoint& e2top, ClipperLib::IntPoint& pt ) { pt.Z = std::numeric_limits::max(); } //-------------------------------------------------------------------------------------------------- /// Assumes x.y plane polygon. Polyline might have a Z, and the returned Z is the polyline Z, interpolated if it is /// clipped. //-------------------------------------------------------------------------------------------------- std::vector> RigCellGeometryTools::clipPolylineByPolygon( const std::vector& polyLine, const std::vector& polygon, ZInterpolationType interpolType ) { std::vector> clippedPolyline; // Adjusting polygon to avoid clipper issue with interpolating z-values when lines crosses though polygon vertecies std::vector adjustedPolygon = ajustPolygonToAvoidIntersectionsAtVertex( polyLine, polygon ); // Convert to int for clipper library and store as clipper "path" ClipperLib::Path polyLinePath; for ( const cvf::Vec3d& v : polyLine ) { polyLinePath.push_back( toClipperPoint( v ) ); } ClipperLib::Path polygonPath; for ( const cvf::Vec3d& v : adjustedPolygon ) { ClipperLib::IntPoint intp = toClipperPoint( v ); intp.Z = std::numeric_limits::max(); polygonPath.push_back( intp ); } ClipperLib::Clipper clpr; clpr.AddPath( polyLinePath, ClipperLib::ptSubject, false ); clpr.AddPath( polygonPath, ClipperLib::ptClip, true ); if ( interpolType == INTERPOLATE_LINE_Z ) { clpr.ZFillFunction( &fillInterpolatedSubjectZ ); } else if ( interpolType == USE_HUGEVAL ) { clpr.ZFillFunction( &fillUndefinedZ ); } ClipperLib::PolyTree solution; clpr.Execute( ClipperLib::ctIntersection, solution, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd ); // We only expect open paths from this method (unless the polyline is self intersecting, a condition we do not handle) ClipperLib::Paths solutionPaths; ClipperLib::OpenPathsFromPolyTree( solution, solutionPaths ); // Convert back to std::vector > for ( ClipperLib::Path pathInSol : solutionPaths ) { std::vector clippedPolygon; for ( ClipperLib::IntPoint IntPosition : pathInSol ) { clippedPolygon.push_back( fromClipperPoint( IntPosition ) ); } clippedPolyline.push_back( clippedPolygon ); } return clippedPolyline; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::pair RigCellGeometryTools::getLineThroughBoundingBox( const cvf::Vec3d& lineDirection, const cvf::BoundingBox& polygonBBox, const cvf::Vec3d& pointOnLine ) { cvf::Vec3d bboxCorners[8]; polygonBBox.cornerVertices( bboxCorners ); cvf::Vec3d startPoint = pointOnLine; cvf::Vec3d endPoint = pointOnLine; cvf::Vec3d lineDir = lineDirection; // To avoid doing many iterations in loops below linedirection should be quite large. lineDir.normalize(); lineDir = lineDir * polygonBBox.extent().length() / 5; // Extend line in positive direction while ( polygonBBox.contains( startPoint ) ) { startPoint = startPoint + lineDir; } // Extend line in negative direction while ( polygonBBox.contains( endPoint ) ) { endPoint = endPoint - lineDir; } std::pair line; line = { startPoint, endPoint }; return line; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RigCellGeometryTools::getLengthOfPolygonAlongLine( const std::pair& line, const std::vector& polygon ) { cvf::BoundingBox lineBoundingBox; for ( cvf::Vec3d polygonPoint : polygon ) { cvf::Vec3d pointOnLine = cvf::GeometryTools::projectPointOnLine( line.first, line.second, polygonPoint, nullptr ); lineBoundingBox.add( pointOnLine ); } double length = lineBoundingBox.extent().length(); return length; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigCellGeometryTools::unionOfPolygons( const std::vector>& polygons ) { // Convert to int for clipper library and store as clipper "path" std::vector polygonPaths; for ( const std::vector& polygon : polygons ) { polygonPaths.emplace_back(); auto& p = polygonPaths.back(); for ( const cvf::Vec3d& pp : polygon ) { p.push_back( toClipperPoint( pp ) ); } } ClipperLib::Clipper clpr; clpr.AddPaths( polygonPaths, ClipperLib::ptSubject, true ); ClipperLib::Paths solution; clpr.Execute( ClipperLib::ctUnion, solution, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd ); // Convert back to std::vector > std::vector unionPolygon; for ( ClipperLib::Path pathInSol : solution ) { for ( ClipperLib::IntPoint IntPosition : pathInSol ) { unionPolygon.push_back( fromClipperPoint( IntPosition ) ); } } return unionPolygon; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RigCellGeometryTools::ajustPolygonToAvoidIntersectionsAtVertex( const std::vector& polyLine, const std::vector& polygon ) { std::vector adjustedPolygon; double treshold = ( 1.0 / 10000.0 ) * 5; // 5 times polygonScaleFactor for converting to int for clipper for ( cvf::Vec3d polygonPoint : polygon ) { for ( size_t i = 0; i < polyLine.size() - 1; i++ ) { cvf::Vec3d linePoint1( polyLine[i].x(), polyLine[i].y(), 0.0 ); cvf::Vec3d linePoint2( polyLine[i + 1].x(), polyLine[i + 1].y(), 0.0 ); double pointDistanceFromLine = cvf::GeometryTools::linePointSquareDist( linePoint1, linePoint2, polygonPoint ); if ( pointDistanceFromLine < treshold ) { // calculate new polygonPoint cvf::Vec3d directionOfLineSegment = linePoint2 - linePoint1; // finding normal to the direction of the line segment in the XY plane (z=0) cvf::Vec3d normalToLine( -directionOfLineSegment.y(), directionOfLineSegment.x(), 0.0 ); normalToLine.normalize(); polygonPoint = polygonPoint + normalToLine * 0.005; } } adjustedPolygon.push_back( polygonPoint ); } return adjustedPolygon; } //-------------------------------------------------------------------------------------------------- /// tests if a point is Left|On|Right of an infinite line. /// Input: three points P1, P2, and P3 /// Return: >0 for P3 left of the line through P1 and P2 /// =0 for P3 on the line /// <0 for P3 right of the line /// ref. http://geomalgorithms.com/a01-_area.html //-------------------------------------------------------------------------------------------------- inline double RigCellGeometryTools::isLeftOfLine2D( const cvf::Vec3d& point1, const cvf::Vec3d& point2, const cvf::Vec3d& point3 ) { return ( ( point2.x() - point1.x() ) * ( point3.y() - point1.y() ) - ( point3.x() - point1.x() ) * ( point2.y() - point1.y() ) ); } //-------------------------------------------------------------------------------------------------- /// winding number test for a point in a polygon /// Input: point = the point to test, /// polygon[] = vertex points of a closed polygon of size n, where polygon[n-1]=polygon[0] /// /// Return: true if inside, false if outside) /// ref. http://geomalgorithms.com/a03-_inclusion.html //-------------------------------------------------------------------------------------------------- bool RigCellGeometryTools::pointInsidePolygon2D( const cvf::Vec3d point, const std::vector& polygon ) { // Copyright 2000 softSurfer, 2012 Dan Sunday // This code may be freely used and modified for any purpose // providing that this copyright notice is included with it. // SoftSurfer makes no warranty for this code, and cannot be held // liable for any real or imagined damage resulting from its use. // Users of this code must verify correctness for their application int wn = 0; // the winding number counter size_t N = polygon.size() - 1; // loop through all edges of the polygon for ( size_t i = 0; i < N; i++ ) { // edge from V[i] to V[i+1] if ( polygon[i].y() <= point.y() ) { // start y <= P.y if ( polygon[i + 1].y() > point.y() ) // an upward crossing if ( isLeftOfLine2D( polygon[i], polygon[i + 1], point ) > 0 ) // P left of edge ++wn; // have a valid up intersect } else { // start y > P.y if ( polygon[i + 1].y() <= point.y() ) // a downward crossing if ( isLeftOfLine2D( polygon[i], polygon[i + 1], point ) < 0 ) // P right of edge --wn; // have a valid down intersect } } return wn != 0; }