ResInsight/ApplicationLibCode/ReservoirDataModel/cvfGeometryTools.inl

1037 lines
44 KiB
Plaintext
Raw Normal View History

2014-09-24 00:14:52 -05:00
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) Statoil ASA
// Copyright (C) Ceetron Solutions AS
//
2014-09-24 00:14:52 -05:00
// 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.
//
2014-09-24 00:14:52 -05:00
// 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 <http://www.gnu.org/licenses/gpl.html>
2014-09-24 00:14:52 -05:00
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
2013-12-09 08:48:55 -06:00
#include <cmath>
2013-12-05 02:45:27 -06:00
namespace cvf
{
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
template <typename Vec3Type>
2020-09-29 02:49:28 -05:00
Vec3Type GeometryTools::computePolygonCenter( const std::vector<Vec3Type>& polygon )
{
Vec3Type s;
2020-09-29 02:49:28 -05:00
for ( size_t i = 0; i < polygon.size(); i++ )
{
2020-09-30 03:47:46 -05:00
s += polygon[i];
}
s /= polygon.size();
return s;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
template <typename DataType>
DataType GeometryTools::interpolateQuad( const cvf::Vec3d& v1,
DataType s1,
const cvf::Vec3d& v2,
DataType s2,
const cvf::Vec3d& v3,
DataType s3,
const cvf::Vec3d& v4,
DataType s4,
const cvf::Vec3d& point )
{
cvf::Vec4d bc = barycentricCoords( v1, v2, v3, v4, point );
return s1 * bc[0] + s2 * bc[1] + s3 * bc[2] + s4 * bc[3];
}
//--------------------------------------------------------------------------------------------------
/// Inserts the vertex into the polygon if it fits along one of the edges within the tolerance.
/// The method returns true if it was inserted, or if it was already in the polygon, or if it was
/// within the tolerance of an existing vertex in the polygon.
/// In the latter situation it replaces the previous vertex in the polygon.
///
/// Todo: If a vertex is replaced, the VxToCv map in TimeStepGeometry should be updated
//--------------------------------------------------------------------------------------------------
template <typename VerticeArrayType, typename IndexType>
bool GeometryTools::insertVertexInPolygon( std::vector<IndexType>* polygon,
ArrayWrapperConst<VerticeArrayType, cvf::Vec3d> nodeCoords,
IndexType vertexIndex,
double tolerance )
{
CVF_ASSERT( polygon );
// Check if vertex is directly included already
for ( typename std::vector<IndexType>::iterator it = polygon->begin(); it != polygon->end(); ++it )
{
if ( *it == vertexIndex ) return true;
}
#if 1
// Check if the new point is within tolerance of one of the polygon vertices
bool existsOrInserted = false;
for ( typename std::vector<IndexType>::iterator it = polygon->begin(); it != polygon->end(); ++it )
{
if ( ( nodeCoords[*it] - nodeCoords[vertexIndex] ).length() < tolerance )
{
if ( vertexIndex < *it ) *it = vertexIndex;
existsOrInserted = true;
}
}
if ( existsOrInserted ) return true;
#endif
// Copy the start polygon to a list
std::list<IndexType> listPolygon;
for ( size_t pcIdx = 0; pcIdx < polygon->size(); ++pcIdx )
{
listPolygon.push_back( ( *polygon )[pcIdx] );
}
// Insert vertex in polygon if the distance to one of the edges is small enough
typename std::list<IndexType>::iterator it2;
typename std::list<IndexType>::iterator insertBefore;
for ( typename std::list<IndexType>::iterator it = listPolygon.begin(); it != listPolygon.end(); ++it )
{
it2 = it;
++it2;
insertBefore = it2;
if ( it2 == listPolygon.end() ) it2 = listPolygon.begin();
double sqDistToLine =
GeometryTools::linePointSquareDist( nodeCoords[*it], nodeCoords[*it2], nodeCoords[vertexIndex] );
if ( fabs( sqDistToLine ) < tolerance * tolerance )
{
it = listPolygon.insert( insertBefore, vertexIndex );
existsOrInserted = true;
}
}
// Write polygon back into the vector
polygon->clear();
for ( typename std::list<IndexType>::iterator it = listPolygon.begin(); it != listPolygon.end(); ++it )
{
polygon->push_back( *it );
}
return existsOrInserted;
}
2013-12-05 02:45:27 -06:00
//--------------------------------------------------------------------------------------------------
/// \brief Test if a point touches a polygon within the specified tolerance
2013-12-05 02:45:27 -06:00
///
/// \param polygonNorm Polygon normal
/// \param pPolygonVerts Array of polygon vertice coordinates
/// \param piVertexIndices Array of integer node indices for this polygon
/// \param iNumVerts Number of vertices in polygon
/// \param point The point to be checked
2013-12-05 02:45:27 -06:00
/// \param tolerance Tolerance in length
/// \param touchedEdgeIndex returns -1 if point is inside, and edge index if point touches an edge.
/// \return true if point lies inside or on the border of the polygon.
///
/// \assumpt Assumes that the polygon is planar
/// \comment First check if point is on an edge, Then check if it is inside by
/// counting the number of times a ray from point along positive X axis
2013-12-05 02:45:27 -06:00
/// crosses an edge. Odd number says inside.
/// \author SP (really by Eric Haines) and JJS
2013-12-05 02:45:27 -06:00
//--------------------------------------------------------------------------------------------------
template <typename VerticeArrayType, typename PolygonArrayType, typename IndexType>
bool GeometryTools::isPointTouchingIndexedPolygon( const cvf::Vec3d& polygonNormal,
cvf::ArrayWrapperConst<VerticeArrayType, cvf::Vec3d> vertices,
cvf::ArrayWrapperConst<PolygonArrayType, IndexType> indices,
const cvf::Vec3d& point,
int* touchedEdgeIndex,
double tolerance )
2013-12-05 02:45:27 -06:00
{
size_t numIndices = indices.size();
int Z = findClosestAxis( polygonNormal );
int X = ( Z + 1 ) % 3;
int Y = ( Z + 2 ) % 3;
2013-12-05 02:45:27 -06:00
int crossings;
2013-12-05 02:45:27 -06:00
int xBelowVx0;
int yBelowVx0;
int yBelowVx1 = 0;
const double* vtx0;
2018-02-18 11:56:43 -06:00
const double* vtx1 = nullptr;
2013-12-05 02:45:27 -06:00
double dv0;
cvf::uint j;
2013-12-05 02:45:27 -06:00
// Check if point is on an edge or vertex
size_t firstIdx;
size_t secondIdx;
CVF_TIGHT_ASSERT( touchedEdgeIndex );
2013-12-05 02:45:27 -06:00
*touchedEdgeIndex = -1;
for ( firstIdx = 0, secondIdx = 1; firstIdx < numIndices; ++firstIdx, ++secondIdx )
2013-12-05 02:45:27 -06:00
{
if ( secondIdx >= numIndices ) secondIdx = 0;
2013-12-05 02:45:27 -06:00
const cvf::Vec3d& vx0 = vertices[indices[firstIdx]];
const cvf::Vec3d& vx1 = vertices[indices[secondIdx]];
double sqDist = GeometryTools::linePointSquareDist( vx0, vx1, point );
if ( sqDist < tolerance * tolerance )
2013-12-05 02:45:27 -06:00
{
*touchedEdgeIndex = static_cast<int>( firstIdx );
2013-12-05 02:45:27 -06:00
return true;
}
}
vtx0 = vertices[indices[numIndices - 1]].ptr();
2013-12-05 02:45:27 -06:00
// get test bit for above/below Y axis. Y of Point is under Y of vtx0
2013-12-05 02:45:27 -06:00
yBelowVx0 = ( dv0 = vtx0[Y] - point[Y] ) >= 0.0;
crossings = 0;
for ( j = 0; j < numIndices; j++ )
2013-12-05 02:45:27 -06:00
{
// cleverness: bobble between filling endpoints of edges, so that the previous edge's shared endpoint is
// maintained.
if ( j & 0x1 )
{
vtx0 = vertices[indices[j]].ptr();
yBelowVx0 = ( dv0 = vtx0[Y] - point[Y] ) >= 0.0;
}
else
{
vtx1 = vertices[indices[j]].ptr();
yBelowVx1 = ( vtx1[Y] >= point[Y] );
2013-12-05 02:45:27 -06:00
}
// check if Y of point is between Y of Vx0 and Vx1
if ( yBelowVx0 != yBelowVx1 )
{
2013-12-05 02:45:27 -06:00
// check if X of point is not between X of Vx0 and Vx1
if ( ( xBelowVx0 = ( vtx0[X] >= point[X] ) ) == ( vtx1[X] >= point[X] ) )
2013-12-05 02:45:27 -06:00
{
if ( xBelowVx0 ) crossings++;
}
else
2013-12-05 02:45:27 -06:00
{
// compute intersection of polygon segment with X ray, note if > point's X.
crossings += ( vtx0[X] - dv0 * ( vtx1[X] - vtx0[X] ) / ( vtx1[Y] - vtx0[Y] ) ) >= point[X];
}
}
2013-12-05 02:45:27 -06:00
}
// test if crossings is odd. If we care about its winding number > 0, then just: inside_flag = crossings > 0;
if ( crossings & 0x01 ) return true;
2013-12-05 02:45:27 -06:00
return false;
}
//--------------------------------------------------------------------------------------------------
/// Returns true if we get an actual polygon
/// The returned polygon will keep the winding from the first face.
/// The second face must have opposite winding of the first
2013-12-05 02:45:27 -06:00
//--------------------------------------------------------------------------------------------------
template <typename VerticeArrayType, typename IndexType>
bool GeometryTools::calculateOverlapPolygonOfTwoQuads( std::vector<IndexType>* polygon,
std::vector<cvf::Vec3d>* createdVertexes,
EdgeIntersectStorage<IndexType>* edgeIntersectionStorage,
ArrayWrapperConst<VerticeArrayType, cvf::Vec3d> nodes,
const IndexType cv1CubeFaceIndices[4],
const IndexType cv2CubeFaceIndices[4],
double tolerance )
2013-12-05 02:45:27 -06:00
{
CVF_ASSERT( polygon );
CVF_ASSERT( createdVertexes );
2013-12-05 02:45:27 -06:00
// Topology analysis
IndexType newVertexIndex = static_cast<IndexType>( nodes.size() + createdVertexes->size() );
2013-12-05 02:45:27 -06:00
2020-11-06 03:46:38 -06:00
bool cv1VxTouchCv2[4] = { false, false, false, false };
bool cv2VxTouchCv1[4] = { false, false, false, false };
int cv1VxTouchCv2Edge[4] = { -1, -1, -1, -1 };
int cv2VxTouchCv1Edge[4] = { -1, -1, -1, -1 };
2013-12-05 02:45:27 -06:00
int cv1Idx, cv2Idx;
int numMatchedNodes = 0;
// First check for complete topological match.
for ( cv1Idx = 0; cv1Idx < 4; ++cv1Idx )
2013-12-05 02:45:27 -06:00
{
for ( cv2Idx = 0; cv2Idx < 4; ++cv2Idx )
2013-12-05 02:45:27 -06:00
{
if ( cv1CubeFaceIndices[cv1Idx] == cv2CubeFaceIndices[cv2Idx] )
2013-12-05 02:45:27 -06:00
{
cv1VxTouchCv2[cv1Idx] = true;
cv2VxTouchCv1[cv2Idx] = true;
++numMatchedNodes;
continue;
}
}
}
if ( numMatchedNodes >= 4 ) // Todo: Handle collapsed cells
2013-12-05 02:45:27 -06:00
{
int k;
for ( k = 0; k < 4; ++k )
2013-12-05 02:45:27 -06:00
{
polygon->push_back( cv1CubeFaceIndices[k] );
2013-12-05 02:45:27 -06:00
}
return true;
}
cvf::Vec3d diag1 = nodes[cv1CubeFaceIndices[2]] - nodes[cv1CubeFaceIndices[0]];
cvf::Vec3d diag2 = nodes[cv1CubeFaceIndices[3]] - nodes[cv1CubeFaceIndices[1]];
cvf::Vec3d normal = diag1 ^ diag2;
int numCv1VxesOnCv2 = numMatchedNodes;
int numCv2VxesOnCv1 = numMatchedNodes;
for ( cv1Idx = 0; cv1Idx < 4; ++cv1Idx )
2013-12-05 02:45:27 -06:00
{
if ( !cv1VxTouchCv2[cv1Idx] )
{
cv1VxTouchCv2[cv1Idx] = GeometryTools::isPointTouchingIndexedPolygon( normal,
nodes,
wrapArrayConst( cv2CubeFaceIndices, 4 ),
nodes[cv1CubeFaceIndices[cv1Idx]],
&( cv1VxTouchCv2Edge[cv1Idx] ),
tolerance );
if ( cv1VxTouchCv2[cv1Idx] ) ++numCv1VxesOnCv2;
}
if ( !cv2VxTouchCv1[cv1Idx] )
{
cv2VxTouchCv1[cv1Idx] = GeometryTools::isPointTouchingIndexedPolygon( normal,
nodes,
wrapArrayConst( cv1CubeFaceIndices, 4 ),
nodes[cv2CubeFaceIndices[cv1Idx]],
&( cv2VxTouchCv1Edge[cv1Idx] ),
tolerance );
if ( cv2VxTouchCv1[cv1Idx] ) ++numCv2VxesOnCv1;
}
}
2013-12-05 02:45:27 -06:00
// Handle case where one of the faces are completely covered by the other
if ( numCv1VxesOnCv2 >= 4 )
2013-12-05 02:45:27 -06:00
{
int k;
for ( k = 0; k < 4; ++k )
2013-12-05 02:45:27 -06:00
{
polygon->push_back( cv1CubeFaceIndices[k] );
2013-12-05 02:45:27 -06:00
}
return true;
}
if ( numCv2VxesOnCv1 >= 4 )
2013-12-05 02:45:27 -06:00
{
int k;
for ( k = 3; k >= 0; --k ) // Return opposite winding, to match winding of face 1
2013-12-05 02:45:27 -06:00
{
polygon->push_back( cv2CubeFaceIndices[k] );
2013-12-05 02:45:27 -06:00
}
return true;
}
// Handle partial coverage
// Algorithm outline as follows:
// Loop over edges in the face of Cv1. Intersect each one with all the edges of the Cv2 face.
// Add first point of the cv1 edge to polygon if it really touches Cv2 ( touch of edge is considered as not
// touching) Add each intersection point along the Cv1 edge if present and finally: if the cv1 edge is going out of
// cv2, the add the cv2 vertexes from that intersection as long as they touch cv1.
2013-12-05 02:45:27 -06:00
int nextCv1Idx = 1;
for ( cv1Idx = 0; cv1Idx < 4; ++cv1Idx, ++nextCv1Idx )
2013-12-05 02:45:27 -06:00
{
if ( nextCv1Idx > 3 ) nextCv1Idx = 0;
2013-12-05 02:45:27 -06:00
if ( cv1VxTouchCv2[cv1Idx] && cv1VxTouchCv2Edge[cv1Idx] == -1 ) // Start of cv1 edge is touching inside the cv2
// polygon (not on an cv2 edge)
2013-12-05 02:45:27 -06:00
{
if ( polygon->empty() || polygon->back() != cv1CubeFaceIndices[cv1Idx] )
2013-12-05 02:45:27 -06:00
{
polygon->push_back( cv1CubeFaceIndices[cv1Idx] );
2013-12-05 02:45:27 -06:00
}
if ( cv1VxTouchCv2[nextCv1Idx] && cv1VxTouchCv2Edge[nextCv1Idx] == -1 )
2013-12-05 02:45:27 -06:00
{
// Both ends of this cv1 edge is touching inside(not on an edge) cv2 polygon, no intersections possible
// (assuming convex cube-face) Continue with next Cv1-edge.
2013-12-05 02:45:27 -06:00
continue;
}
}
// Find intersection(s) on this edge
std::vector<IndexType> intersectionVxIndices;
std::vector<int> intersectedCv2EdgeIdxs;
std::vector<double> intersectionFractionsAlongEdge;
2013-12-05 02:45:27 -06:00
int nextCv2Idx = 1;
for ( cv2Idx = 0; cv2Idx < 4; ++cv2Idx, ++nextCv2Idx )
2013-12-05 02:45:27 -06:00
{
if ( nextCv2Idx > 3 ) nextCv2Idx = 0;
2013-12-05 02:45:27 -06:00
// Find a possible real intersection point.
2013-12-05 02:45:27 -06:00
cvf::Vec3d intersection( 0, 0, 0 );
double fractionAlongEdge1;
GeometryTools::IntersectionStatus intersectStatus = GeometryTools::NO_INTERSECTION;
IndexType intersectionVxIndex = cvf::UNDEFINED_UINT;
2013-12-05 02:45:27 -06:00
// First handle some "trivial" ones to ease the burden for the real intersection calculation
// It could be tested whether it really is necessary to do
if ( cv1VxTouchCv2Edge[cv1Idx] == cv2Idx && cv1VxTouchCv2Edge[nextCv1Idx] == cv2Idx )
2013-12-05 02:45:27 -06:00
{
intersectStatus = GeometryTools::LINES_OVERLAP;
fractionAlongEdge1 = 1;
2013-12-05 02:45:27 -06:00
intersectionVxIndex = cv1CubeFaceIndices[nextCv1Idx];
}
else if ( cv1VxTouchCv2Edge[cv1Idx] == cv2Idx )
2013-12-05 02:45:27 -06:00
{
// When this happens, the cv1 vertex will already have been added to the polygon
2013-12-05 02:45:27 -06:00
// by the statements in the top of the cv1 edge loop. Should it be treated specially ?
intersectStatus = GeometryTools::LINES_TOUCH;
fractionAlongEdge1 = 0;
2013-12-05 02:45:27 -06:00
intersectionVxIndex = cv1CubeFaceIndices[cv1Idx];
}
else if ( cv1VxTouchCv2Edge[nextCv1Idx] == cv2Idx )
2013-12-05 02:45:27 -06:00
{
intersectStatus = GeometryTools::LINES_TOUCH;
fractionAlongEdge1 = 1;
2013-12-05 02:45:27 -06:00
intersectionVxIndex = cv1CubeFaceIndices[nextCv1Idx];
}
2013-12-05 02:45:27 -06:00
else
{
double fractionAlongEdge2;
bool found = false;
if ( edgeIntersectionStorage )
found = edgeIntersectionStorage->findIntersection( cv1CubeFaceIndices[cv1Idx],
cv1CubeFaceIndices[nextCv1Idx],
cv2CubeFaceIndices[cv2Idx],
cv2CubeFaceIndices[nextCv2Idx],
&intersectionVxIndex,
&intersectStatus,
&fractionAlongEdge1,
&fractionAlongEdge2 );
if ( !found )
{
intersectStatus = GeometryTools::inPlaneLineIntersect3D( normal,
nodes[cv1CubeFaceIndices[cv1Idx]],
nodes[cv1CubeFaceIndices[nextCv1Idx]],
nodes[cv2CubeFaceIndices[cv2Idx]],
nodes[cv2CubeFaceIndices[nextCv2Idx]],
&intersection,
&fractionAlongEdge1,
&fractionAlongEdge2,
tolerance );
switch ( intersectStatus )
{
case GeometryTools::LINES_CROSSES:
{
intersectionVxIndex = newVertexIndex;
createdVertexes->push_back( intersection );
++newVertexIndex;
}
break;
case GeometryTools::LINES_TOUCH:
{
if ( fractionAlongEdge1 <= 0.0 )
intersectionVxIndex = cv1CubeFaceIndices[cv1Idx];
else if ( fractionAlongEdge1 >= 1.0 )
intersectionVxIndex = cv1CubeFaceIndices[nextCv1Idx];
else if ( fractionAlongEdge2 <= 0.0 )
intersectionVxIndex = cv2CubeFaceIndices[cv2Idx];
else if ( fractionAlongEdge2 >= 1.0 )
intersectionVxIndex = cv2CubeFaceIndices[nextCv2Idx];
else
CVF_ASSERT( false ); // Tolerance trouble
}
break;
case GeometryTools::LINES_OVERLAP:
{
if ( fractionAlongEdge1 <= 0.0 )
intersectionVxIndex = cv1CubeFaceIndices[cv1Idx];
else if ( fractionAlongEdge1 >= 1.0 )
intersectionVxIndex = cv1CubeFaceIndices[nextCv1Idx];
else if ( fractionAlongEdge2 <= 0.0 )
intersectionVxIndex = cv2CubeFaceIndices[cv2Idx];
else if ( fractionAlongEdge2 >= 1.0 )
intersectionVxIndex = cv2CubeFaceIndices[nextCv2Idx];
else
CVF_ASSERT( false ); // Tolerance trouble
}
break;
default:
break;
}
2013-12-05 02:45:27 -06:00
if ( edgeIntersectionStorage )
edgeIntersectionStorage->addIntersection( cv1CubeFaceIndices[cv1Idx],
cv1CubeFaceIndices[nextCv1Idx],
cv2CubeFaceIndices[cv2Idx],
cv2CubeFaceIndices[nextCv2Idx],
intersectionVxIndex,
intersectStatus,
fractionAlongEdge1,
fractionAlongEdge2 );
2013-12-05 02:45:27 -06:00
}
}
// Store data for each intersection along the Cv1-edge
if ( ( intersectStatus == GeometryTools::LINES_CROSSES ) ||
( intersectStatus == GeometryTools::LINES_TOUCH ) || ( intersectStatus == GeometryTools::LINES_OVERLAP ) )
2013-12-05 02:45:27 -06:00
{
CVF_ASSERT( intersectionVxIndex != cvf::UNDEFINED_UINT );
2013-12-05 02:45:27 -06:00
intersectionFractionsAlongEdge.push_back( fractionAlongEdge1 );
intersectedCv2EdgeIdxs.push_back( cv2Idx );
intersectionVxIndices.push_back( intersectionVxIndex );
2013-12-05 02:45:27 -06:00
}
}
// Insert the intersections into the polygon in the correct order along the Cv1 edge.
// Find the last intersection in order to possibly continue the polygon along Cv2 into Cv1
size_t i;
size_t lastIntersection = std::numeric_limits<size_t>::max();
double largestFraction = -1;
for ( i = 0; i < intersectionFractionsAlongEdge.size(); ++i )
2013-12-05 02:45:27 -06:00
{
if ( intersectionFractionsAlongEdge[i] > largestFraction )
2013-12-05 02:45:27 -06:00
{
lastIntersection = i;
largestFraction = intersectionFractionsAlongEdge[i];
2013-12-05 02:45:27 -06:00
}
}
// Insert indices to the new intersection vertices into the polygon of
2013-12-05 02:45:27 -06:00
// this connection according to fraction along edge
std::map<double, IndexType> sortingMap;
for ( i = 0; i < intersectionFractionsAlongEdge.size(); ++i )
2013-12-05 02:45:27 -06:00
{
sortingMap[intersectionFractionsAlongEdge[i]] = intersectionVxIndices[i];
}
2013-12-09 08:48:55 -06:00
typename std::map<double, IndexType>::iterator it;
for ( it = sortingMap.begin(); it != sortingMap.end(); ++it )
2013-12-05 02:45:27 -06:00
{
if ( polygon->empty() || polygon->back() != it->second )
2013-12-05 02:45:27 -06:00
{
polygon->push_back( it->second );
2013-12-05 02:45:27 -06:00
}
}
// Then if the Cv1 edge is going out of Cv2, add to the polygon, all the Cv2 face vertex-indices
// from the intersection that touches Cv1.
2013-12-05 02:45:27 -06:00
// if cv1 edge in any way touches cv2 and ends up outside, it went out.
/*
if cv1 edge is going out of cv2 then
if intersected cv2 edge has endpoint touching cv1 then
2013-12-05 02:45:27 -06:00
add endpoint to polygon. continue to add next endpoint until it does not touch Cv1
*/
if ( !cv1VxTouchCv2[nextCv1Idx] &&
( cv1VxTouchCv2[cv1Idx] || ( intersectedCv2EdgeIdxs.size() ) ) ) // Two touches along edge also qualifies
2013-12-05 02:45:27 -06:00
{
if ( lastIntersection < intersectedCv2EdgeIdxs.size() )
2013-12-05 02:45:27 -06:00
{
cv2Idx = intersectedCv2EdgeIdxs[lastIntersection];
2013-12-05 02:45:27 -06:00
int count = 0;
// Continue the polygon along the Cv2 edges as long as they touch cv1.
// Depending on the faces having opposite winding, which is guaranteed as long as
// no intersecting CVs share a connection
while ( cv2VxTouchCv1[cv2Idx] && count < 4 && ( cv2VxTouchCv1Edge[cv2Idx] == -1 ) ) // Touch of edge is
// regarded as being
// outside, so we
// must stop
2013-12-05 02:45:27 -06:00
{
if ( polygon->empty() || polygon->back() != cv2CubeFaceIndices[cv2Idx] )
2013-12-05 02:45:27 -06:00
{
polygon->push_back( cv2CubeFaceIndices[cv2Idx] );
2013-12-05 02:45:27 -06:00
}
--cv2Idx;
if ( cv2Idx < 0 ) cv2Idx = 3;
2013-12-05 02:45:27 -06:00
++count;
}
}
else
{
CVF_ASSERT( lastIntersection < intersectedCv2EdgeIdxs.size() );
2013-12-05 02:45:27 -06:00
}
}
}
if ( polygon->size() > 2 )
2013-12-05 02:45:27 -06:00
{
if ( polygon->back() == polygon->front() ) polygon->pop_back();
2013-12-05 02:45:27 -06:00
}
// Sanity checks
if ( polygon->size() < 3 )
2013-12-05 02:45:27 -06:00
{
// cvf::Trace::show(cvf::String("Degenerated connection polygon detected. (Less than 3 vertexes) Cv's probably
// not in contact: %1 , %2").arg(m_ownerCvId).arg(m_neighborCvId));
2013-12-05 02:45:27 -06:00
polygon->clear();
return false;
}
return true;
}
2013-12-05 02:45:27 -06:00
//--------------------------------------------------------------------------------------------------
/// This method assumes that all intersection and mid edge vertexes are created an are already
2013-12-05 02:45:27 -06:00
/// merged into all the polygons. We can also assume that all the connection polygons are completely
/// inside (or sharing edges with) the cube face polygon initially
//--------------------------------------------------------------------------------------------------
#define DEBUG_PRINT 0
// template <typename NodeArrayType, typename NodeType, typename IndicesArrayType, typename IndicesType>
// void setup( ArrayWrapper<NodeArrayType, NodeType> nodeArray, ArrayWrapper<IndicesArrayType, IndicesType> indices)
template <typename VerticeArrayType, typename PolygonArrayType, typename IndexType>
void GeometryTools::calculatePartiallyFreeCubeFacePolygon(
ArrayWrapperConst<VerticeArrayType, cvf::Vec3d> nodeCoords,
ArrayWrapperConst<PolygonArrayType, IndexType> completeFacePolygon,
const cvf::Vec3d& faceNormal,
const std::vector<std::vector<IndexType>*>& faceOverlapPolygons,
const std::vector<bool>& faceOverlapPolygonWindingSameAsCubeFaceFlags,
std::vector<IndexType>* partialFacePolygon,
bool* m_partiallyFreeCubeFaceHasHoles )
2013-12-05 02:45:27 -06:00
{
// Vertex Index to position in polygon
typedef std::map<IndexType, typename std::vector<IndexType>::const_iterator> VxIdxToPolygonPositionMap;
2013-12-05 02:45:27 -06:00
CVF_ASSERT( m_partiallyFreeCubeFaceHasHoles );
CVF_ASSERT( partialFacePolygon != NULL );
2013-12-05 02:45:27 -06:00
// Copy the start polygon
std::list<IndexType> resultPolygon;
for ( size_t pcIdx = 0; pcIdx < completeFacePolygon.size(); ++pcIdx )
2013-12-05 02:45:27 -06:00
{
resultPolygon.push_back( completeFacePolygon[pcIdx] );
2013-12-05 02:45:27 -06:00
}
// First build search maps to fast find whether and where an index is positioned in a polygon
// Map from Vertex-index to position in polygon
std::vector<VxIdxToPolygonPositionMap> polygonSearchMaps;
std::vector<bool> isConnectionPolygonMerged;
2013-12-05 02:45:27 -06:00
polygonSearchMaps.resize( faceOverlapPolygons.size() );
isConnectionPolygonMerged.resize( faceOverlapPolygons.size(), false );
2013-12-05 02:45:27 -06:00
// Build search maps
{
size_t count;
for ( size_t i = 0; i < faceOverlapPolygons.size(); ++i )
2013-12-05 02:45:27 -06:00
{
count = 0;
for ( typename std::vector<IndexType>::const_iterator pcIt = faceOverlapPolygons[i]->begin();
pcIt != faceOverlapPolygons[i]->end();
++pcIt )
2013-12-05 02:45:27 -06:00
{
polygonSearchMaps[i][*pcIt] = pcIt;
++count;
}
if ( count < 3 ) isConnectionPolygonMerged[i] = true; // Ignore false polygons
2013-12-05 02:45:27 -06:00
}
}
#if DEBUG_PRINT
{
cvf::Trace::show( "Circumference polygon: " );
2013-12-05 02:45:27 -06:00
std::list<IndexType>::const_iterator polIt;
for ( polIt = resultPolygon.begin(); polIt != resultPolygon.end(); ++polIt )
2013-12-05 02:45:27 -06:00
{
cvf::Trace::show( cvf::String( "%1 \t%2 %3 %4" )
.arg( (int)( *polIt ) )
.arg( nodeCoords[*polIt].x() )
.arg( nodeCoords[*polIt].y() )
.arg( nodeCoords[*polIt].z() ) );
2013-12-05 02:45:27 -06:00
}
}
#endif
#if DEBUG_PRINT
{
cvf::Trace::show( "Connection polygons: " );
for ( size_t cIdx = 0; cIdx < faceOverlapPolygons.size(); cIdx++ )
2013-12-05 02:45:27 -06:00
{
std::vector<IndexType>::const_iterator polIt;
cvf::Trace::show( "Connection " + cvf::String( (long long)cIdx ) );
for ( polIt = faceOverlapPolygons[cIdx]->begin(); polIt != faceOverlapPolygons[cIdx]->end(); ++polIt )
2013-12-05 02:45:27 -06:00
{
cvf::Trace::show( cvf::String( "%1 \t%2 %3 %4" )
.arg( (int)( *polIt ) )
.arg( nodeCoords[*polIt].x() )
.arg( nodeCoords[*polIt].y() )
.arg( nodeCoords[*polIt].z() ) );
2013-12-05 02:45:27 -06:00
}
}
}
#endif
// Merge connection polygons with the main polygon as long as one of them has something in common.
// For each vx in the m_freeFacePolygons[cubeFace] polygon .
// loop over all connections
// if it has the node in common and that the edge angle will decrease if inserting
// merge the connection polygon into the main polygon,
2013-12-05 02:45:27 -06:00
// and remove the connection polygon from the merge able connection polygons.
for ( typename std::list<IndexType>::iterator pIt = resultPolygon.begin(); pIt != resultPolygon.end(); ++pIt )
2013-12-05 02:45:27 -06:00
{
// Set iterator to previous node in polygon
2013-12-09 08:48:55 -06:00
typename std::list<IndexType>::iterator prevPIt = pIt;
if ( prevPIt == resultPolygon.begin() ) prevPIt = resultPolygon.end();
2013-12-05 02:45:27 -06:00
--prevPIt;
cvf::Vec3d pToPrev = nodeCoords[*prevPIt] - nodeCoords[*pIt];
// Set iterator to next node in polygon. Used to insert before and as pointer to the next point
2013-12-09 08:48:55 -06:00
typename std::list<IndexType>::iterator nextPIt = pIt;
2013-12-05 02:45:27 -06:00
++nextPIt;
2013-12-09 08:48:55 -06:00
typename std::list<IndexType>::iterator insertBeforePIt = nextPIt;
if ( nextPIt == resultPolygon.end() ) nextPIt = resultPolygon.begin();
2013-12-05 02:45:27 -06:00
// Calculate existing edge to edge angle
cvf::Vec3d pToNext = nodeCoords[*nextPIt] - nodeCoords[*pIt];
double mainPolygonEdgeAngle = GeometryTools::getAngle( faceNormal, pToNext, pToPrev );
2013-12-05 02:45:27 -06:00
// Find connections containing the pIt vertex index. Merge them into the main polygon
for ( size_t opIdx = 0; opIdx < faceOverlapPolygons.size(); ++opIdx )
2013-12-05 02:45:27 -06:00
{
if ( isConnectionPolygonMerged[opIdx] ) continue; // Already merged
2013-12-05 02:45:27 -06:00
// Find position of pIt vertex index in the current connection polygon
typename VxIdxToPolygonPositionMap::iterator vxIndexPositionInPolygonIt = polygonSearchMaps[opIdx].find( *pIt );
2013-12-05 02:45:27 -06:00
if ( vxIndexPositionInPolygonIt != polygonSearchMaps[opIdx].end() )
2013-12-05 02:45:27 -06:00
{
// Merge the connection polygon into the main polygon
2013-12-05 02:45:27 -06:00
// if the angle prevPIt pIt nextPIt is larger than angle prevPIt pIt (startCPIt++)
2013-12-09 08:48:55 -06:00
typename std::vector<IndexType>::const_iterator startCPIt;
2013-12-05 02:45:27 -06:00
startCPIt = vxIndexPositionInPolygonIt->second;
// First vx to insert is the one after the match
bool hasSameWinding = faceOverlapPolygonWindingSameAsCubeFaceFlags[opIdx];
if ( hasSameWinding )
2013-12-05 02:45:27 -06:00
{
// Same winding as main polygon. We need to go the opposite way
if ( startCPIt == faceOverlapPolygons[opIdx]->begin() )
startCPIt = faceOverlapPolygons[opIdx]->end();
2013-12-05 02:45:27 -06:00
--startCPIt;
}
else
{
// Opposite winding. Go forward when merging
++startCPIt;
if ( startCPIt == faceOverlapPolygons[opIdx]->end() )
startCPIt = faceOverlapPolygons[opIdx]->begin();
2013-12-05 02:45:27 -06:00
}
// Calculate possible new edge-to-edge angle and test against existing angle
cvf::Vec3d pToStart = nodeCoords[*startCPIt] - nodeCoords[*pIt];
double candidatePolygonEdgeAngle = GeometryTools::getAngle( faceNormal, pToStart, pToPrev );
2013-12-05 02:45:27 -06:00
if ( candidatePolygonEdgeAngle < mainPolygonEdgeAngle )
2013-12-05 02:45:27 -06:00
{
// Merge ok
typename std::vector<IndexType>::const_iterator pcIt = startCPIt;
if ( hasSameWinding )
2013-12-05 02:45:27 -06:00
{
do
2013-12-05 02:45:27 -06:00
{
resultPolygon.insert( insertBeforePIt, ( *pcIt ) );
2013-12-05 02:45:27 -06:00
if ( pcIt == faceOverlapPolygons[opIdx]->begin() ) pcIt = faceOverlapPolygons[opIdx]->end();
2013-12-05 02:45:27 -06:00
--pcIt;
} while ( pcIt != startCPIt );
2013-12-05 02:45:27 -06:00
}
else
{
do
{
resultPolygon.insert( insertBeforePIt, ( *pcIt ) );
2013-12-05 02:45:27 -06:00
++pcIt;
if ( pcIt == faceOverlapPolygons[opIdx]->end() ) pcIt = faceOverlapPolygons[opIdx]->begin();
2013-12-05 02:45:27 -06:00
} while ( pcIt != startCPIt );
2013-12-05 02:45:27 -06:00
}
isConnectionPolygonMerged[opIdx] = true;
// Recalculate the next position to point into the new nodes
// Set iterator in the main polygon to insert before and to the next point
nextPIt = pIt;
++nextPIt;
insertBeforePIt = nextPIt;
if ( nextPIt == resultPolygon.end() ) nextPIt = resultPolygon.begin();
2013-12-05 02:45:27 -06:00
// Recalculate the existing edge to edge angle
pToNext = nodeCoords[*nextPIt] - nodeCoords[*pIt];
mainPolygonEdgeAngle = GeometryTools::getAngle( faceNormal, pToNext, pToPrev );
2013-12-05 02:45:27 -06:00
}
}
}
}
// Now remove all double edges
bool goneAround = false;
for ( typename std::list<IndexType>::iterator pIt = resultPolygon.begin(); pIt != resultPolygon.end() && !goneAround;
++pIt )
2013-12-05 02:45:27 -06:00
{
// Set iterator to next node in polygon.
typename std::list<IndexType>::iterator nextPIt = pIt;
2013-12-05 02:45:27 -06:00
++nextPIt;
if ( nextPIt == resultPolygon.end() )
{
nextPIt = resultPolygon.begin();
2013-12-05 02:45:27 -06:00
goneAround = true; // Gone around polygon. Stop even if pIt is jumping over end()
}
// Set iterator to previous node in polygon
2013-12-09 08:48:55 -06:00
typename std::list<IndexType>::iterator prevPIt = pIt;
2013-12-05 02:45:27 -06:00
if ( prevPIt == resultPolygon.begin() ) prevPIt = resultPolygon.end();
2013-12-05 02:45:27 -06:00
--prevPIt;
// If previous and next node are the same, erase
while ( *nextPIt == *prevPIt )
2013-12-05 02:45:27 -06:00
{
resultPolygon.erase( pIt );
resultPolygon.erase( prevPIt );
2013-12-05 02:45:27 -06:00
if ( resultPolygon.begin() == resultPolygon.end() )
break; // Polygon has been completely removed. Nothing left. Break out of while
2013-12-05 02:45:27 -06:00
pIt = nextPIt;
++nextPIt;
if ( nextPIt == resultPolygon.end() )
{
nextPIt = resultPolygon.begin();
2013-12-05 02:45:27 -06:00
goneAround = true; // Gone around polygon pIt is jumping over end()
}
prevPIt = pIt;
if ( prevPIt == resultPolygon.begin() ) prevPIt = resultPolygon.end();
2013-12-05 02:45:27 -06:00
--prevPIt;
}
if ( resultPolygon.begin() == resultPolygon.end() )
break; // Polygon has been completely removed. Nothing left. Break out of for loop
2013-12-05 02:45:27 -06:00
}
// Check for holes
2013-12-05 02:45:27 -06:00
for ( size_t i = 0; i < isConnectionPolygonMerged.size(); ++i )
2013-12-05 02:45:27 -06:00
{
2020-10-09 06:43:31 -05:00
bool hasHoles = !isConnectionPolygonMerged[i];
if ( hasHoles )
2013-12-05 02:45:27 -06:00
{
*m_partiallyFreeCubeFaceHasHoles = true;
break;
}
}
#if DEBUG_PRINT
{
cvf::Trace::show( "Polygon: " );
for ( std::list<IndexType>::iterator pIt = resultPolygon.begin(); pIt != resultPolygon.end(); ++pIt )
2013-12-05 02:45:27 -06:00
{
cvf::Trace::show( cvf::String( "%1 \t%2 %3 %4" )
.arg( (int)( *pIt ) )
.arg( nodeCoords[*pIt].x() )
.arg( nodeCoords[*pIt].y() )
.arg( nodeCoords[*pIt].z() ) );
2013-12-05 02:45:27 -06:00
}
}
#endif
// Copy the result polygon to the output variable
2013-12-05 02:45:27 -06:00
partialFacePolygon->clear();
for ( typename std::list<IndexType>::iterator pIt = resultPolygon.begin(); pIt != resultPolygon.end(); ++pIt )
2013-12-05 02:45:27 -06:00
{
partialFacePolygon->push_back( *pIt );
2013-12-05 02:45:27 -06:00
}
}
//--------------------------------------------------------------------------------------------------
///
2013-12-05 02:45:27 -06:00
//--------------------------------------------------------------------------------------------------
template <typename IndexType>
void EdgeIntersectStorage<IndexType>::setVertexCount( size_t size )
2013-12-05 02:45:27 -06:00
{
m_edgeIntsectMap.resize( size );
2013-12-05 02:45:27 -06:00
}
template <typename IndexType>
void EdgeIntersectStorage<IndexType>::canonizeAddress( IndexType& e1P1,
IndexType& e1P2,
IndexType& e2P1,
IndexType& e2P2,
bool& flipE1,
bool& flipE2,
bool& flipE1E2 )
2013-12-05 02:45:27 -06:00
{
flipE1 = e1P1 > e1P2;
flipE2 = e2P1 > e2P2;
flipE1E2 = ( flipE1 ? e1P2 : e1P1 ) > ( flipE2 ? e2P2 : e2P1 );
2013-12-05 02:45:27 -06:00
static IndexType temp;
if ( flipE1 )
2013-12-05 02:45:27 -06:00
{
temp = e1P1;
e1P1 = e1P2;
e1P2 = temp;
}
if ( flipE2 )
2013-12-05 02:45:27 -06:00
{
temp = e2P1;
e2P1 = e2P2;
e2P2 = temp;
}
if ( flipE1E2 )
2013-12-05 02:45:27 -06:00
{
temp = e1P1;
e1P1 = e2P1;
e2P1 = temp;
temp = e1P2;
e1P2 = e2P2;
e2P2 = temp;
}
}
//--------------------------------------------------------------------------------------------------
///
2013-12-05 02:45:27 -06:00
//--------------------------------------------------------------------------------------------------
template <typename IndexType>
void EdgeIntersectStorage<IndexType>::addIntersection( IndexType e1P1,
IndexType e1P2,
IndexType e2P1,
IndexType e2P2,
IndexType vxIndexIntersectionPoint,
GeometryTools::IntersectionStatus intersectionStatus,
double fractionAlongEdge1,
double fractionAlongEdge2 )
2013-12-05 02:45:27 -06:00
{
static bool flipE1;
static bool flipE2;
2013-12-05 02:45:27 -06:00
static bool flipE1E2;
canonizeAddress( e1P1, e1P2, e2P1, e2P2, flipE1, flipE2, flipE1E2 );
2013-12-05 02:45:27 -06:00
static IntersectData iData;
iData.fractionAlongEdge1 = flipE1 ? 1 - fractionAlongEdge1 : fractionAlongEdge1;
iData.fractionAlongEdge2 = flipE2 ? 1 - fractionAlongEdge2 : fractionAlongEdge2;
iData.intersectionStatus = intersectionStatus;
if ( flipE1E2 )
2013-12-05 02:45:27 -06:00
{
double temp = iData.fractionAlongEdge1;
2013-12-05 02:45:27 -06:00
iData.fractionAlongEdge1 = iData.fractionAlongEdge2;
iData.fractionAlongEdge2 = temp;
}
iData.intersectionPointIndex = vxIndexIntersectionPoint;
CVF_ASSERT( e1P1 < m_edgeIntsectMap.size() );
2013-12-05 02:45:27 -06:00
m_edgeIntsectMap[e1P1][e1P2][e2P1][e2P2] = iData;
}
//--------------------------------------------------------------------------------------------------
///
2013-12-05 02:45:27 -06:00
//--------------------------------------------------------------------------------------------------
template <typename IndexType>
bool EdgeIntersectStorage<IndexType>::findIntersection( IndexType e1P1,
IndexType e1P2,
IndexType e2P1,
IndexType e2P2,
IndexType* vxIndexIntersectionPoint,
GeometryTools::IntersectionStatus* intersectionStatus,
double* fractionAlongEdge1,
double* fractionAlongEdge2 )
2013-12-05 02:45:27 -06:00
{
static bool flipE1;
static bool flipE2;
2013-12-05 02:45:27 -06:00
static bool flipE1E2;
canonizeAddress( e1P1, e1P2, e2P1, e2P2, flipE1, flipE2, flipE1E2 );
2013-12-05 02:45:27 -06:00
if ( !m_edgeIntsectMap[e1P1].size() ) return false;
2013-12-05 02:45:27 -06:00
typename std::map<IndexType, std::map<IndexType, std::map<IndexType, IntersectData>>>::iterator it;
it = m_edgeIntsectMap[e1P1].find( e1P2 );
if ( it == m_edgeIntsectMap[e1P1].end() ) return false;
2013-12-05 02:45:27 -06:00
typename std::map<IndexType, std::map<IndexType, IntersectData>>::iterator it2;
it2 = it->second.find( e2P1 );
if ( it2 == it->second.end() ) return false;
2013-12-05 02:45:27 -06:00
typename std::map<IndexType, IntersectData>::iterator it3;
it3 = it2->second.find( e2P2 );
if ( it3 == it2->second.end() ) return false;
2013-12-05 02:45:27 -06:00
*vxIndexIntersectionPoint = it3->second.intersectionPointIndex;
*intersectionStatus = it3->second.intersectionStatus;
2013-12-05 02:45:27 -06:00
if ( flipE1E2 )
2013-12-05 02:45:27 -06:00
{
*fractionAlongEdge1 = it3->second.fractionAlongEdge2;
*fractionAlongEdge2 = it3->second.fractionAlongEdge1;
}
else
{
*fractionAlongEdge1 = it3->second.fractionAlongEdge1;
*fractionAlongEdge2 = it3->second.fractionAlongEdge2;
}
if ( flipE1 ) *fractionAlongEdge1 = 1 - *fractionAlongEdge1;
if ( flipE2 ) *fractionAlongEdge2 = 1 - *fractionAlongEdge2;
2013-12-05 02:45:27 -06:00
return true;
}
} // namespace cvf