1 // Copyright (C) 2018-2021 CEA/DEN, EDF R&D, OPEN CASCADE
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 // File : SMESH_Slot.cxx
20 // Created : Fri Nov 30 15:58:37 2018
21 // Author : Edward AGAPOV (eap)
23 #include "SMESH_MeshAlgos.hxx"
25 #include "ObjectPool.hxx"
26 #include "SMDS_LinearEdge.hxx"
27 #include "SMDS_Mesh.hxx"
28 #include "SMDS_MeshGroup.hxx"
30 #include <IntAna_IntConicQuad.hxx>
31 #include <IntAna_Quadric.hxx>
32 #include <NCollection_DataMap.hxx>
33 #include <NCollection_Map.hxx>
34 #include <Precision.hxx>
36 #include <gp_Cylinder.hxx>
43 #include <Utils_SALOME_Exception.hxx>
47 typedef SMESH_MeshAlgos::Edge TEdge;
49 //================================================================================
50 //! point of intersection of a face edge with the cylinder
53 SMESH_NodeXYZ myNode; // point and a node
54 int myEdgeIndex; // face edge index
55 bool myIsOutPln[2]; // isOut of two planes
57 double SquareDistance( const IntPoint& p ) const { return ( myNode-p.myNode ).SquareModulus(); }
60 //================================================================================
64 IntPoint myIntPnt1, myIntPnt2;
65 const SMDS_MeshElement* myFace;
67 const IntPoint& operator[]( size_t i ) const { return i ? myIntPnt2 : myIntPnt1; }
69 double SquareDistance( const gp_Pnt& p, gp_XYZ & pClosest ) const
71 gp_Vec edge( myIntPnt1.myNode, myIntPnt2.myNode );
72 gp_Vec n1p ( myIntPnt1.myNode, p );
73 double u = ( edge * n1p ) / edge.SquareMagnitude(); // param [0,1] on the edge
76 pClosest = myIntPnt1.myNode;
77 return n1p.SquareMagnitude();
81 pClosest = myIntPnt2.myNode;
82 return p.SquareDistance( myIntPnt2.myNode );
84 pClosest = myIntPnt1.myNode + u * edge.XYZ(); // projection of the point on the edge
85 return p.SquareDistance( pClosest );
89 //================================================================================
93 typedef std::vector< Cut > TCutList;
95 const SMDS_MeshElement* myEdge;
97 std::vector< const IntPoint* > myFreeEnds; // ends of cut edges
99 Segment( const SMDS_MeshElement* e = 0 ): myEdge(e) { myCuts.reserve( 4 ); }
102 gp_Ax1 Ax1( bool reversed = false ) const
104 SMESH_NodeXYZ n1 = myEdge->GetNode( reversed );
105 SMESH_NodeXYZ n2 = myEdge->GetNode( !reversed );
106 return gp_Ax1( n1, gp_Dir( n2 - n1 ));
110 const SMDS_MeshNode* Node(int i) const
112 return myEdge->GetNode( i % 2 );
115 // store an intersection edge forming the slot border
116 void AddCutEdge( const IntPoint& p1,
118 const SMDS_MeshElement* myFace )
120 myCuts.push_back( Cut({ p1, p2, myFace }));
123 // return number of not shared IntPoint's
124 int NbFreeEnds( double tol )
126 if ( myCuts.empty() )
128 if ( myFreeEnds.empty() )
130 // remove degenerated cuts
131 // for ( size_t iC1 = 0; iC1 < myCuts.size(); ++iC1 )
132 // if ( myCuts[ iC1 ][ 0 ].myNode == myCuts[ iC1 ][ 1 ].myNode )
134 // if ( iC1 < myCuts.size() - 1 )
135 // myCuts[ iC1 ] = myCuts.back();
136 // myCuts.pop_back();
140 std::vector< bool > isSharedPnt( myCuts.size() * 2, false );
141 for ( size_t iC1 = 0; iC1 < myCuts.size() - 1; ++iC1 )
142 for ( size_t iP1 = 0; iP1 < 2; ++iP1 )
144 size_t i1 = iC1 * 2 + iP1;
145 if ( isSharedPnt[ i1 ])
147 for ( size_t iC2 = iC1 + 1; iC2 < myCuts.size(); ++iC2 )
148 for ( size_t iP2 = 0; iP2 < 2; ++iP2 )
150 size_t i2 = iC2 * 2 + iP2;
151 if ( isSharedPnt[ i2 ])
153 if ( myCuts[ iC1 ][ iP1 ].SquareDistance( myCuts[ iC2 ][ iP2 ]) < tol * tol )
156 if ( myCuts[ iC1 ][ 0 ].SquareDistance( myCuts[ iC1 ][ 1 ]) < tol * tol )
157 isSharedPnt[ iC1 * 2 ] = isSharedPnt[ iC1 * 2 + 1 ] = true;
158 else if ( myCuts[ iC2 ][ 0 ].SquareDistance( myCuts[ iC2 ][ 1 ]) < tol * tol )
159 isSharedPnt[ iC2 * 2 ] = isSharedPnt[ iC2 * 2 + 1 ] = true;
161 isSharedPnt[ i1 ] = isSharedPnt[ i2 ] = true;
165 myFreeEnds.reserve( isSharedPnt.size() - nbShared );
166 for ( size_t i = 0; i < isSharedPnt.size(); ++i )
167 if ( !isSharedPnt[ i ] )
171 myFreeEnds.push_back( & myCuts[ iC ][ iP ]);
174 return myFreeEnds.size();
177 typedef ObjectPoolIterator<Segment> TSegmentIterator;
180 //================================================================================
181 //! Segments and plane separating domains of segments, at common node
184 std::vector< Segment* > mySegments;
185 gp_Ax1 myPlane; // oriented OK for mySegments[0]
187 void AddSegment( Segment* seg, const SMDS_MeshNode* n )
189 mySegments.reserve(2);
190 mySegments.push_back( seg );
191 if ( mySegments.size() == 1 )
193 myPlane = mySegments[0]->Ax1( mySegments[0]->myEdge->GetNodeIndex( n ));
197 gp_Ax1 axis2 = mySegments[1]->Ax1( mySegments[1]->myEdge->GetNodeIndex( n ));
198 myPlane.SetDirection( myPlane.Direction().XYZ() - axis2.Direction().XYZ() );
201 gp_Ax1 Plane( const Segment* seg )
203 return ( seg == mySegments[0] ) ? myPlane : myPlane.Reversed();
206 typedef NCollection_DataMap< const SMDS_MeshNode*, NodeData, SMESH_Hasher > TSegmentsOfNode;
209 //================================================================================
211 * \brief Intersect a face edge given by its nodes with a cylinder.
213 //================================================================================
215 bool intersectEdge( const gp_Cylinder& cyl,
216 const SMESH_NodeXYZ& n1,
217 const SMESH_NodeXYZ& n2,
219 std::vector< IntPoint >& intPoints )
221 gp_Lin line( gp_Ax1( n1, gp_Dir( n2 - n1 )));
222 IntAna_IntConicQuad intersection( line, IntAna_Quadric( cyl ));
224 if ( !intersection.IsDone() ||
225 intersection.IsParallel() ||
226 intersection.IsInQuadric() ||
227 intersection.NbPoints() == 0 )
230 gp_Vec edge( n1, n2 );
232 size_t oldNbPnts = intPoints.size();
233 for ( int iP = 1; iP <= intersection.NbPoints(); ++iP )
235 const gp_Pnt& p = intersection.Point( iP );
237 gp_Vec n1p ( n1, p );
238 const SMDS_MeshNode* n = 0;
240 double u = ( edge * n1p ) / edge.SquareMagnitude(); // param [0,1] on the edge
242 if ( p.SquareDistance( n1 ) < tol * tol )
247 else if ( u >= 1. ) {
248 if ( p.SquareDistance( n2 ) < tol * tol )
254 if ( p.SquareDistance( n1 ) < tol * tol )
256 else if ( p.SquareDistance( n2 ) < tol * tol )
260 intPoints.push_back( IntPoint() );
262 intPoints.back().myNode.Set( n );
264 intPoints.back().myNode.SetCoord( p.X(),p.Y(),p.Z() );
267 // set points order along an edge
268 if ( intPoints.size() - oldNbPnts == 2 &&
269 intersection.ParamOnConic( 1 ) > intersection.ParamOnConic( 2 ))
271 int i = intPoints.size() - 1;
272 std::swap( intPoints[ i ], intPoints[ i - 1 ]);
275 return intPoints.size() - oldNbPnts > 0;
278 //================================================================================
280 * \brief Return signed distance between a point and a plane
282 //================================================================================
284 double signedDist( const gp_Pnt& p, const gp_Ax1& planeNormal )
286 const gp_Pnt& O = planeNormal.Location();
288 return Op * planeNormal.Direction();
291 //================================================================================
293 * \brief Check if a point is outside a segment domain bound by two planes
295 //================================================================================
297 bool isOut( const gp_Pnt& p, const gp_Ax1* planeNormal, bool* isOutPtr, int nbPln = 2 )
299 isOutPtr[0] = isOutPtr[1] = false;
301 for ( int i = 0; i < nbPln; ++i )
303 isOutPtr[i] = ( signedDist( p, planeNormal[i] ) <= 0. );
305 return ( isOutPtr[0] && isOutPtr[1] );
308 //================================================================================
310 * \brief Check if a segment between two points is outside a segment domain bound by two planes
312 //================================================================================
314 bool isSegmentOut( bool* isOutPtr1, bool* isOutPtr2 )
316 return (( isOutPtr1[0] && isOutPtr2[0] ) ||
317 ( isOutPtr1[1] && isOutPtr2[1] ));
320 //================================================================================
322 * \brief cut off ip1 from edge (ip1 - ip2) by a plane
324 //================================================================================
326 void cutOff( IntPoint & ip1, const IntPoint & ip2, const gp_Ax1& planeNormal, double tol )
328 gp_Lin lin( ip1.myNode, ( ip2.myNode - ip1.myNode ));
329 gp_Pln pln( planeNormal.Location(), planeNormal.Direction() );
331 IntAna_IntConicQuad intersection( lin, pln, Precision::Angular/*Tolerance*/() );
332 if ( intersection.IsDone() &&
333 !intersection.IsParallel() &&
334 !intersection.IsInQuadric() &&
335 intersection.NbPoints() == 1 )
337 if ( intersection.Point( 1 ).SquareDistance( ip1.myNode ) > tol * tol )
339 static_cast< gp_XYZ& >( ip1.myNode ) = intersection.Point( 1 ).XYZ();
340 ip1.myNode._node = 0;
341 ip1.myEdgeIndex = -1;
346 //================================================================================
348 * \brief Assure that face normal is computed in faceNormals vector
350 //================================================================================
352 const gp_XYZ& computeNormal( const SMDS_MeshElement* face,
353 std::vector< gp_XYZ >& faceNormals )
356 if ((int) faceNormals.size() <= face->GetID() )
359 faceNormals.resize( face->GetID() + 1 );
363 toCompute = faceNormals[ face->GetID() ].SquareModulus() == 0.;
366 SMESH_MeshAlgos::FaceNormal( face, faceNormals[ face->GetID() ], /*normalized=*/false );
368 return faceNormals[ face->GetID() ];
371 typedef std::vector< SMDS_MeshGroup* > TGroupVec;
373 //================================================================================
375 * \brief Fill theFaceID2Groups map for a given face
376 * \param [in] theFace - the face
377 * \param [in] theGroupsToUpdate - list of groups to treat
378 * \param [out] theFaceID2Groups - the map to fill in
379 * \param [out] theWorkGroups - a working buffer of groups
381 //================================================================================
383 void findGroups( const SMDS_MeshElement * theFace,
384 TGroupVec & theGroupsToUpdate,
385 NCollection_DataMap< smIdType, TGroupVec, smIdHasher > & theFaceID2Groups,
386 TGroupVec & theWorkGroups )
388 theWorkGroups.clear();
389 for ( size_t i = 0; i < theGroupsToUpdate.size(); ++i )
390 if ( theGroupsToUpdate[i]->Contains( theFace ))
391 theWorkGroups.push_back( theGroupsToUpdate[i] );
393 if ( !theWorkGroups.empty() )
394 theFaceID2Groups.Bind( theFace->GetID(), theWorkGroups );
397 //================================================================================
399 * \brief Check distance between a point and an edge defined by a couple of nodes
401 //================================================================================
403 bool isOnEdge( const SMDS_MeshNode* n1,
404 const SMDS_MeshNode* n2,
408 SMDS_LinearEdge edge( n1, n2 );
409 return ( SMESH_MeshAlgos::GetDistance( &edge, p ) < tol );
412 //================================================================================
414 * \return Index of intersection point detected on a triangle cut by planes
415 * \param [in] i - index of a cut triangle side
416 * \param [in] n1 - 1st point of a cut triangle side
417 * \param [in] n2 - 2nd point of a cut triangle side
418 * \param [in] face - a not cut triangle
419 * \param [in] intPoint - the intersection point
420 * \param [in] faceNodes - nodes of not cut triangle
421 * \param [in] tol - tolerance
423 //================================================================================
425 int edgeIndex( const int i,
426 const SMESH_NodeXYZ& n1,
427 const SMESH_NodeXYZ& n2,
428 const SMDS_MeshElement* face,
429 const IntPoint& intPoint,
430 const std::vector< const SMDS_MeshNode* >& faceNodes,
433 if ( n1.Node() && n2.Node() )
434 return face->GetNodeIndex( n1.Node() );
436 // project intPoint to sides of face
437 for ( size_t i = 1; i < faceNodes.size(); ++i )
438 if ( isOnEdge( faceNodes[ i-1 ], faceNodes[ i ], intPoint.myNode, tol ))
444 //================================================================================
446 * \brief Find a neighboring segment and its next node
447 * \param [in] curSegment - a current segment
448 * \param [in,out] curNode - a current node to update
449 * \param [in] segmentsOfNode - map of segments of nodes
450 * \return Segment* - the found segment
452 //================================================================================
454 Segment* nextSegment( const Segment* curSegment,
455 const SMDS_MeshNode* & curNode,
456 const TSegmentsOfNode& segmentsOfNode )
458 Segment* neighborSeg = 0;
459 const NodeData& noData = segmentsOfNode( curNode );
460 for ( size_t iS = 0; iS < noData.mySegments.size() && !neighborSeg; ++iS )
461 if ( noData.mySegments[ iS ] != curSegment )
462 neighborSeg = noData.mySegments[ iS ];
466 int iN = ( neighborSeg->Node(0) == curNode );
467 curNode = neighborSeg->Node( iN );
472 //================================================================================
474 * \brief Tries to find a segment to which a given point is too close
475 * \param [in] p - the point
476 * \param [in] minDist - minimal allowed distance from segment
477 * \param [in] curSegment - start segment
478 * \param [in] curNode - start node
479 * \param [in] segmentsOfNode - map of segments of nodes
480 * \return bool - true if a too close segment found
482 //================================================================================
484 const Segment* findTooCloseSegment( const IntPoint& p,
485 const double minDist,
486 const double /*tol*/,
487 const Segment* curSegment,
488 const SMDS_MeshNode* curNode,
489 const TSegmentsOfNode& segmentsOfNode )
491 double prevDist = Precision::Infinite();
494 double dist = SMESH_MeshAlgos::GetDistance( curSegment->myEdge, p.myNode );
495 if ( dist < minDist )
497 // check if dist is less than distance of curSegment to its cuts
498 // double minCutDist = prevDist;
499 // bool coincide = false;
500 // for ( size_t iC = 0; iC < curSegment->myCuts.size(); ++iC )
502 // if (( coincide = ( curSegment->myCuts[iC].SquareDistance( p.myNode ) < tol * tol )))
504 // for ( size_t iP = 0; iP < 2; ++iP )
506 // double cutDist = SMESH_MeshAlgos::GetDistance( curSegment->myEdge,
507 // curSegment->myCuts[iC][iP].myNode );
508 // minCutDist = std::min( minCutDist, cutDist );
511 // if ( !coincide && minCutDist > dist )
514 if ( dist > prevDist )
517 curSegment = nextSegment( curSegment, curNode, segmentsOfNode );
523 //================================================================================
525 * \brief Create a slot of given width around given 1D elements lying on a triangle mesh.
526 * The slot is constructed by cutting faces by cylindrical surfaces made around each segment.
527 * \return Edges located at the slot boundary
529 //================================================================================
531 std::vector< SMESH_MeshAlgos::Edge >
532 SMESH_MeshAlgos::MakeSlot( SMDS_ElemIteratorPtr theSegmentIt,
535 std::vector< SMDS_MeshGroup* > & theGroupsToUpdate)
537 std::vector< Edge > bndEdges;
539 if ( !theSegmentIt || !theSegmentIt->more() || !theMesh || theWidth == 0.)
542 // ----------------------------------------------------------------------------------
543 // put the input segments to a data map in order to be able finding neighboring ones
544 // ----------------------------------------------------------------------------------
546 TSegmentsOfNode segmentsOfNode;
547 ObjectPool< Segment > segmentPool;
549 while( theSegmentIt->more() )
551 const SMDS_MeshElement* edge = theSegmentIt->next();
552 if ( edge->GetType() != SMDSAbs_Edge )
553 throw SALOME_Exception( "A segment is not a mesh edge");
555 Segment* segment = segmentPool.getNew();
556 segment->myEdge = edge;
558 for ( SMDS_NodeIteratorPtr nIt = edge->nodeIterator(); nIt->more(); )
560 const SMDS_MeshNode* n = nIt->next();
561 NodeData* noData = segmentsOfNode.ChangeSeek( n );
563 noData = segmentsOfNode.Bound( n, NodeData() );
564 noData->AddSegment( segment, n );
568 // ---------------------------------
569 // Cut the mesh around the segments
570 // ---------------------------------
572 const double tol = Precision::Confusion();
573 const double angularTol = 1e-5;
574 std::vector< gp_XYZ > faceNormals;
575 SMESH_MeshAlgos::Intersector meshIntersector( theMesh, tol, faceNormals );
576 std::unique_ptr< SMESH_ElementSearcher> faceSearcher;
578 std::vector< NLink > startEdges;
579 std::vector< const SMDS_MeshNode* > faceNodes(4), edgeNodes(2);
580 std::vector<const SMDS_MeshElement *> faces(2);
581 NCollection_Map<const SMDS_MeshElement*, SMESH_Hasher > checkedFaces;
582 std::vector< IntPoint > intPoints, p(2);
583 std::vector< SMESH_NodeXYZ > facePoints(4);
584 std::vector< Intersector::TFace > cutFacePoints;
586 NCollection_DataMap< smIdType, TGroupVec, smIdHasher > faceID2Groups;
589 std::vector< gp_Ax1 > planeNormalVec(2);
590 gp_Ax1 * planeNormal = & planeNormalVec[0];
592 for ( TSegmentIterator segIt( segmentPool ); segIt.more(); ) // loop on all segments
594 Segment* segment = const_cast< Segment* >( segIt.next() );
596 gp_Lin segLine( segment->Ax1() );
597 gp_Ax3 cylAxis( segLine.Location(), segLine.Direction() );
598 gp_Cylinder segCylinder( cylAxis, 0.5 * theWidth );
599 double radius2( segCylinder.Radius() * segCylinder.Radius() );
601 // get normals of planes separating domains of neighboring segments
602 for ( int i = 0; i < 2; ++i ) // loop on 2 segment ends
604 const SMDS_MeshNode* n = segment->Node( i );
605 planeNormal[i] = segmentsOfNode( n ).Plane( segment );
608 // we explore faces around a segment starting from face edges;
609 // initialize a list of starting edges
612 // get a face to start searching intersected faces from
613 const SMDS_MeshNode* n0 = segment->Node( 0 );
614 SMDS_ElemIteratorPtr fIt = n0->GetInverseElementIterator( SMDSAbs_Face );
615 const SMDS_MeshElement* face = ( fIt->more() ) ? fIt->next() : 0;
616 if ( !theMesh->Contains( face ))
619 faceSearcher.reset( SMESH_MeshAlgos::GetElementSearcher( *theMesh ));
620 face = faceSearcher->FindClosestTo( SMESH_NodeXYZ( n0 ), SMDSAbs_Face );
622 // collect face edges
623 int nbNodes = face->NbCornerNodes();
624 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
625 faceNodes.resize( nbNodes + 1 );
626 faceNodes[ nbNodes ] = faceNodes[ 0 ];
627 for ( int i = 0; i < nbNodes; ++i )
628 startEdges.push_back( NLink( faceNodes[i], faceNodes[i+1] ));
631 // intersect faces located around a segment
632 checkedFaces.Clear();
633 while ( !startEdges.empty() )
635 edgeNodes[0] = startEdges[0].first;
636 edgeNodes[1] = startEdges[0].second;
638 theMesh->GetElementsByNodes( edgeNodes, faces, SMDSAbs_Face );
639 for ( size_t iF = 0; iF < faces.size(); ++iF ) // loop on faces sharing a start edge
641 const SMDS_MeshElement* face = faces[iF];
642 if ( !checkedFaces.Add( face ))
645 int nbNodes = face->NbCornerNodes();
647 throw SALOME_Exception( "MakeSlot() accepts triangles only" );
648 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
649 faceNodes.resize( nbNodes + 1 );
650 faceNodes[ nbNodes ] = faceNodes[ 0 ];
651 facePoints.assign( faceNodes.begin(), faceNodes.end() );
653 // check if cylinder axis || face
654 const gp_XYZ& faceNorm = computeNormal( face, faceNormals );
655 bool isCylinderOnFace = ( Abs( faceNorm * cylAxis.Direction().XYZ() ) < tol );
657 if ( !isCylinderOnFace )
659 if ( Intersector::CutByPlanes( face, planeNormalVec, tol, cutFacePoints ))
660 continue; // whole face cut off
661 facePoints.swap( cutFacePoints[0] );
662 facePoints.push_back( facePoints[0] );
665 // find intersection points on face edges
667 int nbPoints = facePoints.size()-1;
669 for ( int i = 0; i < nbPoints; ++i )
671 const SMESH_NodeXYZ& n1 = facePoints[i];
672 const SMESH_NodeXYZ& n2 = facePoints[i+1];
674 size_t iP = intPoints.size();
675 intersectEdge( segCylinder, n1, n2, tol, intPoints );
678 if ( isCylinderOnFace )
679 for ( ; iP < intPoints.size(); ++iP )
680 intPoints[ iP ].myEdgeIndex = i;
682 for ( ; iP < intPoints.size(); ++iP )
683 intPoints[ iP ].myEdgeIndex = edgeIndex( i, n1, n2, face,
684 intPoints[ iP ], faceNodes, tol );
686 nbFarPoints += ( segLine.SquareDistance( n1 ) > radius2 );
690 if ( nbFarPoints < nbPoints || !intPoints.empty() )
691 for ( size_t i = 1; i < faceNodes.size(); ++i )
693 const SMESH_NodeXYZ& n1 = faceNodes[i];
694 const SMESH_NodeXYZ& n2 = faceNodes[i-1];
695 isOut( n1, planeNormal, p[0].myIsOutPln );
696 isOut( n2, planeNormal, p[1].myIsOutPln );
697 if ( !isSegmentOut( p[0].myIsOutPln, p[1].myIsOutPln ))
699 startEdges.push_back( NLink( n1.Node(), n2.Node() ));
703 if ( intPoints.size() < 2 )
706 // classify intPoints by planes
707 for ( size_t i = 0; i < intPoints.size(); ++i )
708 isOut( intPoints[i].myNode, planeNormal, intPoints[i].myIsOutPln );
712 if ( intPoints.size() > 2 )
713 intPoints.push_back( intPoints[0] );
715 for ( size_t iE = 1; iE < intPoints.size(); ++iE ) // 2 <= intPoints.size() <= 5
717 if (( intPoints[iE].myIsOutPln[0] && intPoints[iE].myIsOutPln[1] ) ||
718 ( isSegmentOut( intPoints[iE].myIsOutPln, intPoints[iE-1].myIsOutPln )))
719 continue; // intPoint is out of domain
721 // check if a cutting edge connecting two intPoints is on cylinder surface
722 if ( intPoints[iE].myEdgeIndex == intPoints[iE-1].myEdgeIndex )
723 continue; // on same edge
724 if ( intPoints[iE].myNode.Node() &&
725 intPoints[iE].myNode == intPoints[iE-1].myNode ) // coincide
728 gp_XYZ edegDir = intPoints[iE].myNode - intPoints[iE-1].myNode;
730 bool toCut; // = edegDir.SquareModulus() > tol * tol;
731 if ( intPoints.size() == 2 )
733 else if ( isCylinderOnFace )
734 toCut = cylAxis.Direction().IsParallel( edegDir, angularTol );
737 SMESH_NodeXYZ nBetween;
738 int eInd = intPoints[iE-1].myEdgeIndex;
740 nBetween = facePoints[( 1 - (eInd-1)) % nbPoints ];
742 nBetween = faceNodes[( 1 + eInd ) % nbNodes ];
743 toCut = ( segLine.SquareDistance( nBetween ) > radius2 );
748 // limit the edge by planes
749 if ( intPoints[iE].myIsOutPln[0] ||
750 intPoints[iE].myIsOutPln[1] )
751 cutOff( intPoints[iE], intPoints[iE-1],
752 planeNormal[ intPoints[iE].myIsOutPln[1] ], tol );
754 if ( intPoints[iE-1].myIsOutPln[0] ||
755 intPoints[iE-1].myIsOutPln[1] )
756 cutOff( intPoints[iE-1], intPoints[iE],
757 planeNormal[ intPoints[iE-1].myIsOutPln[1] ], tol );
759 gp_XYZ edegDirNew = intPoints[iE].myNode - intPoints[iE-1].myNode;
760 if ( edegDir * edegDirNew < 0 ||
761 edegDir.SquareModulus() < tol * tol )
762 continue; // fully cut off
764 segment->AddCutEdge( intPoints[iE], intPoints[iE-1], face );
767 } // loop on faces sharing an edge
769 startEdges[0] = startEdges.back();
770 startEdges.pop_back();
772 } // loop on startEdges
773 } // loop on all input segments
776 // ----------------------------------------------------------
777 // If a plane fully cuts off edges of one side of a segment,
778 // it also may cut edges of adjacent segments
779 // ----------------------------------------------------------
781 for ( TSegmentIterator segIt( segmentPool ); segIt.more(); ) // loop on all segments
783 Segment* segment = const_cast< Segment* >( segIt.next() );
784 if ( segment->NbFreeEnds( tol ) >= 4 )
787 for ( int iE = 0; iE < 2; ++iE ) // loop on 2 segment ends
789 const SMDS_MeshNode* n1 = segment->Node( iE );
790 const SMDS_MeshNode* n2 = segment->Node( 1 - iE );
791 planeNormal[0] = segmentsOfNode( n1 ).Plane( segment );
794 Segment* neighborSeg = segment;
795 do // check segments connected to the segment via n2
797 neighborSeg = nextSegment( neighborSeg, n2, segmentsOfNode );
801 isNeighborCut = false;
802 for ( size_t iC = 0; iC < neighborSeg->myCuts.size(); ++iC ) // check cut edges
804 IntPoint* intPnt = &( neighborSeg->myCuts[iC].myIntPnt1 );
805 isOut( intPnt[0].myNode, planeNormal, intPnt[0].myIsOutPln, 1 );
806 isOut( intPnt[1].myNode, planeNormal, intPnt[1].myIsOutPln, 1 );
807 const Segment * closeSeg[2] = { 0, 0 };
808 if ( intPnt[0].myIsOutPln[0] )
809 closeSeg[0] = findTooCloseSegment( intPnt[0], 0.5 * theWidth - 1e-3*tol, tol,
810 segment, n1, segmentsOfNode );
811 if ( intPnt[1].myIsOutPln[0] )
812 closeSeg[1] = findTooCloseSegment( intPnt[1], 0.5 * theWidth - 1e-3*tol, tol,
813 segment, n1, segmentsOfNode );
814 int nbCut = bool( closeSeg[0] ) + bool( closeSeg[1] );
817 isNeighborCut = true;
818 if ( nbCut == 2 ) // remove a cut
820 if ( iC < neighborSeg->myCuts.size() - 1 )
821 neighborSeg->myCuts[iC] = neighborSeg->myCuts.back();
822 neighborSeg->myCuts.pop_back();
824 else // shorten cuts of 1) neighborSeg and 2) closeSeg
827 int iP = bool( closeSeg[1] );
828 gp_Lin segLine( closeSeg[iP]->Ax1() );
829 gp_Ax3 cylAxis( segLine.Location(), segLine.Direction() );
830 gp_Cylinder cyl( cylAxis, 0.5 * theWidth );
832 if ( intersectEdge( cyl, intPnt[iP].myNode, intPnt[1-iP].myNode, tol, intPoints ) &&
833 intPoints[0].SquareDistance( intPnt[iP] ) > tol * tol )
834 intPnt[iP].myNode = intPoints[0].myNode;
837 double minCutDist = theWidth;
838 gp_XYZ projection, closestProj;
840 for ( size_t iC2 = 0; iC2 < closeSeg[iP]->myCuts.size(); ++iC2 )
842 double cutDist = closeSeg[iP]->myCuts[iC2].SquareDistance( intPnt[iP].myNode,
844 if ( cutDist < minCutDist )
846 closestProj = projection;
847 minCutDist = cutDist;
849 if ( minCutDist < tol * tol )
855 double d1 = SMESH_MeshAlgos::GetDistance( neighborSeg->myEdge,
856 closeSeg[iP]->myCuts[iCut][0].myNode );
857 double d2 = SMESH_MeshAlgos::GetDistance( neighborSeg->myEdge,
858 closeSeg[iP]->myCuts[iCut][1].myNode );
859 int iP2 = ( d2 < d1 );
860 IntPoint& ip = const_cast< IntPoint& >( closeSeg[iP]->myCuts[iCut][iP2] );
864 neighborSeg->myFreeEnds.clear();
865 neighborSeg->NbFreeEnds( tol );
868 while ( isNeighborCut );
872 // -----------------------
873 // Cut faces by cut edges
874 // -----------------------
876 for ( TSegmentIterator segIt( segmentPool ); segIt.more(); ) // loop on all segments
878 Segment* segment = const_cast< Segment* >( segIt.next() );
879 for ( size_t iC = 0; iC < segment->myCuts.size(); ++iC )
881 Cut & cut = segment->myCuts[ iC ];
882 computeNormal( cut.myFace, faceNormals );
883 meshIntersector.Cut( cut.myFace,
884 cut.myIntPnt1.myNode, cut.myIntPnt1.myEdgeIndex,
885 cut.myIntPnt2.myNode, cut.myIntPnt2.myEdgeIndex );
887 Edge e = { cut.myIntPnt1.myNode.Node(), cut.myIntPnt2.myNode.Node(), 0 };
888 bndEdges.push_back( e );
890 findGroups( cut.myFace, theGroupsToUpdate, faceID2Groups, groupVec );
894 // -----------------------------------------
895 // Make cut at the end of group of segments
896 // -----------------------------------------
898 std::vector<const SMDS_MeshElement*> polySegments;
900 for ( TSegmentsOfNode::Iterator nSegsIt( segmentsOfNode ); nSegsIt.More(); nSegsIt.Next() )
902 const NodeData& noData = nSegsIt.Value();
903 if ( noData.mySegments.size() != 1 )
906 const Segment* segment = noData.mySegments[0];
908 // find two IntPoint's of cut edges to make a cut between
909 if ( segment->myFreeEnds.size() != 4 )
910 throw SALOME_Exception( "MakeSlot(): too short end edge?" );
911 std::multimap< double, const IntPoint* > dist2IntPntMap;
912 for ( size_t iE = 0; iE < segment->myFreeEnds.size(); ++iE )
914 const SMESH_NodeXYZ& n = segment->myFreeEnds[ iE ]->myNode;
915 double d = Abs( signedDist( n, noData.myPlane ));
916 dist2IntPntMap.insert( std::make_pair( d, segment->myFreeEnds[ iE ]));
918 std::multimap< double, const IntPoint* >::iterator d2ip = dist2IntPntMap.begin();
919 SMESH_MeshAlgos::PolySegment linkNodes;
920 linkNodes.myXYZ[0] = d2ip->second->myNode;
921 linkNodes.myXYZ[1] = (++d2ip)->second->myNode;
922 linkNodes.myVector = noData.myPlane.Direction() ^ (linkNodes.myXYZ[0] - linkNodes.myXYZ[1]);
923 linkNodes.myNode1[ 0 ] = linkNodes.myNode2[ 0 ] = 0;
924 linkNodes.myNode1[ 1 ] = linkNodes.myNode2[ 1 ] = 0;
926 // create segments connecting linkNodes
927 std::vector<const SMDS_MeshElement*> newSegments;
928 std::vector<const SMDS_MeshNode*> newNodes;
929 SMESH_MeshAlgos::TListOfPolySegments polySegs(1, linkNodes);
930 SMESH_MeshAlgos::MakePolyLine( theMesh, polySegs, newSegments, newNodes,
931 /*group=*/0, faceSearcher.get() );
932 // cut faces by newSegments
934 for ( size_t i = 0; i < newSegments.size(); ++i )
936 intPoints[0].myNode = edgeNodes[0] = newSegments[i]->GetNode(0);
937 intPoints[1].myNode = edgeNodes[1] = newSegments[i]->GetNode(1);
939 // find an underlying face
940 gp_XYZ middle = 0.5 * ( intPoints[0].myNode + intPoints[1].myNode );
941 const SMDS_MeshElement* face = faceSearcher->FindClosestTo( middle, SMDSAbs_Face );
943 // find intersected edges of the face
944 int nbNodes = face->NbCornerNodes();
945 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
946 faceNodes.resize( nbNodes + 1 );
947 faceNodes[ nbNodes ] = faceNodes[ 0 ];
948 for ( int iP = 0; iP < 2; ++iP )
950 intPoints[iP].myEdgeIndex = -1;
951 for ( int iN = 0; iN < nbNodes && intPoints[iP].myEdgeIndex < 0; ++iN )
953 if ( isOnEdge( faceNodes[iN], faceNodes[iN+1], intPoints[iP].myNode, tol ))
954 intPoints[iP].myEdgeIndex = iN;
960 computeNormal( face, faceNormals );
961 meshIntersector.Cut( face,
962 intPoints[0].myNode, intPoints[0].myEdgeIndex,
963 intPoints[1].myNode, intPoints[1].myEdgeIndex );
965 Edge e = { intPoints[0].myNode.Node(), intPoints[1].myNode.Node(), 0 };
966 bndEdges.push_back( e );
968 findGroups( face, theGroupsToUpdate, faceID2Groups, groupVec );
970 // add cut points to an adjacent face at ends of poly-line
971 // if they fall onto face edges
972 if (( i == 0 && intPoints[0].myEdgeIndex >= 0 ) ||
973 ( i == newSegments.size() - 1 && intPoints[1].myEdgeIndex >= 0 ))
975 for ( int iE = 0; iE < 2; ++iE ) // loop on ends of a new segment
977 if ( iE ? ( i != newSegments.size() - 1 ) : ( i != 0 ))
979 int iEdge = intPoints[ iE ].myEdgeIndex;
980 edgeNodes[0] = faceNodes[ iEdge ];
981 edgeNodes[1] = faceNodes[ iEdge+1 ];
982 theMesh->GetElementsByNodes( edgeNodes, faces, SMDSAbs_Face );
983 for ( size_t iF = 0; iF < faces.size(); ++iF )
984 if ( faces[iF] != face )
986 int iN1 = faces[iF]->GetNodeIndex( edgeNodes[0] );
987 int iN2 = faces[iF]->GetNodeIndex( edgeNodes[1] );
988 intPoints[ iE ].myEdgeIndex = Abs( iN1 - iN2 ) == 1 ? Min( iN1, iN2 ) : 2;
989 computeNormal( faces[iF], faceNormals );
990 meshIntersector.Cut( faces[iF],
991 intPoints[iE].myNode, intPoints[iE].myEdgeIndex,
992 intPoints[iE].myNode, intPoints[iE].myEdgeIndex );
994 findGroups( faces[iF], theGroupsToUpdate, faceID2Groups, groupVec );
999 } // loop on newSegments
1001 polySegments.insert( polySegments.end(), newSegments.begin(), newSegments.end() );
1003 } // loop on map of input segments
1005 // actual mesh splitting
1006 TElemIntPairVec new2OldFaces;
1007 TNodeIntPairVec new2OldNodes;
1008 meshIntersector.MakeNewFaces( new2OldFaces, new2OldNodes, /*sign=*/1, /*optimize=*/true );
1010 // add new faces to theGroupsToUpdate
1011 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
1013 const SMDS_MeshElement* newFace = new2OldFaces[i].first;
1014 const int oldFaceID = new2OldFaces[i].second;
1015 if ( !newFace ) continue;
1017 if ( TGroupVec* groups = const_cast< TGroupVec* >( faceID2Groups.Seek( oldFaceID )))
1018 for ( size_t iG = 0; iG < groups->size(); ++iG )
1019 (*groups)[ iG ]->Add( newFace );
1022 // remove poly-line edges
1023 for ( size_t i = 0; i < polySegments.size(); ++i )
1025 edgeNodes[0] = polySegments[i]->GetNode(0);
1026 edgeNodes[1] = polySegments[i]->GetNode(1);
1028 theMesh->RemoveFreeElement( polySegments[i] );
1030 if ( edgeNodes[0]->NbInverseElements() == 0 )
1031 theMesh->RemoveNode( edgeNodes[0] );
1032 if ( edgeNodes[1]->NbInverseElements() == 0 )
1033 theMesh->RemoveNode( edgeNodes[1] );