-// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE
//
// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
//--------------------------------------------------------------------------------
/*!
* \brief Intersected face side storing a node created at this intersection
- * and a intersected face
+ * and an intersected face
*/
struct CutLink
{
int myIndex; // positive -> side index, negative -> State
const SMDS_MeshElement* myFace;
- enum State { _INTERNAL = -1, _COPLANAR = -2 };
+ enum State { _INTERNAL = -1, _COPLANAR = -2, _PENDING = -3 };
void Set( const SMDS_MeshNode* Node1,
const SMDS_MeshNode* Node2,
EdgeLoop() : SMDS_PolygonalFaceOfNodes( std::vector<const SMDS_MeshNode *>() ) {}
void Clear() { myLinks.clear(); myIsBndConnected = false; myHasPending = false; }
bool SetConnected() { bool was = myIsBndConnected; myIsBndConnected = true; return !was; }
- bool Contains( const SMDS_MeshNode* n ) const
+ size_t Contains( const SMDS_MeshNode* n ) const
{
for ( size_t i = 0; i < myLinks.size(); ++i )
- if ( myLinks[i]->myNode1 == n ) return true;
- return false;
+ if ( myLinks[i]->myNode1 == n ) return i + 1;
+ return 0;
}
virtual int NbNodes() const { return myLinks.size(); }
virtual SMDS_ElemIteratorPtr nodesIterator() const
myLoopOfEdge[ Index( *loop->myLinks[ iE ] )] = 0;
loop->Clear();
}
+ void Join( EdgeLoop& loop1, size_t iAfterConcact,
+ EdgeLoop& loop2, size_t iFromEdge2 )
+ {
+ std::vector< const EdgePart* > linksAfterContact( loop1.myLinks.begin() + iAfterConcact,
+ loop1.myLinks.end() );
+ loop1.myLinks.reserve( loop2.myLinks.size() + loop1.myLinks.size() );
+ loop1.myLinks.resize( iAfterConcact );
+ loop1.myLinks.insert( loop1.myLinks.end(),
+ loop2.myLinks.begin() + iFromEdge2, loop2.myLinks.end() );
+ loop1.myLinks.insert( loop1.myLinks.end(),
+ loop2.myLinks.begin(), loop2.myLinks.begin() + iFromEdge2 );
+ loop1.myLinks.insert( loop1.myLinks.end(),
+ linksAfterContact.begin(), linksAfterContact.end() );
+ loop1.myIsBndConnected = loop2.myIsBndConnected;
+ loop2.Clear();
+ for ( size_t iE = 0; iE < loop1.myLinks.size(); ++iE )
+ myLoopOfEdge[ Index( *loop1.myLinks[ iE ] )] = & loop1;
+ }
size_t Index( const EdgePart& edge ) const { return &edge - myEdge0; }
EdgeLoop* GetLoopOf( const EdgePart* edge ) { return myLoopOfEdge[ Index( *edge )]; }
};
static Standard_Integer HashCode(const CutFace& f, const Standard_Integer upper)
{
- return ::HashCode( f.myInitFace->GetID(), upper );
+ return ::HashCode( FromIdType<int>(f.myInitFace->GetID()), upper );
}
static Standard_Boolean IsEqual(const CutFace& f1, const CutFace& f2 )
{
bool getTranslatedPosition( const SMDS_MeshNode* theNewNode,
const double theOffset,
- const double theTol,
+ const double /*theTol*/,
const double theSign,
const std::vector< gp_XYZ >& theFaceNormals,
SMDS_Mesh& theSrcMesh,
return useOneNormal;
}
+ //================================================================================
+ /*!
+ * \brief Remove small faces
+ */
+ //================================================================================
+
+ void removeSmallFaces( SMDS_Mesh* theMesh,
+ SMESH_MeshAlgos::TElemIntPairVec& theNew2OldFaces,
+ const double theTol2 )
+ {
+ std::vector< SMESH_NodeXYZ > points(3);
+ std::vector< const SMDS_MeshNode* > nodes(3);
+ for ( SMDS_ElemIteratorPtr faceIt = theMesh->elementsIterator(); faceIt->more(); )
+ {
+ const SMDS_MeshElement* face = faceIt->next();
+ points.assign( face->begin_nodes(), face->end_nodes() );
+
+ SMESH_NodeXYZ* prevN = & points.back();
+ for ( size_t i = 0; i < points.size(); ++i )
+ {
+ double dist2 = ( *prevN - points[ i ]).SquareModulus();
+ if ( dist2 < theTol2 )
+ {
+ const SMDS_MeshNode* nToRemove =
+ (*prevN)->GetID() > points[ i ]->GetID() ? prevN->Node() : points[ i ].Node();
+ const SMDS_MeshNode* nToKeep =
+ nToRemove == points[ i ].Node() ? prevN->Node() : points[ i ].Node();
+ for ( SMDS_ElemIteratorPtr fIt = nToRemove->GetInverseElementIterator(); fIt->more(); )
+ {
+ const SMDS_MeshElement* f = fIt->next();
+ if ( f == face )
+ continue;
+ nodes.assign( f->begin_nodes(), f->end_nodes() );
+ nodes[ f->GetNodeIndex( nToRemove )] = nToKeep;
+ theMesh->ChangeElementNodes( f, &nodes[0], nodes.size() );
+ }
+ theNew2OldFaces[ face->GetID() ].first = 0;
+ theMesh->RemoveFreeElement( face );
+ break;
+ }
+ prevN = & points[ i ];
+ }
+ continue;
+ }
+ return;
+ }
+
} // namespace
namespace SMESH_MeshAlgos
const double theSign,
const bool theOptimize );
- //! Cut a face by planes, whose normals point to parts to keep
- bool CutByPlanes(const SMDS_MeshElement* face,
- const std::vector< gp_Ax1 > & planes,
- std::vector< SMESH_NodeXYZ > & newConnectivity );
+ void IntersectNewEdges( const CutFace& theCFace );
private:
bool & isCollinear );
bool intersectEdgeEdge( int iE1, int iE2, IntPoint2D& intPoint );
bool isPointInTriangle( const gp_XYZ& p, const std::vector< SMESH_NodeXYZ >& nodes );
- void intersectNewEdges( const CutFace& theCFace );
const SMDS_MeshNode* createNode( const gp_XYZ& p );
};
if ( !link2.IntNode() ) link2.myIntNode = link1.myIntNode;
cf1.AddPoint( link1, link2, myTol );
+ if ( l1n1 ) link1.Set( l1n1, l1n2, face2 );
+ if ( l2n1 ) link2.Set( l2n1, l2n2, face2 );
cf2.AddPoint( link1, link2, myTol );
}
else
SMESH_MeshAlgos::GetBarycentricCoords( p2D( p ),
p2D( nodes[0] ), p2D( nodes[1] ), p2D( nodes[2] ),
bc1, bc2 );
- return ( 0. < bc1 && 0. < bc2 && bc1 + bc2 < 1. );
+ //return ( 0. < bc1 && 0. < bc2 && bc1 + bc2 < 1. );
+ return ( myTol < bc1 && myTol < bc2 && bc1 + bc2 + myTol < 1. );
}
//================================================================================
*/
//================================================================================
- void Intersector::Algo::intersectNewEdges( const CutFace& cf )
+ void Intersector::Algo::IntersectNewEdges( const CutFace& cf )
{
IntPoint2D intPoint;
size_t limit = cf.myLinks.size() * cf.myLinks.size() * 2;
- for ( size_t i1 = 3; i1 < cf.myLinks.size(); ++i1 )
+ size_t i1 = 3;
+ while ( cf.myLinks[i1-1].IsInternal() && i1 > 0 )
+ --i1;
+
+ for ( ; i1 < cf.myLinks.size(); ++i1 )
{
if ( !cf.myLinks[i1].IsInternal() )
continue;
}
}
if ( cf.myLinks.size() >= limit )
- throw SALOME_Exception( "Infinite loop in Intersector::Algo::intersectNewEdges()" );
+ throw SALOME_Exception( "Infinite loop in Intersector::Algo::IntersectNewEdges()" );
}
++i1; // each internal edge encounters twice
}
{
const CutFace& cf = *cutFacesIt;
cf.ReplaceNodes( myRemove2KeepNodes );
- intersectNewEdges( cf );
+ IntersectNewEdges( cf );
}
// make new faces
// avoid loops that are not connected to boundary edges of cf.myInitFace
if ( cf.RemoveInternalLoops( loopSet ))
{
- intersectNewEdges( cf );
+ IntersectNewEdges( cf );
cf.MakeLoops( loopSet, normal );
}
// erase loops that are cut off by face intersections
#ifdef _DEBUG_
std::cerr << "BAD tria" << std::endl;
cf.Dump();
+#else
+ if ( i < 0 ) cf.Dump(); // avoid "CutFace::Dump() unused in release mode"
#endif
continue;
}
myMesh->RemoveFreeElement( f );
}
+ // remove faces that are merged off
+ for ( cutFacesIt = myCutFaces.cbegin(); cutFacesIt != myCutFaces.cend(); ++cutFacesIt )
+ {
+ const CutFace& cf = *cutFacesIt;
+ if ( !cf.myLinks.empty() || cf.myInitFace->IsNull() )
+ continue;
+
+ nodes.assign( cf.myInitFace->begin_nodes(), cf.myInitFace->end_nodes() );
+ for ( size_t i = 0; i < nodes.size(); ++i )
+ {
+ const SMDS_MeshNode* n = nodes[ i ];
+ while ( myRemove2KeepNodes.IsBound( n ))
+ n = myRemove2KeepNodes( n );
+ if ( n != nodes[ i ] && cf.myInitFace->GetNodeIndex( n ) >= 0 )
+ {
+ theNew2OldFaces[ cf.myInitFace->GetID() ].first = 0;
+ myMesh->RemoveFreeElement( cf.myInitFace );
+ break;
+ }
+ }
+ }
+
// remove faces connected to cut off parts of cf.myInitFace
nodes.resize(2);
if ( nodes[0] != nodes[1] &&
myMesh->GetElementsByNodes( nodes, faces ))
{
- if ( cutOffLinks[i].myFace &&
- cutOffLinks[i].myIndex != EdgePart::_COPLANAR &&
- faces.size() == 2 )
+ if ( // cutOffLinks[i].myFace &&
+ cutOffLinks[i].myIndex != EdgePart::_COPLANAR &&
+ faces.size() != 1 )
continue;
for ( size_t iF = 0; iF < faces.size(); ++iF )
{
for ( size_t i = 0; i < touchedFaces.size(); ++i )
{
const CutFace& cf = *touchedFaces[i];
+ if ( cf.myInitFace->IsNull() )
+ continue;
int index = cf.myInitFace->GetID(); // index in theNew2OldFaces
if ( !theNew2OldFaces[ index ].first )
continue; // already cut off
+ cf.InitLinks();
if ( !cf.ReplaceNodes( myRemove2KeepNodes ))
- continue; // just keep as is
+ {
+ if ( cf.myLinks.size() == 3 &&
+ cf.myInitFace->GetNodeIndex( cf.myLinks[0].myNode1 ) >= 0 &&
+ cf.myInitFace->GetNodeIndex( cf.myLinks[1].myNode1 ) >= 0 &&
+ cf.myInitFace->GetNodeIndex( cf.myLinks[2].myNode1 ) >= 0 )
+ continue; // just keep as is
+ }
if ( cf.myLinks.size() == 3 )
{
theNewFaceConnectivity.push_back( facePoints );
break;
}
+
+ // intersect cut lines
+ algo.IntersectNewEdges( cf );
+
// form loops of new faces
EdgeLoopSet loopSet;
cf.MakeLoops( loopSet, normals[ faceToCut->GetID() ]);
// if ( !myLinks[i].IsInternal() )
// myLinks[ i ].myFace = cutterFace;
// else
- myLinks[ i ].ReplaceCoplanar( newEdge );
- myLinks[ i+1 ].ReplaceCoplanar( newEdge );
+ myLinks[ i ].ReplaceCoplanar( newEdge );
+ if ( myLinks[i].IsInternal() && i+1 < myLinks.size() )
+ myLinks[ i+1 ].ReplaceCoplanar( newEdge );
return;
}
i += myLinks[i].IsInternal();
*/
//================================================================================
- void CutFace::AddPoint( const CutLink& p1, const CutLink& p2, double tol ) const
+ void CutFace::AddPoint( const CutLink& p1, const CutLink& p2, double /*tol*/ ) const
{
if ( myInitFace->GetNodeIndex( p1.IntNode() ) >= 0 ||
myInitFace->GetNodeIndex( p2.IntNode() ) >= 0 )
//================================================================================
/*!
* \brief Remove loops that are not connected to boundary edges of myFace by
- * adding edges connecting these loops to the boundary
+ * adding edges connecting these loops to the boundary.
+ * Such loops must be removed as they form polygons with ring topology.
*/
//================================================================================
while ( prevNbReached < nbReachedLoops );
- // add links connecting internal loops with the boundary ones
for ( size_t iL = 0; iL < theLoops.myNbLoops; ++iL )
{
EdgeLoop& loop = theLoops.myLoops[ iL ];
- if ( loop.myIsBndConnected )
+ if ( loop.myIsBndConnected || loop.myLinks.size() == 0 )
continue;
+ if ( loop.myHasPending )
+ {
+ // try to join the loop to another one, with which it contacts at a node
+
+ // look for a node where the loop reverses
+ const EdgePart* edgePrev = loop.myLinks.back();
+ for ( size_t iE = 0; iE < loop.myLinks.size(); edgePrev = loop.myLinks[ iE++ ] )
+ {
+ if ( !edgePrev->IsTwin( *loop.myLinks[ iE ]))
+ continue;
+ const SMDS_MeshNode* reverseNode = edgePrev->myNode2;
+
+ // look for a loop including reverseNode
+ size_t iContactEdge2; // index(+1) of edge starting at reverseNode
+ for ( size_t iL2 = 0; iL2 < theLoops.myNbLoops; ++iL2 )
+ {
+ if ( iL == iL2 )
+ continue;
+ EdgeLoop& loop2 = theLoops.myLoops[ iL2 ];
+ if ( ! ( iContactEdge2 = loop2.Contains( reverseNode )))
+ continue;
+
+ // insert loop2 into the loop
+ theLoops.Join( loop, iE, loop2, iContactEdge2 - 1 );
+ break;
+ }
+ if ( loop.myIsBndConnected )
+ break;
+ }
+
+ if ( loop.myIsBndConnected )
+ continue;
+ }
+
+ // add links connecting internal loops with the boundary ones
+
// find a pair of closest nodes
- const SMDS_MeshNode *closestNode1, *closestNode2;
+ const SMDS_MeshNode *closestNode1 = 0, *closestNode2 = 0;
double minDist = 1e100;
for ( size_t iE = 0; iE < loop.myLinks.size(); ++iE )
{
{
theLoops.AddNewLoop();
theLoops.AddEdge( myLinks[0] );
- theLoops.AddEdge( myLinks[1] );
- theLoops.AddEdge( myLinks[2] );
+ if ( myLinks[0].myNode2 == myLinks[1].myNode1 )
+ {
+ theLoops.AddEdge( myLinks[1] );
+ theLoops.AddEdge( myLinks[2] );
+ }
+ else
+ {
+ theLoops.AddEdge( myLinks[2] );
+ theLoops.AddEdge( myLinks[1] );
+ }
return;
}
while ( !theLoops.AllEdgesUsed() )
{
- theLoops.AddNewLoop();
+ EdgeLoop& loop = theLoops.AddNewLoop();
// add 1st edge to a new loop
size_t i1;
// choose among candidates
if ( theLoops.myCandidates.size() == 0 )
{
- theLoops.GetLoopOf( lastEdge )->myHasPending = true;
+ loop.myHasPending = bool( twinEdge );
lastEdge = twinEdge;
}
else if ( theLoops.myCandidates.size() == 1 )
}
while ( lastNode != firstNode );
+
+ if ( twinEdge == & myLinks[ i1 ])
+ loop.myHasPending = true;
+
} // while ( !theLoops.AllEdgesUsed() )
return;
const double theSign,
const std::vector< gp_XYZ >& theNormals,
std::vector< EdgePart >& theCutOffLinks,
- TLinkMap& theCutOffCoplanarLinks) const
+ TLinkMap& /*theCutOffCoplanarLinks*/) const
{
EdgePart sideEdge;
+ boost::container::flat_set< const SMDS_MeshElement* > checkedCoplanar;
+
for ( size_t i = 0; i < myLinks.size(); ++i )
{
if ( !myLinks[i].myFace )
loop = theLoops.GetLoopOf( twin );
toErase = ( loop && !loop->myLinks.empty() );
}
+
+ if ( toErase ) // do not erase if cutFace is connected to a co-planar cutFace
+ {
+ checkedCoplanar.clear();
+ for ( size_t iE = 0; iE < myLinks.size() && toErase; ++iE )
+ {
+ if ( !myLinks[iE].myFace || myLinks[iE].myIndex != EdgePart::_COPLANAR )
+ continue;
+ bool isAdded = checkedCoplanar.insert( myLinks[iE].myFace ).second;
+ if ( !isAdded )
+ continue;
+ toErase = ( SMESH_MeshAlgos::NbCommonNodes( myLinks[i ].myFace,
+ myLinks[iE].myFace ) < 1 );
+ }
+ }
}
if ( toErase )
{
if ( !loop->myLinks[ iE ]->myFace &&
!loop->myLinks[ iE ]->IsInternal() )// &&
- // !loop->myLinks[ iE ]->myNode1->isMarked() && // cut nodes are marked
- // !loop->myLinks[ iE ]->myNode2->isMarked() )
+ // !loop->myLinks[ iE ]->myNode1->isMarked() && // cut nodes are marked
+ // !loop->myLinks[ iE ]->myNode2->isMarked() )
{
int i = loop->myLinks[ iE ]->myIndex;
sideEdge.Set( myInitFace->GetNode ( i ),
if ( myIndex + e.myIndex == _COPLANAR + _INTERNAL )
{
//check if the faces are connected
- int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e.myFace, myFace ).size();
+ int nbCommonNodes = 0;
+ if ( e.myFace && myFace )
+ nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e.myFace, myFace );
bool toReplace = (( myIndex == _INTERNAL && nbCommonNodes > 1 ) ||
( myIndex == _COPLANAR && nbCommonNodes < 2 ));
if ( toReplace )
{
p.Set( nodes[i] );
double dist = ( pPrev - p ).SquareModulus();
- if ( dist > std::numeric_limits<double>::min() )
+ if ( dist < minNodeDist && dist > std::numeric_limits<double>::min() )
minNodeDist = dist;
pPrev = p;
}
// mark all new nodes located closer than theOffset from theSrcMesh
}
+ removeSmallFaces( newMesh, theNew2OldFaces, tol*tol );
+
// ==================================================
// find self-intersections of new faces and fix them
// ==================================================
Intersector intersector( newMesh, tol, normals );
std::vector< const SMDS_MeshElement* > closeFaces;
- std::vector< const SMDS_MeshNode* > faceNodes;
+ std::vector< SMESH_NodeXYZ > faceNodes;
Bnd_B3d faceBox;
+
for ( size_t iF = 1; iF < theNew2OldFaces.size(); ++iF )
{
const SMDS_MeshElement* newFace = theNew2OldFaces[iF].first;
closeFaces.clear();
faceBox.Clear();
for ( size_t i = 0; i < faceNodes.size(); ++i )
- faceBox.Add( SMESH_NodeXYZ( faceNodes[i] ));
+ faceBox.Add( faceNodes[i] );
faceBox.Enlarge( tol );
fSearcher->GetElementsInBox( faceBox, SMDSAbs_Face, closeFaces );
// do not intersect connected faces if they have no concave nodes
int nbCommonNodes = 0;
for ( size_t iN = 0; iN < faceNodes.size(); ++iN )
- nbCommonNodes += ( closeFace->GetNodeIndex( faceNodes[iN] ) >= 0 );
+ nbCommonNodes += ( closeFace->GetNodeIndex( faceNodes[iN].Node() ) >= 0 );
if ( !isConcaveNode1 )
{
break;
if ( !isConcaveNode2 && nbCommonNodes > 0 )
- continue;
+ {
+ if ( normals[ newFace->GetID() ] * normals[ closeFace->GetID() ] < 1.0 )
+ continue; // not co-planar
+ }
}
intersector.Cut( newFace, closeFace, nbCommonNodes );
}
}
- intersector.MakeNewFaces( theNew2OldFaces, theNew2OldNodes, sign );
+ intersector.MakeNewFaces( theNew2OldFaces, theNew2OldNodes, sign, /*optimize=*/true );
return newMesh;
}