1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 // File : SMESH_Offset.cxx
23 // Created : Mon Dec 25 15:52:38 2017
24 // Author : Edward AGAPOV (eap)
26 #include "SMESH_MeshAlgos.hxx"
28 #include <SMDS_PolygonalFaceOfNodes.hxx>
29 #include "SMDS_Mesh.hxx"
31 #include <Utils_SALOME_Exception.hxx>
33 #include <Bnd_B3d.hxx>
34 #include <NCollection_Map.hxx>
38 #include <boost/container/flat_set.hpp>
39 #include <boost/dynamic_bitset.hpp>
43 const int theMaxNbFaces = 256; // max number of faces sharing a node
45 typedef NCollection_DataMap< Standard_Address, const SMDS_MeshNode* > TNNMap;
46 typedef NCollection_Map< SMESH_Link, SMESH_Link > TLinkMap;
48 //--------------------------------------------------------------------------------
50 * \brief Intersected face side storing a node created at this intersection
51 * and a intersected face
56 const SMDS_MeshNode* myNode[2]; // side nodes
57 mutable SMESH_NodeXYZ myIntNode; // intersection node
58 const SMDS_MeshElement* myFace; // cutter face
59 int myIndex; // index of a node on the same link
61 CutLink(const SMDS_MeshNode* node1=0,
62 const SMDS_MeshNode* node2=0,
63 const SMDS_MeshElement* face=0,
64 const int index=0) { Set ( node1, node2, face, index ); }
66 void Set( const SMDS_MeshNode* node1,
67 const SMDS_MeshNode* node2,
68 const SMDS_MeshElement* face,
71 myNode[0] = node1; myNode[1] = node2; myFace = face; myIndex = index; myReverse = false;
72 if ( myNode[0] && ( myReverse = ( myNode[0]->GetID() > myNode[1]->GetID() )))
73 std::swap( myNode[0], myNode[1] );
75 const SMDS_MeshNode* IntNode() const { return myIntNode.Node(); }
76 const SMDS_MeshNode* Node1() const { return myNode[ myReverse ]; }
77 const SMDS_MeshNode* Node2() const { return myNode[ !myReverse ]; }
79 static Standard_Integer HashCode(const CutLink& link,
80 const Standard_Integer upper)
82 Standard_Integer n = ( link.myNode[0]->GetID() +
83 link.myNode[1]->GetID() +
85 return ::HashCode( n, upper );
87 static Standard_Boolean IsEqual(const CutLink& link1, const CutLink& link2 )
89 return ( link1.myNode[0] == link2.myNode[0] &&
90 link1.myNode[1] == link2.myNode[1] &&
91 link1.myIndex == link2.myIndex );
95 typedef NCollection_Map< CutLink, CutLink > TCutLinkMap;
97 //--------------------------------------------------------------------------------
99 * \brief Part of a divided face edge
103 const SMDS_MeshNode* myNode1;
104 const SMDS_MeshNode* myNode2;
105 int myIndex; // positive -> side index, negative -> State
106 const SMDS_MeshElement* myFace;
108 enum State { _INTERNAL = -1, _COPLANAR = -2 };
110 void Set( const SMDS_MeshNode* Node1,
111 const SMDS_MeshNode* Node2,
112 const SMDS_MeshElement* Face = 0,
113 int EdgeIndex = _INTERNAL )
114 { myNode1 = Node1; myNode2 = Node2; myIndex = EdgeIndex; myFace = Face; }
116 // bool HasSameNode( const EdgePart& other ) { return ( myNode1 == other.myNode1 ||
117 // myNode1 == other.myNode2 ||
118 // myNode2 == other.myNode1 ||
119 // myNode2 == other.myNode2 );
121 bool IsInternal() const { return myIndex < 0; }
122 bool IsTwin( const EdgePart& e ) const { return myNode1 == e.myNode2 && myNode2 == e.myNode1; }
123 bool IsSame( const EdgePart& e ) const {
124 return (( myNode1 == e.myNode2 && myNode2 == e.myNode1 ) ||
125 ( myNode1 == e.myNode1 && myNode2 == e.myNode2 )); }
126 bool ReplaceCoplanar( const EdgePart& e );
127 operator SMESH_Link() const { return SMESH_Link( myNode1, myNode2 ); }
128 operator gp_Vec() const { return SMESH_NodeXYZ( myNode2 ) - SMESH_NodeXYZ( myNode1 ); }
131 //--------------------------------------------------------------------------------
133 * \brief Loop of EdgePart's forming a new face which is a part of CutFace
135 struct EdgeLoop : public SMDS_PolygonalFaceOfNodes
137 std::vector< const EdgePart* > myLinks;
138 bool myIsBndConnected; //!< is there a path to CutFace side edges
139 bool myHasPending; //!< an edge encounters twice
141 EdgeLoop() : SMDS_PolygonalFaceOfNodes( std::vector<const SMDS_MeshNode *>() ) {}
142 void Clear() { myLinks.clear(); myIsBndConnected = false; myHasPending = false; }
143 bool SetConnected() { bool was = myIsBndConnected; myIsBndConnected = true; return !was; }
144 bool Contains( const SMDS_MeshNode* n ) const
146 for ( size_t i = 0; i < myLinks.size(); ++i )
147 if ( myLinks[i]->myNode1 == n ) return true;
150 virtual int NbNodes() const { return myLinks.size(); }
151 virtual SMDS_ElemIteratorPtr nodesIterator() const
153 return setNodes(), SMDS_PolygonalFaceOfNodes::nodesIterator();
155 virtual SMDS_NodeIteratorPtr nodeIterator() const
157 return setNodes(), SMDS_PolygonalFaceOfNodes::nodeIterator();
159 void setNodes() const //!< set nodes to SMDS_PolygonalFaceOfNodes
161 EdgeLoop* me = const_cast<EdgeLoop*>( this );
162 me->myNodes.resize( NbNodes() );
164 for ( size_t i = 1; i < myNodes.size(); ++i ) {
165 if ( myLinks[ i ]->myNode1->GetID() < myLinks[ iMin ]->myNode1->GetID() )
168 for ( size_t i = 0; i < myNodes.size(); ++i )
169 me->myNodes[ i ] = myLinks[ ( iMin + i ) % myNodes.size() ]->myNode1;
173 //--------------------------------------------------------------------------------
175 * \brief Set of EdgeLoop's constructed from a CutFace
179 std::vector< EdgeLoop > myLoops; //!< buffer of EdgeLoop's
180 size_t myNbLoops; //!< number of constructed loops
182 const EdgePart* myEdge0; //!< & CutFace.myLinks[0]
183 size_t myNbUsedEdges; //!< nb of EdgePart's added to myLoops
184 boost::dynamic_bitset<> myIsUsedEdge; //!< is i-th EdgePart of CutFace is in any EdgeLoop
185 std::vector< EdgeLoop* > myLoopOfEdge; //!< EdgeLoop of CutFace.myLinks[i]
186 std::vector< EdgePart* > myCandidates; //!< EdgePart's starting at the same node
188 EdgeLoopSet(): myLoops(100) {}
190 void Init( const std::vector< EdgePart >& edges )
192 size_t nb = edges.size();
193 myEdge0 = & edges[0];
196 myIsUsedEdge.reset();
197 myIsUsedEdge.resize( nb, false );
198 myLoopOfEdge.clear();
199 myLoopOfEdge.resize( nb, (EdgeLoop*) 0 );
201 EdgeLoop& AddNewLoop()
203 if ( ++myNbLoops >= myLoops.size() )
204 myLoops.resize( myNbLoops + 10 );
205 myLoops[ myNbLoops-1 ].Clear();
206 return myLoops[ myNbLoops-1 ];
208 bool AllEdgesUsed() const { return myNbUsedEdges == myLoopOfEdge.size(); }
210 bool AddEdge( EdgePart& edge )
212 size_t i = Index( edge );
213 if ( myIsUsedEdge[ i ])
215 myLoops[ myNbLoops-1 ].myLinks.push_back( &edge );
216 myLoopOfEdge[ i ] = & myLoops[ myNbLoops-1 ];
217 myIsUsedEdge[ i ] = true;
221 void Erase( EdgeLoop* loop )
223 for ( size_t iE = 0; iE < loop->myLinks.size(); ++iE )
224 myLoopOfEdge[ Index( *loop->myLinks[ iE ] )] = 0;
227 size_t Index( const EdgePart& edge ) const { return &edge - myEdge0; }
228 EdgeLoop* GetLoopOf( const EdgePart* edge ) { return myLoopOfEdge[ Index( *edge )]; }
231 //--------------------------------------------------------------------------------
233 * \brief Intersections of a face
237 mutable std::vector< EdgePart > myLinks;
238 const SMDS_MeshElement* myInitFace;
240 CutFace( const SMDS_MeshElement* face ): myInitFace( face ) {}
241 void AddEdge( const CutLink& p1,
243 const SMDS_MeshElement* cutter,
245 const int iNotOnPlane = -1) const;
246 void AddPoint( const CutLink& p1, const CutLink& p2, double tol ) const;
247 bool ReplaceNodes( const TNNMap& theRm2KeepMap ) const;
249 int NbInternalEdges() const;
250 void MakeLoops( EdgeLoopSet& loops, const gp_XYZ& theFaceNorm ) const;
251 bool RemoveInternalLoops( EdgeLoopSet& theLoops ) const;
252 void CutOffLoops( EdgeLoopSet& theLoops,
253 const double theSign,
254 const std::vector< gp_XYZ >& theNormals,
255 std::vector< EdgePart >& theCutOffLinks,
256 TLinkMap& theCutOffCoplanarLinks) const;
257 void InitLinks() const;
258 bool IsCoplanar( const EdgePart* edge ) const;
260 static Standard_Integer HashCode(const CutFace& f, const Standard_Integer upper)
262 return ::HashCode( f.myInitFace->GetID(), upper );
264 static Standard_Boolean IsEqual(const CutFace& f1, const CutFace& f2 )
266 return f1.myInitFace == f2.myInitFace;
272 EdgePart* getTwin( const EdgePart* edge ) const;
275 typedef NCollection_Map< CutFace, CutFace > TCutFaceMap;
277 //--------------------------------------------------------------------------------
279 * \brief Intersection point of two edges of co-planar triangles
283 size_t myEdgeInd[2]; //!< edge indices of triangles
284 double myU [2]; //!< parameter [0,1] on edges of triangles
285 SMESH_NodeXYZ myNode; //!< intersection node
286 bool myIsCollinear;//!< edges are collinear
288 IntPoint2D() : myIsCollinear( false ) {}
290 void InitLink( CutLink& link, int iFace, const std::vector< SMESH_NodeXYZ >& nodes ) const
292 link.Set( nodes[ myEdgeInd[ iFace ] ].Node(),
293 nodes[( myEdgeInd[ iFace ] + 1 ) % nodes.size() ].Node(),
295 link.myIntNode = myNode;
297 const SMDS_MeshNode* Node() const { return myNode.Node(); }
299 struct IntPoint2DCompare
302 IntPoint2DCompare( int iFace=0 ): myI( iFace ) {}
303 bool operator() ( const IntPoint2D* ip1, const IntPoint2D* ip2 ) const
305 return ip1->myU[ myI ] < ip2->myU[ myI ];
307 bool operator() ( const IntPoint2D& ip1, const IntPoint2D& ip2 ) const
309 return ip1.myU[ myI ] < ip2.myU[ myI ];
312 typedef boost::container::flat_set< IntPoint2D, IntPoint2DCompare > TIntPointSet;
313 typedef boost::container::flat_set< IntPoint2D*, IntPoint2DCompare > TIntPointPtrSet;
315 //--------------------------------------------------------------------------------
317 * \brief Face used to find translated position of the node
321 const SMDS_MeshElement* myFace;
322 SMESH_TNodeXYZ myNode1; //!< nodes neighboring another node of myFace
323 SMESH_TNodeXYZ myNode2;
324 const gp_XYZ* myNorm;
325 bool myNodeRightOrder;
326 void operator=(const SMDS_MeshElement* f) { myFace = f; }
327 const SMDS_MeshElement* operator->() { return myFace; }
328 void SetNodes( int i0, int i1 ) //!< set myNode's
330 myNode1.Set( myFace->GetNode( i1 ));
331 int i2 = ( i0 - 1 + myFace->NbCornerNodes() ) % myFace->NbCornerNodes();
333 i2 = ( i0 + 1 ) % myFace->NbCornerNodes();
334 myNode2.Set( myFace->GetNode( i2 ));
335 myNodeRightOrder = ( Abs( i2-i1 ) == 1 ) ? i2 > i1 : i2 < i1;
337 void SetOldNodes( const SMDS_Mesh& theSrcMesh )
339 myNode1.Set( theSrcMesh.FindNode( myNode1->GetID() ));
340 myNode2.Set( theSrcMesh.FindNode( myNode2->GetID() ));
342 bool SetNormal( const std::vector< gp_XYZ >& faceNormals )
344 myNorm = & faceNormals[ myFace->GetID() ];
345 return ( myNorm->SquareModulus() > gp::Resolution() * gp::Resolution() );
347 const gp_XYZ& Norm() const { return *myNorm; }
350 //--------------------------------------------------------------------------------
352 * \brief Offset plane used to find translated position of the node
359 gp_Lin myLines[2]; //!< line of intersection with neighbor OffsetPlane's
363 void Init( const gp_XYZ& node, Face& tria, double offset )
367 myPln = gp_Pln( node + tria.Norm() * offset, tria.Norm() );
368 myIsLineOk[0] = myIsLineOk[1] = false;
369 myWeight[0] = myWeight[1] = 0;
371 bool ComputeIntersectionLine( OffsetPlane& pln );
372 void SetSkewLine( const gp_Lin& line );
373 gp_XYZ GetCommonPoint( int & nbOkPoints, double& sumWeight );
374 gp_XYZ ProjectNodeOnLine( int & nbOkPoints );
375 double Weight() const { return myWeight[0] + myWeight[1]; }
378 //================================================================================
380 * \brief Set the second line
382 //================================================================================
384 void OffsetPlane::SetSkewLine( const gp_Lin& line )
387 gp_XYZ n = myLines[0].Direction().XYZ() ^ myLines[1].Direction().XYZ();
388 if (( myIsLineOk[1] = n.SquareModulus() > gp::Resolution() ))
389 myPln = gp_Pln( myPln.Location(), n );
392 //================================================================================
394 * \brief Project myNode on myLine[0]
396 //================================================================================
398 gp_XYZ OffsetPlane::ProjectNodeOnLine( int & nbOkPoints )
400 gp_XYZ p = gp::Origin().XYZ();
403 gp_Vec l2n( myLines[0].Location(), myNode );
404 double u = l2n * myLines[0].Direction();
405 p = myLines[0].Location().XYZ() + u * myLines[0].Direction().XYZ();
411 //================================================================================
413 * \brief Computes intersection point of myLines
415 //================================================================================
417 gp_XYZ OffsetPlane::GetCommonPoint( int & nbOkPoints, double& sumWeight )
419 if ( !myIsLineOk[0] || !myIsLineOk[1] )
421 // sumWeight += myWeight[0];
422 // return ProjectNodeOnLine( nbOkPoints ) * myWeight[0];
423 return gp::Origin().XYZ();
428 gp_Vec lPerp0 = myLines[0].Direction().XYZ() ^ myPln.Axis().Direction().XYZ();
429 double dot01 = lPerp0 * myLines[1].Direction().XYZ();
430 if ( Abs( dot01 ) > 0.05 )
432 gp_Vec l0l1 = myLines[1].Location().XYZ() - myLines[0].Location().XYZ();
433 double u1 = - ( lPerp0 * l0l1 ) / dot01;
434 p = ( myLines[1].Location().XYZ() + myLines[1].Direction().XYZ() * u1 );
438 gp_Vec lv0( myLines[0].Location(), myNode), lv1(myLines[1].Location(), myNode );
439 double dot0( lv0 * myLines[0].Direction() ), dot1( lv1 * myLines[1].Direction() );
440 p = 0.5 * ( myLines[0].Location().XYZ() + myLines[0].Direction().XYZ() * dot0 );
441 p += 0.5 * ( myLines[1].Location().XYZ() + myLines[1].Direction().XYZ() * dot1 );
444 sumWeight += Weight();
450 //================================================================================
452 * \brief Compute line of intersection of 2 planes
454 //================================================================================
456 bool OffsetPlane::ComputeIntersectionLine( OffsetPlane& theNextPln )
458 const gp_XYZ& n1 = myFace->Norm();
459 const gp_XYZ& n2 = theNextPln.myFace->Norm();
461 gp_XYZ lineDir = n1 ^ n2;
464 double x = Abs( lineDir.X() );
465 double y = Abs( lineDir.Y() );
466 double z = Abs( lineDir.Z() );
468 int cooMax; // max coordinate
470 if (x > z) cooMax = 1;
474 if (y > z) cooMax = 2;
479 if ( Abs( lineDir.Coord( cooMax )) < 0.05 )
481 // parallel planes - intersection is an offset of the common edge
482 linePos = 0.5 * ( myPln.Location().XYZ() + theNextPln.myPln.Location().XYZ() );
483 lineDir = myNode - myFace->myNode2;
489 // the constants in the 2 plane equations
490 double d1 = - ( n1 * myPln.Location().XYZ() );
491 double d2 = - ( n2 * theNextPln.myPln.Location().XYZ() );
496 linePos.SetY(( d2*n1.Z() - d1*n2.Z()) / lineDir.X() );
497 linePos.SetZ(( d1*n2.Y() - d2*n1.Y()) / lineDir.X() );
500 linePos.SetX(( d1*n2.Z() - d2*n1.Z()) / lineDir.Y() );
502 linePos.SetZ(( d2*n1.X() - d1*n2.X()) / lineDir.Y() );
505 linePos.SetX(( d2*n1.Y() - d1*n2.Y()) / lineDir.Z() );
506 linePos.SetY(( d1*n2.X() - d2*n1.X()) / lineDir.Z() );
509 myWeight[0] = lineDir.SquareModulus();
511 myWeight[0] = 2. - myWeight[0];
513 myLines [ 0 ].SetDirection( lineDir );
514 myLines [ 0 ].SetLocation ( linePos );
515 myIsLineOk[ 0 ] = ok;
517 theNextPln.myLines [ 1 ] = myLines[ 0 ];
518 theNextPln.myIsLineOk[ 1 ] = ok;
519 theNextPln.myWeight [ 1 ] = myWeight[ 0 ];
524 //================================================================================
526 * \brief Return a translated position of a node
527 * \param [in] new2OldNodes - new and old nodes
528 * \param [in] faceNormals - normals to input faces
529 * \param [in] theSrcMesh - initial mesh
530 * \param [in] theNewPos - a computed normal
531 * \return bool - true if theNewPos is computed
533 //================================================================================
535 bool getTranslatedPosition( const SMDS_MeshNode* theNewNode,
536 const double theOffset,
538 const double theSign,
539 const std::vector< gp_XYZ >& theFaceNormals,
540 SMDS_Mesh& theSrcMesh,
543 bool useOneNormal = true;
545 // check if theNewNode needs an average position, i.e. theNewNode is convex
546 // SMDS_ElemIteratorPtr faceIt = theNewNode->GetInverseElementIterator();
547 // const SMDS_MeshElement* f0 = faceIt->next();
548 // const gp_XYZ& norm0 = theFaceNormals[ f0->GetID() ];
549 // const SMESH_NodeXYZ nodePos = theNewNode;
550 // while ( faceIt->more() )
552 // const SMDS_MeshElement* f = faceIt->next();
553 // const int nodeInd = f->GetNodeIndex( theNewNode );
554 // SMESH_NodeXYZ nodePos2 = f->GetWrapNode( nodeInd + 1 );
556 // const gp_XYZ nnDir = ( nodePos2 - nodePos ).Normalized();
561 // const double dot = norm0 * nnDir;
566 // get faces surrounding theNewNode and sort them
567 Face faces[ theMaxNbFaces ];
568 SMDS_ElemIteratorPtr faceIt = theNewNode->GetInverseElementIterator();
569 faces[0] = faceIt->next();
570 while ( !faces[0].SetNormal( theFaceNormals ) && faceIt->more() )
571 faces[0] = faceIt->next();
572 int i0 = faces[0]->GetNodeIndex( theNewNode );
573 int i1 = ( i0 + 1 ) % faces[0]->NbCornerNodes();
574 faces[0].SetNodes( i0, i1 );
575 TIDSortedElemSet elemSet, avoidSet;
577 const SMDS_MeshElement* f;
578 for ( ; faceIt->more() && iFace < theMaxNbFaces; faceIt->next() )
580 avoidSet.insert( faces[ iFace ].myFace );
581 f = SMESH_MeshAlgos::FindFaceInSet( theNewNode, faces[ iFace ].myNode2.Node(),
582 elemSet, avoidSet, &i0, &i1 );
585 std::reverse( &faces[0], &faces[0] + iFace + 1 );
586 for ( int i = 0; i <= iFace; ++i )
588 std::swap( faces[i].myNode1, faces[i].myNode2 );
589 faces[i].myNodeRightOrder = !faces[i].myNodeRightOrder;
591 f = SMESH_MeshAlgos::FindFaceInSet( theNewNode, faces[ iFace ].myNode2.Node(),
592 elemSet, avoidSet, &i0, &i1 );
596 faces[ ++iFace ] = f;
597 faces[ iFace ].SetNodes( i0, i1 );
598 faces[ iFace ].SetNormal( theFaceNormals );
600 int nbFaces = iFace + 1;
602 theNewPos.SetCoord( 0, 0, 0 );
603 gp_XYZ oldXYZ = SMESH_NodeXYZ( theNewNode );
605 // check if all faces are co-planar
606 bool isPlanar = true;
607 const double tol = 1e-2;
608 for ( int i = 1; i < nbFaces && isPlanar; ++i )
609 isPlanar = ( faces[i].Norm() - faces[i-1].Norm() ).SquareModulus() < tol*tol;
613 theNewPos = oldXYZ + faces[0].Norm() * theOffset;
617 // prepare OffsetPlane's
618 OffsetPlane pln[ theMaxNbFaces ];
619 for ( int i = 0; i < nbFaces; ++i )
621 faces[i].SetOldNodes( theSrcMesh );
622 pln[i].Init( oldXYZ, faces[i], theOffset );
624 // intersect neighboring OffsetPlane's
626 for ( int i = 1; i < nbFaces; ++i )
627 nbOkPoints += pln[ i-1 ].ComputeIntersectionLine( pln[ i ]);
628 nbOkPoints += pln[ nbFaces-1 ].ComputeIntersectionLine( pln[ 0 ]);
630 // move intersection lines to over parallel planes
631 if ( nbOkPoints > 1 )
632 for ( int i = 0; i < nbFaces; ++i )
633 if ( pln[i].myIsLineOk[0] && !pln[i].myIsLineOk[1] )
634 for ( int j = 1; j < nbFaces && !pln[i].myIsLineOk[1]; ++j )
636 int i2 = ( i + j ) % nbFaces;
637 if ( pln[i2].myIsLineOk[0] )
638 pln[i].SetSkewLine( pln[i2].myLines[0] );
641 // get the translated position
643 double sumWegith = 0;
644 const double minWeight = Sin( 30 * M_PI / 180. ) * Sin( 30 * M_PI / 180. );
645 for ( int i = 0; i < nbFaces; ++i )
646 if ( pln[ i ].Weight() > minWeight )
647 theNewPos += pln[ i ].GetCommonPoint( nbOkPoints, sumWegith );
649 if ( nbOkPoints == 0 )
651 // there is only one feature edge;
652 // find the theNewPos by projecting oldXYZ to any intersection line
653 for ( int i = 0; i < nbFaces; ++i )
654 theNewPos += pln[ i ].ProjectNodeOnLine( nbOkPoints );
656 if ( nbOkPoints == 0 )
658 theNewPos = oldXYZ + faces[0].Norm() * theOffset;
661 sumWegith = nbOkPoints;
663 theNewPos /= sumWegith;
666 // mark theNewNode if it is concave
667 useOneNormal = false;
668 gp_Vec moveVec( oldXYZ, theNewPos );
669 for ( int i = 0, iPrev = nbFaces-1; i < nbFaces; iPrev = i++ )
671 gp_Vec nodeVec( oldXYZ, faces[ i ].myNode1 );
672 double u = ( moveVec * nodeVec ) / nodeVec.SquareMagnitude();
673 if ( u > 0.5 ) // param [0,1] on nodeVec
675 theNewNode->setIsMarked( true );
679 gp_XYZ inFaceVec = faces[ i ].Norm() ^ nodeVec.XYZ();
680 double dot = inFaceVec * faces[ iPrev ].Norm();
681 if ( !faces[ i ].myNodeRightOrder )
683 if ( dot * theSign < 0 )
686 // gp_XYZ p1 = oldXYZ + faces[ i ].Norm() * theOffset;
687 // gp_XYZ p2 = oldXYZ + faces[ iPrev ].Norm() * theOffset;
688 // useOneNormal = ( p1 - p2 ).SquareModulus() > theTol * theTol;
691 if ( useOneNormal && theNewNode->isMarked() )
698 //--------------------------------------------------------------------------------
700 * \brief Intersect faces of a mesh
706 const std::vector< gp_XYZ >& myNormals;
707 TCutLinkMap myCutLinks; //!< assure sharing of new nodes
708 TCutFaceMap myCutFaces;
709 TNNMap myRemove2KeepNodes; //!< node merge map
711 // data to intersect 2 faces
712 const SMDS_MeshElement* myFace1;
713 const SMDS_MeshElement* myFace2;
714 std::vector< SMESH_NodeXYZ > myNodes1, myNodes2;
715 std::vector< double > myDist1, myDist2;
716 int myInd1, myInd2; // coordinate indices on an axis-aligned plane
717 int myNbOnPlane1, myNbOnPlane2;
718 TIntPointSet myIntPointSet;
720 Intersector( SMDS_Mesh* mesh, double tol, const std::vector< gp_XYZ >& normals )
724 //myEps( Sqrt( std::numeric_limits<double>::min() )),
725 //myEps( gp::Resolution() ),
728 void Cut( const SMDS_MeshElement* face1,
729 const SMDS_MeshElement* face2,
730 const int nbCommonNodes );
731 void MakeNewFaces( SMESH_MeshAlgos::TEPairVec& theNew2OldFaces,
732 SMESH_MeshAlgos::TNPairVec& theNew2OldNodes,
733 const double theSign );
737 bool isPlaneIntersected( const gp_XYZ& n2,
739 const std::vector< SMESH_NodeXYZ >& nodes1,
740 std::vector< double > & dist1,
743 void computeIntervals( const std::vector< SMESH_NodeXYZ >& nodes,
744 const std::vector< double >& dist,
750 void addLink ( CutLink& link );
751 bool findLink( CutLink& link );
752 bool coincide( const gp_XYZ& p1, const gp_XYZ& p2, const double tol ) const
754 return ( p1 - p2 ).SquareModulus() < tol * tol;
756 gp_XY p2D( const gp_XYZ& p ) const { return gp_XY( p.Coord( myInd1 ), p.Coord( myInd2 )); }
758 void intersectLink( const std::vector< SMESH_NodeXYZ >& nodes1,
759 const std::vector< double > & dist1,
761 const SMDS_MeshElement* face2,
763 void findIntPointOnPlane( const std::vector< SMESH_NodeXYZ >& nodes,
764 const std::vector< double > & dist,
766 void replaceIntNode( const SMDS_MeshNode* nToKeep, const SMDS_MeshNode* nToRemove );
767 void computeIntPoint( const double u1,
772 const SMDS_MeshNode* & node1,
773 const SMDS_MeshNode* & node2);
774 void cutCollinearLink( const int iNotOnPlane1,
775 const std::vector< SMESH_NodeXYZ >& nodes1,
776 const SMDS_MeshElement* face2,
777 const CutLink& link1,
778 const CutLink& link2);
779 void setPlaneIndices( const gp_XYZ& planeNorm );
780 bool intersectEdgeEdge( const gp_XY s1p0, const gp_XY s1p1,
781 const gp_XY s2p0, const gp_XY s2p1,
782 double & t1, double & t2,
783 bool & isCollinear );
784 bool intersectEdgeEdge( int iE1, int iE2, IntPoint2D& intPoint );
785 bool isPointInTriangle( const gp_XYZ& p, const std::vector< SMESH_NodeXYZ >& nodes );
786 void intersectNewEdges( const CutFace& theCFace );
787 const SMDS_MeshNode* createNode( const gp_XYZ& p );
790 //================================================================================
792 * \brief Return coordinate index with maximal abs value
794 //================================================================================
796 int MaxIndex( const gp_XYZ& x )
798 int iMaxCoo = ( Abs( x.X()) < Abs( x.Y() )) + 1;
799 if ( Abs( x.Coord( iMaxCoo )) < Abs( x.Z() ))
803 //================================================================================
805 * \brief Store a CutLink
807 //================================================================================
809 const SMDS_MeshNode* Intersector::createNode( const gp_XYZ& p )
811 const SMDS_MeshNode* n = myMesh->AddNode( p.X(), p.Y(), p.Z() );
812 n->setIsMarked( true ); // cut nodes are marked
816 //================================================================================
818 * \brief Store a CutLink
820 //================================================================================
822 void Intersector::addLink( CutLink& link )
825 const CutLink* added = & myCutLinks.Added( link );
826 while ( added->myIntNode.Node() != link.myIntNode.Node() )
828 if ( !added->myIntNode )
830 added->myIntNode = link.myIntNode;
836 added = & myCutLinks.Added( link );
842 //================================================================================
844 * \brief Find a CutLink with an intersection point coincident with that of a given link
846 //================================================================================
848 bool Intersector::findLink( CutLink& link )
851 while ( myCutLinks.Contains( link ))
853 const CutLink* added = & myCutLinks.Added( link );
854 if ( !!added->myIntNode && coincide( added->myIntNode, link.myIntNode, myTol ))
856 link.myIntNode = added->myIntNode;
864 //================================================================================
866 * \brief Check if a triangle intersects the plane of another triangle
867 * \param [in] nodes1 - nodes of triangle 1
868 * \param [in] n2 - normal of triangle 2
869 * \param [in] d2 - a constant of the plane equation 2
870 * \param [out] dist1 - distance of nodes1 from the plane 2
871 * \param [out] nbOnPlane - number of nodes1 lying on the plane 2
872 * \return bool - true if the triangle intersects the plane 2
874 //================================================================================
876 bool Intersector::isPlaneIntersected( const gp_XYZ& n2,
878 const std::vector< SMESH_NodeXYZ >& nodes1,
879 std::vector< double > & dist1,
883 iNotOnPlane1 = nbOnPlane1 = 0;
884 dist1.resize( nodes1.size() );
885 for ( size_t i = 0; i < nodes1.size(); ++i )
887 dist1[i] = n2 * nodes1[i] + d2;
888 if ( Abs( dist1[i] ) < myTol )
898 if ( nbOnPlane1 == 0 )
899 for ( size_t i = 0; i < nodes1.size(); ++i )
900 if ( dist1[iNotOnPlane1] * dist1[i] < 0 )
906 //================================================================================
908 * \brief Compute parameters on the plane intersection line of intersections
909 * of edges of a triangle
910 * \param [in] nodes - triangle nodes
911 * \param [in] dist - distance of triangle nodes from the plane of another triangle
912 * \param [in] nbOnPln - number of nodes lying on the plane of another triangle
913 * \param [in] iMaxCoo - index of coordinate of max component of the plane intersection line
914 * \param [out] u - two computed parameters on the plane intersection line
915 * \param [out] iE - indices of intersected edges
917 //================================================================================
919 void Intersector::computeIntervals( const std::vector< SMESH_NodeXYZ >& nodes,
920 const std::vector< double >& dist,
928 u[0] = u[1] = 1e+100;
933 if ( nbOnPln == 1 && ( dist[i1] == 0. || dist[i2] == 0 ))
935 int i = dist[i1] == 0 ? i1 : i2;
936 u [ 1 ] = nodes[ i ].Coord( iMaxCoo );
940 for ( ; i2 < 3 && nb < 2; i1 = i2++ )
942 double dd = dist[i1] - dist[i2];
943 if ( dd != 0. && dist[i2] * dist[i1] <= 0. )
945 double x1 = nodes[i1].Coord( iMaxCoo );
946 double x2 = nodes[i2].Coord( iMaxCoo );
947 u [ nb ] = x1 + ( x2 - x1 ) * dist[i1] / dd;
954 std::swap( u [0], u [1] );
955 std::swap( iE[0], iE[1] );
959 //================================================================================
961 * \brief Try to find an intersection node on a link collinear with the plane intersection line
963 //================================================================================
965 void Intersector::findIntPointOnPlane( const std::vector< SMESH_NodeXYZ >& nodes,
966 const std::vector< double > & dist,
969 int i1 = ( dist[0] == 0 ? 0 : 1 ), i2 = ( dist[2] == 0 ? 2 : 1 );
970 CutLink link2 = link;
971 link2.Set( nodes[i1].Node(), nodes[i2].Node(), 0 );
972 if ( findLink( link2 ))
973 link.myIntNode = link2.myIntNode;
976 //================================================================================
978 * \brief Compute intersection point of a link1 with a face2
980 //================================================================================
982 void Intersector::intersectLink( const std::vector< SMESH_NodeXYZ >& nodes1,
983 const std::vector< double > & dist1,
985 const SMDS_MeshElement* face2,
988 const int iEdge2 = ( iEdge1 + 1 ) % nodes1.size();
989 const SMESH_NodeXYZ& p1 = nodes1[ iEdge1 ];
990 const SMESH_NodeXYZ& p2 = nodes1[ iEdge2 ];
992 link1.Set( p1.Node(), p2.Node(), face2 );
993 const CutLink* link = & myCutLinks.Added( link1 );
994 if ( !link->IntNode() )
996 if ( dist1[ iEdge1 ] == 0. ) link1.myIntNode = p1;
997 else if ( dist1[ iEdge2 ] == 0. ) link1.myIntNode = p2;
1000 gp_XYZ p = p1 + ( p2 - p1 ) * dist1[ iEdge1 ] / ( dist1[ iEdge1 ] - dist1[ iEdge2 ]);
1001 (gp_XYZ&)link1.myIntNode = p;
1006 gp_XYZ p = p1 + ( p2 - p1 ) * dist1[ iEdge1 ] / ( dist1[ iEdge1 ] - dist1[ iEdge2 ]);
1007 while ( link->IntNode() )
1009 if ( coincide( p, link->myIntNode, myTol ))
1011 link1.myIntNode = link->myIntNode;
1015 link = & myCutLinks.Added( link1 );
1017 if ( !link1.IntNode() )
1019 if ( dist1[ iEdge1 ] == 0. ) link1.myIntNode = p1;
1020 else if ( dist1[ iEdge2 ] == 0. ) link1.myIntNode = p2;
1021 else (gp_XYZ&)link1.myIntNode = p;
1026 //================================================================================
1028 * \brief Store node replacement in myCutFaces
1030 //================================================================================
1032 void Intersector::replaceIntNode( const SMDS_MeshNode* nToKeep,
1033 const SMDS_MeshNode* nToRemove )
1035 if ( nToKeep == nToRemove )
1037 if ( nToRemove->GetID() < nToKeep->GetID() ) // keep node with lower ID
1038 myRemove2KeepNodes.Bind((void*) nToKeep, nToRemove );
1040 myRemove2KeepNodes.Bind((void*) nToRemove, nToKeep );
1043 //================================================================================
1045 * \brief Compute intersection point on a link of either of faces by choosing
1046 * a link whose parameter on the intersection line in maximal
1047 * \param [in] u1 - parameter on the intersection line of link iE1 of myFace1
1048 * \param [in] u2 - parameter on the intersection line of link iE2 of myFace2
1049 * \param [in] iE1 - index of a link myFace1
1050 * \param [in] iE2 - index of a link myFace2
1051 * \param [out] link - CutLink storing the intersection point
1052 * \param [out] node1 - a node of the 2nd link if two links intersect
1053 * \param [out] node2 - a node of the 2nd link if two links intersect
1055 //================================================================================
1057 void Intersector::computeIntPoint( const double u1,
1062 const SMDS_MeshNode* & node1,
1063 const SMDS_MeshNode* & node2)
1065 if ( u1 > u2 + myTol )
1067 intersectLink( myNodes1, myDist1, iE1, myFace2, link );
1069 if ( myNbOnPlane2 == 2 )
1070 findIntPointOnPlane( myNodes2, myDist2, link );
1072 else if ( u2 > u1 + myTol )
1074 intersectLink( myNodes2, myDist2, iE2, myFace1, link );
1076 if ( myNbOnPlane1 == 2 )
1077 findIntPointOnPlane( myNodes1, myDist1, link );
1079 else // edges of two faces intersect the line at the same point
1082 intersectLink( myNodes1, myDist1, iE1, myFace2, link );
1083 intersectLink( myNodes2, myDist2, iE2, myFace1, link2 );
1084 node1 = link2.Node1();
1085 node2 = link2.Node2();
1087 if ( !link.IntNode() && link2.IntNode() )
1088 link.myIntNode = link2.myIntNode;
1090 else if ( !link.IntNode() && !link2.IntNode() )
1091 (gp_XYZ&)link.myIntNode = 0.5 * ( link.myIntNode + link2.myIntNode );
1093 else if ( link.IntNode() && link2.IntNode() )
1094 replaceIntNode( link.IntNode(), link2.IntNode() );
1098 //================================================================================
1100 * \brief Add intersections to a link collinear with the intersection line
1102 //================================================================================
1104 void Intersector::cutCollinearLink( const int iNotOnPlane1,
1105 const std::vector< SMESH_NodeXYZ >& nodes1,
1106 const SMDS_MeshElement* face2,
1107 const CutLink& link1,
1108 const CutLink& link2)
1111 int iN1 = ( iNotOnPlane1 + 1 ) % 3;
1112 int iN2 = ( iNotOnPlane1 + 2 ) % 3;
1113 CutLink link( nodes1[ iN1 ].Node(), nodes1[ iN2 ].Node(), face2 );
1114 if ( link1.myFace != face2 )
1116 link.myIntNode = link1.myIntNode;
1119 if ( link2.myFace != face2 )
1121 link.myIntNode = link2.myIntNode;
1126 //================================================================================
1128 * \brief Choose indices on an axis-aligned plane
1130 //================================================================================
1132 void Intersector::setPlaneIndices( const gp_XYZ& planeNorm )
1134 switch ( MaxIndex( planeNorm )) {
1135 case 1: myInd1 = 2; myInd2 = 3; break;
1136 case 2: myInd1 = 3; myInd2 = 1; break;
1137 case 3: myInd1 = 1; myInd2 = 2; break;
1141 //================================================================================
1143 * \brief Intersect two faces
1145 //================================================================================
1147 void Intersector::Cut( const SMDS_MeshElement* face1,
1148 const SMDS_MeshElement* face2,
1149 const int nbCommonNodes)
1153 myNodes1.assign( face1->begin_nodes(), face1->end_nodes() );
1154 myNodes2.assign( face2->begin_nodes(), face2->end_nodes() );
1156 const gp_XYZ& n1 = myNormals[ face1->GetID() ];
1157 const gp_XYZ& n2 = myNormals[ face2->GetID() ];
1159 // check if triangles intersect
1160 int iNotOnPlane1, iNotOnPlane2;
1161 const double d2 = -( n2 * myNodes2[0]);
1162 if ( !isPlaneIntersected( n2, d2, myNodes1, myDist1, myNbOnPlane1, iNotOnPlane1 ))
1164 const double d1 = -( n1 * myNodes1[0]);
1165 if ( !isPlaneIntersected( n1, d1, myNodes2, myDist2, myNbOnPlane2, iNotOnPlane2 ))
1168 if ( myNbOnPlane1 == 3 || myNbOnPlane2 == 3 )// triangles are co-planar
1170 setPlaneIndices( myNbOnPlane1 == 3 ? n2 : n1 ); // choose indices on an axis-aligned plane
1173 else if ( nbCommonNodes < 2 ) // triangle planes intersect
1175 gp_XYZ lineDir = n1 ^ n2; // intersection line
1177 // check if intervals of intersections of triangles with lineDir overlap
1179 double u1[2], u2 [2]; // parameters on lineDir of edge intersection points { minU, maxU }
1180 int iE1[2], iE2[2]; // indices of edges
1181 int iMaxCoo = MaxIndex( lineDir );
1182 computeIntervals( myNodes1, myDist1, myNbOnPlane1, iMaxCoo, u1, iE1 );
1183 computeIntervals( myNodes2, myDist2, myNbOnPlane2, iMaxCoo, u2, iE2 );
1184 if ( u1[1] < u2[0] - myTol || u2[1] < u1[0] - myTol )
1185 return; // intervals do not overlap
1187 // make intersection nodes
1189 const SMDS_MeshNode *l1n1, *l1n2, *l2n1, *l2n2;
1190 CutLink link1; // intersection with smaller u on lineDir
1191 computeIntPoint( u1[0], u2[0], iE1[0], iE2[0], link1, l1n1, l1n2 );
1192 CutLink link2; // intersection with larger u on lineDir
1193 computeIntPoint( -u1[1], -u2[1], iE1[1], iE2[1], link2, l2n1, l2n2 );
1195 const CutFace& cf1 = myCutFaces.Added( CutFace( face1 ));
1196 const CutFace& cf2 = myCutFaces.Added( CutFace( face2 ));
1198 if ( coincide( link1.myIntNode, link2.myIntNode, myTol ))
1200 // intersection is a point
1201 if ( link1.IntNode() && link2.IntNode() )
1202 replaceIntNode( link1.IntNode(), link2.IntNode() );
1204 CutLink* link = link2.IntNode() ? &link2 : &link1;
1205 if ( !link->IntNode() )
1207 gp_XYZ p = 0.5 * ( link1.myIntNode + link2.myIntNode );
1208 link->myIntNode.Set( createNode( p ));
1210 if ( !link1.IntNode() ) link1.myIntNode = link2.myIntNode;
1211 if ( !link2.IntNode() ) link2.myIntNode = link1.myIntNode;
1213 cf1.AddPoint( link1, link2, myTol );
1214 cf2.AddPoint( link1, link2, myTol );
1218 // intersection is a line segment
1219 if ( !link1.IntNode() )
1220 link1.myIntNode.Set( createNode( link1.myIntNode ));
1221 if ( !link2.IntNode() )
1222 link2.myIntNode.Set( createNode( link2.myIntNode ));
1224 cf1.AddEdge( link1, link2, face2, myNbOnPlane1, iNotOnPlane1 );
1225 if ( l1n1 ) link1.Set( l1n1, l1n2, face2 );
1226 if ( l2n1 ) link2.Set( l2n1, l2n2, face2 );
1227 cf2.AddEdge( link1, link2, face1, myNbOnPlane2, iNotOnPlane2 );
1229 // add intersections to a link collinear with the intersection line
1230 if ( myNbOnPlane1 == 2 && ( link1.myFace != face2 || link2.myFace != face2 ))
1231 cutCollinearLink( iNotOnPlane1, myNodes1, face2, link1, link2 );
1233 if ( myNbOnPlane2 == 2 && ( link1.myFace != face1 || link2.myFace != face1 ))
1234 cutCollinearLink( iNotOnPlane2, myNodes2, face1, link1, link2 );
1240 } // non co-planar case
1245 //================================================================================
1247 * \brief Intersect two 2D line segments
1249 //================================================================================
1251 bool Intersector::intersectEdgeEdge( const gp_XY s1p0, const gp_XY s1p1,
1252 const gp_XY s2p0, const gp_XY s2p1,
1253 double & t1, double & t2,
1254 bool & isCollinear )
1256 gp_XY u = s1p1 - s1p0;
1257 gp_XY v = s2p1 - s2p0;
1258 gp_XY w = s1p0 - s2p0;
1259 double perpDotUV = u * gp_XY( -v.Y(), v.X() );
1260 double perpDotVW = v * gp_XY( -w.Y(), w.X() );
1261 double perpDotUW = u * gp_XY( -w.Y(), w.X() );
1262 double u2 = u.SquareModulus();
1263 double v2 = v.SquareModulus();
1264 if ( u2 < myEps * myEps || v2 < myEps * myEps )
1266 if ( perpDotUV * perpDotUV / u2 / v2 < 1e-6 ) // cos ^ 2
1269 return false; // no need in collinear solution
1270 if ( perpDotUW * perpDotUW / u2 > myTol * myTol )
1271 return false; // parallel
1274 gp_XY w2 = s1p1 - s2p0;
1275 if ( Abs( v.X()) + Abs( u.X()) > Abs( v.Y()) + Abs( u.Y())) {
1276 t1 = w.X() / v.X(); // params on segment 2
1277 t2 = w2.X() / v.X();
1281 t2 = w2.Y() / v.Y();
1283 if ( Max( t1,t2 ) <= 0 || Min( t1,t2 ) >= 1 )
1284 return false; // no overlap
1287 isCollinear = false;
1289 t1 = perpDotVW / perpDotUV; // param on segment 1
1290 if ( t1 < 0. || t1 > 1. )
1291 return false; // intersection not within the segment
1293 t2 = perpDotUW / perpDotUV; // param on segment 2
1294 if ( t2 < 0. || t2 > 1. )
1295 return false; // intersection not within the segment
1300 //================================================================================
1302 * \brief Intersect two edges of co-planar triangles
1303 * \param [inout] iE1 - edge index of triangle 1
1304 * \param [inout] iE2 - edge index of triangle 2
1305 * \param [inout] intPoints - intersection points
1306 * \param [inout] nbIntPoints - nb of found intersection points
1308 //================================================================================
1310 bool Intersector::intersectEdgeEdge( int iE1, int iE2, IntPoint2D& intPoint )
1312 int i01 = iE1, i11 = ( iE1 + 1 ) % 3;
1313 int i02 = iE2, i12 = ( iE2 + 1 ) % 3;
1314 if (( !intPoint.myIsCollinear ) &&
1315 ( myNodes1[ i01 ] == myNodes2[ i02 ] ||
1316 myNodes1[ i01 ] == myNodes2[ i12 ] ||
1317 myNodes1[ i11 ] == myNodes2[ i02 ] ||
1318 myNodes1[ i11 ] == myNodes2[ i12 ] ))
1322 gp_XY s1p0 = p2D( myNodes1[ i01 ]);
1323 gp_XY s1p1 = p2D( myNodes1[ i11 ]);
1326 gp_XY s2p0 = p2D( myNodes2[ i02 ]);
1327 gp_XY s2p1 = p2D( myNodes2[ i12 ]);
1330 if ( !intersectEdgeEdge( s1p0,s1p1, s2p0,s2p1, t1, t2, intPoint.myIsCollinear ))
1333 intPoint.myEdgeInd[0] = iE1;
1334 intPoint.myEdgeInd[1] = iE2;
1335 intPoint.myU[0] = t1;
1336 intPoint.myU[1] = t2;
1337 (gp_XYZ&)intPoint.myNode = myNodes1[i01] * ( 1 - t1 ) + myNodes1[i11] * t1;
1339 if ( intPoint.myIsCollinear )
1342 // try to find existing node at intPoint.myNode
1344 if ( myNodes1[ i01 ] == myNodes2[ i02 ] ||
1345 myNodes1[ i01 ] == myNodes2[ i12 ] ||
1346 myNodes1[ i11 ] == myNodes2[ i02 ] ||
1347 myNodes1[ i11 ] == myNodes2[ i12 ] )
1350 const double coincTol = myTol * 1e-3;
1352 CutLink link1( myNodes1[i01].Node(), myNodes1[i11].Node(), myFace2 );
1353 CutLink link2( myNodes2[i02].Node(), myNodes2[i12].Node(), myFace1 );
1355 SMESH_NodeXYZ& n1 = myNodes1[ t1 < 0.5 ? i01 : i11 ];
1356 bool same1 = coincide( n1, intPoint.myNode, coincTol );
1359 link2.myIntNode = intPoint.myNode = n1;
1362 SMESH_NodeXYZ& n2 = myNodes2[ t2 < 0.5 ? i02 : i12 ];
1363 bool same2 = coincide( n2, intPoint.myNode, coincTol );
1366 link1.myIntNode = intPoint.myNode = n2;
1370 replaceIntNode( n1.Node(), n2.Node() );
1378 link1.myIntNode = intPoint.myNode;
1379 if ( findLink( link1 ))
1381 intPoint.myNode = link2.myIntNode = link1.myIntNode;
1386 link2.myIntNode = intPoint.myNode;
1387 if ( findLink( link2 ))
1389 intPoint.myNode = link1.myIntNode = link2.myIntNode;
1394 for ( int is2nd = 0; is2nd < 2; ++is2nd )
1396 const SMDS_MeshElement* f = is2nd ? myFace1 : myFace2;
1398 const CutFace& cf = myCutFaces.Added( CutFace( is2nd ? myFace2 : myFace1 ));
1399 for ( size_t i = 0; i < cf.myLinks.size(); ++i )
1400 if ( cf.myLinks[i].myFace == f &&
1401 //cf.myLinks[i].myIndex != EdgePart::_COPLANAR &&
1402 coincide( intPoint.myNode, SMESH_NodeXYZ( cf.myLinks[i].myNode1 ), coincTol ))
1404 intPoint.myNode.Set( cf.myLinks[i].myNode1 );
1411 intPoint.myNode._node = createNode( intPoint.myNode );
1412 link1.myIntNode = link2.myIntNode = intPoint.myNode;
1420 //================================================================================
1422 * \brief Check if a point is contained in a triangle
1424 //================================================================================
1426 bool Intersector::isPointInTriangle( const gp_XYZ& p, const std::vector< SMESH_NodeXYZ >& nodes )
1429 SMESH_MeshAlgos::GetBarycentricCoords( p2D( p ),
1430 p2D( nodes[0] ), p2D( nodes[1] ), p2D( nodes[2] ),
1432 return ( 0. < bc1 && 0. < bc2 && bc1 + bc2 < 1. );
1435 //================================================================================
1437 * \brief Intersect two co-planar faces
1439 //================================================================================
1441 void Intersector::cutCoplanar()
1443 // find intersections of edges
1445 IntPoint2D intPoints[ 6 ];
1446 int nbIntPoints = 0;
1447 for ( int iE1 = 0; iE1 < 3; ++iE1 )
1449 int maxNbIntPoints = nbIntPoints + 2;
1450 for ( int iE2 = 0; iE2 < 3 && nbIntPoints < maxNbIntPoints; ++iE2 )
1451 nbIntPoints += intersectEdgeEdge( iE1, iE2, intPoints[ nbIntPoints ]);
1453 const int minNbOnPlane = Min( myNbOnPlane1, myNbOnPlane2 );
1455 if ( nbIntPoints == 0 ) // no intersections of edges
1458 if ( isPointInTriangle( myNodes1[0], myNodes2 )) // face2 includes face1
1460 else if ( isPointInTriangle( myNodes2[0], myNodes1 )) // face1 includes face2
1465 // add edges of an inner triangle to an outer one
1467 const std::vector< SMESH_NodeXYZ >& nodesIn = is1in2 ? myNodes1 : myNodes2;
1468 const SMDS_MeshElement* faceOut = is1in2 ? myFace2 : myFace1;
1469 const SMDS_MeshElement* faceIn = is1in2 ? myFace1 : myFace2;
1471 const CutFace& outFace = myCutFaces.Added( CutFace( faceOut ));
1472 CutLink link1( nodesIn.back().Node(), nodesIn.back().Node(), faceOut );
1473 CutLink link2( nodesIn.back().Node(), nodesIn.back().Node(), faceOut );
1475 link1.myIntNode = nodesIn.back();
1476 for ( size_t i = 0; i < nodesIn.size(); ++i )
1478 link2.myIntNode = nodesIn[ i ];
1479 outFace.AddEdge( link1, link2, faceIn, minNbOnPlane );
1480 link1.myIntNode = link2.myIntNode;
1485 // add parts of edges to a triangle including them
1487 CutLink link1, link2;
1488 IntPoint2D ip0, ip1;
1489 ip0.myU[0] = ip0.myU[1] = 0.;
1490 ip1.myU[0] = ip1.myU[1] = 1.;
1491 ip0.myEdgeInd[0] = ip0.myEdgeInd[1] = ip1.myEdgeInd[0] = ip1.myEdgeInd[1] = 0;
1493 for ( int isFromFace1 = 0; isFromFace1 < 2; ++isFromFace1 )
1495 const SMDS_MeshElement* faceTo = isFromFace1 ? myFace2 : myFace1;
1496 const SMDS_MeshElement* faceFrom = isFromFace1 ? myFace1 : myFace2;
1497 const std::vector< SMESH_NodeXYZ >& nodesTo = isFromFace1 ? myNodes2 : myNodes1;
1498 const std::vector< SMESH_NodeXYZ >& nodesFrom = isFromFace1 ? myNodes1 : myNodes2;
1499 const int iTo = isFromFace1 ? 1 : 0;
1500 const int iFrom = isFromFace1 ? 0 : 1;
1501 //const int nbOnPlaneFrom = isFromFace1 ? myNbOnPlane1 : myNbOnPlane2;
1503 const CutFace* cutFaceTo = & myCutFaces.Added( CutFace( faceTo ));
1504 // const CutFace* cutFaceFrom = 0;
1505 // if ( nbOnPlaneFrom > minNbOnPlane )
1506 // cutFaceFrom = & myCutFaces.Added( CutFace( faceTo ));
1508 link1.myFace = link2.myFace = faceTo;
1510 IntPoint2DCompare ipCompare( iFrom );
1511 TIntPointPtrSet pointsOnEdge( ipCompare ); // IntPoint2D sorted by parameter on edge
1513 for ( size_t iE = 0; iE < nodesFrom.size(); ++iE )
1515 // get parts of an edge iE
1517 ip0.myEdgeInd[ iTo ] = iE;
1518 ip1.myEdgeInd[ iTo ] = ( iE + 1 ) % nodesFrom.size();
1519 ip0.myNode = nodesFrom[ ip0.myEdgeInd[ iTo ]];
1520 ip1.myNode = nodesFrom[ ip1.myEdgeInd[ iTo ]];
1522 pointsOnEdge.clear();
1524 for ( int iP = 0; iP < nbIntPoints; ++iP )
1525 if ( intPoints[ iP ].myEdgeInd[ iFrom ] == iE )
1526 pointsOnEdge.insert( & intPoints[ iP ] );
1528 pointsOnEdge.insert( pointsOnEdge.begin(), & ip0 );
1529 pointsOnEdge.insert( pointsOnEdge.end(), & ip1 );
1531 // add edge parts to faceTo
1533 TIntPointPtrSet::iterator ipIt = pointsOnEdge.begin() + 1;
1534 for ( ; ipIt != pointsOnEdge.end(); ++ipIt )
1536 const IntPoint2D* p1 = *(ipIt-1);
1537 const IntPoint2D* p2 = *ipIt;
1538 gp_XYZ middle = 0.5 * ( p1->myNode + p2->myNode );
1539 if ( isPointInTriangle( middle, nodesTo ))
1541 p1->InitLink( link1, iTo, ( p1 != & ip0 ) ? nodesTo : nodesFrom );
1542 p2->InitLink( link2, iTo, ( p2 != & ip1 ) ? nodesTo : nodesFrom );
1543 cutFaceTo->AddEdge( link1, link2, faceFrom, minNbOnPlane );
1545 // if ( cutFaceFrom )
1547 // p1->InitLink( link1, iFrom, nodesFrom );
1548 // p2->InitLink( link2, iFrom, nodesFrom );
1549 // cutFaceTo->AddEdge( link1, link2, faceTo, minNbOnPlane );
1558 } // Intersector::cutCoplanar()
1560 //================================================================================
1562 * \brief Intersect edges added to myCutFaces
1564 //================================================================================
1566 void Intersector::intersectNewEdges( const CutFace& cf )
1568 IntPoint2D intPoint;
1570 if ( cf.NbInternalEdges() < 2 )
1573 const gp_XYZ& faceNorm = myNormals[ cf.myInitFace->GetID() ];
1574 setPlaneIndices( faceNorm ); // choose indices on an axis-aligned plane
1576 size_t limit = cf.myLinks.size() * cf.myLinks.size() * 2;
1578 for ( size_t i1 = 3; i1 < cf.myLinks.size(); ++i1 )
1580 if ( !cf.myLinks[i1].IsInternal() )
1583 myIntPointSet.clear();
1584 for ( size_t i2 = i1 + 2; i2 < cf.myLinks.size(); ++i2 )
1586 if ( !cf.myLinks[i2].IsInternal() )
1589 // prepare to intersection
1590 myFace1 = cf.myLinks[i1].myFace;
1591 myNodes1[0] = cf.myLinks[i1].myNode1;
1592 myNodes1[1] = cf.myLinks[i1].myNode2;
1593 myFace2 = cf.myLinks[i2].myFace;
1594 myNodes2[0] = cf.myLinks[i2].myNode1;
1595 myNodes2[1] = cf.myLinks[i2].myNode2;
1598 intPoint.myIsCollinear = true; // to find collinear solutions
1599 if ( intersectEdgeEdge( 0, 0, intPoint ))
1601 if ( cf.myLinks[i1].IsSame( cf.myLinks[i2] )) // remove i2
1603 cf.myLinks[i1].ReplaceCoplanar( cf.myLinks[i2] );
1604 cf.myLinks.erase( cf.myLinks.begin() + i2, cf.myLinks.begin() + i2 + 2 );
1608 if ( !intPoint.myIsCollinear )
1610 intPoint.myEdgeInd[1] = i2;
1611 myIntPointSet.insert( intPoint );
1613 else // if ( intPoint.myIsCollinear ) // overlapping edges
1615 myIntPointSet.clear(); // to recompute
1617 if ( intPoint.myU[0] > intPoint.myU[1] ) // orient in same direction
1619 std::swap( intPoint.myU[0], intPoint.myU[1] );
1620 std::swap( myNodes1[0], myNodes1[1] );
1622 // replace _COPLANAR by _INTERNAL
1623 cf.myLinks[i1].ReplaceCoplanar( cf.myLinks[i1+1] );
1624 cf.myLinks[i2].ReplaceCoplanar( cf.myLinks[i2+1] );
1626 if ( coincide( myNodes1[0], myNodes2[0], myTol ) &&
1627 coincide( myNodes1[1], myNodes2[1], myTol ))
1629 cf.myLinks.erase( cf.myLinks.begin() + i2, cf.myLinks.begin() + i2 + 2 );
1634 EdgePart common = cf.myLinks[i1];
1635 common.ReplaceCoplanar( cf.myLinks[i2] );
1637 const SMDS_MeshNode* n1 = myNodes1[0].Node(); // end nodes of an overlapping part
1638 const SMDS_MeshNode* n2 = myNodes1[1].Node();
1639 size_t i3 = cf.myLinks.size();
1641 if ( myNodes1[0] != myNodes2[0] ) // a part before the overlapping one
1643 if ( intPoint.myU[0] < 0 )
1644 cf.myLinks[i1].Set( myNodes1[0].Node(), myNodes2[0].Node(),
1645 cf.myLinks[i1].myFace, cf.myLinks[i1].myIndex );
1647 cf.myLinks[i1].Set( myNodes2[0].Node(), myNodes1[0].Node(),
1648 cf.myLinks[i2].myFace, cf.myLinks[i2].myIndex );
1650 cf.myLinks[i1+1].Set( cf.myLinks[i1].myNode2,
1651 cf.myLinks[i1].myNode1,
1652 cf.myLinks[i1].myFace,
1653 cf.myLinks[i1].myIndex);
1654 n1 = cf.myLinks[i1].myNode2;
1659 if ( myNodes1[1] != myNodes2[1] ) // a part after the overlapping one
1661 if ( intPoint.myU[1] < 1 )
1662 cf.myLinks[i2].Set( myNodes1[1].Node(), myNodes2[1].Node(),
1663 cf.myLinks[i2].myFace, cf.myLinks[i2].myIndex );
1665 cf.myLinks[i2].Set( myNodes2[1].Node(), myNodes1[1].Node(),
1666 cf.myLinks[i1].myFace, cf.myLinks[i1].myIndex );
1668 cf.myLinks[i2+1].Set( cf.myLinks[i2].myNode2,
1669 cf.myLinks[i2].myNode1,
1670 cf.myLinks[i2].myFace,
1671 cf.myLinks[i2].myIndex);
1672 n2 = cf.myLinks[i2].myNode1;
1677 if ( i3 == cf.myLinks.size() )
1678 cf.myLinks.resize( i3 + 2 );
1680 cf.myLinks[i3].Set ( n1, n2, common.myFace, common.myIndex );
1681 cf.myLinks[i3+1].Set( n2, n1, common.myFace, common.myIndex );
1683 i2 = i1 + 1; // recheck modified i1
1688 // // remember a new node
1689 // CutLink link1( myNodes1[0].Node(), myNodes1[1].Node(), cf.myInitFace );
1690 // CutLink link2( myNodes2[0].Node(), myNodes2[1].Node(), cf.myInitFace );
1691 // link2.myIntNode = link1.myIntNode = intPoint.myNode;
1692 // addLink( link1 );
1693 // addLink( link2 );
1696 // size_t i = cf.myLinks.size();
1697 // if ( intPoint.myNode != cf.myLinks[ i1 ].myNode1 &&
1698 // intPoint.myNode != cf.myLinks[ i1 ].myNode2 )
1700 // cf.myLinks.push_back( cf.myLinks[ i1 ]);
1701 // cf.myLinks.push_back( cf.myLinks[ i1 + 1 ]);
1702 // cf.myLinks[ i1 ].myNode2 = cf.myLinks[ i1 + 1 ].myNode1 = intPoint.Node();
1703 // cf.myLinks[ i ].myNode1 = cf.myLinks[ i + 1 ].myNode2 = intPoint.Node();
1705 // if ( intPoint.myNode != cf.myLinks[ i2 ].myNode1 &&
1706 // intPoint.myNode != cf.myLinks[ i2 ].myNode2 )
1708 // i = cf.myLinks.size();
1709 // cf.myLinks.push_back( cf.myLinks[ i2 ]);
1710 // cf.myLinks.push_back( cf.myLinks[ i2 + 1 ]);
1711 // cf.myLinks[ i2 ].myNode2 = cf.myLinks[ i2 + 1 ].myNode1 = intPoint.Node();
1712 // cf.myLinks[ i ].myNode1 = cf.myLinks[ i + 1 ].myNode2 = intPoint.Node();
1716 } // if ( intersectEdgeEdge( 0, 0, intPoint ))
1722 // split i1 edge and all edges it intersects
1723 // don't do it inside intersection loop in order not to loose direction of i1 edge
1724 if ( !myIntPointSet.empty() )
1726 cf.myLinks.reserve( cf.myLinks.size() + myIntPointSet.size() * 2 + 2 );
1728 EdgePart* edge1 = &cf.myLinks[ i1 ];
1729 EdgePart* twin1 = &cf.myLinks[ i1 + 1 ];
1731 TIntPointSet::iterator ipIt = myIntPointSet.begin();
1732 for ( ; ipIt != myIntPointSet.end(); ++ipIt ) // int points sorted on i1 edge
1734 size_t i = cf.myLinks.size();
1735 if ( ipIt->myNode != edge1->myNode1 &&
1736 ipIt->myNode != edge1->myNode2 )
1738 cf.myLinks.push_back( *edge1 );
1739 cf.myLinks.push_back( *twin1 );
1740 edge1->myNode2 = twin1->myNode1 = ipIt->Node();
1741 cf.myLinks[ i ].myNode1 = cf.myLinks[ i + 1 ].myNode2 = ipIt->Node();
1742 edge1 = & cf.myLinks[ i ];
1743 twin1 = & cf.myLinks[ i + 1 ];
1745 size_t i2 = ipIt->myEdgeInd[1];
1746 if ( ipIt->myNode != cf.myLinks[ i2 ].myNode1 &&
1747 ipIt->myNode != cf.myLinks[ i2 ].myNode2 )
1749 i = cf.myLinks.size();
1750 cf.myLinks.push_back( cf.myLinks[ i2 ]);
1751 cf.myLinks.push_back( cf.myLinks[ i2 + 1 ]);
1752 cf.myLinks[ i2 ].myNode2 = cf.myLinks[ i2 + 1 ].myNode1 = ipIt->Node();
1753 cf.myLinks[ i ].myNode1 = cf.myLinks[ i + 1 ].myNode2 = ipIt->Node();
1756 if ( cf.myLinks.size() >= limit )
1757 throw SALOME_Exception( "Infinite loop in Intersector::intersectNewEdges()" );
1759 ++i1; // each internal edge encounters twice
1764 //================================================================================
1766 * \brief Split intersected faces
1768 //================================================================================
1770 void Intersector::MakeNewFaces( SMESH_MeshAlgos::TEPairVec& theNew2OldFaces,
1771 SMESH_MeshAlgos::TNPairVec& theNew2OldNodes,
1772 const double theSign)
1774 // unmark all nodes except intersection ones
1776 for ( SMDS_NodeIteratorPtr nIt = myMesh->nodesIterator(); nIt->more(); )
1778 const SMDS_MeshNode* n = nIt->next();
1779 if ( n->isMarked() && n->GetID()-1 < (int) theNew2OldNodes.size() )
1780 n->setIsMarked( false );
1782 // SMESH_MeshAlgos::MarkElems( myMesh->nodesIterator(), false );
1784 TCutLinkMap::const_iterator cutLinksIt = myCutLinks.cbegin();
1785 // for ( ; cutLinksIt != myCutLinks.cend(); ++cutLinksIt )
1787 // const CutLink& link = *cutLinksIt;
1788 // if ( link.IntNode() && link.IntNode()->GetID()-1 < (int) theNew2OldNodes.size() )
1789 // link.IntNode()->setIsMarked( true );
1792 // intersect edges added to myCutFaces
1794 TCutFaceMap::const_iterator cutFacesIt = myCutFaces.cbegin();
1795 for ( ; cutFacesIt != myCutFaces.cend(); ++cutFacesIt )
1797 const CutFace& cf = *cutFacesIt;
1798 cf.ReplaceNodes( myRemove2KeepNodes );
1799 intersectNewEdges( cf );
1804 EdgeLoopSet loopSet;
1805 SMESH_MeshAlgos::Triangulate triangulator;
1806 std::vector< EdgePart > cutOffLinks;
1807 TLinkMap cutOffCoplanarLinks;
1808 std::vector< const CutFace* > touchedFaces;
1809 SMESH_MeshAlgos::TEPairVec::value_type new2OldTria;
1811 std::vector< const SMDS_MeshNode* > nodes;
1812 std::vector<const SMDS_MeshElement *> faces;
1814 cutOffLinks.reserve( myCutFaces.Extent() * 2 );
1816 for ( cutFacesIt = myCutFaces.cbegin(); cutFacesIt != myCutFaces.cend(); ++cutFacesIt )
1818 const CutFace& cf = *cutFacesIt;
1821 touchedFaces.push_back( & cf );
1825 const gp_XYZ& normal = myNormals[ cf.myInitFace->GetID() ];
1827 // form loops of new faces
1828 cf.ReplaceNodes( myRemove2KeepNodes );
1829 cf.MakeLoops( loopSet, normal );
1831 // avoid loops that are not connected to boundary edges of cf.myInitFace
1832 if ( cf.RemoveInternalLoops( loopSet ))
1834 intersectNewEdges( cf );
1835 cf.MakeLoops( loopSet, normal );
1837 // erase loops that are cut off by face intersections
1838 cf.CutOffLoops( loopSet, theSign, myNormals, cutOffLinks, cutOffCoplanarLinks );
1840 int index = cf.myInitFace->GetID(); // index in theNew2OldFaces
1842 const SMDS_MeshElement* tria;
1843 for ( size_t iL = 0; iL < loopSet.myNbLoops; ++iL )
1845 EdgeLoop& loop = loopSet.myLoops[ iL ];
1846 if ( loop.myLinks.size() == 0 )
1849 int nbTria = triangulator.GetTriangles( &loop, nodes );
1850 int nbNodes = 3 * nbTria;
1851 for ( int i = 0; i < nbNodes; i += 3 )
1853 if ( nodes[i] == nodes[i+1] || nodes[i] == nodes[i+2] || nodes[i+1] == nodes[i+2] )
1856 std::cerr << "BAD tria" << std::endl;
1861 if (!( tria = myMesh->FindFace( nodes[i], nodes[i+1], nodes[i+2] )))
1862 tria = myMesh->AddFace( nodes[i], nodes[i+1], nodes[i+2] );
1863 tria->setIsMarked( true ); // not to remove it
1865 new2OldTria = std::make_pair( tria, theNew2OldFaces[ index ].second );
1866 if ( tria->GetID() < (int)theNew2OldFaces.size() )
1867 theNew2OldFaces[ tria->GetID() ] = new2OldTria;
1869 theNew2OldFaces.push_back( new2OldTria );
1871 if ( index == tria->GetID() )
1872 index = 0; // do not remove tria
1875 theNew2OldFaces[ index ].first = 0;
1878 // remove split faces
1879 for ( size_t id = 1; id < theNew2OldFaces.size(); ++id )
1881 if ( theNew2OldFaces[id].first )
1883 if ( const SMDS_MeshElement* f = myMesh->FindElement( id ))
1884 myMesh->RemoveFreeElement( f );
1887 // remove face connected to cut off parts of cf.myInitFace
1890 for ( size_t i = 0; i < cutOffLinks.size(); ++i )
1893 nodes[0] = cutOffLinks[i].myNode1;
1894 nodes[1] = cutOffLinks[i].myNode2;
1896 if ( nodes[0] != nodes[1] &&
1897 myMesh->GetElementsByNodes( nodes, faces ))
1899 if ( cutOffLinks[i].myFace &&
1900 cutOffLinks[i].myIndex != EdgePart::_COPLANAR &&
1903 for ( size_t iF = 0; iF < faces.size(); ++iF )
1905 int index = faces[iF]->GetID();
1906 // if ( //faces[iF]->isMarked() || // kept part of cutFace
1907 // !theNew2OldFaces[ index ].first ) // already removed
1909 cutFace.myInitFace = faces[iF];
1910 // if ( myCutFaces.Contains( cutFace )) // keep cutting faces needed in CutOffLoops()
1912 // if ( !myCutFaces.Added( cutFace ).IsCut() )
1913 // theNew2OldFaces[ index ].first = 0;
1916 cutFace.myLinks.clear();
1917 cutFace.InitLinks();
1918 for ( size_t iL = 0; iL < cutFace.myLinks.size(); ++iL )
1919 if ( !cutOffLinks[i].IsSame( cutFace.myLinks[ iL ]))
1920 cutOffLinks.push_back( cutFace.myLinks[ iL ]);
1922 theNew2OldFaces[ index ].first = 0;
1923 myMesh->RemoveFreeElement( faces[iF] );
1928 // replace nodes in touched faces
1930 // treat touched faces
1931 for ( size_t i = 0; i < touchedFaces.size(); ++i )
1933 const CutFace& cf = *touchedFaces[i];
1935 int index = cf.myInitFace->GetID(); // index in theNew2OldFaces
1936 if ( !theNew2OldFaces[ index ].first )
1937 continue; // already cut off
1939 if ( !cf.ReplaceNodes( myRemove2KeepNodes ))
1940 continue; // just keep as is
1942 if ( cf.myLinks.size() == 3 )
1944 const SMDS_MeshElement* tria = myMesh->AddFace( cf.myLinks[0].myNode1,
1945 cf.myLinks[1].myNode1,
1946 cf.myLinks[2].myNode1 );
1947 new2OldTria = std::make_pair( tria, theNew2OldFaces[ index ].second );
1948 if ( tria->GetID() < (int)theNew2OldFaces.size() )
1949 theNew2OldFaces[ tria->GetID() ] = new2OldTria;
1951 theNew2OldFaces.push_back( new2OldTria );
1953 theNew2OldFaces[ index ].first = 0;
1957 // add used new nodes to theNew2OldNodes
1958 SMESH_MeshAlgos::TNPairVec::value_type new2OldNode;
1959 new2OldNode.second = NULL;
1960 for ( cutLinksIt = myCutLinks.cbegin(); cutLinksIt != myCutLinks.cend(); ++cutLinksIt )
1962 const CutLink& link = *cutLinksIt;
1963 if ( link.IntNode() ) // && link.IntNode()->NbInverseElements() > 0 )
1965 new2OldNode.first = link.IntNode();
1966 theNew2OldNodes.push_back( new2OldNode );
1973 //================================================================================
1977 //================================================================================
1979 void CutFace::Dump() const
1981 std::cout << std::endl << "INI F " << myInitFace->GetID() << std::endl;
1982 for ( size_t i = 0; i < myLinks.size(); ++i )
1983 std::cout << "[" << i << "] ("
1984 << char(( myLinks[i].IsInternal() ? 'j' : '0' ) + myLinks[i].myIndex ) << ") "
1985 << myLinks[i].myNode1->GetID() << " - " << myLinks[i].myNode2->GetID()
1986 << " " << ( myLinks[i].myFace ? 'F' : 'C' )
1987 << ( myLinks[i].myFace ? myLinks[i].myFace->GetID() : 0 ) << " " << std::endl;
1990 //================================================================================
1992 * \brief Add an edge cutting this face
1993 * \param [in] p1 - start point of the edge
1994 * \param [in] p2 - end point of the edge
1995 * \param [in] cutter - a face producing the added cut edge.
1996 * \param [in] nbOnPlane - nb of triangle nodes lying on the plane of the cutter face
1998 //================================================================================
2000 void CutFace::AddEdge( const CutLink& p1,
2002 const SMDS_MeshElement* cutterFace,
2003 const int nbOnPlane,
2004 const int iNotOnPlane) const
2006 int iN[2] = { myInitFace->GetNodeIndex( p1.IntNode() ),
2007 myInitFace->GetNodeIndex( p2.IntNode() ) };
2008 if ( iN[0] >= 0 && iN[1] >= 0 )
2010 // the cutting edge is a whole side
2011 if (( cutterFace && nbOnPlane < 3 ) &&
2012 !( cutterFace->GetNodeIndex( p1.IntNode() ) >= 0 &&
2013 cutterFace->GetNodeIndex( p2.IntNode() ) >= 0 ))
2016 myLinks[ Abs( iN[0] - iN[1] ) == 1 ? Min( iN[0], iN[1] ) : 2 ].myFace = cutterFace;
2021 if ( p1.IntNode() == p2.IntNode() )
2023 AddPoint( p1, p2, 1e-10 );
2029 // cut side edges by a new one
2031 int iEOnPlane = ( nbOnPlane == 2 ) ? ( iNotOnPlane + 1 ) % 3 : -1;
2034 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2036 const CutLink& p = is2nd ? p2 : p1;
2038 if ( iN[ is2nd ] >= 0 )
2041 int iE = Max( iEOnPlane, myInitFace->GetNodeIndex( p.Node1() ));
2043 continue; // link of other face
2045 SMESH_NodeXYZ n0 = myLinks[iE].myNode1;
2046 dist[ is2nd ] = ( n0 - p.myIntNode ).SquareModulus();
2048 for ( size_t i = 0; i < myLinks.size(); ++i )
2049 if ( myLinks[i].myIndex == iE )
2051 double d1 = n0.SquareDistance( myLinks[i].myNode1 );
2052 if ( d1 < dist[ is2nd ] )
2054 double d2 = n0.SquareDistance( myLinks[i].myNode2 );
2055 if ( dist[ is2nd ] < d2 )
2057 myLinks.push_back( myLinks[i] );
2058 myLinks.back().myNode1 = myLinks[i].myNode2 = p.IntNode();
2065 int state = nbOnPlane == 3 ? EdgePart::_COPLANAR : EdgePart::_INTERNAL;
2067 // look for an existing equal edge
2068 if ( nbOnPlane == 2 )
2070 SMESH_NodeXYZ n0 = myLinks[ iEOnPlane ].myNode1;
2071 if ( iN[0] >= 0 ) dist[0] = ( n0 - p1.myIntNode ).SquareModulus();
2072 if ( iN[1] >= 0 ) dist[1] = ( n0 - p2.myIntNode ).SquareModulus();
2073 if ( dist[0] > dist[1] )
2074 std::swap( dist[0], dist[1] );
2075 for ( size_t i = 0; i < myLinks.size(); ++i )
2077 if ( myLinks[i].myIndex != iEOnPlane )
2079 gp_XYZ mid = 0.5 * ( SMESH_NodeXYZ( myLinks[i].myNode1 ) +
2080 SMESH_NodeXYZ( myLinks[i].myNode2 ));
2081 double d = ( n0 - mid ).SquareModulus();
2082 if ( dist[0] < d && d < dist[1] )
2083 myLinks[i].myFace = cutterFace;
2089 EdgePart newEdge; newEdge.Set( p1.IntNode(), p2.IntNode(), cutterFace, state );
2090 for ( size_t i = 0; i < myLinks.size(); ++i )
2092 if ( myLinks[i].IsSame( newEdge ))
2094 // if ( !myLinks[i].IsInternal() )
2095 // myLinks[ i ].myFace = cutterFace;
2097 myLinks[ i ].ReplaceCoplanar( newEdge );
2098 myLinks[ i+1 ].ReplaceCoplanar( newEdge );
2101 i += myLinks[i].IsInternal();
2105 size_t i = myLinks.size();
2106 myLinks.resize( i + 2 );
2107 myLinks[ i ].Set( p1.IntNode(), p2.IntNode(), cutterFace, state );
2108 myLinks[ i+1 ].Set( p2.IntNode(), p1.IntNode(), cutterFace, state );
2111 //================================================================================
2113 * \brief Add a point cutting this face
2115 //================================================================================
2117 void CutFace::AddPoint( const CutLink& p1, const CutLink& p2, double tol ) const
2119 if ( myInitFace->GetNodeIndex( p1.IntNode() ) >= 0 ||
2120 myInitFace->GetNodeIndex( p2.IntNode() ) >= 0 )
2125 const CutLink* link = &p1;
2126 int iE = myInitFace->GetNodeIndex( link->Node1() );
2130 iE = myInitFace->GetNodeIndex( link->Node1() );
2134 // cut an existing edge by the point
2135 SMESH_NodeXYZ n0 = link->Node1();
2136 double d = ( n0 - link->myIntNode ).SquareModulus();
2138 for ( size_t i = 0; i < myLinks.size(); ++i )
2139 if ( myLinks[i].myIndex == iE )
2141 double d1 = n0.SquareDistance( myLinks[i].myNode1 );
2144 double d2 = n0.SquareDistance( myLinks[i].myNode2 );
2147 myLinks.push_back( myLinks[i] );
2148 myLinks.back().myNode1 = myLinks[i].myNode2 = link->IntNode();
2154 else // point is inside the triangle
2156 // // check if a point already added
2157 // for ( size_t i = 3; i < myLinks.size(); ++i )
2158 // if ( myLinks[i].myNode1 == p1.IntNode() )
2161 // // create a link between the point and the closest corner node
2162 // const SMDS_MeshNode* closeNode = myLinks[0].myNode1;
2163 // double minDist = p1.myIntNode.SquareDistance( closeNode );
2164 // for ( int i = 1; i < 3; ++i )
2166 // double dist = p1.myIntNode.SquareDistance( myLinks[i].myNode1 );
2167 // if ( dist < minDist )
2170 // closeNode = myLinks[i].myNode1;
2173 // if ( minDist > tol * tol )
2175 // size_t i = myLinks.size();
2176 // myLinks.resize( i + 2 );
2177 // myLinks[ i ].Set( p1.IntNode(), closeNode );
2178 // myLinks[ i+1 ].Set( closeNode, p1.IntNode() );
2183 //================================================================================
2185 * \brief Perform node replacement
2187 //================================================================================
2189 bool CutFace::ReplaceNodes( const TNNMap& theRm2KeepMap ) const
2191 bool replaced = false;
2192 for ( size_t i = 0; i < myLinks.size(); ++i )
2194 while ( theRm2KeepMap.IsBound((Standard_Address) myLinks[i].myNode1 ))
2195 replaced = ( myLinks[i].myNode1 = theRm2KeepMap((Standard_Address) myLinks[i].myNode1 ));
2197 while ( theRm2KeepMap.IsBound((Standard_Address) myLinks[i].myNode2 ))
2198 replaced = ( myLinks[i].myNode2 = theRm2KeepMap((Standard_Address) myLinks[i].myNode2 ));
2201 //if ( replaced ) // remove equal links
2203 for ( size_t i1 = 0; i1 < myLinks.size(); ++i1 )
2205 if ( myLinks[i1].myNode1 == myLinks[i1].myNode2 )
2207 myLinks.erase( myLinks.begin() + i1,
2208 myLinks.begin() + i1 + 1 + myLinks[i1].IsInternal() );
2212 size_t i2 = i1 + 1 + myLinks[i1].IsInternal();
2213 for ( ; i2 < myLinks.size(); ++i2 )
2215 if ( !myLinks[i2].IsInternal() )
2217 if ( myLinks[i1].IsSame( myLinks[i2] ))
2219 myLinks[i1]. ReplaceCoplanar( myLinks[i2] );
2220 if ( myLinks[i1].IsInternal() )
2221 myLinks[i1+1].ReplaceCoplanar( myLinks[i2+1] );
2222 if ( !myLinks[i1].myFace && myLinks[i2].myFace )
2224 myLinks[i1]. myFace = myLinks[i2].myFace;
2225 if ( myLinks[i1].IsInternal() )
2226 myLinks[i1+1].myFace = myLinks[i2+1].myFace;
2228 myLinks.erase( myLinks.begin() + i2,
2229 myLinks.begin() + i2 + 2 );
2235 i1 += myLinks[i1].IsInternal();
2242 //================================================================================
2244 * \brief Initialize myLinks with edges of myInitFace
2246 //================================================================================
2248 void CutFace::InitLinks() const
2250 if ( !myLinks.empty() ) return;
2252 int nbNodes = myInitFace->NbNodes();
2253 myLinks.reserve( nbNodes * 2 );
2254 myLinks.resize( nbNodes );
2256 for ( int i = 0; i < nbNodes; ++i )
2258 const SMDS_MeshNode* n1 = myInitFace->GetNode( i );
2259 const SMDS_MeshNode* n2 = myInitFace->GetNodeWrap( i + 1);
2260 myLinks[i].Set( n1, n2, 0, i );
2264 //================================================================================
2266 * \brief Return number of internal edges
2268 //================================================================================
2270 int CutFace::NbInternalEdges() const
2273 for ( size_t i = 3; i < myLinks.size(); ++i )
2274 nb += myLinks[i].IsInternal();
2276 return nb / 2; // each internal edge encounters twice
2279 //================================================================================
2281 * \brief Remove loops that are not connected to boundary edges of myFace by
2282 * adding edges connecting these loops to the boundary
2284 //================================================================================
2286 bool CutFace::RemoveInternalLoops( EdgeLoopSet& theLoops ) const
2288 size_t nbReachedLoops = 0;
2290 // count loops including boundary EdgeParts
2291 for ( size_t iL = 0; iL < theLoops.myNbLoops; ++iL )
2293 EdgeLoop& loop = theLoops.myLoops[ iL ];
2295 for ( size_t iE = 0; iE < loop.myLinks.size(); ++iE )
2296 if ( !loop.myLinks[ iE ]->IsInternal() )
2298 nbReachedLoops += loop.SetConnected();
2302 if ( nbReachedLoops == theLoops.myNbLoops )
2303 return false; // no unreachable loops
2306 // try to reach all loops by propagating via internal edges shared by loops
2307 size_t prevNbReached;
2310 prevNbReached = nbReachedLoops;
2312 for ( size_t iL = 0; iL < theLoops.myNbLoops; ++iL )
2314 EdgeLoop& loop = theLoops.myLoops[ iL ];
2315 if ( !loop.myIsBndConnected )
2318 for ( size_t iE = 0; iE < loop.myLinks.size(); ++iE )
2319 if ( loop.myLinks[ iE ]->IsInternal() )
2321 const EdgePart* twinEdge = getTwin( loop.myLinks[ iE ]);
2322 EdgeLoop* loop2 = theLoops.GetLoopOf( twinEdge );
2323 if ( loop2->SetConnected() && ++nbReachedLoops == theLoops.myNbLoops )
2324 return false; // no unreachable loops
2328 while ( prevNbReached < nbReachedLoops );
2331 // add links connecting internal loops with the boundary ones
2333 for ( size_t iL = 0; iL < theLoops.myNbLoops; ++iL )
2335 EdgeLoop& loop = theLoops.myLoops[ iL ];
2336 if ( loop.myIsBndConnected )
2339 // find a pair of closest nodes
2340 const SMDS_MeshNode *closestNode1, *closestNode2;
2341 double minDist = 1e100;
2342 for ( size_t iE = 0; iE < loop.myLinks.size(); ++iE )
2344 SMESH_NodeXYZ n1 = loop.myLinks[ iE ]->myNode1;
2346 for ( size_t i = 0; i < myLinks.size(); ++i )
2348 if ( !loop.Contains( myLinks[i].myNode1 ))
2350 double dist = n1.SquareDistance( myLinks[i].myNode1 );
2351 if ( dist < minDist )
2354 closestNode1 = loop.myLinks[ iE ]->myNode1;
2355 closestNode2 = myLinks[i].myNode1;
2358 if ( myLinks[i].IsInternal() )
2363 size_t i = myLinks.size();
2364 myLinks.resize( i + 2 );
2365 myLinks[ i ].Set( closestNode1, closestNode2 );
2366 myLinks[ i+1 ].Set( closestNode2, closestNode1 );
2372 //================================================================================
2374 * \brief Return equal reversed edge
2376 //================================================================================
2378 EdgePart* CutFace::getTwin( const EdgePart* edge ) const
2380 size_t i = edge - & myLinks[0];
2382 if ( i > 2 && myLinks[ i-1 ].IsTwin( *edge ))
2383 return & myLinks[ i-1 ];
2385 if ( i+1 < myLinks.size() &&
2386 myLinks[ i+1 ].IsTwin( *edge ))
2387 return & myLinks[ i+1 ];
2392 //================================================================================
2394 * \brief Fill loops of edges
2396 //================================================================================
2398 void CutFace::MakeLoops( EdgeLoopSet& theLoops, const gp_XYZ& theFaceNorm ) const
2400 theLoops.Init( myLinks );
2402 if ( myLinks.size() == 3 )
2404 theLoops.AddNewLoop();
2405 theLoops.AddEdge( myLinks[0] );
2406 theLoops.AddEdge( myLinks[1] );
2407 theLoops.AddEdge( myLinks[2] );
2411 while ( !theLoops.AllEdgesUsed() )
2413 theLoops.AddNewLoop();
2415 // add 1st edge to a new loop
2417 for ( i1 = theLoops.myNbLoops - 1; i1 < myLinks.size(); ++i1 )
2418 if ( theLoops.AddEdge( myLinks[i1] ))
2421 EdgePart* lastEdge = & myLinks[ i1 ];
2422 EdgePart* twinEdge = getTwin( lastEdge );
2423 const SMDS_MeshNode* firstNode = lastEdge->myNode1;
2424 const SMDS_MeshNode* lastNode = lastEdge->myNode2;
2426 do // add the rest edges
2428 theLoops.myCandidates.clear(); // edges starting at lastNode
2431 // find candidate edges
2432 for ( size_t i = i1 + 1; i < myLinks.size(); ++i )
2433 if ( myLinks[ i ].myNode1 == lastNode &&
2434 &myLinks[ i ] != twinEdge &&
2435 !theLoops.myIsUsedEdge[ i ])
2437 theLoops.myCandidates.push_back( & myLinks[ i ]);
2438 nbInternal += myLinks[ i ].IsInternal();
2441 // choose among candidates
2442 if ( theLoops.myCandidates.size() == 0 )
2444 theLoops.GetLoopOf( lastEdge )->myHasPending = true;
2445 lastEdge = twinEdge;
2447 else if ( theLoops.myCandidates.size() == 1 )
2449 lastEdge = theLoops.myCandidates[0];
2451 else if ( nbInternal == 1 && !lastEdge->IsInternal() )
2453 lastEdge = theLoops.myCandidates[ !theLoops.myCandidates[0]->IsInternal() ];
2457 gp_Vec lastVec = *lastEdge;
2458 double maxAngle = -2 * M_PI;
2459 for ( size_t i = 0; i < theLoops.myCandidates.size(); ++i )
2461 double angle = lastVec.AngleWithRef( *theLoops.myCandidates[i], theFaceNorm );
2462 if ( angle > maxAngle )
2465 lastEdge = theLoops.myCandidates[i];
2469 theLoops.AddEdge( *lastEdge );
2470 lastNode = lastEdge->myNode2;
2471 twinEdge = getTwin( lastEdge );
2473 while ( lastNode != firstNode );
2475 } // while ( !theLoops.AllEdgesUsed() )
2480 //================================================================================
2482 * \brief Erase loops that are cut off by face intersections
2484 //================================================================================
2486 void CutFace::CutOffLoops( EdgeLoopSet& theLoops,
2487 const double theSign,
2488 const std::vector< gp_XYZ >& theNormals,
2489 std::vector< EdgePart >& theCutOffLinks,
2490 TLinkMap& theCutOffCoplanarLinks) const
2493 for ( size_t i = 0; i < myLinks.size(); ++i )
2495 if ( !myLinks[i].myFace )
2498 EdgeLoop* loop = theLoops.GetLoopOf( & myLinks[i] );
2499 if ( !loop || loop->myLinks.empty() || loop->myHasPending )
2502 bool toErase, isCoplanar = ( myLinks[i].myIndex == EdgePart::_COPLANAR );
2504 gp_Vec iniNorm = theNormals[ myInitFace->GetID() ];
2507 toErase = ( myLinks[i].myFace->GetID() > myInitFace->GetID() );
2509 const EdgePart* twin = getTwin( & myLinks[i] );
2510 if ( !twin || twin->myFace == myLinks[i].myFace )
2512 // only one co-planar face includes myLinks[i]
2513 gp_Vec inFaceDir = iniNorm ^ myLinks[i];
2514 gp_XYZ edgePnt = SMESH_NodeXYZ( myLinks[i].myNode1 );
2515 for ( int iN = 0; iN < 3; ++iN )
2517 gp_Vec inCutFaceDir = ( SMESH_NodeXYZ( myLinks[i].myFace->GetNode( iN )) - edgePnt );
2518 if ( inCutFaceDir * inFaceDir < 0 )
2528 gp_Vec cutNorm = theNormals[ myLinks[i].myFace->GetID() ];
2529 gp_Vec inFaceDir = iniNorm ^ myLinks[i];
2531 toErase = inFaceDir * cutNorm * theSign < 0;
2534 // erase a neighboring loop
2536 if ( const EdgePart* twin = getTwin( & myLinks[i] ))
2537 loop = theLoops.GetLoopOf( twin );
2538 toErase = ( loop && !loop->myLinks.empty() );
2546 // remember whole sides of myInitFace that are cut off
2547 for ( size_t iE = 0; iE < loop->myLinks.size(); ++iE )
2549 if ( !loop->myLinks[ iE ]->myFace &&
2550 !loop->myLinks[ iE ]->IsInternal() )// &&
2551 // !loop->myLinks[ iE ]->myNode1->isMarked() && // cut nodes are marked
2552 // !loop->myLinks[ iE ]->myNode2->isMarked() )
2554 int i = loop->myLinks[ iE ]->myIndex;
2555 sideEdge.Set( myInitFace->GetNode ( i ),
2556 myInitFace->GetNodeWrap( i+1 ));
2557 theCutOffLinks.push_back( sideEdge );
2559 if ( !sideEdge.IsSame( *loop->myLinks[ iE ] )) // nodes replaced
2561 theCutOffLinks.push_back( *loop->myLinks[ iE ] );
2564 else if ( IsCoplanar( loop->myLinks[ iE ]))
2566 // propagate erasure to a co-planar face
2567 theCutOffLinks.push_back( *loop->myLinks[ iE ]);
2569 else if ( loop->myLinks[ iE ]->myFace &&
2570 loop->myLinks[ iE ]->IsInternal() )
2571 theCutOffLinks.push_back( *loop->myLinks[ iE ]);
2575 theLoops.Erase( loop );
2582 //================================================================================
2584 * \brief Check if the face has cut edges
2586 //================================================================================
2588 bool CutFace::IsCut() const
2590 if ( myLinks.size() > 3 )
2593 if ( myLinks.size() == 3 )
2594 for ( size_t i = 0; i < 3; ++i )
2595 if ( myLinks[i].myFace )
2601 //================================================================================
2603 * \brief Check if an edge is produced by a co-planar cut
2605 //================================================================================
2607 bool CutFace::IsCoplanar( const EdgePart* edge ) const
2609 if ( edge->myIndex == EdgePart::_COPLANAR )
2611 const EdgePart* twin = getTwin( edge );
2612 return ( !twin || twin->myIndex == EdgePart::_COPLANAR );
2617 //================================================================================
2619 * \brief Replace _COPLANAR cut edge by _INTERNAL oe vice versa
2621 //================================================================================
2623 bool EdgePart::ReplaceCoplanar( const EdgePart& e )
2625 if ( myIndex + e.myIndex == _COPLANAR + _INTERNAL )
2627 //check if the faces are connected
2628 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e.myFace, myFace ).size();
2629 bool toReplace = (( myIndex == _INTERNAL && nbCommonNodes > 1 ) ||
2630 ( myIndex == _COPLANAR && nbCommonNodes < 2 ));
2633 myIndex = e.myIndex;
2643 //================================================================================
2645 * \brief Create an offsetMesh of given faces
2646 * \param [in] faceIt - the input faces
2647 * \param [out] new2OldFaces - history of faces
2648 * \param [out] new2OldNodes - history of nodes
2649 * \return SMDS_Mesh* - the new offset mesh, a caller should delete
2651 //================================================================================
2653 SMDS_Mesh* SMESH_MeshAlgos::MakeOffset( SMDS_ElemIteratorPtr theFaceIt,
2654 SMDS_Mesh& theSrcMesh,
2655 const double theOffset,
2656 const bool theFixIntersections,
2657 TEPairVec& theNew2OldFaces,
2658 TNPairVec& theNew2OldNodes)
2660 SMDS_Mesh* newMesh = new SMDS_Mesh;
2661 theNew2OldFaces.clear();
2662 theNew2OldNodes.clear();
2663 theNew2OldFaces.push_back
2664 ( std::make_pair(( const SMDS_MeshElement*) 0,
2665 ( const SMDS_MeshElement*) 0)); // to have index == face->GetID()
2667 if ( theSrcMesh.GetMeshInfo().NbFaces( ORDER_QUADRATIC ) > 0 )
2668 throw SALOME_Exception( "Offset of quadratic mesh not supported" );
2669 if ( theSrcMesh.GetMeshInfo().NbFaces() > theSrcMesh.GetMeshInfo().NbTriangles() )
2670 throw SALOME_Exception( "Offset of non-triangular mesh not supported" );
2672 // copy input faces to the newMesh keeping IDs of nodes
2674 double minNodeDist = 1e100;
2676 std::vector< const SMDS_MeshNode* > nodes;
2677 while ( theFaceIt->more() )
2679 const SMDS_MeshElement* face = theFaceIt->next();
2680 if ( face->GetType() != SMDSAbs_Face ) continue;
2683 nodes.assign( face->begin_nodes(), face->end_nodes() );
2684 for ( size_t i = 0; i < nodes.size(); ++i )
2686 const SMDS_MeshNode* newNode = newMesh->FindNode( nodes[i]->GetID() );
2689 SMESH_NodeXYZ xyz( nodes[i] );
2690 newNode = newMesh->AddNodeWithID( xyz.X(), xyz.Y(), xyz.Z(), nodes[i]->GetID() );
2691 theNew2OldNodes.push_back( std::make_pair( newNode, nodes[i] ));
2695 const SMDS_MeshElement* newFace = 0;
2696 switch ( face->GetEntityType() )
2698 case SMDSEntity_Triangle:
2699 newFace = newMesh->AddFace( nodes[0],nodes[1],nodes[2] );
2701 case SMDSEntity_Quad_Triangle:
2702 newFace = newMesh->AddFace( nodes[0],nodes[1],nodes[2],
2703 nodes[3],nodes[4],nodes[5] );
2705 case SMDSEntity_BiQuad_Triangle:
2706 newFace = newMesh->AddFace( nodes[0],nodes[1],nodes[2],
2707 nodes[3],nodes[4],nodes[5],nodes[6] );
2709 case SMDSEntity_Quadrangle:
2710 newFace = newMesh->AddFace( nodes[0],nodes[1],nodes[2],nodes[3] );
2712 case SMDSEntity_Quad_Quadrangle:
2713 newFace = newMesh->AddFace( nodes[0],nodes[1],nodes[2],nodes[3],
2714 nodes[4],nodes[5],nodes[6],nodes[7] );
2716 case SMDSEntity_BiQuad_Quadrangle:
2717 newFace = newMesh->AddFace( nodes[0],nodes[1],nodes[2],nodes[3],nodes[4],
2718 nodes[5],nodes[6],nodes[7],nodes[8] );
2720 case SMDSEntity_Polygon:
2721 newFace = newMesh->AddPolygonalFace( nodes );
2723 case SMDSEntity_Quad_Polygon:
2724 newFace = newMesh->AddQuadPolygonalFace( nodes );
2729 theNew2OldFaces.push_back( std::make_pair( newFace, face ));
2731 SMESH_NodeXYZ pPrev = nodes.back(), p;
2732 for ( size_t i = 0; i < nodes.size(); ++i )
2735 double dist = ( pPrev - p ).SquareModulus();
2736 if ( dist > std::numeric_limits<double>::min() )
2740 } // while ( faceIt->more() )
2743 // compute normals to faces
2744 std::vector< gp_XYZ > normals( theNew2OldFaces.size() );
2745 for ( size_t i = 1; i < normals.size(); ++i )
2747 if ( !SMESH_MeshAlgos::FaceNormal( theNew2OldFaces[i].second, normals[i] ))
2748 normals[i].SetCoord( 0,0,0 ); // TODO find norm by neighbors
2751 const double tol = 1e-3 * Sqrt( minNodeDist );
2752 const double sign = ( theOffset < 0 ? -1 : +1 );
2754 // translate new nodes by normal to input faces
2756 std::vector< const SMDS_MeshNode* > multiNormalNodes;
2757 for ( size_t i = 0; i < theNew2OldNodes.size(); ++i )
2759 const SMDS_MeshNode* newNode = theNew2OldNodes[i].first;
2761 if ( getTranslatedPosition( newNode, theOffset, tol*10., sign, normals, theSrcMesh, newXYZ ))
2762 newMesh->MoveNode( newNode, newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
2764 multiNormalNodes.push_back( newNode );
2766 // make multi-normal translation
2767 std::vector< SMESH_NodeXYZ > multiPos(10);
2768 for ( size_t i = 0; i < multiNormalNodes.size(); ++i )
2770 const SMDS_MeshNode* newNode = multiNormalNodes[i];
2771 newNode->setIsMarked( true );
2772 SMESH_NodeXYZ oldXYZ = newNode;
2774 for ( SMDS_ElemIteratorPtr fIt = newNode->GetInverseElementIterator(); fIt->more(); )
2776 const SMDS_MeshElement* newFace = fIt->next();
2777 const int faceIndex = newFace->GetID();
2778 const gp_XYZ& oldNorm = normals[ faceIndex ];
2779 const gp_XYZ newXYZ = oldXYZ + oldNorm * theOffset;
2780 if ( multiPos.empty() )
2782 newMesh->MoveNode( newNode, newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
2783 multiPos.emplace_back( newNode );
2788 for ( size_t iP = 0; iP < multiPos.size() && !newNode; ++iP )
2789 if (( multiPos[iP] - newXYZ ).SquareModulus() < tol * tol )
2790 newNode = multiPos[iP].Node();
2793 newNode = newMesh->AddNode( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
2794 newNode->setIsMarked( true );
2795 theNew2OldNodes.push_back( std::make_pair( newNode, theNew2OldNodes[i].second ));
2796 multiPos.emplace_back( newNode );
2799 if ( newNode != oldXYZ.Node() )
2801 nodes.assign( newFace->begin_nodes(), newFace->end_nodes() );
2802 nodes[ newFace->GetNodeIndex( oldXYZ.Node() )] = newNode;
2803 newMesh->ChangeElementNodes( newFace, & nodes[0], nodes.size() );
2808 if ( !theFixIntersections )
2812 // remove new faces around concave nodes (they are marked) if the faces are inverted
2814 for ( size_t i = 0; i < theNew2OldNodes.size(); ++i )
2816 const SMDS_MeshNode* newNode = theNew2OldNodes[i].first;
2817 //const SMDS_MeshNode* oldNode = theNew2OldNodes[i].second;
2818 if ( newNode->isMarked() )
2820 //gp_XYZ moveVec = sign * ( SMESH_NodeXYZ( newNode ) - SMESH_NodeXYZ( oldNode ));
2822 //bool haveInverseFace = false;
2823 for ( SMDS_ElemIteratorPtr fIt = newNode->GetInverseElementIterator(); fIt->more(); )
2825 const SMDS_MeshElement* newFace = fIt->next();
2826 const int faceIndex = newFace->GetID();
2827 const gp_XYZ& oldNorm = normals[ faceIndex ];
2828 if ( !SMESH_MeshAlgos::FaceNormal( newFace, faceNorm, /*normalize=*/false ) ||
2829 //faceNorm * moveVec < 0 )
2830 faceNorm * oldNorm < 0 )
2832 //haveInverseFace = true;
2833 theNew2OldFaces[ faceIndex ].first = 0;
2834 newMesh->RemoveFreeElement( newFace );
2838 // if ( haveInverseFace )
2840 // newMesh->MoveNode( newNode, oldNode->X(), oldNode->Y(), oldNode->Z() );
2842 // for ( SMDS_ElemIteratorPtr fIt = newNode->GetInverseElementIterator(); fIt->more(); )
2844 // const SMDS_MeshElement* newFace = fIt->next();
2845 // if ( !SMESH_MeshAlgos::FaceNormal( newFace, normals[ newFace->GetID() ] ))
2846 // normals[i].SetCoord( 0,0,0 ); // TODO find norm by neighbors
2850 // mark all new nodes located closer than theOffset from theSrcMesh
2853 // ==================================================
2854 // find self-intersections of new faces and fix them
2855 // ==================================================
2857 std::unique_ptr< SMESH_ElementSearcher > fSearcher
2858 ( SMESH_MeshAlgos::GetElementSearcher( *newMesh, tol ));
2860 Intersector intersector( newMesh, tol, normals );
2862 std::vector< const SMDS_MeshElement* > closeFaces;
2863 std::vector< const SMDS_MeshNode* > faceNodes;
2865 for ( size_t iF = 1; iF < theNew2OldFaces.size(); ++iF )
2867 const SMDS_MeshElement* newFace = theNew2OldFaces[iF].first;
2868 if ( !newFace ) continue;
2869 faceNodes.assign( newFace->begin_nodes(), newFace->end_nodes() );
2871 bool isConcaveNode1 = false;
2872 for ( size_t iN = 0; iN < faceNodes.size() && !isConcaveNode1; ++iN )
2873 isConcaveNode1 = faceNodes[iN]->isMarked();
2875 // get faces close to a newFace
2878 for ( size_t i = 0; i < faceNodes.size(); ++i )
2879 faceBox.Add( SMESH_NodeXYZ( faceNodes[i] ));
2880 faceBox.Enlarge( tol );
2882 fSearcher->GetElementsInBox( faceBox, SMDSAbs_Face, closeFaces );
2884 // intersect the newFace with closeFaces
2886 for ( size_t i = 0; i < closeFaces.size(); ++i )
2888 const SMDS_MeshElement* closeFace = closeFaces[i];
2889 if ( closeFace->GetID() <= newFace->GetID() )
2890 continue; // this pair already treated
2892 // do not intersect connected faces if they have no concave nodes
2893 int nbCommonNodes = 0;
2894 for ( size_t iN = 0; iN < faceNodes.size(); ++iN )
2895 nbCommonNodes += ( closeFace->GetNodeIndex( faceNodes[iN] ) >= 0 );
2897 if ( !isConcaveNode1 )
2899 bool isConcaveNode2 = false;
2900 for ( SMDS_ElemIteratorPtr nIt = closeFace->nodesIterator(); nIt->more(); )
2901 if (( isConcaveNode2 = nIt->next()->isMarked() ))
2904 if ( !isConcaveNode2 && nbCommonNodes > 0 )
2908 intersector.Cut( newFace, closeFace, nbCommonNodes );
2911 intersector.MakeNewFaces( theNew2OldFaces, theNew2OldNodes, sign );