1 // Copyright (C) 2007-2013 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.
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
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
86 #include <gp_Trsf.hxx>
100 #include <boost/tuple/tuple.hpp>
102 #include <Standard_Failure.hxx>
103 #include <Standard_ErrorHandler.hxx>
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
110 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
111 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
113 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
115 //=======================================================================
116 //function : SMESH_MeshEditor
118 //=======================================================================
120 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
121 :myMesh( theMesh ) // theMesh may be NULL
125 //================================================================================
127 * \brief Clears myLastCreatedNodes and myLastCreatedElems
129 //================================================================================
131 void SMESH_MeshEditor::CrearLastCreated()
133 myLastCreatedNodes.Clear();
134 myLastCreatedElems.Clear();
138 //=======================================================================
142 //=======================================================================
145 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
146 const SMDSAbs_ElementType type,
149 const double ballDiameter)
151 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
152 SMDS_MeshElement* e = 0;
153 int nbnode = node.size();
154 SMESHDS_Mesh* mesh = GetMeshDS();
159 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
160 else e = mesh->AddFace (node[0], node[1], node[2] );
162 else if (nbnode == 4) {
163 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
164 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
166 else if (nbnode == 6) {
167 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
168 node[4], node[5], ID);
169 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
172 else if (nbnode == 8) {
173 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
174 node[4], node[5], node[6], node[7], ID);
175 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
176 node[4], node[5], node[6], node[7] );
178 else if (nbnode == 9) {
179 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
180 node[4], node[5], node[6], node[7], node[8], ID);
181 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
182 node[4], node[5], node[6], node[7], node[8] );
185 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
186 else e = mesh->AddPolygonalFace (node );
193 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
194 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
196 else if (nbnode == 5) {
197 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
199 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
202 else if (nbnode == 6) {
203 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
204 node[4], node[5], ID);
205 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
208 else if (nbnode == 8) {
209 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], node[7], ID);
211 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], node[7] );
214 else if (nbnode == 10) {
215 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7],
217 node[8], node[9], ID);
218 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7],
222 else if (nbnode == 12) {
223 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7],
225 node[8], node[9], node[10], node[11], ID);
226 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7],
228 node[8], node[9], node[10], node[11] );
230 else if (nbnode == 13) {
231 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232 node[4], node[5], node[6], node[7],
233 node[8], node[9], node[10],node[11],
235 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
236 node[4], node[5], node[6], node[7],
237 node[8], node[9], node[10],node[11],
240 else if (nbnode == 15) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
242 node[4], node[5], node[6], node[7],
243 node[8], node[9], node[10],node[11],
244 node[12],node[13],node[14],ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
246 node[4], node[5], node[6], node[7],
247 node[8], node[9], node[10],node[11],
248 node[12],node[13],node[14] );
250 else if (nbnode == 20) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], node[6], node[7],
253 node[8], node[9], node[10],node[11],
254 node[12],node[13],node[14],node[15],
255 node[16],node[17],node[18],node[19],ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
257 node[4], node[5], node[6], node[7],
258 node[8], node[9], node[10],node[11],
259 node[12],node[13],node[14],node[15],
260 node[16],node[17],node[18],node[19] );
262 else if (nbnode == 27) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], node[10],node[11],
266 node[12],node[13],node[14],node[15],
267 node[16],node[17],node[18],node[19],
268 node[20],node[21],node[22],node[23],
269 node[24],node[25],node[26], ID);
270 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
271 node[4], node[5], node[6], node[7],
272 node[8], node[9], node[10],node[11],
273 node[12],node[13],node[14],node[15],
274 node[16],node[17],node[18],node[19],
275 node[20],node[21],node[22],node[23],
276 node[24],node[25],node[26] );
283 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
284 else e = mesh->AddEdge (node[0], node[1] );
286 else if ( nbnode == 3 ) {
287 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
288 else e = mesh->AddEdge (node[0], node[1], node[2] );
292 case SMDSAbs_0DElement:
294 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
295 else e = mesh->Add0DElement (node[0] );
300 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
301 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
305 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
306 else e = mesh->AddBall (node[0], ballDiameter);
311 if ( e ) myLastCreatedElems.Append( e );
315 //=======================================================================
319 //=======================================================================
321 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
322 const SMDSAbs_ElementType type,
326 vector<const SMDS_MeshNode*> nodes;
327 nodes.reserve( nodeIDs.size() );
328 vector<int>::const_iterator id = nodeIDs.begin();
329 while ( id != nodeIDs.end() ) {
330 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
331 nodes.push_back( node );
335 return AddElement( nodes, type, isPoly, ID );
338 //=======================================================================
340 //purpose : Remove a node or an element.
341 // Modify a compute state of sub-meshes which become empty
342 //=======================================================================
344 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
347 myLastCreatedElems.Clear();
348 myLastCreatedNodes.Clear();
350 SMESHDS_Mesh* aMesh = GetMeshDS();
351 set< SMESH_subMesh *> smmap;
354 list<int>::const_iterator it = theIDs.begin();
355 for ( ; it != theIDs.end(); it++ ) {
356 const SMDS_MeshElement * elem;
358 elem = aMesh->FindNode( *it );
360 elem = aMesh->FindElement( *it );
364 // Notify VERTEX sub-meshes about modification
366 const SMDS_MeshNode* node = cast2Node( elem );
367 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
368 if ( int aShapeID = node->getshapeId() )
369 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
372 // Find sub-meshes to notify about modification
373 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
374 // while ( nodeIt->more() ) {
375 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
376 // const SMDS_PositionPtr& aPosition = node->GetPosition();
377 // if ( aPosition.get() ) {
378 // if ( int aShapeID = aPosition->GetShapeId() ) {
379 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
380 // smmap.insert( sm );
387 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
389 aMesh->RemoveElement( elem );
393 // Notify sub-meshes about modification
394 if ( !smmap.empty() ) {
395 set< SMESH_subMesh *>::iterator smIt;
396 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
397 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
400 // // Check if the whole mesh becomes empty
401 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
402 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
407 //================================================================================
409 * \brief Create 0D elements on all nodes of the given object except those
410 * nodes on which a 0D element already exists.
411 * \param elements - Elements on whose nodes to create 0D elements; if empty,
412 * the all mesh is treated
413 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
415 //================================================================================
417 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
418 TIDSortedElemSet& all0DElems )
420 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
421 SMDS_ElemIteratorPtr elemIt;
422 if ( elements.empty() )
423 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
425 elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
427 while ( elemIt->more() )
429 const SMDS_MeshElement* e = elemIt->next();
430 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
431 while ( nodeIt->more() )
433 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
434 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
436 all0DElems.insert( it0D->next() );
438 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
439 all0DElems.insert( myLastCreatedElems.Last() );
445 //=======================================================================
446 //function : FindShape
447 //purpose : Return an index of the shape theElem is on
448 // or zero if a shape not found
449 //=======================================================================
451 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
453 myLastCreatedElems.Clear();
454 myLastCreatedNodes.Clear();
456 SMESHDS_Mesh * aMesh = GetMeshDS();
457 if ( aMesh->ShapeToMesh().IsNull() )
460 int aShapeID = theElem->getshapeId();
464 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
465 if ( sm->Contains( theElem ))
468 if ( theElem->GetType() == SMDSAbs_Node ) {
469 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
472 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
475 TopoDS_Shape aShape; // the shape a node of theElem is on
476 if ( theElem->GetType() != SMDSAbs_Node )
478 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
479 while ( nodeIt->more() ) {
480 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
481 if ((aShapeID = node->getshapeId()) > 0) {
482 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
483 if ( sm->Contains( theElem ))
485 if ( aShape.IsNull() )
486 aShape = aMesh->IndexToShape( aShapeID );
492 // None of nodes is on a proper shape,
493 // find the shape among ancestors of aShape on which a node is
494 if ( !aShape.IsNull() ) {
495 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
496 for ( ; ancIt.More(); ancIt.Next() ) {
497 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
498 if ( sm && sm->Contains( theElem ))
499 return aMesh->ShapeToIndex( ancIt.Value() );
504 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
505 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
506 for ( ; id_sm != id2sm.end(); ++id_sm )
507 if ( id_sm->second->Contains( theElem ))
511 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
515 //=======================================================================
516 //function : IsMedium
518 //=======================================================================
520 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
521 const SMDSAbs_ElementType typeToCheck)
523 bool isMedium = false;
524 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
525 while (it->more() && !isMedium ) {
526 const SMDS_MeshElement* elem = it->next();
527 isMedium = elem->IsMediumNode(node);
532 //=======================================================================
533 //function : ShiftNodesQuadTria
535 // Shift nodes in the array corresponded to quadratic triangle
536 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
537 //=======================================================================
538 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
540 const SMDS_MeshNode* nd1 = aNodes[0];
541 aNodes[0] = aNodes[1];
542 aNodes[1] = aNodes[2];
544 const SMDS_MeshNode* nd2 = aNodes[3];
545 aNodes[3] = aNodes[4];
546 aNodes[4] = aNodes[5];
550 //=======================================================================
551 //function : edgeConnectivity
553 // return number of the edges connected with the theNode.
554 // if theEdges has connections with the other type of the
555 // elements, return -1
556 //=======================================================================
557 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
559 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
561 while(elemIt->more()) {
569 //=======================================================================
570 //function : GetNodesFromTwoTria
572 // Shift nodes in the array corresponded to quadratic triangle
573 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
574 //=======================================================================
575 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
576 const SMDS_MeshElement * theTria2,
577 const SMDS_MeshNode* N1[],
578 const SMDS_MeshNode* N2[])
580 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
583 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
586 if(it->more()) return false;
587 it = theTria2->nodesIterator();
590 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
593 if(it->more()) return false;
595 int sames[3] = {-1,-1,-1};
607 if(nbsames!=2) return false;
609 ShiftNodesQuadTria(N1);
611 ShiftNodesQuadTria(N1);
614 i = sames[0] + sames[1] + sames[2];
616 ShiftNodesQuadTria(N2);
618 // now we receive following N1 and N2 (using numeration as above image)
619 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
620 // i.e. first nodes from both arrays determ new diagonal
624 //=======================================================================
625 //function : InverseDiag
626 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
627 // but having other common link.
628 // Return False if args are improper
629 //=======================================================================
631 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
632 const SMDS_MeshElement * theTria2 )
634 MESSAGE("InverseDiag");
635 myLastCreatedElems.Clear();
636 myLastCreatedNodes.Clear();
638 if (!theTria1 || !theTria2)
641 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
642 if (!F1) return false;
643 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
644 if (!F2) return false;
645 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
646 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
648 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
649 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
653 // put nodes in array and find out indices of the same ones
654 const SMDS_MeshNode* aNodes [6];
655 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
657 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
658 while ( it->more() ) {
659 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
661 if ( i > 2 ) // theTria2
662 // find same node of theTria1
663 for ( int j = 0; j < 3; j++ )
664 if ( aNodes[ i ] == aNodes[ j ]) {
673 return false; // theTria1 is not a triangle
674 it = theTria2->nodesIterator();
676 if ( i == 6 && it->more() )
677 return false; // theTria2 is not a triangle
680 // find indices of 1,2 and of A,B in theTria1
681 int iA = 0, iB = 0, i1 = 0, i2 = 0;
682 for ( i = 0; i < 6; i++ ) {
683 if ( sameInd [ i ] == 0 ) {
692 // nodes 1 and 2 should not be the same
693 if ( aNodes[ i1 ] == aNodes[ i2 ] )
697 aNodes[ iA ] = aNodes[ i2 ];
699 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
701 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
702 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
706 } // end if(F1 && F2)
708 // check case of quadratic faces
709 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
711 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
715 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
716 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
724 const SMDS_MeshNode* N1 [6];
725 const SMDS_MeshNode* N2 [6];
726 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
728 // now we receive following N1 and N2 (using numeration as above image)
729 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
730 // i.e. first nodes from both arrays determ new diagonal
732 const SMDS_MeshNode* N1new [6];
733 const SMDS_MeshNode* N2new [6];
746 // replaces nodes in faces
747 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
748 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
753 //=======================================================================
754 //function : findTriangles
755 //purpose : find triangles sharing theNode1-theNode2 link
756 //=======================================================================
758 static bool findTriangles(const SMDS_MeshNode * theNode1,
759 const SMDS_MeshNode * theNode2,
760 const SMDS_MeshElement*& theTria1,
761 const SMDS_MeshElement*& theTria2)
763 if ( !theNode1 || !theNode2 ) return false;
765 theTria1 = theTria2 = 0;
767 set< const SMDS_MeshElement* > emap;
768 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
770 const SMDS_MeshElement* elem = it->next();
771 if ( elem->NbNodes() == 3 )
774 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
776 const SMDS_MeshElement* elem = it->next();
777 if ( emap.find( elem ) != emap.end() ) {
779 // theTria1 must be element with minimum ID
780 if( theTria1->GetID() < elem->GetID() ) {
794 return ( theTria1 && theTria2 );
797 //=======================================================================
798 //function : InverseDiag
799 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
800 // with ones built on the same 4 nodes but having other common link.
801 // Return false if proper faces not found
802 //=======================================================================
804 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
805 const SMDS_MeshNode * theNode2)
807 myLastCreatedElems.Clear();
808 myLastCreatedNodes.Clear();
810 MESSAGE( "::InverseDiag()" );
812 const SMDS_MeshElement *tr1, *tr2;
813 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
816 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
817 if (!F1) return false;
818 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
819 if (!F2) return false;
820 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
821 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
823 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
824 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
828 // put nodes in array
829 // and find indices of 1,2 and of A in tr1 and of B in tr2
830 int i, iA1 = 0, i1 = 0;
831 const SMDS_MeshNode* aNodes1 [3];
832 SMDS_ElemIteratorPtr it;
833 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
834 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
835 if ( aNodes1[ i ] == theNode1 )
836 iA1 = i; // node A in tr1
837 else if ( aNodes1[ i ] != theNode2 )
841 const SMDS_MeshNode* aNodes2 [3];
842 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
843 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
844 if ( aNodes2[ i ] == theNode2 )
845 iB2 = i; // node B in tr2
846 else if ( aNodes2[ i ] != theNode1 )
850 // nodes 1 and 2 should not be the same
851 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
855 aNodes1[ iA1 ] = aNodes2[ i2 ];
857 aNodes2[ iB2 ] = aNodes1[ i1 ];
859 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
860 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
865 // check case of quadratic faces
866 return InverseDiag(tr1,tr2);
869 //=======================================================================
870 //function : getQuadrangleNodes
871 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
872 // fusion of triangles tr1 and tr2 having shared link on
873 // theNode1 and theNode2
874 //=======================================================================
876 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
877 const SMDS_MeshNode * theNode1,
878 const SMDS_MeshNode * theNode2,
879 const SMDS_MeshElement * tr1,
880 const SMDS_MeshElement * tr2 )
882 if( tr1->NbNodes() != tr2->NbNodes() )
884 // find the 4-th node to insert into tr1
885 const SMDS_MeshNode* n4 = 0;
886 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
888 while ( !n4 && i<3 ) {
889 const SMDS_MeshNode * n = cast2Node( it->next() );
891 bool isDiag = ( n == theNode1 || n == theNode2 );
895 // Make an array of nodes to be in a quadrangle
896 int iNode = 0, iFirstDiag = -1;
897 it = tr1->nodesIterator();
900 const SMDS_MeshNode * n = cast2Node( it->next() );
902 bool isDiag = ( n == theNode1 || n == theNode2 );
904 if ( iFirstDiag < 0 )
906 else if ( iNode - iFirstDiag == 1 )
907 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
909 else if ( n == n4 ) {
910 return false; // tr1 and tr2 should not have all the same nodes
912 theQuadNodes[ iNode++ ] = n;
914 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
915 theQuadNodes[ iNode ] = n4;
920 //=======================================================================
921 //function : DeleteDiag
922 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
923 // with a quadrangle built on the same 4 nodes.
924 // Return false if proper faces not found
925 //=======================================================================
927 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
928 const SMDS_MeshNode * theNode2)
930 myLastCreatedElems.Clear();
931 myLastCreatedNodes.Clear();
933 MESSAGE( "::DeleteDiag()" );
935 const SMDS_MeshElement *tr1, *tr2;
936 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
939 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
940 if (!F1) return false;
941 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
942 if (!F2) return false;
943 SMESHDS_Mesh * aMesh = GetMeshDS();
945 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
946 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
948 const SMDS_MeshNode* aNodes [ 4 ];
949 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
952 const SMDS_MeshElement* newElem = 0;
953 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
954 myLastCreatedElems.Append(newElem);
955 AddToSameGroups( newElem, tr1, aMesh );
956 int aShapeId = tr1->getshapeId();
959 aMesh->SetMeshElementOnShape( newElem, aShapeId );
961 aMesh->RemoveElement( tr1 );
962 aMesh->RemoveElement( tr2 );
967 // check case of quadratic faces
968 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
970 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
974 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
975 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
983 const SMDS_MeshNode* N1 [6];
984 const SMDS_MeshNode* N2 [6];
985 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
987 // now we receive following N1 and N2 (using numeration as above image)
988 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
989 // i.e. first nodes from both arrays determ new diagonal
991 const SMDS_MeshNode* aNodes[8];
1001 const SMDS_MeshElement* newElem = 0;
1002 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1003 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1004 myLastCreatedElems.Append(newElem);
1005 AddToSameGroups( newElem, tr1, aMesh );
1006 int aShapeId = tr1->getshapeId();
1009 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1011 aMesh->RemoveElement( tr1 );
1012 aMesh->RemoveElement( tr2 );
1014 // remove middle node (9)
1015 GetMeshDS()->RemoveNode( N1[4] );
1020 //=======================================================================
1021 //function : Reorient
1022 //purpose : Reverse theElement orientation
1023 //=======================================================================
1025 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1027 MESSAGE("Reorient");
1028 myLastCreatedElems.Clear();
1029 myLastCreatedNodes.Clear();
1033 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1034 if ( !it || !it->more() )
1037 switch ( theElem->GetType() ) {
1040 case SMDSAbs_Face: {
1041 if(!theElem->IsQuadratic()) {
1042 int i = theElem->NbNodes();
1043 vector<const SMDS_MeshNode*> aNodes( i );
1044 while ( it->more() )
1045 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1046 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1049 // quadratic elements
1050 if(theElem->GetType()==SMDSAbs_Edge) {
1051 vector<const SMDS_MeshNode*> aNodes(3);
1052 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1053 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1054 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1055 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1058 int nbn = theElem->NbNodes();
1059 vector<const SMDS_MeshNode*> aNodes(nbn);
1060 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1062 for(; i<nbn/2; i++) {
1063 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1065 for(i=0; i<nbn/2; i++) {
1066 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1068 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1072 case SMDSAbs_Volume: {
1073 if (theElem->IsPoly()) {
1074 // TODO reorient vtk polyhedron
1075 MESSAGE("reorient vtk polyhedron ?");
1076 const SMDS_VtkVolume* aPolyedre =
1077 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1079 MESSAGE("Warning: bad volumic element");
1083 int nbFaces = aPolyedre->NbFaces();
1084 vector<const SMDS_MeshNode *> poly_nodes;
1085 vector<int> quantities (nbFaces);
1087 // reverse each face of the polyedre
1088 for (int iface = 1; iface <= nbFaces; iface++) {
1089 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1090 quantities[iface - 1] = nbFaceNodes;
1092 for (inode = nbFaceNodes; inode >= 1; inode--) {
1093 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1094 poly_nodes.push_back(curNode);
1098 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1102 SMDS_VolumeTool vTool;
1103 if ( !vTool.Set( theElem ))
1106 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1107 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1116 //================================================================================
1118 * \brief Reorient faces.
1119 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1120 * \param theDirection - desired direction of normal of \a theFace
1121 * \param theFace - one of \a theFaces that sould be oriented according to
1122 * \a theDirection and whose orientation defines orientation of other faces
1123 * \return number of reoriented faces.
1125 //================================================================================
1127 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1128 const gp_Dir& theDirection,
1129 const SMDS_MeshElement * theFace)
1132 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1134 if ( theFaces.empty() )
1136 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1137 while ( fIt->more() )
1138 theFaces.insert( theFaces.end(), fIt->next() );
1141 // orient theFace according to theDirection
1143 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1144 if ( normal * theDirection.XYZ() < 0 )
1145 nbReori += Reorient( theFace );
1147 // Orient other faces
1149 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1150 TIDSortedElemSet avoidSet;
1151 set< SMESH_TLink > checkedLinks;
1152 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1154 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1155 theFaces.erase( theFace );
1156 startFaces.insert( theFace );
1158 int nodeInd1, nodeInd2;
1159 const SMDS_MeshElement* otherFace;
1160 vector< const SMDS_MeshElement* > facesNearLink;
1161 vector< std::pair< int, int > > nodeIndsOfFace;
1163 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1164 while ( !startFaces.empty() )
1166 startFace = startFaces.begin();
1167 theFace = *startFace;
1168 startFaces.erase( startFace );
1169 if ( !visitedFaces.insert( theFace ).second )
1173 avoidSet.insert(theFace);
1175 NLink link( theFace->GetNode( 0 ), 0 );
1177 const int nbNodes = theFace->NbCornerNodes();
1178 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1180 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1181 linkIt_isNew = checkedLinks.insert( link );
1182 if ( !linkIt_isNew.second )
1184 // link has already been checked and won't be encountered more
1185 // if the group (theFaces) is manifold
1186 //checkedLinks.erase( linkIt_isNew.first );
1190 facesNearLink.clear();
1191 nodeIndsOfFace.clear();
1192 while (( otherFace = FindFaceInSet( link.first, link.second,
1193 theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1194 if ( otherFace != theFace)
1196 facesNearLink.push_back( otherFace );
1197 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1198 avoidSet.insert( otherFace );
1200 if ( facesNearLink.size() > 1 )
1202 // NON-MANIFOLD mesh shell !
1203 // select a face most co-directed with theFace,
1204 // other faces won't be visited this time
1206 SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1207 double proj, maxProj = -1;
1208 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1209 SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1210 if (( proj = Abs( NF * NOF )) > maxProj ) {
1212 otherFace = facesNearLink[i];
1213 nodeInd1 = nodeIndsOfFace[i].first;
1214 nodeInd2 = nodeIndsOfFace[i].second;
1217 // not to visit rejected faces
1218 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1219 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1220 visitedFaces.insert( facesNearLink[i] );
1222 else if ( facesNearLink.size() == 1 )
1224 otherFace = facesNearLink[0];
1225 nodeInd1 = nodeIndsOfFace.back().first;
1226 nodeInd2 = nodeIndsOfFace.back().second;
1228 if ( otherFace && otherFace != theFace)
1230 // link must be reverse in otherFace if orientation ot otherFace
1231 // is same as that of theFace
1232 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1234 nbReori += Reorient( otherFace );
1236 startFaces.insert( otherFace );
1239 std::swap( link.first, link.second ); // reverse the link
1245 //=======================================================================
1246 //function : getBadRate
1248 //=======================================================================
1250 static double getBadRate (const SMDS_MeshElement* theElem,
1251 SMESH::Controls::NumericalFunctorPtr& theCrit)
1253 SMESH::Controls::TSequenceOfXYZ P;
1254 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1256 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1257 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1260 //=======================================================================
1261 //function : QuadToTri
1262 //purpose : Cut quadrangles into triangles.
1263 // theCrit is used to select a diagonal to cut
1264 //=======================================================================
1266 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1267 SMESH::Controls::NumericalFunctorPtr theCrit)
1269 myLastCreatedElems.Clear();
1270 myLastCreatedNodes.Clear();
1272 MESSAGE( "::QuadToTri()" );
1274 if ( !theCrit.get() )
1277 SMESHDS_Mesh * aMesh = GetMeshDS();
1279 Handle(Geom_Surface) surface;
1280 SMESH_MesherHelper helper( *GetMesh() );
1282 TIDSortedElemSet::iterator itElem;
1283 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1284 const SMDS_MeshElement* elem = *itElem;
1285 if ( !elem || elem->GetType() != SMDSAbs_Face )
1287 if ( elem->NbCornerNodes() != 4 )
1290 // retrieve element nodes
1291 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1293 // compare two sets of possible triangles
1294 double aBadRate1, aBadRate2; // to what extent a set is bad
1295 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1296 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1297 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1299 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1300 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1301 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1303 int aShapeId = FindShape( elem );
1304 const SMDS_MeshElement* newElem1 = 0;
1305 const SMDS_MeshElement* newElem2 = 0;
1307 if( !elem->IsQuadratic() ) {
1309 // split liner quadrangle
1310 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1311 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1312 if ( aBadRate1 <= aBadRate2 ) {
1313 // tr1 + tr2 is better
1314 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1315 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1318 // tr3 + tr4 is better
1319 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1320 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1325 // split quadratic quadrangle
1327 // get surface elem is on
1328 if ( aShapeId != helper.GetSubShapeID() ) {
1332 shape = aMesh->IndexToShape( aShapeId );
1333 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1334 TopoDS_Face face = TopoDS::Face( shape );
1335 surface = BRep_Tool::Surface( face );
1336 if ( !surface.IsNull() )
1337 helper.SetSubShape( shape );
1340 // find middle point for (0,1,2,3)
1341 // and create a node in this point;
1342 const SMDS_MeshNode* newN = 0;
1343 if ( aNodes.size() == 9 )
1345 // SMDSEntity_BiQuad_Quadrangle
1346 newN = aNodes.back();
1351 if ( surface.IsNull() )
1353 for ( int i = 0; i < 4; i++ )
1354 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1359 const SMDS_MeshNode* inFaceNode = 0;
1360 if ( helper.GetNodeUVneedInFaceNode() )
1361 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1362 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1363 inFaceNode = aNodes[ i ];
1365 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1367 for ( int i = 0; i < 4; i++ )
1368 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1370 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1372 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1373 myLastCreatedNodes.Append(newN);
1375 // create a new element
1376 if ( aBadRate1 <= aBadRate2 ) {
1377 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1378 aNodes[6], aNodes[7], newN );
1379 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1380 newN, aNodes[4], aNodes[5] );
1383 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1384 aNodes[7], aNodes[4], newN );
1385 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1386 newN, aNodes[5], aNodes[6] );
1390 // care of a new element
1392 myLastCreatedElems.Append(newElem1);
1393 myLastCreatedElems.Append(newElem2);
1394 AddToSameGroups( newElem1, elem, aMesh );
1395 AddToSameGroups( newElem2, elem, aMesh );
1397 // put a new triangle on the same shape
1400 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1401 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1403 aMesh->RemoveElement( elem );
1408 //=======================================================================
1409 //function : BestSplit
1410 //purpose : Find better diagonal for cutting.
1411 //=======================================================================
1413 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1414 SMESH::Controls::NumericalFunctorPtr theCrit)
1416 myLastCreatedElems.Clear();
1417 myLastCreatedNodes.Clear();
1422 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1425 if( theQuad->NbNodes()==4 ||
1426 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1428 // retrieve element nodes
1429 const SMDS_MeshNode* aNodes [4];
1430 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1432 //while (itN->more())
1434 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1436 // compare two sets of possible triangles
1437 double aBadRate1, aBadRate2; // to what extent a set is bad
1438 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1439 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1440 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1442 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1443 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1444 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1445 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1448 return 1; // diagonal 1-3
1450 return 2; // diagonal 2-4
1457 // Methods of splitting volumes into tetra
1459 const int theHexTo5_1[5*4+1] =
1461 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1463 const int theHexTo5_2[5*4+1] =
1465 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1467 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1469 const int theHexTo6_1[6*4+1] =
1471 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1473 const int theHexTo6_2[6*4+1] =
1475 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1477 const int theHexTo6_3[6*4+1] =
1479 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1481 const int theHexTo6_4[6*4+1] =
1483 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1485 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1487 const int thePyraTo2_1[2*4+1] =
1489 0, 1, 2, 4, 0, 2, 3, 4, -1
1491 const int thePyraTo2_2[2*4+1] =
1493 1, 2, 3, 4, 1, 3, 0, 4, -1
1495 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1497 const int thePentaTo3_1[3*4+1] =
1499 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1501 const int thePentaTo3_2[3*4+1] =
1503 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1505 const int thePentaTo3_3[3*4+1] =
1507 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1509 const int thePentaTo3_4[3*4+1] =
1511 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1513 const int thePentaTo3_5[3*4+1] =
1515 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1517 const int thePentaTo3_6[3*4+1] =
1519 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1521 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1522 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1524 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1527 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1528 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1529 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1534 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1535 bool _baryNode; //!< additional node is to be created at cell barycenter
1536 bool _ownConn; //!< to delete _connectivity in destructor
1537 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1539 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1540 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1541 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1542 bool hasFacet( const TTriangleFacet& facet ) const
1544 const int* tetConn = _connectivity;
1545 for ( ; tetConn[0] >= 0; tetConn += 4 )
1546 if (( facet.contains( tetConn[0] ) +
1547 facet.contains( tetConn[1] ) +
1548 facet.contains( tetConn[2] ) +
1549 facet.contains( tetConn[3] )) == 3 )
1555 //=======================================================================
1557 * \brief return TSplitMethod for the given element
1559 //=======================================================================
1561 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1563 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1565 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1566 // an edge and a face barycenter; tertaherdons are based on triangles and
1567 // a volume barycenter
1568 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1570 // Find out how adjacent volumes are split
1572 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1573 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1574 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1576 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1577 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1578 if ( nbNodes < 4 ) continue;
1580 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1581 const int* nInd = vol.GetFaceNodesIndices( iF );
1584 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1585 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1586 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1587 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1591 int iCom = 0; // common node of triangle faces to split into
1592 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1594 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1595 nInd[ iQ * ( (iCom+1)%nbNodes )],
1596 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1597 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1598 nInd[ iQ * ( (iCom+2)%nbNodes )],
1599 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1600 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1602 triaSplits.push_back( t012 );
1603 triaSplits.push_back( t023 );
1608 if ( !triaSplits.empty() )
1609 hasAdjacentSplits = true;
1612 // Among variants of split method select one compliant with adjacent volumes
1614 TSplitMethod method;
1615 if ( !vol.Element()->IsPoly() && !is24TetMode )
1617 int nbVariants = 2, nbTet = 0;
1618 const int** connVariants = 0;
1619 switch ( vol.Element()->GetEntityType() )
1621 case SMDSEntity_Hexa:
1622 case SMDSEntity_Quad_Hexa:
1623 case SMDSEntity_TriQuad_Hexa:
1624 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1625 connVariants = theHexTo5, nbTet = 5;
1627 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1629 case SMDSEntity_Pyramid:
1630 case SMDSEntity_Quad_Pyramid:
1631 connVariants = thePyraTo2; nbTet = 2;
1633 case SMDSEntity_Penta:
1634 case SMDSEntity_Quad_Penta:
1635 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1640 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1642 // check method compliancy with adjacent tetras,
1643 // all found splits must be among facets of tetras described by this method
1644 method = TSplitMethod( nbTet, connVariants[variant] );
1645 if ( hasAdjacentSplits && method._nbTetra > 0 )
1647 bool facetCreated = true;
1648 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1650 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1651 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1652 facetCreated = method.hasFacet( *facet );
1654 if ( !facetCreated )
1655 method = TSplitMethod(0); // incompatible method
1659 if ( method._nbTetra < 1 )
1661 // No standard method is applicable, use a generic solution:
1662 // each facet of a volume is split into triangles and
1663 // each of triangles and a volume barycenter form a tetrahedron.
1665 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1667 int* connectivity = new int[ maxTetConnSize + 1 ];
1668 method._connectivity = connectivity;
1669 method._ownConn = true;
1670 method._baryNode = !isHex27; // to create central node or not
1673 int baryCenInd = vol.NbNodes() - int( isHex27 );
1674 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1676 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1677 const int* nInd = vol.GetFaceNodesIndices( iF );
1678 // find common node of triangle facets of tetra to create
1679 int iCommon = 0; // index in linear numeration
1680 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1681 if ( !triaSplits.empty() )
1684 const TTriangleFacet* facet = &triaSplits.front();
1685 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1686 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1687 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1690 else if ( nbNodes > 3 && !is24TetMode )
1692 // find the best method of splitting into triangles by aspect ratio
1693 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1694 map< double, int > badness2iCommon;
1695 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1696 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1697 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1700 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1702 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1703 nodes[ iQ*((iLast-1)%nbNodes)],
1704 nodes[ iQ*((iLast )%nbNodes)]);
1705 badness += getBadRate( &tria, aspectRatio );
1707 badness2iCommon.insert( make_pair( badness, iCommon ));
1709 // use iCommon with lowest badness
1710 iCommon = badness2iCommon.begin()->second;
1712 if ( iCommon >= nbNodes )
1713 iCommon = 0; // something wrong
1715 // fill connectivity of tetrahedra based on a current face
1716 int nbTet = nbNodes - 2;
1717 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1722 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1723 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1727 method._faceBaryNode[ iF ] = 0;
1728 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1731 for ( int i = 0; i < nbTet; ++i )
1733 int i1 = i, i2 = (i+1) % nbNodes;
1734 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1735 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1736 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1737 connectivity[ connSize++ ] = faceBaryCenInd;
1738 connectivity[ connSize++ ] = baryCenInd;
1743 for ( int i = 0; i < nbTet; ++i )
1745 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1746 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1747 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1748 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1749 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1750 connectivity[ connSize++ ] = baryCenInd;
1753 method._nbTetra += nbTet;
1755 } // loop on volume faces
1757 connectivity[ connSize++ ] = -1;
1759 } // end of generic solution
1763 //================================================================================
1765 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1767 //================================================================================
1769 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1771 // find the tetrahedron including the three nodes of facet
1772 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1773 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1774 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1775 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1776 while ( volIt1->more() )
1778 const SMDS_MeshElement* v = volIt1->next();
1779 SMDSAbs_EntityType type = v->GetEntityType();
1780 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1782 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1783 continue; // medium node not allowed
1784 const int ind2 = v->GetNodeIndex( n2 );
1785 if ( ind2 < 0 || 3 < ind2 )
1787 const int ind3 = v->GetNodeIndex( n3 );
1788 if ( ind3 < 0 || 3 < ind3 )
1795 //=======================================================================
1797 * \brief A key of a face of volume
1799 //=======================================================================
1801 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1803 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1805 TIDSortedNodeSet sortedNodes;
1806 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1807 int nbNodes = vol.NbFaceNodes( iF );
1808 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1809 for ( int i = 0; i < nbNodes; i += iQ )
1810 sortedNodes.insert( fNodes[i] );
1811 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1812 first.first = (*(n++))->GetID();
1813 first.second = (*(n++))->GetID();
1814 second.first = (*(n++))->GetID();
1815 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1820 //=======================================================================
1821 //function : SplitVolumesIntoTetra
1822 //purpose : Split volume elements into tetrahedra.
1823 //=======================================================================
1825 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1826 const int theMethodFlags)
1828 // std-like iterator on coordinates of nodes of mesh element
1829 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1830 NXyzIterator xyzEnd;
1832 SMDS_VolumeTool volTool;
1833 SMESH_MesherHelper helper( *GetMesh());
1835 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1836 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1838 SMESH_SequenceOfElemPtr newNodes, newElems;
1840 // map face of volume to it's baricenrtic node
1841 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1844 TIDSortedElemSet::const_iterator elem = theElems.begin();
1845 for ( ; elem != theElems.end(); ++elem )
1847 if ( (*elem)->GetType() != SMDSAbs_Volume )
1849 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1850 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1853 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1855 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1856 if ( splitMethod._nbTetra < 1 ) continue;
1858 // find submesh to add new tetras to
1859 if ( !subMesh || !subMesh->Contains( *elem ))
1861 int shapeID = FindShape( *elem );
1862 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1863 subMesh = GetMeshDS()->MeshElements( shapeID );
1866 if ( (*elem)->IsQuadratic() )
1869 // add quadratic links to the helper
1870 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1872 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1873 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1874 for ( int iN = 0; iN < nbN; iN += iQ )
1875 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1877 helper.SetIsQuadratic( true );
1882 helper.SetIsQuadratic( false );
1884 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1885 helper.SetElementsOnShape( true );
1886 if ( splitMethod._baryNode )
1888 // make a node at barycenter
1889 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1890 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1891 nodes.push_back( gcNode );
1892 newNodes.Append( gcNode );
1894 if ( !splitMethod._faceBaryNode.empty() )
1896 // make or find baricentric nodes of faces
1897 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1898 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1900 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1901 volFace2BaryNode.insert
1902 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1905 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1906 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1908 nodes.push_back( iF_n->second = f_n->second );
1913 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1914 const int* tetConn = splitMethod._connectivity;
1915 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1916 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1917 nodes[ tetConn[1] ],
1918 nodes[ tetConn[2] ],
1919 nodes[ tetConn[3] ]));
1921 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1923 // Split faces on sides of the split volume
1925 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1926 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1928 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1929 if ( nbNodes < 4 ) continue;
1931 // find an existing face
1932 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1933 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1934 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1935 /*noMedium=*/false))
1938 helper.SetElementsOnShape( false );
1939 vector< const SMDS_MeshElement* > triangles;
1941 // find submesh to add new triangles in
1942 if ( !fSubMesh || !fSubMesh->Contains( face ))
1944 int shapeID = FindShape( face );
1945 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1947 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1948 if ( iF_n != splitMethod._faceBaryNode.end() )
1950 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1952 const SMDS_MeshNode* n1 = fNodes[iN];
1953 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1954 const SMDS_MeshNode *n3 = iF_n->second;
1955 if ( !volTool.IsFaceExternal( iF ))
1957 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1959 if ( fSubMesh && n3->getshapeId() < 1 )
1960 fSubMesh->AddNode( n3 );
1965 // among possible triangles create ones discribed by split method
1966 const int* nInd = volTool.GetFaceNodesIndices( iF );
1967 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1968 int iCom = 0; // common node of triangle faces to split into
1969 list< TTriangleFacet > facets;
1970 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1972 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1973 nInd[ iQ * ( (iCom+1)%nbNodes )],
1974 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1975 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1976 nInd[ iQ * ( (iCom+2)%nbNodes )],
1977 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1978 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1980 facets.push_back( t012 );
1981 facets.push_back( t023 );
1982 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1983 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1984 nInd[ iQ * ((iLast-1)%nbNodes )],
1985 nInd[ iQ * ((iLast )%nbNodes )]));
1989 list< TTriangleFacet >::iterator facet = facets.begin();
1990 for ( ; facet != facets.end(); ++facet )
1992 if ( !volTool.IsFaceExternal( iF ))
1993 swap( facet->_n2, facet->_n3 );
1994 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1995 volNodes[ facet->_n2 ],
1996 volNodes[ facet->_n3 ]));
1999 for ( int i = 0; i < triangles.size(); ++i )
2001 if ( !triangles[i] ) continue;
2003 fSubMesh->AddElement( triangles[i]);
2004 newElems.Append( triangles[i] );
2006 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2007 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2010 } // loop on volume faces to split them into triangles
2012 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2014 if ( geomType == SMDSEntity_TriQuad_Hexa )
2016 // remove medium nodes that could become free
2017 for ( int i = 20; i < volTool.NbNodes(); ++i )
2018 if ( volNodes[i]->NbInverseElements() == 0 )
2019 GetMeshDS()->RemoveNode( volNodes[i] );
2021 } // loop on volumes to split
2023 myLastCreatedNodes = newNodes;
2024 myLastCreatedElems = newElems;
2027 //=======================================================================
2028 //function : AddToSameGroups
2029 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2030 //=======================================================================
2032 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2033 const SMDS_MeshElement* elemInGroups,
2034 SMESHDS_Mesh * aMesh)
2036 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2037 if (!groups.empty()) {
2038 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2039 for ( ; grIt != groups.end(); grIt++ ) {
2040 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2041 if ( group && group->Contains( elemInGroups ))
2042 group->SMDSGroup().Add( elemToAdd );
2048 //=======================================================================
2049 //function : RemoveElemFromGroups
2050 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2051 //=======================================================================
2052 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2053 SMESHDS_Mesh * aMesh)
2055 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2056 if (!groups.empty())
2058 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2059 for (; GrIt != groups.end(); GrIt++)
2061 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2062 if (!grp || grp->IsEmpty()) continue;
2063 grp->SMDSGroup().Remove(removeelem);
2068 //================================================================================
2070 * \brief Replace elemToRm by elemToAdd in the all groups
2072 //================================================================================
2074 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2075 const SMDS_MeshElement* elemToAdd,
2076 SMESHDS_Mesh * aMesh)
2078 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2079 if (!groups.empty()) {
2080 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2081 for ( ; grIt != groups.end(); grIt++ ) {
2082 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2083 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2084 group->SMDSGroup().Add( elemToAdd );
2089 //================================================================================
2091 * \brief Replace elemToRm by elemToAdd in the all groups
2093 //================================================================================
2095 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2096 const vector<const SMDS_MeshElement*>& elemToAdd,
2097 SMESHDS_Mesh * aMesh)
2099 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2100 if (!groups.empty())
2102 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2103 for ( ; grIt != groups.end(); grIt++ ) {
2104 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2105 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2106 for ( int i = 0; i < elemToAdd.size(); ++i )
2107 group->SMDSGroup().Add( elemToAdd[ i ] );
2112 //=======================================================================
2113 //function : QuadToTri
2114 //purpose : Cut quadrangles into triangles.
2115 // theCrit is used to select a diagonal to cut
2116 //=======================================================================
2118 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2119 const bool the13Diag)
2121 myLastCreatedElems.Clear();
2122 myLastCreatedNodes.Clear();
2124 MESSAGE( "::QuadToTri()" );
2126 SMESHDS_Mesh * aMesh = GetMeshDS();
2128 Handle(Geom_Surface) surface;
2129 SMESH_MesherHelper helper( *GetMesh() );
2131 TIDSortedElemSet::iterator itElem;
2132 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2133 const SMDS_MeshElement* elem = *itElem;
2134 if ( !elem || elem->GetType() != SMDSAbs_Face )
2136 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2137 if(!isquad) continue;
2139 if(elem->NbNodes()==4) {
2140 // retrieve element nodes
2141 const SMDS_MeshNode* aNodes [4];
2142 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2144 while ( itN->more() )
2145 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2147 int aShapeId = FindShape( elem );
2148 const SMDS_MeshElement* newElem1 = 0;
2149 const SMDS_MeshElement* newElem2 = 0;
2151 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2152 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2155 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2156 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2158 myLastCreatedElems.Append(newElem1);
2159 myLastCreatedElems.Append(newElem2);
2160 // put a new triangle on the same shape and add to the same groups
2163 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2164 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2166 AddToSameGroups( newElem1, elem, aMesh );
2167 AddToSameGroups( newElem2, elem, aMesh );
2168 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2169 aMesh->RemoveElement( elem );
2172 // Quadratic quadrangle
2174 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2176 // get surface elem is on
2177 int aShapeId = FindShape( elem );
2178 if ( aShapeId != helper.GetSubShapeID() ) {
2182 shape = aMesh->IndexToShape( aShapeId );
2183 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2184 TopoDS_Face face = TopoDS::Face( shape );
2185 surface = BRep_Tool::Surface( face );
2186 if ( !surface.IsNull() )
2187 helper.SetSubShape( shape );
2191 const SMDS_MeshNode* aNodes [8];
2192 const SMDS_MeshNode* inFaceNode = 0;
2193 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2195 while ( itN->more() ) {
2196 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2197 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2198 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2200 inFaceNode = aNodes[ i-1 ];
2204 // find middle point for (0,1,2,3)
2205 // and create a node in this point;
2207 if ( surface.IsNull() ) {
2209 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2213 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2216 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2218 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2220 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2221 myLastCreatedNodes.Append(newN);
2223 // create a new element
2224 const SMDS_MeshElement* newElem1 = 0;
2225 const SMDS_MeshElement* newElem2 = 0;
2227 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2228 aNodes[6], aNodes[7], newN );
2229 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2230 newN, aNodes[4], aNodes[5] );
2233 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2234 aNodes[7], aNodes[4], newN );
2235 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2236 newN, aNodes[5], aNodes[6] );
2238 myLastCreatedElems.Append(newElem1);
2239 myLastCreatedElems.Append(newElem2);
2240 // put a new triangle on the same shape and add to the same groups
2243 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2244 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2246 AddToSameGroups( newElem1, elem, aMesh );
2247 AddToSameGroups( newElem2, elem, aMesh );
2248 aMesh->RemoveElement( elem );
2255 //=======================================================================
2256 //function : getAngle
2258 //=======================================================================
2260 double getAngle(const SMDS_MeshElement * tr1,
2261 const SMDS_MeshElement * tr2,
2262 const SMDS_MeshNode * n1,
2263 const SMDS_MeshNode * n2)
2265 double angle = 2. * M_PI; // bad angle
2268 SMESH::Controls::TSequenceOfXYZ P1, P2;
2269 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2270 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2273 if(!tr1->IsQuadratic())
2274 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2276 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2277 if ( N1.SquareMagnitude() <= gp::Resolution() )
2279 if(!tr2->IsQuadratic())
2280 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2282 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2283 if ( N2.SquareMagnitude() <= gp::Resolution() )
2286 // find the first diagonal node n1 in the triangles:
2287 // take in account a diagonal link orientation
2288 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2289 for ( int t = 0; t < 2; t++ ) {
2290 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2291 int i = 0, iDiag = -1;
2292 while ( it->more()) {
2293 const SMDS_MeshElement *n = it->next();
2294 if ( n == n1 || n == n2 ) {
2298 if ( i - iDiag == 1 )
2299 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2308 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2311 angle = N1.Angle( N2 );
2316 // =================================================
2317 // class generating a unique ID for a pair of nodes
2318 // and able to return nodes by that ID
2319 // =================================================
2323 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2324 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2327 long GetLinkID (const SMDS_MeshNode * n1,
2328 const SMDS_MeshNode * n2) const
2330 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2333 bool GetNodes (const long theLinkID,
2334 const SMDS_MeshNode* & theNode1,
2335 const SMDS_MeshNode* & theNode2) const
2337 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2338 if ( !theNode1 ) return false;
2339 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2340 if ( !theNode2 ) return false;
2346 const SMESHDS_Mesh* myMesh;
2351 //=======================================================================
2352 //function : TriToQuad
2353 //purpose : Fuse neighbour triangles into quadrangles.
2354 // theCrit is used to select a neighbour to fuse with.
2355 // theMaxAngle is a max angle between element normals at which
2356 // fusion is still performed.
2357 //=======================================================================
2359 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2360 SMESH::Controls::NumericalFunctorPtr theCrit,
2361 const double theMaxAngle)
2363 myLastCreatedElems.Clear();
2364 myLastCreatedNodes.Clear();
2366 MESSAGE( "::TriToQuad()" );
2368 if ( !theCrit.get() )
2371 SMESHDS_Mesh * aMesh = GetMeshDS();
2373 // Prepare data for algo: build
2374 // 1. map of elements with their linkIDs
2375 // 2. map of linkIDs with their elements
2377 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2378 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2379 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2380 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2382 TIDSortedElemSet::iterator itElem;
2383 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2384 const SMDS_MeshElement* elem = *itElem;
2385 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2386 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2387 if(!IsTria) continue;
2389 // retrieve element nodes
2390 const SMDS_MeshNode* aNodes [4];
2391 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2394 aNodes[ i++ ] = cast2Node( itN->next() );
2395 aNodes[ 3 ] = aNodes[ 0 ];
2398 for ( i = 0; i < 3; i++ ) {
2399 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2400 // check if elements sharing a link can be fused
2401 itLE = mapLi_listEl.find( link );
2402 if ( itLE != mapLi_listEl.end() ) {
2403 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2405 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2406 //if ( FindShape( elem ) != FindShape( elem2 ))
2407 // continue; // do not fuse triangles laying on different shapes
2408 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2409 continue; // avoid making badly shaped quads
2410 (*itLE).second.push_back( elem );
2413 mapLi_listEl[ link ].push_back( elem );
2415 mapEl_setLi [ elem ].insert( link );
2418 // Clean the maps from the links shared by a sole element, ie
2419 // links to which only one element is bound in mapLi_listEl
2421 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2422 int nbElems = (*itLE).second.size();
2423 if ( nbElems < 2 ) {
2424 const SMDS_MeshElement* elem = (*itLE).second.front();
2425 SMESH_TLink link = (*itLE).first;
2426 mapEl_setLi[ elem ].erase( link );
2427 if ( mapEl_setLi[ elem ].empty() )
2428 mapEl_setLi.erase( elem );
2432 // Algo: fuse triangles into quadrangles
2434 while ( ! mapEl_setLi.empty() ) {
2435 // Look for the start element:
2436 // the element having the least nb of shared links
2437 const SMDS_MeshElement* startElem = 0;
2439 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2440 int nbLinks = (*itEL).second.size();
2441 if ( nbLinks < minNbLinks ) {
2442 startElem = (*itEL).first;
2443 minNbLinks = nbLinks;
2444 if ( minNbLinks == 1 )
2449 // search elements to fuse starting from startElem or links of elements
2450 // fused earlyer - startLinks
2451 list< SMESH_TLink > startLinks;
2452 while ( startElem || !startLinks.empty() ) {
2453 while ( !startElem && !startLinks.empty() ) {
2454 // Get an element to start, by a link
2455 SMESH_TLink linkId = startLinks.front();
2456 startLinks.pop_front();
2457 itLE = mapLi_listEl.find( linkId );
2458 if ( itLE != mapLi_listEl.end() ) {
2459 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2460 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2461 for ( ; itE != listElem.end() ; itE++ )
2462 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2464 mapLi_listEl.erase( itLE );
2469 // Get candidates to be fused
2470 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2471 const SMESH_TLink *link12, *link13;
2473 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2474 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2475 ASSERT( !setLi.empty() );
2476 set< SMESH_TLink >::iterator itLi;
2477 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2479 const SMESH_TLink & link = (*itLi);
2480 itLE = mapLi_listEl.find( link );
2481 if ( itLE == mapLi_listEl.end() )
2484 const SMDS_MeshElement* elem = (*itLE).second.front();
2486 elem = (*itLE).second.back();
2487 mapLi_listEl.erase( itLE );
2488 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2499 // add other links of elem to list of links to re-start from
2500 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2501 set< SMESH_TLink >::iterator it;
2502 for ( it = links.begin(); it != links.end(); it++ ) {
2503 const SMESH_TLink& link2 = (*it);
2504 if ( link2 != link )
2505 startLinks.push_back( link2 );
2509 // Get nodes of possible quadrangles
2510 const SMDS_MeshNode *n12 [4], *n13 [4];
2511 bool Ok12 = false, Ok13 = false;
2512 const SMDS_MeshNode *linkNode1, *linkNode2;
2514 linkNode1 = link12->first;
2515 linkNode2 = link12->second;
2516 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2520 linkNode1 = link13->first;
2521 linkNode2 = link13->second;
2522 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2526 // Choose a pair to fuse
2527 if ( Ok12 && Ok13 ) {
2528 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2529 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2530 double aBadRate12 = getBadRate( &quad12, theCrit );
2531 double aBadRate13 = getBadRate( &quad13, theCrit );
2532 if ( aBadRate13 < aBadRate12 )
2539 // and remove fused elems and removed links from the maps
2540 mapEl_setLi.erase( tr1 );
2542 mapEl_setLi.erase( tr2 );
2543 mapLi_listEl.erase( *link12 );
2544 if(tr1->NbNodes()==3) {
2545 const SMDS_MeshElement* newElem = 0;
2546 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2547 myLastCreatedElems.Append(newElem);
2548 AddToSameGroups( newElem, tr1, aMesh );
2549 int aShapeId = tr1->getshapeId();
2552 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2554 aMesh->RemoveElement( tr1 );
2555 aMesh->RemoveElement( tr2 );
2558 const SMDS_MeshNode* N1 [6];
2559 const SMDS_MeshNode* N2 [6];
2560 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2561 // now we receive following N1 and N2 (using numeration as above image)
2562 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2563 // i.e. first nodes from both arrays determ new diagonal
2564 const SMDS_MeshNode* aNodes[8];
2573 const SMDS_MeshElement* newElem = 0;
2574 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2575 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2576 myLastCreatedElems.Append(newElem);
2577 AddToSameGroups( newElem, tr1, aMesh );
2578 int aShapeId = tr1->getshapeId();
2581 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2583 aMesh->RemoveElement( tr1 );
2584 aMesh->RemoveElement( tr2 );
2585 // remove middle node (9)
2586 GetMeshDS()->RemoveNode( N1[4] );
2590 mapEl_setLi.erase( tr3 );
2591 mapLi_listEl.erase( *link13 );
2592 if(tr1->NbNodes()==3) {
2593 const SMDS_MeshElement* newElem = 0;
2594 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2595 myLastCreatedElems.Append(newElem);
2596 AddToSameGroups( newElem, tr1, aMesh );
2597 int aShapeId = tr1->getshapeId();
2600 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2602 aMesh->RemoveElement( tr1 );
2603 aMesh->RemoveElement( tr3 );
2606 const SMDS_MeshNode* N1 [6];
2607 const SMDS_MeshNode* N2 [6];
2608 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2609 // now we receive following N1 and N2 (using numeration as above image)
2610 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2611 // i.e. first nodes from both arrays determ new diagonal
2612 const SMDS_MeshNode* aNodes[8];
2621 const SMDS_MeshElement* newElem = 0;
2622 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2623 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2624 myLastCreatedElems.Append(newElem);
2625 AddToSameGroups( newElem, tr1, aMesh );
2626 int aShapeId = tr1->getshapeId();
2629 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2631 aMesh->RemoveElement( tr1 );
2632 aMesh->RemoveElement( tr3 );
2633 // remove middle node (9)
2634 GetMeshDS()->RemoveNode( N1[4] );
2638 // Next element to fuse: the rejected one
2640 startElem = Ok12 ? tr3 : tr2;
2642 } // if ( startElem )
2643 } // while ( startElem || !startLinks.empty() )
2644 } // while ( ! mapEl_setLi.empty() )
2650 /*#define DUMPSO(txt) \
2651 // cout << txt << endl;
2652 //=============================================================================
2656 //=============================================================================
2657 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2661 int tmp = idNodes[ i1 ];
2662 idNodes[ i1 ] = idNodes[ i2 ];
2663 idNodes[ i2 ] = tmp;
2664 gp_Pnt Ptmp = P[ i1 ];
2667 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2670 //=======================================================================
2671 //function : SortQuadNodes
2672 //purpose : Set 4 nodes of a quadrangle face in a good order.
2673 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2675 //=======================================================================
2677 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2682 for ( i = 0; i < 4; i++ ) {
2683 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2685 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2688 gp_Vec V1(P[0], P[1]);
2689 gp_Vec V2(P[0], P[2]);
2690 gp_Vec V3(P[0], P[3]);
2692 gp_Vec Cross1 = V1 ^ V2;
2693 gp_Vec Cross2 = V2 ^ V3;
2696 if (Cross1.Dot(Cross2) < 0)
2701 if (Cross1.Dot(Cross2) < 0)
2705 swap ( i, i + 1, idNodes, P );
2707 // for ( int ii = 0; ii < 4; ii++ ) {
2708 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2709 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2715 //=======================================================================
2716 //function : SortHexaNodes
2717 //purpose : Set 8 nodes of a hexahedron in a good order.
2718 // Return success status
2719 //=======================================================================
2721 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2726 DUMPSO( "INPUT: ========================================");
2727 for ( i = 0; i < 8; i++ ) {
2728 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2729 if ( !n ) return false;
2730 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2731 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2733 DUMPSO( "========================================");
2736 set<int> faceNodes; // ids of bottom face nodes, to be found
2737 set<int> checkedId1; // ids of tried 2-nd nodes
2738 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2739 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2740 int iMin, iLoop1 = 0;
2742 // Loop to try the 2-nd nodes
2744 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2746 // Find not checked 2-nd node
2747 for ( i = 1; i < 8; i++ )
2748 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2749 int id1 = idNodes[i];
2750 swap ( 1, i, idNodes, P );
2751 checkedId1.insert ( id1 );
2755 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2756 // ie that all but meybe one (id3 which is on the same face) nodes
2757 // lay on the same side from the triangle plane.
2759 bool manyInPlane = false; // more than 4 nodes lay in plane
2761 while ( ++iLoop2 < 6 ) {
2763 // get 1-2-3 plane coeffs
2764 Standard_Real A, B, C, D;
2765 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2766 if ( N.SquareMagnitude() > gp::Resolution() )
2768 gp_Pln pln ( P[0], N );
2769 pln.Coefficients( A, B, C, D );
2771 // find the node (iMin) closest to pln
2772 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2774 for ( i = 3; i < 8; i++ ) {
2775 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2776 if ( fabs( dist[i] ) < minDist ) {
2777 minDist = fabs( dist[i] );
2780 if ( fabs( dist[i] ) <= tol )
2781 idInPln.insert( idNodes[i] );
2784 // there should not be more than 4 nodes in bottom plane
2785 if ( idInPln.size() > 1 )
2787 DUMPSO( "### idInPln.size() = " << idInPln.size());
2788 // idInPlane does not contain the first 3 nodes
2789 if ( manyInPlane || idInPln.size() == 5)
2790 return false; // all nodes in one plane
2793 // set the 1-st node to be not in plane
2794 for ( i = 3; i < 8; i++ ) {
2795 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2796 DUMPSO( "### Reset 0-th node");
2797 swap( 0, i, idNodes, P );
2802 // reset to re-check second nodes
2803 leastDist = DBL_MAX;
2807 break; // from iLoop2;
2810 // check that the other 4 nodes are on the same side
2811 bool sameSide = true;
2812 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2813 for ( i = 3; sameSide && i < 8; i++ ) {
2815 sameSide = ( isNeg == dist[i] <= 0.);
2818 // keep best solution
2819 if ( sameSide && minDist < leastDist ) {
2820 leastDist = minDist;
2822 faceNodes.insert( idNodes[ 1 ] );
2823 faceNodes.insert( idNodes[ 2 ] );
2824 faceNodes.insert( idNodes[ iMin ] );
2825 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2826 << " leastDist = " << leastDist);
2827 if ( leastDist <= DBL_MIN )
2832 // set next 3-d node to check
2833 int iNext = 2 + iLoop2;
2835 DUMPSO( "Try 2-nd");
2836 swap ( 2, iNext, idNodes, P );
2838 } // while ( iLoop2 < 6 )
2841 if ( faceNodes.empty() ) return false;
2843 // Put the faceNodes in proper places
2844 for ( i = 4; i < 8; i++ ) {
2845 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2846 // find a place to put
2848 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2850 DUMPSO( "Set faceNodes");
2851 swap ( iTo, i, idNodes, P );
2856 // Set nodes of the found bottom face in good order
2857 DUMPSO( " Found bottom face: ");
2858 i = SortQuadNodes( theMesh, idNodes );
2860 gp_Pnt Ptmp = P[ i ];
2865 // for ( int ii = 0; ii < 4; ii++ ) {
2866 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2867 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2870 // Gravity center of the top and bottom faces
2871 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2872 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2874 // Get direction from the bottom to the top face
2875 gp_Vec upDir ( aGCb, aGCt );
2876 Standard_Real upDirSize = upDir.Magnitude();
2877 if ( upDirSize <= gp::Resolution() ) return false;
2880 // Assure that the bottom face normal points up
2881 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2882 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2883 if ( Nb.Dot( upDir ) < 0 ) {
2884 DUMPSO( "Reverse bottom face");
2885 swap( 1, 3, idNodes, P );
2888 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2889 Standard_Real minDist = DBL_MAX;
2890 for ( i = 4; i < 8; i++ ) {
2891 // projection of P[i] to the plane defined by P[0] and upDir
2892 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2893 Standard_Real sqDist = P[0].SquareDistance( Pp );
2894 if ( sqDist < minDist ) {
2899 DUMPSO( "Set 4-th");
2900 swap ( 4, iMin, idNodes, P );
2902 // Set nodes of the top face in good order
2903 DUMPSO( "Sort top face");
2904 i = SortQuadNodes( theMesh, &idNodes[4] );
2907 gp_Pnt Ptmp = P[ i ];
2912 // Assure that direction of the top face normal is from the bottom face
2913 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2914 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2915 if ( Nt.Dot( upDir ) < 0 ) {
2916 DUMPSO( "Reverse top face");
2917 swap( 5, 7, idNodes, P );
2920 // DUMPSO( "OUTPUT: ========================================");
2921 // for ( i = 0; i < 8; i++ ) {
2922 // float *p = ugrid->GetPoint(idNodes[i]);
2923 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2929 //================================================================================
2931 * \brief Return nodes linked to the given one
2932 * \param theNode - the node
2933 * \param linkedNodes - the found nodes
2934 * \param type - the type of elements to check
2936 * Medium nodes are ignored
2938 //================================================================================
2940 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2941 TIDSortedElemSet & linkedNodes,
2942 SMDSAbs_ElementType type )
2944 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2945 while ( elemIt->more() )
2947 const SMDS_MeshElement* elem = elemIt->next();
2948 if(elem->GetType() == SMDSAbs_0DElement)
2951 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2952 if ( elem->GetType() == SMDSAbs_Volume )
2954 SMDS_VolumeTool vol( elem );
2955 while ( nodeIt->more() ) {
2956 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2957 if ( theNode != n && vol.IsLinked( theNode, n ))
2958 linkedNodes.insert( n );
2963 for ( int i = 0; nodeIt->more(); ++i ) {
2964 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2965 if ( n == theNode ) {
2966 int iBefore = i - 1;
2968 if ( elem->IsQuadratic() ) {
2969 int nb = elem->NbNodes() / 2;
2970 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2971 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2973 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2974 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2981 //=======================================================================
2982 //function : laplacianSmooth
2983 //purpose : pulls theNode toward the center of surrounding nodes directly
2984 // connected to that node along an element edge
2985 //=======================================================================
2987 void laplacianSmooth(const SMDS_MeshNode* theNode,
2988 const Handle(Geom_Surface)& theSurface,
2989 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2991 // find surrounding nodes
2993 TIDSortedElemSet nodeSet;
2994 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2996 // compute new coodrs
2998 double coord[] = { 0., 0., 0. };
2999 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3000 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3001 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3002 if ( theSurface.IsNull() ) { // smooth in 3D
3003 coord[0] += node->X();
3004 coord[1] += node->Y();
3005 coord[2] += node->Z();
3007 else { // smooth in 2D
3008 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3009 gp_XY* uv = theUVMap[ node ];
3010 coord[0] += uv->X();
3011 coord[1] += uv->Y();
3014 int nbNodes = nodeSet.size();
3017 coord[0] /= nbNodes;
3018 coord[1] /= nbNodes;
3020 if ( !theSurface.IsNull() ) {
3021 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3022 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3023 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3029 coord[2] /= nbNodes;
3033 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3036 //=======================================================================
3037 //function : centroidalSmooth
3038 //purpose : pulls theNode toward the element-area-weighted centroid of the
3039 // surrounding elements
3040 //=======================================================================
3042 void centroidalSmooth(const SMDS_MeshNode* theNode,
3043 const Handle(Geom_Surface)& theSurface,
3044 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3046 gp_XYZ aNewXYZ(0.,0.,0.);
3047 SMESH::Controls::Area anAreaFunc;
3048 double totalArea = 0.;
3053 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3054 while ( elemIt->more() )
3056 const SMDS_MeshElement* elem = elemIt->next();
3059 gp_XYZ elemCenter(0.,0.,0.);
3060 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3061 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3062 int nn = elem->NbNodes();
3063 if(elem->IsQuadratic()) nn = nn/2;
3065 //while ( itN->more() ) {
3067 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3069 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3070 aNodePoints.push_back( aP );
3071 if ( !theSurface.IsNull() ) { // smooth in 2D
3072 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3073 gp_XY* uv = theUVMap[ aNode ];
3074 aP.SetCoord( uv->X(), uv->Y(), 0. );
3078 double elemArea = anAreaFunc.GetValue( aNodePoints );
3079 totalArea += elemArea;
3081 aNewXYZ += elemCenter * elemArea;
3083 aNewXYZ /= totalArea;
3084 if ( !theSurface.IsNull() ) {
3085 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3086 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3091 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3094 //=======================================================================
3095 //function : getClosestUV
3096 //purpose : return UV of closest projection
3097 //=======================================================================
3099 static bool getClosestUV (Extrema_GenExtPS& projector,
3100 const gp_Pnt& point,
3103 projector.Perform( point );
3104 if ( projector.IsDone() ) {
3105 double u, v, minVal = DBL_MAX;
3106 for ( int i = projector.NbExt(); i > 0; i-- )
3107 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3108 if ( projector.SquareDistance( i ) < minVal ) {
3109 minVal = projector.SquareDistance( i );
3111 if ( projector.Value( i ) < minVal ) {
3112 minVal = projector.Value( i );
3114 projector.Point( i ).Parameter( u, v );
3116 result.SetCoord( u, v );
3122 //=======================================================================
3124 //purpose : Smooth theElements during theNbIterations or until a worst
3125 // element has aspect ratio <= theTgtAspectRatio.
3126 // Aspect Ratio varies in range [1.0, inf].
3127 // If theElements is empty, the whole mesh is smoothed.
3128 // theFixedNodes contains additionally fixed nodes. Nodes built
3129 // on edges and boundary nodes are always fixed.
3130 //=======================================================================
3132 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3133 set<const SMDS_MeshNode*> & theFixedNodes,
3134 const SmoothMethod theSmoothMethod,
3135 const int theNbIterations,
3136 double theTgtAspectRatio,
3139 myLastCreatedElems.Clear();
3140 myLastCreatedNodes.Clear();
3142 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3144 if ( theTgtAspectRatio < 1.0 )
3145 theTgtAspectRatio = 1.0;
3147 const double disttol = 1.e-16;
3149 SMESH::Controls::AspectRatio aQualityFunc;
3151 SMESHDS_Mesh* aMesh = GetMeshDS();
3153 if ( theElems.empty() ) {
3154 // add all faces to theElems
3155 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3156 while ( fIt->more() ) {
3157 const SMDS_MeshElement* face = fIt->next();
3158 theElems.insert( theElems.end(), face );
3161 // get all face ids theElems are on
3162 set< int > faceIdSet;
3163 TIDSortedElemSet::iterator itElem;
3165 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3166 int fId = FindShape( *itElem );
3167 // check that corresponding submesh exists and a shape is face
3169 faceIdSet.find( fId ) == faceIdSet.end() &&
3170 aMesh->MeshElements( fId )) {
3171 TopoDS_Shape F = aMesh->IndexToShape( fId );
3172 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3173 faceIdSet.insert( fId );
3176 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3178 // ===============================================
3179 // smooth elements on each TopoDS_Face separately
3180 // ===============================================
3182 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3183 for ( ; fId != faceIdSet.rend(); ++fId ) {
3184 // get face surface and submesh
3185 Handle(Geom_Surface) surface;
3186 SMESHDS_SubMesh* faceSubMesh = 0;
3188 double fToler2 = 0, f,l;
3189 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3190 bool isUPeriodic = false, isVPeriodic = false;
3192 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3193 surface = BRep_Tool::Surface( face );
3194 faceSubMesh = aMesh->MeshElements( *fId );
3195 fToler2 = BRep_Tool::Tolerance( face );
3196 fToler2 *= fToler2 * 10.;
3197 isUPeriodic = surface->IsUPeriodic();
3200 isVPeriodic = surface->IsVPeriodic();
3203 surface->Bounds( u1, u2, v1, v2 );
3205 // ---------------------------------------------------------
3206 // for elements on a face, find movable and fixed nodes and
3207 // compute UV for them
3208 // ---------------------------------------------------------
3209 bool checkBoundaryNodes = false;
3210 bool isQuadratic = false;
3211 set<const SMDS_MeshNode*> setMovableNodes;
3212 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3213 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3214 list< const SMDS_MeshElement* > elemsOnFace;
3216 Extrema_GenExtPS projector;
3217 GeomAdaptor_Surface surfAdaptor;
3218 if ( !surface.IsNull() ) {
3219 surfAdaptor.Load( surface );
3220 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3222 int nbElemOnFace = 0;
3223 itElem = theElems.begin();
3224 // loop on not yet smoothed elements: look for elems on a face
3225 while ( itElem != theElems.end() ) {
3226 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3227 break; // all elements found
3229 const SMDS_MeshElement* elem = *itElem;
3230 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3231 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3235 elemsOnFace.push_back( elem );
3236 theElems.erase( itElem++ );
3240 isQuadratic = elem->IsQuadratic();
3242 // get movable nodes of elem
3243 const SMDS_MeshNode* node;
3244 SMDS_TypeOfPosition posType;
3245 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3246 int nn = 0, nbn = elem->NbNodes();
3247 if(elem->IsQuadratic())
3249 while ( nn++ < nbn ) {
3250 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3251 const SMDS_PositionPtr& pos = node->GetPosition();
3252 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3253 if (posType != SMDS_TOP_EDGE &&
3254 posType != SMDS_TOP_VERTEX &&
3255 theFixedNodes.find( node ) == theFixedNodes.end())
3257 // check if all faces around the node are on faceSubMesh
3258 // because a node on edge may be bound to face
3259 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3261 if ( faceSubMesh ) {
3262 while ( eIt->more() && all ) {
3263 const SMDS_MeshElement* e = eIt->next();
3264 all = faceSubMesh->Contains( e );
3268 setMovableNodes.insert( node );
3270 checkBoundaryNodes = true;
3272 if ( posType == SMDS_TOP_3DSPACE )
3273 checkBoundaryNodes = true;
3276 if ( surface.IsNull() )
3279 // get nodes to check UV
3280 list< const SMDS_MeshNode* > uvCheckNodes;
3281 itN = elem->nodesIterator();
3282 nn = 0; nbn = elem->NbNodes();
3283 if(elem->IsQuadratic())
3285 while ( nn++ < nbn ) {
3286 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3287 if ( uvMap.find( node ) == uvMap.end() )
3288 uvCheckNodes.push_back( node );
3289 // add nodes of elems sharing node
3290 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3291 // while ( eIt->more() ) {
3292 // const SMDS_MeshElement* e = eIt->next();
3293 // if ( e != elem ) {
3294 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3295 // while ( nIt->more() ) {
3296 // const SMDS_MeshNode* n =
3297 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3298 // if ( uvMap.find( n ) == uvMap.end() )
3299 // uvCheckNodes.push_back( n );
3305 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3306 for ( ; n != uvCheckNodes.end(); ++n ) {
3309 const SMDS_PositionPtr& pos = node->GetPosition();
3310 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3312 switch ( posType ) {
3313 case SMDS_TOP_FACE: {
3314 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3315 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3318 case SMDS_TOP_EDGE: {
3319 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3320 Handle(Geom2d_Curve) pcurve;
3321 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3322 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3323 if ( !pcurve.IsNull() ) {
3324 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3325 uv = pcurve->Value( u ).XY();
3329 case SMDS_TOP_VERTEX: {
3330 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3331 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3332 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3337 // check existing UV
3338 bool project = true;
3339 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3340 double dist1 = DBL_MAX, dist2 = 0;
3341 if ( posType != SMDS_TOP_3DSPACE ) {
3342 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3343 project = dist1 > fToler2;
3345 if ( project ) { // compute new UV
3347 if ( !getClosestUV( projector, pNode, newUV )) {
3348 MESSAGE("Node Projection Failed " << node);
3352 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3354 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3356 if ( posType != SMDS_TOP_3DSPACE )
3357 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3358 if ( dist2 < dist1 )
3362 // store UV in the map
3363 listUV.push_back( uv );
3364 uvMap.insert( make_pair( node, &listUV.back() ));
3366 } // loop on not yet smoothed elements
3368 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3369 checkBoundaryNodes = true;
3371 // fix nodes on mesh boundary
3373 if ( checkBoundaryNodes ) {
3374 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3375 map< SMESH_TLink, int >::iterator link_nb;
3376 // put all elements links to linkNbMap
3377 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3378 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3379 const SMDS_MeshElement* elem = (*elemIt);
3380 int nbn = elem->NbCornerNodes();
3381 // loop on elem links: insert them in linkNbMap
3382 for ( int iN = 0; iN < nbn; ++iN ) {
3383 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3384 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3385 SMESH_TLink link( n1, n2 );
3386 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3390 // remove nodes that are in links encountered only once from setMovableNodes
3391 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3392 if ( link_nb->second == 1 ) {
3393 setMovableNodes.erase( link_nb->first.node1() );
3394 setMovableNodes.erase( link_nb->first.node2() );
3399 // -----------------------------------------------------
3400 // for nodes on seam edge, compute one more UV ( uvMap2 );
3401 // find movable nodes linked to nodes on seam and which
3402 // are to be smoothed using the second UV ( uvMap2 )
3403 // -----------------------------------------------------
3405 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3406 if ( !surface.IsNull() ) {
3407 TopExp_Explorer eExp( face, TopAbs_EDGE );
3408 for ( ; eExp.More(); eExp.Next() ) {
3409 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3410 if ( !BRep_Tool::IsClosed( edge, face ))
3412 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3413 if ( !sm ) continue;
3414 // find out which parameter varies for a node on seam
3417 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3418 if ( pcurve.IsNull() ) continue;
3419 uv1 = pcurve->Value( f );
3421 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3422 if ( pcurve.IsNull() ) continue;
3423 uv2 = pcurve->Value( f );
3424 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3426 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3427 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3429 // get nodes on seam and its vertices
3430 list< const SMDS_MeshNode* > seamNodes;
3431 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3432 while ( nSeamIt->more() ) {
3433 const SMDS_MeshNode* node = nSeamIt->next();
3434 if ( !isQuadratic || !IsMedium( node ))
3435 seamNodes.push_back( node );
3437 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3438 for ( ; vExp.More(); vExp.Next() ) {
3439 sm = aMesh->MeshElements( vExp.Current() );
3441 nSeamIt = sm->GetNodes();
3442 while ( nSeamIt->more() )
3443 seamNodes.push_back( nSeamIt->next() );
3446 // loop on nodes on seam
3447 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3448 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3449 const SMDS_MeshNode* nSeam = *noSeIt;
3450 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3451 if ( n_uv == uvMap.end() )
3454 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3455 // set the second UV
3456 listUV.push_back( *n_uv->second );
3457 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3458 if ( uvMap2.empty() )
3459 uvMap2 = uvMap; // copy the uvMap contents
3460 uvMap2[ nSeam ] = &listUV.back();
3462 // collect movable nodes linked to ones on seam in nodesNearSeam
3463 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3464 while ( eIt->more() ) {
3465 const SMDS_MeshElement* e = eIt->next();
3466 int nbUseMap1 = 0, nbUseMap2 = 0;
3467 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3468 int nn = 0, nbn = e->NbNodes();
3469 if(e->IsQuadratic()) nbn = nbn/2;
3470 while ( nn++ < nbn )
3472 const SMDS_MeshNode* n =
3473 static_cast<const SMDS_MeshNode*>( nIt->next() );
3475 setMovableNodes.find( n ) == setMovableNodes.end() )
3477 // add only nodes being closer to uv2 than to uv1
3478 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3479 0.5 * ( n->Y() + nSeam->Y() ),
3480 0.5 * ( n->Z() + nSeam->Z() ));
3482 getClosestUV( projector, pMid, uv );
3483 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3484 nodesNearSeam.insert( n );
3490 // for centroidalSmooth all element nodes must
3491 // be on one side of a seam
3492 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3493 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3495 while ( nn++ < nbn ) {
3496 const SMDS_MeshNode* n =
3497 static_cast<const SMDS_MeshNode*>( nIt->next() );
3498 setMovableNodes.erase( n );
3502 } // loop on nodes on seam
3503 } // loop on edge of a face
3504 } // if ( !face.IsNull() )
3506 if ( setMovableNodes.empty() ) {
3507 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3508 continue; // goto next face
3516 double maxRatio = -1., maxDisplacement = -1.;
3517 set<const SMDS_MeshNode*>::iterator nodeToMove;
3518 for ( it = 0; it < theNbIterations; it++ ) {
3519 maxDisplacement = 0.;
3520 nodeToMove = setMovableNodes.begin();
3521 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3522 const SMDS_MeshNode* node = (*nodeToMove);
3523 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3526 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3527 if ( theSmoothMethod == LAPLACIAN )
3528 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3530 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3532 // node displacement
3533 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3534 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3535 if ( aDispl > maxDisplacement )
3536 maxDisplacement = aDispl;
3538 // no node movement => exit
3539 //if ( maxDisplacement < 1.e-16 ) {
3540 if ( maxDisplacement < disttol ) {
3541 MESSAGE("-- no node movement --");
3545 // check elements quality
3547 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3548 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3549 const SMDS_MeshElement* elem = (*elemIt);
3550 if ( !elem || elem->GetType() != SMDSAbs_Face )
3552 SMESH::Controls::TSequenceOfXYZ aPoints;
3553 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3554 double aValue = aQualityFunc.GetValue( aPoints );
3555 if ( aValue > maxRatio )
3559 if ( maxRatio <= theTgtAspectRatio ) {
3560 MESSAGE("-- quality achived --");
3563 if (it+1 == theNbIterations) {
3564 MESSAGE("-- Iteration limit exceeded --");
3566 } // smoothing iterations
3568 MESSAGE(" Face id: " << *fId <<
3569 " Nb iterstions: " << it <<
3570 " Displacement: " << maxDisplacement <<
3571 " Aspect Ratio " << maxRatio);
3573 // ---------------------------------------
3574 // new nodes positions are computed,
3575 // record movement in DS and set new UV
3576 // ---------------------------------------
3577 nodeToMove = setMovableNodes.begin();
3578 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3579 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3580 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3581 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3582 if ( node_uv != uvMap.end() ) {
3583 gp_XY* uv = node_uv->second;
3585 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3589 // move medium nodes of quadratic elements
3592 SMESH_MesherHelper helper( *GetMesh() );
3593 if ( !face.IsNull() )
3594 helper.SetSubShape( face );
3595 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3596 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3597 const SMDS_VtkFace* QF =
3598 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3599 if(QF && QF->IsQuadratic()) {
3600 vector<const SMDS_MeshNode*> Ns;
3601 Ns.reserve(QF->NbNodes()+1);
3602 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3603 while ( anIter->more() )
3604 Ns.push_back( cast2Node(anIter->next()) );
3605 Ns.push_back( Ns[0] );
3607 for(int i=0; i<QF->NbNodes(); i=i+2) {
3608 if ( !surface.IsNull() ) {
3609 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3610 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3611 gp_XY uv = ( uv1 + uv2 ) / 2.;
3612 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3613 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3616 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3617 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3618 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3620 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3621 fabs( Ns[i+1]->Y() - y ) > disttol ||
3622 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3623 // we have to move i+1 node
3624 aMesh->MoveNode( Ns[i+1], x, y, z );
3631 } // loop on face ids
3635 //=======================================================================
3636 //function : isReverse
3637 //purpose : Return true if normal of prevNodes is not co-directied with
3638 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3639 // iNotSame is where prevNodes and nextNodes are different.
3640 // If result is true then future volume orientation is OK
3641 //=======================================================================
3643 static bool isReverse(const SMDS_MeshElement* face,
3644 const vector<const SMDS_MeshNode*>& prevNodes,
3645 const vector<const SMDS_MeshNode*>& nextNodes,
3649 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3650 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3651 gp_XYZ extrDir( pN - pP ), faceNorm;
3652 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3654 return faceNorm * extrDir < 0.0;
3657 //=======================================================================
3659 * \brief Create elements by sweeping an element
3660 * \param elem - element to sweep
3661 * \param newNodesItVec - nodes generated from each node of the element
3662 * \param newElems - generated elements
3663 * \param nbSteps - number of sweeping steps
3664 * \param srcElements - to append elem for each generated element
3666 //=======================================================================
3668 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3669 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3670 list<const SMDS_MeshElement*>& newElems,
3672 SMESH_SequenceOfElemPtr& srcElements)
3674 //MESSAGE("sweepElement " << nbSteps);
3675 SMESHDS_Mesh* aMesh = GetMeshDS();
3677 const int nbNodes = elem->NbNodes();
3678 const int nbCorners = elem->NbCornerNodes();
3679 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3680 polyhedron creation !!! */
3681 // Loop on elem nodes:
3682 // find new nodes and detect same nodes indices
3683 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3684 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3685 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3686 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3688 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3689 vector<int> sames(nbNodes);
3690 vector<bool> isSingleNode(nbNodes);
3692 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3693 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3694 const SMDS_MeshNode* node = nnIt->first;
3695 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3696 if ( listNewNodes.empty() )
3699 itNN [ iNode ] = listNewNodes.begin();
3700 prevNod[ iNode ] = node;
3701 nextNod[ iNode ] = listNewNodes.front();
3703 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3704 corner node of linear */
3705 if ( prevNod[ iNode ] != nextNod [ iNode ])
3706 nbDouble += !isSingleNode[iNode];
3708 if( iNode < nbCorners ) { // check corners only
3709 if ( prevNod[ iNode ] == nextNod [ iNode ])
3710 sames[nbSame++] = iNode;
3712 iNotSameNode = iNode;
3716 if ( nbSame == nbNodes || nbSame > 2) {
3717 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3721 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3723 // fix nodes order to have bottom normal external
3724 if ( baseType == SMDSEntity_Polygon )
3726 std::reverse( itNN.begin(), itNN.end() );
3727 std::reverse( prevNod.begin(), prevNod.end() );
3728 std::reverse( midlNod.begin(), midlNod.end() );
3729 std::reverse( nextNod.begin(), nextNod.end() );
3730 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3734 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3735 SMDS_MeshCell::applyInterlace( ind, itNN );
3736 SMDS_MeshCell::applyInterlace( ind, prevNod );
3737 SMDS_MeshCell::applyInterlace( ind, nextNod );
3738 SMDS_MeshCell::applyInterlace( ind, midlNod );
3739 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3742 sames[nbSame] = iNotSameNode;
3743 for ( int j = 0; j <= nbSame; ++j )
3744 for ( size_t i = 0; i < ind.size(); ++i )
3745 if ( ind[i] == sames[j] )
3750 iNotSameNode = sames[nbSame];
3755 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3757 iSameNode = sames[ nbSame-1 ];
3758 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3759 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3760 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3763 // make new elements
3764 for (int iStep = 0; iStep < nbSteps; iStep++ )
3767 for ( iNode = 0; iNode < nbNodes; iNode++ )
3769 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3770 nextNod[ iNode ] = *itNN[ iNode ]++;
3773 SMDS_MeshElement* aNewElem = 0;
3774 /*if(!elem->IsPoly())*/ {
3775 switch ( baseType ) {
3777 case SMDSEntity_Node: { // sweep NODE
3778 if ( nbSame == 0 ) {
3779 if ( isSingleNode[0] )
3780 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3782 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3788 case SMDSEntity_Edge: { // sweep EDGE
3789 if ( nbDouble == 0 )
3791 if ( nbSame == 0 ) // ---> quadrangle
3792 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3793 nextNod[ 1 ], nextNod[ 0 ] );
3794 else // ---> triangle
3795 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3796 nextNod[ iNotSameNode ] );
3798 else // ---> polygon
3800 vector<const SMDS_MeshNode*> poly_nodes;
3801 poly_nodes.push_back( prevNod[0] );
3802 poly_nodes.push_back( prevNod[1] );
3803 if ( prevNod[1] != nextNod[1] )
3805 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3806 poly_nodes.push_back( nextNod[1] );
3808 if ( prevNod[0] != nextNod[0] )
3810 poly_nodes.push_back( nextNod[0] );
3811 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3813 switch ( poly_nodes.size() ) {
3815 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3818 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3819 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3822 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3827 case SMDSEntity_Triangle: // TRIANGLE --->
3829 if ( nbDouble > 0 ) break;
3830 if ( nbSame == 0 ) // ---> pentahedron
3831 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3832 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3834 else if ( nbSame == 1 ) // ---> pyramid
3835 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3836 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3837 nextNod[ iSameNode ]);
3839 else // 2 same nodes: ---> tetrahedron
3840 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3841 nextNod[ iNotSameNode ]);
3844 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3848 if ( nbDouble+nbSame == 2 )
3850 if(nbSame==0) { // ---> quadratic quadrangle
3851 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3852 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3854 else { //(nbSame==1) // ---> quadratic triangle
3856 return; // medium node on axis
3858 else if(sames[0]==0)
3859 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3860 nextNod[2], midlNod[1], prevNod[2]);
3862 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3863 midlNod[0], nextNod[2], prevNod[2]);
3866 else if ( nbDouble == 3 )
3868 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3869 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3870 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3877 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3878 if ( nbDouble > 0 ) break;
3880 if ( nbSame == 0 ) // ---> hexahedron
3881 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3882 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3884 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3885 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3886 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3887 nextNod[ iSameNode ]);
3888 newElems.push_back( aNewElem );
3889 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3890 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3891 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3893 else if ( nbSame == 2 ) { // ---> pentahedron
3894 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3895 // iBeforeSame is same too
3896 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3897 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3898 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3900 // iAfterSame is same too
3901 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3902 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3903 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3907 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3908 if ( nbDouble+nbSame != 3 ) break;
3910 // ---> pentahedron with 15 nodes
3911 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3912 nextNod[0], nextNod[1], nextNod[2],
3913 prevNod[3], prevNod[4], prevNod[5],
3914 nextNod[3], nextNod[4], nextNod[5],
3915 midlNod[0], midlNod[1], midlNod[2]);
3917 else if(nbSame==1) {
3918 // ---> 2d order pyramid of 13 nodes
3919 int apex = iSameNode;
3920 int i0 = ( apex + 1 ) % nbCorners;
3921 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3925 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3926 nextNod[i0], nextNod[i1], prevNod[apex],
3927 prevNod[i01], midlNod[i0],
3928 nextNod[i01], midlNod[i1],
3929 prevNod[i1a], prevNod[i0a],
3930 nextNod[i0a], nextNod[i1a]);
3932 else if(nbSame==2) {
3933 // ---> 2d order tetrahedron of 10 nodes
3934 int n1 = iNotSameNode;
3935 int n2 = ( n1 + 1 ) % nbCorners;
3936 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3940 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3941 prevNod[n12], prevNod[n23], prevNod[n31],
3942 midlNod[n1], nextNod[n12], nextNod[n31]);
3946 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3948 if ( nbDouble != 4 ) break;
3949 // ---> hexahedron with 20 nodes
3950 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3951 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3952 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3953 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3954 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3956 else if(nbSame==1) {
3957 // ---> pyramid + pentahedron - can not be created since it is needed
3958 // additional middle node at the center of face
3959 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3962 else if( nbSame == 2 ) {
3963 if ( nbDouble != 2 ) break;
3964 // ---> 2d order Pentahedron with 15 nodes
3966 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3967 // iBeforeSame is same too
3974 // iAfterSame is same too
3984 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3985 prevNod[n4], prevNod[n5], nextNod[n5],
3986 prevNod[n12], midlNod[n2], nextNod[n12],
3987 prevNod[n45], midlNod[n5], nextNod[n45],
3988 prevNod[n14], prevNod[n25], nextNod[n25]);
3992 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3994 if( nbSame == 0 && nbDouble == 9 ) {
3995 // ---> tri-quadratic hexahedron with 27 nodes
3996 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3997 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3998 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3999 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4000 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4001 prevNod[8], // bottom center
4002 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4003 nextNod[8], // top center
4004 midlNod[8]);// elem center
4012 case SMDSEntity_Polygon: { // sweep POLYGON
4014 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4015 // ---> hexagonal prism
4016 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4017 prevNod[3], prevNod[4], prevNod[5],
4018 nextNod[0], nextNod[1], nextNod[2],
4019 nextNod[3], nextNod[4], nextNod[5]);
4023 case SMDSEntity_Ball:
4031 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4033 if ( baseType != SMDSEntity_Polygon )
4035 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4036 SMDS_MeshCell::applyInterlace( ind, prevNod );
4037 SMDS_MeshCell::applyInterlace( ind, nextNod );
4038 SMDS_MeshCell::applyInterlace( ind, midlNod );
4039 SMDS_MeshCell::applyInterlace( ind, itNN );
4040 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4041 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4043 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4044 vector<int> quantities (nbNodes + 2);
4045 polyedre_nodes.clear();
4049 for (int inode = 0; inode < nbNodes; inode++)
4050 polyedre_nodes.push_back( prevNod[inode] );
4051 quantities.push_back( nbNodes );
4054 polyedre_nodes.push_back( nextNod[0] );
4055 for (int inode = nbNodes; inode-1; --inode )
4056 polyedre_nodes.push_back( nextNod[inode-1] );
4057 quantities.push_back( nbNodes );
4060 for (int iface = 0; iface < nbNodes; iface++)
4062 const int prevNbNodes = polyedre_nodes.size();
4063 int inextface = (iface+1) % nbNodes;
4064 polyedre_nodes.push_back( prevNod[inextface] );
4065 polyedre_nodes.push_back( prevNod[iface] );
4066 if ( prevNod[iface] != nextNod[iface] )
4068 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4069 polyedre_nodes.push_back( nextNod[iface] );
4071 if ( prevNod[inextface] != nextNod[inextface] )
4073 polyedre_nodes.push_back( nextNod[inextface] );
4074 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4076 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4077 if ( nbFaceNodes > 2 )
4078 quantities.push_back( nbFaceNodes );
4079 else // degenerated face
4080 polyedre_nodes.resize( prevNbNodes );
4082 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4086 newElems.push_back( aNewElem );
4087 myLastCreatedElems.Append(aNewElem);
4088 srcElements.Append( elem );
4091 // set new prev nodes
4092 for ( iNode = 0; iNode < nbNodes; iNode++ )
4093 prevNod[ iNode ] = nextNod[ iNode ];
4098 //=======================================================================
4100 * \brief Create 1D and 2D elements around swept elements
4101 * \param mapNewNodes - source nodes and ones generated from them
4102 * \param newElemsMap - source elements and ones generated from them
4103 * \param elemNewNodesMap - nodes generated from each node of each element
4104 * \param elemSet - all swept elements
4105 * \param nbSteps - number of sweeping steps
4106 * \param srcElements - to append elem for each generated element
4108 //=======================================================================
4110 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4111 TElemOfElemListMap & newElemsMap,
4112 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4113 TIDSortedElemSet& elemSet,
4115 SMESH_SequenceOfElemPtr& srcElements)
4117 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4118 SMESHDS_Mesh* aMesh = GetMeshDS();
4120 // Find nodes belonging to only one initial element - sweep them to get edges.
4122 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4123 for ( ; nList != mapNewNodes.end(); nList++ )
4125 const SMDS_MeshNode* node =
4126 static_cast<const SMDS_MeshNode*>( nList->first );
4127 if ( newElemsMap.count( node ))
4128 continue; // node was extruded into edge
4129 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4130 int nbInitElems = 0;
4131 const SMDS_MeshElement* el = 0;
4132 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4133 while ( eIt->more() && nbInitElems < 2 ) {
4135 SMDSAbs_ElementType type = el->GetType();
4136 if ( type == SMDSAbs_Volume || type < highType ) continue;
4137 if ( type > highType ) {
4141 nbInitElems += elemSet.count(el);
4143 if ( nbInitElems < 2 ) {
4144 bool NotCreateEdge = el && el->IsMediumNode(node);
4145 if(!NotCreateEdge) {
4146 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4147 list<const SMDS_MeshElement*> newEdges;
4148 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4153 // Make a ceiling for each element ie an equal element of last new nodes.
4154 // Find free links of faces - make edges and sweep them into faces.
4156 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4157 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4158 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4160 const SMDS_MeshElement* elem = itElem->first;
4161 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4163 if(itElem->second.size()==0) continue;
4165 const bool isQuadratic = elem->IsQuadratic();
4167 if ( elem->GetType() == SMDSAbs_Edge ) {
4168 // create a ceiling edge
4169 if ( !isQuadratic ) {
4170 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4171 vecNewNodes[ 1 ]->second.back())) {
4172 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4173 vecNewNodes[ 1 ]->second.back()));
4174 srcElements.Append( elem );
4178 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4179 vecNewNodes[ 1 ]->second.back(),
4180 vecNewNodes[ 2 ]->second.back())) {
4181 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4182 vecNewNodes[ 1 ]->second.back(),
4183 vecNewNodes[ 2 ]->second.back()));
4184 srcElements.Append( elem );
4188 if ( elem->GetType() != SMDSAbs_Face )
4191 bool hasFreeLinks = false;
4193 TIDSortedElemSet avoidSet;
4194 avoidSet.insert( elem );
4196 set<const SMDS_MeshNode*> aFaceLastNodes;
4197 int iNode, nbNodes = vecNewNodes.size();
4198 if ( !isQuadratic ) {
4199 // loop on the face nodes
4200 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4202 // look for free links of the face
4203 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4204 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4205 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4206 // check if a link n1-n2 is free
4207 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4208 hasFreeLinks = true;
4209 // make a new edge and a ceiling for a new edge
4210 const SMDS_MeshElement* edge;
4211 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4212 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4213 srcElements.Append( myLastCreatedElems.Last() );
4215 n1 = vecNewNodes[ iNode ]->second.back();
4216 n2 = vecNewNodes[ iNext ]->second.back();
4217 if ( !aMesh->FindEdge( n1, n2 )) {
4218 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4219 srcElements.Append( edge );
4224 else { // elem is quadratic face
4225 int nbn = nbNodes/2;
4226 for ( iNode = 0; iNode < nbn; iNode++ ) {
4227 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4228 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4229 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4230 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4231 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4232 // check if a link is free
4233 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4234 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4235 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4236 hasFreeLinks = true;
4237 // make an edge and a ceiling for a new edge
4239 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4240 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4241 srcElements.Append( elem );
4243 n1 = vecNewNodes[ iNode ]->second.back();
4244 n2 = vecNewNodes[ iNext ]->second.back();
4245 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4246 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4247 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4248 srcElements.Append( elem );
4252 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4253 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4257 // sweep free links into faces
4259 if ( hasFreeLinks ) {
4260 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4261 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4263 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4264 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4265 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4266 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4268 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4269 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4270 std::advance( v, volNb );
4271 // find indices of free faces of a volume and their source edges
4272 list< int > freeInd;
4273 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4274 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4275 int iF, nbF = vTool.NbFaces();
4276 for ( iF = 0; iF < nbF; iF ++ ) {
4277 if (vTool.IsFreeFace( iF ) &&
4278 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4279 initNodeSet != faceNodeSet) // except an initial face
4281 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4283 freeInd.push_back( iF );
4284 // find source edge of a free face iF
4285 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4286 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4287 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4288 initNodeSet.begin(), initNodeSet.end(),
4289 commonNodes.begin());
4290 if ( (*v)->IsQuadratic() )
4291 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4293 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4295 if ( !srcEdges.back() )
4297 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4298 << iF << " of volume #" << vTool.ID() << endl;
4303 if ( freeInd.empty() )
4306 // create faces for all steps;
4307 // if such a face has been already created by sweep of edge,
4308 // assure that its orientation is OK
4309 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4310 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4311 vTool.SetExternalNormal();
4312 const int nextShift = vTool.IsForward() ? +1 : -1;
4313 list< int >::iterator ind = freeInd.begin();
4314 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4315 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4317 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4318 int nbn = vTool.NbFaceNodes( *ind );
4319 const SMDS_MeshElement * f = 0;
4320 if ( nbn == 3 ) ///// triangle
4322 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4324 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4326 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4328 nodes[ 1 + nextShift ] };
4330 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4332 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4336 else if ( nbn == 4 ) ///// quadrangle
4338 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4340 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4342 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4343 nodes[ 2 ], nodes[ 2+nextShift ] };
4345 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4347 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4348 newOrder[ 2 ], newOrder[ 3 ]));
4351 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4353 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4355 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4357 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4359 nodes[2 + 2*nextShift],
4360 nodes[3 - 2*nextShift],
4362 nodes[3 + 2*nextShift]};
4364 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4366 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4374 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4376 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4377 nodes[1], nodes[3], nodes[5], nodes[7] );
4379 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4381 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4382 nodes[4 - 2*nextShift],
4384 nodes[4 + 2*nextShift],
4386 nodes[5 - 2*nextShift],
4388 nodes[5 + 2*nextShift] };
4390 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4392 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4393 newOrder[ 2 ], newOrder[ 3 ],
4394 newOrder[ 4 ], newOrder[ 5 ],
4395 newOrder[ 6 ], newOrder[ 7 ]));
4398 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4400 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4401 SMDSAbs_Face, /*noMedium=*/false);
4403 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4405 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4406 nodes[4 - 2*nextShift],
4408 nodes[4 + 2*nextShift],
4410 nodes[5 - 2*nextShift],
4412 nodes[5 + 2*nextShift],
4415 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4417 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4418 newOrder[ 2 ], newOrder[ 3 ],
4419 newOrder[ 4 ], newOrder[ 5 ],
4420 newOrder[ 6 ], newOrder[ 7 ],
4424 else //////// polygon
4426 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4427 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4429 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4431 if ( !vTool.IsForward() )
4432 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4434 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4436 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4440 while ( srcElements.Length() < myLastCreatedElems.Length() )
4441 srcElements.Append( *srcEdge );
4443 } // loop on free faces
4445 // go to the next volume
4447 while ( iVol++ < nbVolumesByStep ) v++;
4450 } // loop on volumes of one step
4451 } // sweep free links into faces
4453 // Make a ceiling face with a normal external to a volume
4455 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4457 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4459 lastVol.SetExternalNormal();
4460 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4461 int nbn = lastVol.NbFaceNodes( iF );
4463 if (!hasFreeLinks ||
4464 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4465 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4467 else if ( nbn == 4 )
4469 if (!hasFreeLinks ||
4470 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4471 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4473 else if ( nbn == 6 && isQuadratic )
4475 if (!hasFreeLinks ||
4476 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4477 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4478 nodes[1], nodes[3], nodes[5]));
4480 else if ( nbn == 8 && isQuadratic )
4482 if (!hasFreeLinks ||
4483 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4484 nodes[1], nodes[3], nodes[5], nodes[7]) )
4485 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4486 nodes[1], nodes[3], nodes[5], nodes[7]));
4488 else if ( nbn == 9 && isQuadratic )
4490 if (!hasFreeLinks ||
4491 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4492 SMDSAbs_Face, /*noMedium=*/false) )
4493 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4494 nodes[1], nodes[3], nodes[5], nodes[7],
4498 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4499 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4500 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4503 while ( srcElements.Length() < myLastCreatedElems.Length() )
4504 srcElements.Append( elem );
4506 } // loop on swept elements
4509 //=======================================================================
4510 //function : RotationSweep
4512 //=======================================================================
4514 SMESH_MeshEditor::PGroupIDs
4515 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4516 const gp_Ax1& theAxis,
4517 const double theAngle,
4518 const int theNbSteps,
4519 const double theTol,
4520 const bool theMakeGroups,
4521 const bool theMakeWalls)
4523 myLastCreatedElems.Clear();
4524 myLastCreatedNodes.Clear();
4526 // source elements for each generated one
4527 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4529 MESSAGE( "RotationSweep()");
4531 aTrsf.SetRotation( theAxis, theAngle );
4533 aTrsf2.SetRotation( theAxis, theAngle/2. );
4535 gp_Lin aLine( theAxis );
4536 double aSqTol = theTol * theTol;
4538 SMESHDS_Mesh* aMesh = GetMeshDS();
4540 TNodeOfNodeListMap mapNewNodes;
4541 TElemOfVecOfNnlmiMap mapElemNewNodes;
4542 TElemOfElemListMap newElemsMap;
4544 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4545 myMesh->NbFaces(ORDER_QUADRATIC) +
4546 myMesh->NbVolumes(ORDER_QUADRATIC) );
4548 TIDSortedElemSet::iterator itElem;
4549 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4550 const SMDS_MeshElement* elem = *itElem;
4551 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4553 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4554 newNodesItVec.reserve( elem->NbNodes() );
4556 // loop on elem nodes
4557 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4558 while ( itN->more() )
4560 // check if a node has been already sweeped
4561 const SMDS_MeshNode* node = cast2Node( itN->next() );
4563 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4565 aXYZ.Coord( coord[0], coord[1], coord[2] );
4566 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4568 TNodeOfNodeListMapItr nIt =
4569 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4570 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4571 if ( listNewNodes.empty() )
4573 // check if we are to create medium nodes between corner ones
4574 bool needMediumNodes = false;
4575 if ( isQuadraticMesh )
4577 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4578 while (it->more() && !needMediumNodes )
4580 const SMDS_MeshElement* invElem = it->next();
4581 if ( invElem != elem && !theElems.count( invElem )) continue;
4582 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4583 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4584 needMediumNodes = true;
4589 const SMDS_MeshNode * newNode = node;
4590 for ( int i = 0; i < theNbSteps; i++ ) {
4592 if ( needMediumNodes ) // create a medium node
4594 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4595 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4596 myLastCreatedNodes.Append(newNode);
4597 srcNodes.Append( node );
4598 listNewNodes.push_back( newNode );
4599 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4602 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4604 // create a corner node
4605 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4606 myLastCreatedNodes.Append(newNode);
4607 srcNodes.Append( node );
4608 listNewNodes.push_back( newNode );
4611 listNewNodes.push_back( newNode );
4612 // if ( needMediumNodes )
4613 // listNewNodes.push_back( newNode );
4617 newNodesItVec.push_back( nIt );
4619 // make new elements
4620 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4624 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4626 PGroupIDs newGroupIDs;
4627 if ( theMakeGroups )
4628 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4634 //=======================================================================
4635 //function : CreateNode
4637 //=======================================================================
4638 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4641 const double tolnode,
4642 SMESH_SequenceOfNode& aNodes)
4644 // myLastCreatedElems.Clear();
4645 // myLastCreatedNodes.Clear();
4648 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4650 // try to search in sequence of existing nodes
4651 // if aNodes.Length()>0 we 'nave to use given sequence
4652 // else - use all nodes of mesh
4653 if(aNodes.Length()>0) {
4655 for(i=1; i<=aNodes.Length(); i++) {
4656 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4657 if(P1.Distance(P2)<tolnode)
4658 return aNodes.Value(i);
4662 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4663 while(itn->more()) {
4664 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4665 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4666 if(P1.Distance(P2)<tolnode)
4671 // create new node and return it
4672 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4673 //myLastCreatedNodes.Append(NewNode);
4678 //=======================================================================
4679 //function : ExtrusionSweep
4681 //=======================================================================
4683 SMESH_MeshEditor::PGroupIDs
4684 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4685 const gp_Vec& theStep,
4686 const int theNbSteps,
4687 TElemOfElemListMap& newElemsMap,
4688 const bool theMakeGroups,
4690 const double theTolerance)
4692 ExtrusParam aParams;
4693 aParams.myDir = gp_Dir(theStep);
4694 aParams.myNodes.Clear();
4695 aParams.mySteps = new TColStd_HSequenceOfReal;
4697 for(i=1; i<=theNbSteps; i++)
4698 aParams.mySteps->Append(theStep.Magnitude());
4701 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4705 //=======================================================================
4706 //function : ExtrusionSweep
4708 //=======================================================================
4710 SMESH_MeshEditor::PGroupIDs
4711 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4712 ExtrusParam& theParams,
4713 TElemOfElemListMap& newElemsMap,
4714 const bool theMakeGroups,
4716 const double theTolerance)
4718 myLastCreatedElems.Clear();
4719 myLastCreatedNodes.Clear();
4721 // source elements for each generated one
4722 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4724 SMESHDS_Mesh* aMesh = GetMeshDS();
4726 int nbsteps = theParams.mySteps->Length();
4728 TNodeOfNodeListMap mapNewNodes;
4729 //TNodeOfNodeVecMap mapNewNodes;
4730 TElemOfVecOfNnlmiMap mapElemNewNodes;
4731 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4733 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4734 myMesh->NbFaces(ORDER_QUADRATIC) +
4735 myMesh->NbVolumes(ORDER_QUADRATIC) );
4737 TIDSortedElemSet::iterator itElem;
4738 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4739 // check element type
4740 const SMDS_MeshElement* elem = *itElem;
4741 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4744 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4745 newNodesItVec.reserve( elem->NbNodes() );
4747 // loop on elem nodes
4748 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4749 while ( itN->more() )
4751 // check if a node has been already sweeped
4752 const SMDS_MeshNode* node = cast2Node( itN->next() );
4753 TNodeOfNodeListMap::iterator nIt =
4754 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4755 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4756 if ( listNewNodes.empty() )
4760 // check if we are to create medium nodes between corner ones
4761 bool needMediumNodes = false;
4762 if ( isQuadraticMesh )
4764 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4765 while (it->more() && !needMediumNodes )
4767 const SMDS_MeshElement* invElem = it->next();
4768 if ( invElem != elem && !theElems.count( invElem )) continue;
4769 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4770 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4771 needMediumNodes = true;
4775 double coord[] = { node->X(), node->Y(), node->Z() };
4776 for ( int i = 0; i < nbsteps; i++ )
4778 if ( needMediumNodes ) // create a medium node
4780 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4781 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4782 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4783 if( theFlags & EXTRUSION_FLAG_SEW ) {
4784 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4785 theTolerance, theParams.myNodes);
4786 listNewNodes.push_back( newNode );
4789 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4790 myLastCreatedNodes.Append(newNode);
4791 srcNodes.Append( node );
4792 listNewNodes.push_back( newNode );
4795 // create a corner node
4796 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4797 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4798 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4799 if( theFlags & EXTRUSION_FLAG_SEW ) {
4800 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4801 theTolerance, theParams.myNodes);
4802 listNewNodes.push_back( newNode );
4805 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4806 myLastCreatedNodes.Append(newNode);
4807 srcNodes.Append( node );
4808 listNewNodes.push_back( newNode );
4812 newNodesItVec.push_back( nIt );
4814 // make new elements
4815 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4818 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4819 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4821 PGroupIDs newGroupIDs;
4822 if ( theMakeGroups )
4823 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4828 //=======================================================================
4829 //function : ExtrusionAlongTrack
4831 //=======================================================================
4832 SMESH_MeshEditor::Extrusion_Error
4833 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4834 SMESH_subMesh* theTrack,
4835 const SMDS_MeshNode* theN1,
4836 const bool theHasAngles,
4837 list<double>& theAngles,
4838 const bool theLinearVariation,
4839 const bool theHasRefPoint,
4840 const gp_Pnt& theRefPoint,
4841 const bool theMakeGroups)
4843 MESSAGE("ExtrusionAlongTrack");
4844 myLastCreatedElems.Clear();
4845 myLastCreatedNodes.Clear();
4848 std::list<double> aPrms;
4849 TIDSortedElemSet::iterator itElem;
4852 TopoDS_Edge aTrackEdge;
4853 TopoDS_Vertex aV1, aV2;
4855 SMDS_ElemIteratorPtr aItE;
4856 SMDS_NodeIteratorPtr aItN;
4857 SMDSAbs_ElementType aTypeE;
4859 TNodeOfNodeListMap mapNewNodes;
4862 aNbE = theElements.size();
4865 return EXTR_NO_ELEMENTS;
4867 // 1.1 Track Pattern
4870 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4872 aItE = pSubMeshDS->GetElements();
4873 while ( aItE->more() ) {
4874 const SMDS_MeshElement* pE = aItE->next();
4875 aTypeE = pE->GetType();
4876 // Pattern must contain links only
4877 if ( aTypeE != SMDSAbs_Edge )
4878 return EXTR_PATH_NOT_EDGE;
4881 list<SMESH_MeshEditor_PathPoint> fullList;
4883 const TopoDS_Shape& aS = theTrack->GetSubShape();
4884 // Sub-shape for the Pattern must be an Edge or Wire
4885 if( aS.ShapeType() == TopAbs_EDGE ) {
4886 aTrackEdge = TopoDS::Edge( aS );
4887 // the Edge must not be degenerated
4888 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4889 return EXTR_BAD_PATH_SHAPE;
4890 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4891 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4892 const SMDS_MeshNode* aN1 = aItN->next();
4893 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4894 const SMDS_MeshNode* aN2 = aItN->next();
4895 // starting node must be aN1 or aN2
4896 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4897 return EXTR_BAD_STARTING_NODE;
4898 aItN = pSubMeshDS->GetNodes();
4899 while ( aItN->more() ) {
4900 const SMDS_MeshNode* pNode = aItN->next();
4901 const SMDS_EdgePosition* pEPos =
4902 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4903 double aT = pEPos->GetUParameter();
4904 aPrms.push_back( aT );
4906 //Extrusion_Error err =
4907 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4908 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4909 list< SMESH_subMesh* > LSM;
4910 TopTools_SequenceOfShape Edges;
4911 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4912 while(itSM->more()) {
4913 SMESH_subMesh* SM = itSM->next();
4915 const TopoDS_Shape& aS = SM->GetSubShape();
4918 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4919 int startNid = theN1->GetID();
4920 TColStd_MapOfInteger UsedNums;
4922 int NbEdges = Edges.Length();
4924 for(; i<=NbEdges; i++) {
4926 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4927 for(; itLSM!=LSM.end(); itLSM++) {
4929 if(UsedNums.Contains(k)) continue;
4930 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4931 SMESH_subMesh* locTrack = *itLSM;
4932 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4933 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4934 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4935 const SMDS_MeshNode* aN1 = aItN->next();
4936 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4937 const SMDS_MeshNode* aN2 = aItN->next();
4938 // starting node must be aN1 or aN2
4939 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4940 // 2. Collect parameters on the track edge
4942 aItN = locMeshDS->GetNodes();
4943 while ( aItN->more() ) {
4944 const SMDS_MeshNode* pNode = aItN->next();
4945 const SMDS_EdgePosition* pEPos =
4946 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4947 double aT = pEPos->GetUParameter();
4948 aPrms.push_back( aT );
4950 list<SMESH_MeshEditor_PathPoint> LPP;
4951 //Extrusion_Error err =
4952 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4953 LLPPs.push_back(LPP);
4955 // update startN for search following egde
4956 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4957 else startNid = aN1->GetID();
4961 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4962 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4963 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4964 for(; itPP!=firstList.end(); itPP++) {
4965 fullList.push_back( *itPP );
4967 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4968 fullList.pop_back();
4970 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4971 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4972 itPP = currList.begin();
4973 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4974 gp_Dir D1 = PP1.Tangent();
4975 gp_Dir D2 = PP2.Tangent();
4976 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4977 (D1.Z()+D2.Z())/2 ) );
4978 PP1.SetTangent(Dnew);
4979 fullList.push_back(PP1);
4981 for(; itPP!=firstList.end(); itPP++) {
4982 fullList.push_back( *itPP );
4984 PP1 = fullList.back();
4985 fullList.pop_back();
4987 // if wire not closed
4988 fullList.push_back(PP1);
4992 return EXTR_BAD_PATH_SHAPE;
4995 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4996 theHasRefPoint, theRefPoint, theMakeGroups);
5000 //=======================================================================
5001 //function : ExtrusionAlongTrack
5003 //=======================================================================
5004 SMESH_MeshEditor::Extrusion_Error
5005 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5006 SMESH_Mesh* theTrack,
5007 const SMDS_MeshNode* theN1,
5008 const bool theHasAngles,
5009 list<double>& theAngles,
5010 const bool theLinearVariation,
5011 const bool theHasRefPoint,
5012 const gp_Pnt& theRefPoint,
5013 const bool theMakeGroups)
5015 myLastCreatedElems.Clear();
5016 myLastCreatedNodes.Clear();
5019 std::list<double> aPrms;
5020 TIDSortedElemSet::iterator itElem;
5023 TopoDS_Edge aTrackEdge;
5024 TopoDS_Vertex aV1, aV2;
5026 SMDS_ElemIteratorPtr aItE;
5027 SMDS_NodeIteratorPtr aItN;
5028 SMDSAbs_ElementType aTypeE;
5030 TNodeOfNodeListMap mapNewNodes;
5033 aNbE = theElements.size();
5036 return EXTR_NO_ELEMENTS;
5038 // 1.1 Track Pattern
5041 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5043 aItE = pMeshDS->elementsIterator();
5044 while ( aItE->more() ) {
5045 const SMDS_MeshElement* pE = aItE->next();
5046 aTypeE = pE->GetType();
5047 // Pattern must contain links only
5048 if ( aTypeE != SMDSAbs_Edge )
5049 return EXTR_PATH_NOT_EDGE;
5052 list<SMESH_MeshEditor_PathPoint> fullList;
5054 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5056 if( aS == SMESH_Mesh::PseudoShape() ) {
5057 //Mesh without shape
5058 const SMDS_MeshNode* currentNode = NULL;
5059 const SMDS_MeshNode* prevNode = theN1;
5060 std::vector<const SMDS_MeshNode*> aNodesList;
5061 aNodesList.push_back(theN1);
5062 int nbEdges = 0, conn=0;
5063 const SMDS_MeshElement* prevElem = NULL;
5064 const SMDS_MeshElement* currentElem = NULL;
5065 int totalNbEdges = theTrack->NbEdges();
5066 SMDS_ElemIteratorPtr nIt;
5069 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5070 return EXTR_BAD_STARTING_NODE;
5073 conn = nbEdgeConnectivity(theN1);
5075 return EXTR_PATH_NOT_EDGE;
5077 aItE = theN1->GetInverseElementIterator();
5078 prevElem = aItE->next();
5079 currentElem = prevElem;
5081 if(totalNbEdges == 1 ) {
5082 nIt = currentElem->nodesIterator();
5083 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5084 if(currentNode == prevNode)
5085 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5086 aNodesList.push_back(currentNode);
5088 nIt = currentElem->nodesIterator();
5089 while( nIt->more() ) {
5090 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5091 if(currentNode == prevNode)
5092 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5093 aNodesList.push_back(currentNode);
5095 //case of the closed mesh
5096 if(currentNode == theN1) {
5101 conn = nbEdgeConnectivity(currentNode);
5103 return EXTR_PATH_NOT_EDGE;
5104 }else if( conn == 1 && nbEdges > 0 ) {
5109 prevNode = currentNode;
5110 aItE = currentNode->GetInverseElementIterator();
5111 currentElem = aItE->next();
5112 if( currentElem == prevElem)
5113 currentElem = aItE->next();
5114 nIt = currentElem->nodesIterator();
5115 prevElem = currentElem;
5121 if(nbEdges != totalNbEdges)
5122 return EXTR_PATH_NOT_EDGE;
5124 TopTools_SequenceOfShape Edges;
5125 double x1,x2,y1,y2,z1,z2;
5126 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5127 int startNid = theN1->GetID();
5128 for(int i = 1; i < aNodesList.size(); i++) {
5129 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5130 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5131 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5132 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5133 list<SMESH_MeshEditor_PathPoint> LPP;
5135 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5136 LLPPs.push_back(LPP);
5137 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5138 else startNid = aNodesList[i-1]->GetID();
5142 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5143 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5144 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5145 for(; itPP!=firstList.end(); itPP++) {
5146 fullList.push_back( *itPP );
5149 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5150 SMESH_MeshEditor_PathPoint PP2;
5151 fullList.pop_back();
5153 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5154 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5155 itPP = currList.begin();
5156 PP2 = currList.front();
5157 gp_Dir D1 = PP1.Tangent();
5158 gp_Dir D2 = PP2.Tangent();
5159 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5160 (D1.Z()+D2.Z())/2 ) );
5161 PP1.SetTangent(Dnew);
5162 fullList.push_back(PP1);
5164 for(; itPP!=currList.end(); itPP++) {
5165 fullList.push_back( *itPP );
5167 PP1 = fullList.back();
5168 fullList.pop_back();
5170 fullList.push_back(PP1);
5172 } // Sub-shape for the Pattern must be an Edge or Wire
5173 else if( aS.ShapeType() == TopAbs_EDGE ) {
5174 aTrackEdge = TopoDS::Edge( aS );
5175 // the Edge must not be degenerated
5176 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5177 return EXTR_BAD_PATH_SHAPE;
5178 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5179 const SMDS_MeshNode* aN1 = 0;
5180 const SMDS_MeshNode* aN2 = 0;
5181 if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5182 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5185 if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5186 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5189 // starting node must be aN1 or aN2
5190 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5191 return EXTR_BAD_STARTING_NODE;
5192 aItN = pMeshDS->nodesIterator();
5193 while ( aItN->more() ) {
5194 const SMDS_MeshNode* pNode = aItN->next();
5195 if( pNode==aN1 || pNode==aN2 ) continue;
5196 const SMDS_EdgePosition* pEPos =
5197 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5198 double aT = pEPos->GetUParameter();
5199 aPrms.push_back( aT );
5201 //Extrusion_Error err =
5202 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5204 else if( aS.ShapeType() == TopAbs_WIRE ) {
5205 list< SMESH_subMesh* > LSM;
5206 TopTools_SequenceOfShape Edges;
5207 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5208 for(; eExp.More(); eExp.Next()) {
5209 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5210 if( BRep_Tool::Degenerated(E) ) continue;
5211 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5217 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5218 TopoDS_Vertex aVprev;
5219 TColStd_MapOfInteger UsedNums;
5220 int NbEdges = Edges.Length();
5222 for(; i<=NbEdges; i++) {
5224 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5225 for(; itLSM!=LSM.end(); itLSM++) {
5227 if(UsedNums.Contains(k)) continue;
5228 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5229 SMESH_subMesh* locTrack = *itLSM;
5230 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5231 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5232 bool aN1isOK = false, aN2isOK = false;
5233 if ( aVprev.IsNull() ) {
5234 // if previous vertex is not yet defined, it means that we in the beginning of wire
5235 // and we have to find initial vertex corresponding to starting node theN1
5236 const SMDS_MeshNode* aN1 = 0;
5237 const SMDS_MeshNode* aN2 = 0;
5239 if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5240 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5243 if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5244 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5247 // starting node must be aN1 or aN2
5248 aN1isOK = ( aN1 && aN1 == theN1 );
5249 aN2isOK = ( aN2 && aN2 == theN1 );
5252 // we have specified ending vertex of the previous edge on the previous iteration
5253 // and we have just to check that it corresponds to any vertex in current segment
5254 aN1isOK = aVprev.IsSame( aV1 );
5255 aN2isOK = aVprev.IsSame( aV2 );
5257 if ( !aN1isOK && !aN2isOK ) continue;
5258 // 2. Collect parameters on the track edge
5260 aItN = locMeshDS->GetNodes();
5261 while ( aItN->more() ) {
5262 const SMDS_MeshNode* pNode = aItN->next();
5263 const SMDS_EdgePosition* pEPos =
5264 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5265 double aT = pEPos->GetUParameter();
5266 aPrms.push_back( aT );
5268 list<SMESH_MeshEditor_PathPoint> LPP;
5269 //Extrusion_Error err =
5270 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5271 LLPPs.push_back(LPP);
5273 // update startN for search following egde
5274 if ( aN1isOK ) aVprev = aV2;
5279 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5280 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5281 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5282 for(; itPP!=firstList.end(); itPP++) {
5283 fullList.push_back( *itPP );
5285 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5286 fullList.pop_back();
5288 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5289 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5290 itPP = currList.begin();
5291 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5292 gp_Dir D1 = PP1.Tangent();
5293 gp_Dir D2 = PP2.Tangent();
5294 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5295 PP1.SetTangent(Dnew);
5296 fullList.push_back(PP1);
5298 for(; itPP!=currList.end(); itPP++) {
5299 fullList.push_back( *itPP );
5301 PP1 = fullList.back();
5302 fullList.pop_back();
5304 // if wire not closed
5305 fullList.push_back(PP1);
5309 return EXTR_BAD_PATH_SHAPE;
5312 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5313 theHasRefPoint, theRefPoint, theMakeGroups);
5317 //=======================================================================
5318 //function : MakeEdgePathPoints
5319 //purpose : auxilary for ExtrusionAlongTrack
5320 //=======================================================================
5321 SMESH_MeshEditor::Extrusion_Error
5322 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5323 const TopoDS_Edge& aTrackEdge,
5325 list<SMESH_MeshEditor_PathPoint>& LPP)
5327 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5329 aTolVec2=aTolVec*aTolVec;
5331 TopoDS_Vertex aV1, aV2;
5332 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5333 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5334 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5335 // 2. Collect parameters on the track edge
5336 aPrms.push_front( aT1 );
5337 aPrms.push_back( aT2 );
5340 if( FirstIsStart ) {
5351 SMESH_MeshEditor_PathPoint aPP;
5352 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5353 std::list<double>::iterator aItD = aPrms.begin();
5354 for(; aItD != aPrms.end(); ++aItD) {
5358 aC3D->D1( aT, aP3D, aVec );
5359 aL2 = aVec.SquareMagnitude();
5360 if ( aL2 < aTolVec2 )
5361 return EXTR_CANT_GET_TANGENT;
5362 gp_Dir aTgt( aVec );
5364 aPP.SetTangent( aTgt );
5365 aPP.SetParameter( aT );
5372 //=======================================================================
5373 //function : MakeExtrElements
5374 //purpose : auxilary for ExtrusionAlongTrack
5375 //=======================================================================
5376 SMESH_MeshEditor::Extrusion_Error
5377 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5378 list<SMESH_MeshEditor_PathPoint>& fullList,
5379 const bool theHasAngles,
5380 list<double>& theAngles,
5381 const bool theLinearVariation,
5382 const bool theHasRefPoint,
5383 const gp_Pnt& theRefPoint,
5384 const bool theMakeGroups)
5386 MESSAGE("MakeExtrElements");
5387 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5388 int aNbTP = fullList.size();
5389 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5391 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5392 LinearAngleVariation(aNbTP-1, theAngles);
5394 vector<double> aAngles( aNbTP );
5396 for(; j<aNbTP; ++j) {
5399 if ( theHasAngles ) {
5401 std::list<double>::iterator aItD = theAngles.begin();
5402 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5404 aAngles[j] = anAngle;
5407 // fill vector of path points with angles
5408 //aPPs.resize(fullList.size());
5410 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5411 for(; itPP!=fullList.end(); itPP++) {
5413 SMESH_MeshEditor_PathPoint PP = *itPP;
5414 PP.SetAngle(aAngles[j]);
5418 TNodeOfNodeListMap mapNewNodes;
5419 TElemOfVecOfNnlmiMap mapElemNewNodes;
5420 TElemOfElemListMap newElemsMap;
5421 TIDSortedElemSet::iterator itElem;
5424 SMDSAbs_ElementType aTypeE;
5425 // source elements for each generated one
5426 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5428 // 3. Center of rotation aV0
5429 gp_Pnt aV0 = theRefPoint;
5431 if ( !theHasRefPoint ) {
5433 aGC.SetCoord( 0.,0.,0. );
5435 itElem = theElements.begin();
5436 for ( ; itElem != theElements.end(); itElem++ ) {
5437 const SMDS_MeshElement* elem = *itElem;
5439 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5440 while ( itN->more() ) {
5441 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5446 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5447 list<const SMDS_MeshNode*> aLNx;
5448 mapNewNodes[node] = aLNx;
5450 gp_XYZ aXYZ( aX, aY, aZ );
5458 } // if (!theHasRefPoint) {
5459 mapNewNodes.clear();
5461 // 4. Processing the elements
5462 SMESHDS_Mesh* aMesh = GetMeshDS();
5464 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5465 // check element type
5466 const SMDS_MeshElement* elem = *itElem;
5467 aTypeE = elem->GetType();
5468 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5471 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5472 newNodesItVec.reserve( elem->NbNodes() );
5474 // loop on elem nodes
5476 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5477 while ( itN->more() )
5480 // check if a node has been already processed
5481 const SMDS_MeshNode* node =
5482 static_cast<const SMDS_MeshNode*>( itN->next() );
5483 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5484 if ( nIt == mapNewNodes.end() ) {
5485 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5486 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5489 aX = node->X(); aY = node->Y(); aZ = node->Z();
5491 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5492 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5493 gp_Ax1 anAx1, anAxT1T0;
5494 gp_Dir aDT1x, aDT0x, aDT1T0;
5499 aPN0.SetCoord(aX, aY, aZ);
5501 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5503 aDT0x= aPP0.Tangent();
5504 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5506 for ( j = 1; j < aNbTP; ++j ) {
5507 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5509 aDT1x = aPP1.Tangent();
5510 aAngle1x = aPP1.Angle();
5512 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5514 gp_Vec aV01x( aP0x, aP1x );
5515 aTrsf.SetTranslation( aV01x );
5518 aV1x = aV0x.Transformed( aTrsf );
5519 aPN1 = aPN0.Transformed( aTrsf );
5521 // rotation 1 [ T1,T0 ]
5522 aAngleT1T0=-aDT1x.Angle( aDT0x );
5523 if (fabs(aAngleT1T0) > aTolAng) {
5525 anAxT1T0.SetLocation( aV1x );
5526 anAxT1T0.SetDirection( aDT1T0 );
5527 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5529 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5533 if ( theHasAngles ) {
5534 anAx1.SetLocation( aV1x );
5535 anAx1.SetDirection( aDT1x );
5536 aTrsfRot.SetRotation( anAx1, aAngle1x );
5538 aPN1 = aPN1.Transformed( aTrsfRot );
5542 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5543 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5544 // create additional node
5545 double x = ( aPN1.X() + aPN0.X() )/2.;
5546 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5547 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5548 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5549 myLastCreatedNodes.Append(newNode);
5550 srcNodes.Append( node );
5551 listNewNodes.push_back( newNode );
5556 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5557 myLastCreatedNodes.Append(newNode);
5558 srcNodes.Append( node );
5559 listNewNodes.push_back( newNode );
5569 // if current elem is quadratic and current node is not medium
5570 // we have to check - may be it is needed to insert additional nodes
5571 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5572 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5573 if(listNewNodes.size()==aNbTP-1) {
5574 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5575 gp_XYZ P(node->X(), node->Y(), node->Z());
5576 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5578 for(i=0; i<aNbTP-1; i++) {
5579 const SMDS_MeshNode* N = *it;
5580 double x = ( N->X() + P.X() )/2.;
5581 double y = ( N->Y() + P.Y() )/2.;
5582 double z = ( N->Z() + P.Z() )/2.;
5583 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5584 srcNodes.Append( node );
5585 myLastCreatedNodes.Append(newN);
5588 P = gp_XYZ(N->X(),N->Y(),N->Z());
5590 listNewNodes.clear();
5591 for(i=0; i<2*(aNbTP-1); i++) {
5592 listNewNodes.push_back(aNodes[i]);
5598 newNodesItVec.push_back( nIt );
5600 // make new elements
5601 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5602 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5603 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5606 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5608 if ( theMakeGroups )
5609 generateGroups( srcNodes, srcElems, "extruded");
5615 //=======================================================================
5616 //function : LinearAngleVariation
5617 //purpose : auxilary for ExtrusionAlongTrack
5618 //=======================================================================
5619 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5620 list<double>& Angles)
5622 int nbAngles = Angles.size();
5623 if( nbSteps > nbAngles ) {
5624 vector<double> theAngles(nbAngles);
5625 list<double>::iterator it = Angles.begin();
5627 for(; it!=Angles.end(); it++) {
5629 theAngles[i] = (*it);
5632 double rAn2St = double( nbAngles ) / double( nbSteps );
5633 double angPrev = 0, angle;
5634 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5635 double angCur = rAn2St * ( iSt+1 );
5636 double angCurFloor = floor( angCur );
5637 double angPrevFloor = floor( angPrev );
5638 if ( angPrevFloor == angCurFloor )
5639 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5641 int iP = int( angPrevFloor );
5642 double angPrevCeil = ceil(angPrev);
5643 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5645 int iC = int( angCurFloor );
5646 if ( iC < nbAngles )
5647 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5649 iP = int( angPrevCeil );
5651 angle += theAngles[ iC ];
5653 res.push_back(angle);
5658 for(; it!=res.end(); it++)
5659 Angles.push_back( *it );
5664 //================================================================================
5666 * \brief Move or copy theElements applying theTrsf to their nodes
5667 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5668 * \param theTrsf - transformation to apply
5669 * \param theCopy - if true, create translated copies of theElems
5670 * \param theMakeGroups - if true and theCopy, create translated groups
5671 * \param theTargetMesh - mesh to copy translated elements into
5672 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5674 //================================================================================
5676 SMESH_MeshEditor::PGroupIDs
5677 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5678 const gp_Trsf& theTrsf,
5680 const bool theMakeGroups,
5681 SMESH_Mesh* theTargetMesh)
5683 myLastCreatedElems.Clear();
5684 myLastCreatedNodes.Clear();
5686 bool needReverse = false;
5687 string groupPostfix;
5688 switch ( theTrsf.Form() ) {
5690 MESSAGE("gp_PntMirror");
5692 groupPostfix = "mirrored";
5695 MESSAGE("gp_Ax1Mirror");
5696 groupPostfix = "mirrored";
5699 MESSAGE("gp_Ax2Mirror");
5701 groupPostfix = "mirrored";
5704 MESSAGE("gp_Rotation");
5705 groupPostfix = "rotated";
5707 case gp_Translation:
5708 MESSAGE("gp_Translation");
5709 groupPostfix = "translated";
5712 MESSAGE("gp_Scale");
5713 groupPostfix = "scaled";
5715 case gp_CompoundTrsf: // different scale by axis
5716 MESSAGE("gp_CompoundTrsf");
5717 groupPostfix = "scaled";
5721 needReverse = false;
5722 groupPostfix = "transformed";
5725 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5726 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5727 SMESHDS_Mesh* aMesh = GetMeshDS();
5730 // map old node to new one
5731 TNodeNodeMap nodeMap;
5733 // elements sharing moved nodes; those of them which have all
5734 // nodes mirrored but are not in theElems are to be reversed
5735 TIDSortedElemSet inverseElemSet;
5737 // source elements for each generated one
5738 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5740 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5741 TIDSortedElemSet orphanNode;
5743 if ( theElems.empty() ) // transform the whole mesh
5746 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5747 while ( eIt->more() ) theElems.insert( eIt->next() );
5749 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5750 while ( nIt->more() )
5752 const SMDS_MeshNode* node = nIt->next();
5753 if ( node->NbInverseElements() == 0)
5754 orphanNode.insert( node );
5758 // loop on elements to transform nodes : first orphan nodes then elems
5759 TIDSortedElemSet::iterator itElem;
5760 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5761 for (int i=0; i<2; i++)
5762 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5763 const SMDS_MeshElement* elem = *itElem;
5767 // loop on elem nodes
5768 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5769 while ( itN->more() ) {
5771 const SMDS_MeshNode* node = cast2Node( itN->next() );
5772 // check if a node has been already transformed
5773 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5774 nodeMap.insert( make_pair ( node, node ));
5775 if ( !n2n_isnew.second )
5779 coord[0] = node->X();
5780 coord[1] = node->Y();
5781 coord[2] = node->Z();
5782 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5783 if ( theTargetMesh ) {
5784 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5785 n2n_isnew.first->second = newNode;
5786 myLastCreatedNodes.Append(newNode);
5787 srcNodes.Append( node );
5789 else if ( theCopy ) {
5790 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5791 n2n_isnew.first->second = newNode;
5792 myLastCreatedNodes.Append(newNode);
5793 srcNodes.Append( node );
5796 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5797 // node position on shape becomes invalid
5798 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5799 ( SMDS_SpacePosition::originSpacePosition() );
5802 // keep inverse elements
5803 if ( !theCopy && !theTargetMesh && needReverse ) {
5804 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5805 while ( invElemIt->more() ) {
5806 const SMDS_MeshElement* iel = invElemIt->next();
5807 inverseElemSet.insert( iel );
5813 // either create new elements or reverse mirrored ones
5814 if ( !theCopy && !needReverse && !theTargetMesh )
5817 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5818 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5819 theElems.insert( *invElemIt );
5821 // Replicate or reverse elements
5823 std::vector<int> iForw;
5824 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5826 const SMDS_MeshElement* elem = *itElem;
5827 if ( !elem ) continue;
5829 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5830 int nbNodes = elem->NbNodes();
5831 if ( geomType == SMDSGeom_NONE ) continue; // node
5833 switch ( geomType ) {
5835 case SMDSGeom_POLYGON: // ---------------------- polygon
5837 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5839 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5840 while (itN->more()) {
5841 const SMDS_MeshNode* node =
5842 static_cast<const SMDS_MeshNode*>(itN->next());
5843 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5844 if (nodeMapIt == nodeMap.end())
5845 break; // not all nodes transformed
5847 // reverse mirrored faces and volumes
5848 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5850 poly_nodes[iNode] = (*nodeMapIt).second;
5854 if ( iNode != nbNodes )
5855 continue; // not all nodes transformed
5857 if ( theTargetMesh ) {
5858 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5859 srcElems.Append( elem );
5861 else if ( theCopy ) {
5862 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5863 srcElems.Append( elem );
5866 aMesh->ChangePolygonNodes(elem, poly_nodes);
5871 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5873 const SMDS_VtkVolume* aPolyedre =
5874 dynamic_cast<const SMDS_VtkVolume*>( elem );
5876 MESSAGE("Warning: bad volumic element");
5880 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5881 vector<int> quantities; quantities.reserve( nbNodes );
5883 bool allTransformed = true;
5884 int nbFaces = aPolyedre->NbFaces();
5885 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5886 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5887 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5888 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5889 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5890 if (nodeMapIt == nodeMap.end()) {
5891 allTransformed = false; // not all nodes transformed
5893 poly_nodes.push_back((*nodeMapIt).second);
5895 if ( needReverse && allTransformed )
5896 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5898 quantities.push_back(nbFaceNodes);
5900 if ( !allTransformed )
5901 continue; // not all nodes transformed
5903 if ( theTargetMesh ) {
5904 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5905 srcElems.Append( elem );
5907 else if ( theCopy ) {
5908 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5909 srcElems.Append( elem );
5912 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5917 case SMDSGeom_BALL: // -------------------- Ball
5919 if ( !theCopy && !theTargetMesh ) continue;
5921 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5922 if (nodeMapIt == nodeMap.end())
5923 continue; // not all nodes transformed
5925 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5926 if ( theTargetMesh ) {
5927 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5928 srcElems.Append( elem );
5931 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5932 srcElems.Append( elem );
5937 default: // ----------------------- Regular elements
5939 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5940 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5941 const std::vector<int>& i = needReverse ? iRev : iForw;
5943 // find transformed nodes
5944 vector<const SMDS_MeshNode*> nodes(nbNodes);
5946 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5947 while ( itN->more() ) {
5948 const SMDS_MeshNode* node =
5949 static_cast<const SMDS_MeshNode*>( itN->next() );
5950 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5951 if ( nodeMapIt == nodeMap.end() )
5952 break; // not all nodes transformed
5953 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5955 if ( iNode != nbNodes )
5956 continue; // not all nodes transformed
5958 if ( theTargetMesh ) {
5959 if ( SMDS_MeshElement* copy =
5960 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5961 myLastCreatedElems.Append( copy );
5962 srcElems.Append( elem );
5965 else if ( theCopy ) {
5966 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5967 srcElems.Append( elem );
5970 // reverse element as it was reversed by transformation
5972 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5974 } // switch ( geomType )
5976 } // loop on elements
5978 PGroupIDs newGroupIDs;
5980 if ( ( theMakeGroups && theCopy ) ||
5981 ( theMakeGroups && theTargetMesh ) )
5982 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5987 //=======================================================================
5989 * \brief Create groups of elements made during transformation
5990 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5991 * \param elemGens - elements making corresponding myLastCreatedElems
5992 * \param postfix - to append to names of new groups
5994 //=======================================================================
5996 SMESH_MeshEditor::PGroupIDs
5997 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5998 const SMESH_SequenceOfElemPtr& elemGens,
5999 const std::string& postfix,
6000 SMESH_Mesh* targetMesh)
6002 PGroupIDs newGroupIDs( new list<int> );
6003 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6005 // Sort existing groups by types and collect their names
6007 // to store an old group and a generated new ones
6009 using boost::make_tuple;
6010 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6011 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6012 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6014 set< string > groupNames;
6016 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6017 if ( !groupIt->more() ) return newGroupIDs;
6019 int newGroupID = mesh->GetGroupIds().back()+1;
6020 while ( groupIt->more() )
6022 SMESH_Group * group = groupIt->next();
6023 if ( !group ) continue;
6024 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6025 if ( !groupDS || groupDS->IsEmpty() ) continue;
6026 groupNames.insert ( group->GetName() );
6027 groupDS->SetStoreName( group->GetName() );
6028 const SMDSAbs_ElementType type = groupDS->GetType();
6029 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6030 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6031 groupsByType[ groupDS->GetType() ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6032 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6035 // Loop on nodes and elements to add them in new groups
6037 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6039 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6040 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6041 if ( gens.Length() != elems.Length() )
6042 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6044 // loop on created elements
6045 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6047 const SMDS_MeshElement* sourceElem = gens( iElem );
6048 if ( !sourceElem ) {
6049 MESSAGE("generateGroups(): NULL source element");
6052 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6053 if ( groupsOldNew.empty() ) { // no groups of this type at all
6054 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6055 ++iElem; // skip all elements made by sourceElem
6058 // collect all elements made by the iElem-th sourceElem
6059 list< const SMDS_MeshElement* > resultElems;
6060 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6061 if ( resElem != sourceElem )
6062 resultElems.push_back( resElem );
6063 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6064 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6065 if ( resElem != sourceElem )
6066 resultElems.push_back( resElem );
6068 // add resultElems to groups made by ones the sourceElem belongs to
6069 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6070 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6072 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6073 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6075 // fill in a new group
6076 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6077 list< const SMDS_MeshElement* > rejectedElems; // elements of other type
6078 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6079 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6080 if ( !newGroup.Add( *resElemIt ))
6081 rejectedElems.push_back( *resElemIt );
6084 if ( !rejectedElems.empty() )
6086 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6087 resLast = rejectedElems.end();
6088 for ( resElemIt = rejectedElems.begin(); resElemIt != resLast; ++resElemIt )
6089 !newTopGroup.Add( *resElemIt );
6093 } // loop on created elements
6094 }// loop on nodes and elements
6096 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6098 list<int> topGrouIds;
6099 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6101 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6102 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6103 orderedOldNewGroups[i]->get<2>() };
6104 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6105 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6107 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6108 if ( newGroupDS->IsEmpty() )
6110 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6115 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6118 const bool isTop = ( nbNewGroups == 2 &&
6119 newGroupDS->GetType() == oldGroupDS->GetType() );
6120 string name = oldGroupDS->GetStoreName();
6121 if ( !targetMesh ) {
6122 string suffix = ( isTop ? "top": postfix.c_str() );
6126 while ( !groupNames.insert( name ).second ) // name exists
6127 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6132 newGroupDS->SetStoreName( name.c_str() );
6134 // make a SMESH_Groups
6135 mesh->AddGroup( newGroupDS );
6137 topGrouIds.push_back( newGroupDS->GetID() );
6139 newGroupIDs->push_back( newGroupDS->GetID() );
6143 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6148 //================================================================================
6150 * \brief Return list of group of nodes close to each other within theTolerance
6151 * Search among theNodes or in the whole mesh if theNodes is empty using
6152 * an Octree algorithm
6154 //================================================================================
6156 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6157 const double theTolerance,
6158 TListOfListOfNodes & theGroupsOfNodes)
6160 myLastCreatedElems.Clear();
6161 myLastCreatedNodes.Clear();
6163 if ( theNodes.empty() )
6164 { // get all nodes in the mesh
6165 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6166 while ( nIt->more() )
6167 theNodes.insert( theNodes.end(),nIt->next());
6170 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6174 //=======================================================================
6176 * \brief Implementation of search for the node closest to point
6178 //=======================================================================
6180 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6182 //---------------------------------------------------------------------
6184 * \brief Constructor
6186 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6188 myMesh = ( SMESHDS_Mesh* ) theMesh;
6190 TIDSortedNodeSet nodes;
6192 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6193 while ( nIt->more() )
6194 nodes.insert( nodes.end(), nIt->next() );
6196 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6198 // get max size of a leaf box
6199 SMESH_OctreeNode* tree = myOctreeNode;
6200 while ( !tree->isLeaf() )
6202 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6206 myHalfLeafSize = tree->maxSize() / 2.;
6209 //---------------------------------------------------------------------
6211 * \brief Move node and update myOctreeNode accordingly
6213 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6215 myOctreeNode->UpdateByMoveNode( node, toPnt );
6216 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6219 //---------------------------------------------------------------------
6221 * \brief Do it's job
6223 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6225 map<double, const SMDS_MeshNode*> dist2Nodes;
6226 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6227 if ( !dist2Nodes.empty() )
6228 return dist2Nodes.begin()->second;
6229 list<const SMDS_MeshNode*> nodes;
6230 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6232 double minSqDist = DBL_MAX;
6233 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6235 // sort leafs by their distance from thePnt
6236 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6237 TDistTreeMap treeMap;
6238 list< SMESH_OctreeNode* > treeList;
6239 list< SMESH_OctreeNode* >::iterator trIt;
6240 treeList.push_back( myOctreeNode );
6242 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6243 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6244 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6246 SMESH_OctreeNode* tree = *trIt;
6247 if ( !tree->isLeaf() ) // put children to the queue
6249 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6250 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6251 while ( cIt->more() )
6252 treeList.push_back( cIt->next() );
6254 else if ( tree->NbNodes() ) // put a tree to the treeMap
6256 const Bnd_B3d& box = *tree->getBox();
6257 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6258 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6259 if ( !it_in.second ) // not unique distance to box center
6260 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6263 // find distance after which there is no sense to check tree's
6264 double sqLimit = DBL_MAX;
6265 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6266 if ( treeMap.size() > 5 ) {
6267 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6268 const Bnd_B3d& box = *closestTree->getBox();
6269 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6270 sqLimit = limit * limit;
6272 // get all nodes from trees
6273 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6274 if ( sqDist_tree->first > sqLimit )
6276 SMESH_OctreeNode* tree = sqDist_tree->second;
6277 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6280 // find closest among nodes
6281 minSqDist = DBL_MAX;
6282 const SMDS_MeshNode* closestNode = 0;
6283 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6284 for ( ; nIt != nodes.end(); ++nIt ) {
6285 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6286 if ( minSqDist > sqDist ) {
6294 //---------------------------------------------------------------------
6298 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6300 //---------------------------------------------------------------------
6302 * \brief Return the node tree
6304 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6307 SMESH_OctreeNode* myOctreeNode;
6308 SMESHDS_Mesh* myMesh;
6309 double myHalfLeafSize; // max size of a leaf box
6312 //=======================================================================
6314 * \brief Return SMESH_NodeSearcher
6316 //=======================================================================
6318 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6320 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6323 // ========================================================================
6324 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6326 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6327 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6328 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6330 //=======================================================================
6332 * \brief Octal tree of bounding boxes of elements
6334 //=======================================================================
6336 class ElementBndBoxTree : public SMESH_Octree
6340 ElementBndBoxTree(const SMDS_Mesh& mesh,
6341 SMDSAbs_ElementType elemType,
6342 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6343 double tolerance = NodeRadius );
6344 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6345 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6346 void getElementsInSphere ( const gp_XYZ& center,
6347 const double radius, TIDSortedElemSet& foundElems);
6348 size_t getSize() { return std::max( _size, _elements.size() ); }
6349 ~ElementBndBoxTree();
6352 ElementBndBoxTree():_size(0) {}
6353 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6354 void buildChildrenData();
6355 Bnd_B3d* buildRootBox();
6357 //!< Bounding box of element
6358 struct ElementBox : public Bnd_B3d
6360 const SMDS_MeshElement* _element;
6361 int _refCount; // an ElementBox can be included in several tree branches
6362 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6364 vector< ElementBox* > _elements;
6368 //================================================================================
6370 * \brief ElementBndBoxTree creation
6372 //================================================================================
6374 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6375 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6377 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6378 _elements.reserve( nbElems );
6380 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6381 while ( elemIt->more() )
6382 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6387 //================================================================================
6391 //================================================================================
6393 ElementBndBoxTree::~ElementBndBoxTree()
6395 for ( int i = 0; i < _elements.size(); ++i )
6396 if ( --_elements[i]->_refCount <= 0 )
6397 delete _elements[i];
6400 //================================================================================
6402 * \brief Return the maximal box
6404 //================================================================================
6406 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6408 Bnd_B3d* box = new Bnd_B3d;
6409 for ( int i = 0; i < _elements.size(); ++i )
6410 box->Add( *_elements[i] );
6414 //================================================================================
6416 * \brief Redistrubute element boxes among children
6418 //================================================================================
6420 void ElementBndBoxTree::buildChildrenData()
6422 for ( int i = 0; i < _elements.size(); ++i )
6424 for (int j = 0; j < 8; j++)
6426 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6428 _elements[i]->_refCount++;
6429 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6432 _elements[i]->_refCount--;
6434 _size = _elements.size();
6435 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6437 for (int j = 0; j < 8; j++)
6439 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6440 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6441 child->myIsLeaf = true;
6443 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6444 SMESHUtils::CompactVector( child->_elements );
6448 //================================================================================
6450 * \brief Return elements which can include the point
6452 //================================================================================
6454 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6455 TIDSortedElemSet& foundElems)
6457 if ( getBox()->IsOut( point.XYZ() ))
6462 for ( int i = 0; i < _elements.size(); ++i )
6463 if ( !_elements[i]->IsOut( point.XYZ() ))
6464 foundElems.insert( _elements[i]->_element );
6468 for (int i = 0; i < 8; i++)
6469 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6473 //================================================================================
6475 * \brief Return elements which can be intersected by the line
6477 //================================================================================
6479 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6480 TIDSortedElemSet& foundElems)
6482 if ( getBox()->IsOut( line ))
6487 for ( int i = 0; i < _elements.size(); ++i )
6488 if ( !_elements[i]->IsOut( line ))
6489 foundElems.insert( _elements[i]->_element );
6493 for (int i = 0; i < 8; i++)
6494 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6498 //================================================================================
6500 * \brief Return elements from leaves intersecting the sphere
6502 //================================================================================
6504 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6505 const double radius,
6506 TIDSortedElemSet& foundElems)
6508 if ( getBox()->IsOut( center, radius ))
6513 for ( int i = 0; i < _elements.size(); ++i )
6514 if ( !_elements[i]->IsOut( center, radius ))
6515 foundElems.insert( _elements[i]->_element );
6519 for (int i = 0; i < 8; i++)
6520 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6524 //================================================================================
6526 * \brief Construct the element box
6528 //================================================================================
6530 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6534 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6535 while ( nIt->more() )
6536 Add( SMESH_TNodeXYZ( nIt->next() ));
6537 Enlarge( tolerance );
6542 //=======================================================================
6544 * \brief Implementation of search for the elements by point and
6545 * of classification of point in 2D mesh
6547 //=======================================================================
6549 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6551 SMESHDS_Mesh* _mesh;
6552 SMDS_ElemIteratorPtr _meshPartIt;
6553 ElementBndBoxTree* _ebbTree;
6554 SMESH_NodeSearcherImpl* _nodeSearcher;
6555 SMDSAbs_ElementType _elementType;
6557 bool _outerFacesFound;
6558 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6560 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6561 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6562 ~SMESH_ElementSearcherImpl()
6564 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6565 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6567 virtual int FindElementsByPoint(const gp_Pnt& point,
6568 SMDSAbs_ElementType type,
6569 vector< const SMDS_MeshElement* >& foundElements);
6570 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6571 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6572 SMDSAbs_ElementType type );
6574 void GetElementsNearLine( const gp_Ax1& line,
6575 SMDSAbs_ElementType type,
6576 vector< const SMDS_MeshElement* >& foundElems);
6577 double getTolerance();
6578 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6579 const double tolerance, double & param);
6580 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6581 bool isOuterBoundary(const SMDS_MeshElement* face) const
6583 return _outerFaces.empty() || _outerFaces.count(face);
6585 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6587 const SMDS_MeshElement* _face;
6589 bool _coincides; //!< the line lays in face plane
6590 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6591 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6593 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6596 TIDSortedElemSet _faces;
6597 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6598 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6602 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6604 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6605 << ", _coincides="<<i._coincides << ")";
6608 //=======================================================================
6610 * \brief define tolerance for search
6612 //=======================================================================
6614 double SMESH_ElementSearcherImpl::getTolerance()
6616 if ( _tolerance < 0 )
6618 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6621 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6623 double boxSize = _nodeSearcher->getTree()->maxSize();
6624 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6626 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6628 double boxSize = _ebbTree->maxSize();
6629 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6631 if ( _tolerance == 0 )
6633 // define tolerance by size of a most complex element
6634 int complexType = SMDSAbs_Volume;
6635 while ( complexType > SMDSAbs_All &&
6636 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6638 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6640 if ( complexType == int( SMDSAbs_Node ))
6642 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6644 if ( meshInfo.NbNodes() > 2 )
6645 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6649 SMDS_ElemIteratorPtr elemIt =
6650 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6651 const SMDS_MeshElement* elem = elemIt->next();
6652 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6653 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6655 while ( nodeIt->more() )
6657 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6658 elemSize = max( dist, elemSize );
6661 _tolerance = 1e-4 * elemSize;
6667 //================================================================================
6669 * \brief Find intersection of the line and an edge of face and return parameter on line
6671 //================================================================================
6673 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6674 const SMDS_MeshElement* face,
6681 GeomAPI_ExtremaCurveCurve anExtCC;
6682 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6684 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6685 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6687 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6688 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6689 anExtCC.Init( lineCurve, edge);
6690 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6692 Quantity_Parameter pl, pe;
6693 anExtCC.LowerDistanceParameters( pl, pe );
6695 if ( ++nbInts == 2 )
6699 if ( nbInts > 0 ) param /= nbInts;
6702 //================================================================================
6704 * \brief Find all faces belonging to the outer boundary of mesh
6706 //================================================================================
6708 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6710 if ( _outerFacesFound ) return;
6712 // Collect all outer faces by passing from one outer face to another via their links
6713 // and BTW find out if there are internal faces at all.
6715 // checked links and links where outer boundary meets internal one
6716 set< SMESH_TLink > visitedLinks, seamLinks;
6718 // links to treat with already visited faces sharing them
6719 list < TFaceLink > startLinks;
6721 // load startLinks with the first outerFace
6722 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6723 _outerFaces.insert( outerFace );
6725 TIDSortedElemSet emptySet;
6726 while ( !startLinks.empty() )
6728 const SMESH_TLink& link = startLinks.front()._link;
6729 TIDSortedElemSet& faces = startLinks.front()._faces;
6731 outerFace = *faces.begin();
6732 // find other faces sharing the link
6733 const SMDS_MeshElement* f;
6734 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6737 // select another outer face among the found
6738 const SMDS_MeshElement* outerFace2 = 0;
6739 if ( faces.size() == 2 )
6741 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6743 else if ( faces.size() > 2 )
6745 seamLinks.insert( link );
6747 // link direction within the outerFace
6748 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6749 SMESH_TNodeXYZ( link.node2()));
6750 int i1 = outerFace->GetNodeIndex( link.node1() );
6751 int i2 = outerFace->GetNodeIndex( link.node2() );
6752 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6753 if ( rev ) n1n2.Reverse();
6755 gp_XYZ ofNorm, fNorm;
6756 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6758 // direction from the link inside outerFace
6759 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6760 // sort all other faces by angle with the dirInOF
6761 map< double, const SMDS_MeshElement* > angle2Face;
6762 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6763 for ( ; face != faces.end(); ++face )
6765 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6767 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6768 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6769 if ( angle < 0 ) angle += 2. * M_PI;
6770 angle2Face.insert( make_pair( angle, *face ));
6772 if ( !angle2Face.empty() )
6773 outerFace2 = angle2Face.begin()->second;
6776 // store the found outer face and add its links to continue seaching from
6779 _outerFaces.insert( outerFace );
6780 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6781 for ( int i = 0; i < nbNodes; ++i )
6783 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6784 if ( visitedLinks.insert( link2 ).second )
6785 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6788 startLinks.pop_front();
6790 _outerFacesFound = true;
6792 if ( !seamLinks.empty() )
6794 // There are internal boundaries touching the outher one,
6795 // find all faces of internal boundaries in order to find
6796 // faces of boundaries of holes, if any.
6801 _outerFaces.clear();
6805 //=======================================================================
6807 * \brief Find elements of given type where the given point is IN or ON.
6808 * Returns nb of found elements and elements them-selves.
6810 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6812 //=======================================================================
6814 int SMESH_ElementSearcherImpl::
6815 FindElementsByPoint(const gp_Pnt& point,
6816 SMDSAbs_ElementType type,
6817 vector< const SMDS_MeshElement* >& foundElements)
6819 foundElements.clear();
6821 double tolerance = getTolerance();
6823 // =================================================================================
6824 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6826 if ( !_nodeSearcher )
6827 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6829 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6830 if ( !closeNode ) return foundElements.size();
6832 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6833 return foundElements.size(); // to far from any node
6835 if ( type == SMDSAbs_Node )
6837 foundElements.push_back( closeNode );
6841 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6842 while ( elemIt->more() )
6843 foundElements.push_back( elemIt->next() );
6846 // =================================================================================
6847 else // elements more complex than 0D
6849 if ( !_ebbTree || _elementType != type )
6851 if ( _ebbTree ) delete _ebbTree;
6852 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6854 TIDSortedElemSet suspectElems;
6855 _ebbTree->getElementsNearPoint( point, suspectElems );
6856 TIDSortedElemSet::iterator elem = suspectElems.begin();
6857 for ( ; elem != suspectElems.end(); ++elem )
6858 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6859 foundElements.push_back( *elem );
6861 return foundElements.size();
6864 //=======================================================================
6866 * \brief Find an element of given type most close to the given point
6868 * WARNING: Only face search is implemeneted so far
6870 //=======================================================================
6872 const SMDS_MeshElement*
6873 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6874 SMDSAbs_ElementType type )
6876 const SMDS_MeshElement* closestElem = 0;
6878 if ( type == SMDSAbs_Face )
6880 if ( !_ebbTree || _elementType != type )
6882 if ( _ebbTree ) delete _ebbTree;
6883 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6885 TIDSortedElemSet suspectElems;
6886 _ebbTree->getElementsNearPoint( point, suspectElems );
6888 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6890 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6891 _ebbTree->getBox()->CornerMax() );
6893 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6894 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6896 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6897 while ( suspectElems.empty() )
6899 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6903 double minDist = std::numeric_limits<double>::max();
6904 multimap< double, const SMDS_MeshElement* > dist2face;
6905 TIDSortedElemSet::iterator elem = suspectElems.begin();
6906 for ( ; elem != suspectElems.end(); ++elem )
6908 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6910 if ( dist < minDist + 1e-10)
6913 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6916 if ( !dist2face.empty() )
6918 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6919 closestElem = d2f->second;
6920 // if there are several elements at the same distance, select one
6921 // with GC closest to the point
6922 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6923 double minDistToGC = 0;
6924 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6926 if ( minDistToGC == 0 )
6929 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6930 TXyzIterator(), gc ) / closestElem->NbNodes();
6931 minDistToGC = point.SquareDistance( gc );
6934 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6935 TXyzIterator(), gc ) / d2f->second->NbNodes();
6936 double d = point.SquareDistance( gc );
6937 if ( d < minDistToGC )
6940 closestElem = d2f->second;
6943 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6944 // <<closestElem->GetID() << " DIST " << minDist << endl;
6949 // NOT IMPLEMENTED SO FAR
6955 //================================================================================
6957 * \brief Classify the given point in the closed 2D mesh
6959 //================================================================================
6961 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6963 double tolerance = getTolerance();
6964 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6966 if ( _ebbTree ) delete _ebbTree;
6967 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6969 // Algo: analyse transition of a line starting at the point through mesh boundary;
6970 // try three lines parallel to axis of the coordinate system and perform rough
6971 // analysis. If solution is not clear perform thorough analysis.
6973 const int nbAxes = 3;
6974 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6975 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6976 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6977 multimap< int, int > nbInt2Axis; // to find the simplest case
6978 for ( int axis = 0; axis < nbAxes; ++axis )
6980 gp_Ax1 lineAxis( point, axisDir[axis]);
6981 gp_Lin line ( lineAxis );
6983 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6984 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6986 // Intersect faces with the line
6988 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6989 TIDSortedElemSet::iterator face = suspectFaces.begin();
6990 for ( ; face != suspectFaces.end(); ++face )
6994 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6995 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6997 // perform intersection
6998 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6999 if ( !intersection.IsDone() )
7001 if ( intersection.IsInQuadric() )
7003 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7005 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7007 gp_Pnt intersectionPoint = intersection.Point(1);
7008 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7009 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7012 // Analyse intersections roughly
7014 int nbInter = u2inters.size();
7018 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7019 if ( nbInter == 1 ) // not closed mesh
7020 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7022 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7025 if ( (f<0) == (l<0) )
7028 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7029 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
7030 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7033 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7035 if ( _outerFacesFound ) break; // pass to thorough analysis
7037 } // three attempts - loop on CS axes
7039 // Analyse intersections thoroughly.
7040 // We make two loops maximum, on the first one we only exclude touching intersections,
7041 // on the second, if situation is still unclear, we gather and use information on
7042 // position of faces (internal or outer). If faces position is already gathered,
7043 // we make the second loop right away.
7045 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7047 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7048 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7050 int axis = nb_axis->second;
7051 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7053 gp_Ax1 lineAxis( point, axisDir[axis]);
7054 gp_Lin line ( lineAxis );
7056 // add tangent intersections to u2inters
7058 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7059 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7060 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7061 u2inters.insert(make_pair( param, *tgtInt ));
7062 tangentInters[ axis ].clear();
7064 // Count intersections before and after the point excluding touching ones.
7065 // If hasPositionInfo we count intersections of outer boundary only
7067 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7068 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7069 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7070 bool ok = ! u_int1->second._coincides;
7071 while ( ok && u_int1 != u2inters.end() )
7073 double u = u_int1->first;
7074 bool touchingInt = false;
7075 if ( ++u_int2 != u2inters.end() )
7077 // skip intersections at the same point (if the line passes through edge or node)
7079 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7085 // skip tangent intersections
7087 const SMDS_MeshElement* prevFace = u_int1->second._face;
7088 while ( ok && u_int2->second._coincides )
7090 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7096 ok = ( u_int2 != u2inters.end() );
7101 // skip intersections at the same point after tangent intersections
7104 double u2 = u_int2->first;
7106 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7112 // decide if we skipped a touching intersection
7113 if ( nbSamePnt + nbTgt > 0 )
7115 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7116 map< double, TInters >::iterator u_int = u_int1;
7117 for ( ; u_int != u_int2; ++u_int )
7119 if ( u_int->second._coincides ) continue;
7120 double dot = u_int->second._faceNorm * line.Direction();
7121 if ( dot > maxDot ) maxDot = dot;
7122 if ( dot < minDot ) minDot = dot;
7124 touchingInt = ( minDot*maxDot < 0 );
7129 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7140 u_int1 = u_int2; // to next intersection
7142 } // loop on intersections with one line
7146 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7149 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7152 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7153 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7155 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7158 if ( (f<0) == (l<0) )
7161 if ( hasPositionInfo )
7162 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7164 } // loop on intersections of the tree lines - thorough analysis
7166 if ( !hasPositionInfo )
7168 // gather info on faces position - is face in the outer boundary or not
7169 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7170 findOuterBoundary( u2inters.begin()->second._face );
7173 } // two attempts - with and w/o faces position info in the mesh
7175 return TopAbs_UNKNOWN;
7178 //=======================================================================
7180 * \brief Return elements possibly intersecting the line
7182 //=======================================================================
7184 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7185 SMDSAbs_ElementType type,
7186 vector< const SMDS_MeshElement* >& foundElems)
7188 if ( !_ebbTree || _elementType != type )
7190 if ( _ebbTree ) delete _ebbTree;
7191 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7193 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7194 _ebbTree->getElementsNearLine( line, suspectFaces );
7195 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7198 //=======================================================================
7200 * \brief Return SMESH_ElementSearcher
7202 //=======================================================================
7204 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7206 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7209 //=======================================================================
7211 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7213 //=======================================================================
7215 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7217 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7220 //=======================================================================
7222 * \brief Return true if the point is IN or ON of the element
7224 //=======================================================================
7226 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7228 if ( element->GetType() == SMDSAbs_Volume)
7230 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7233 // get ordered nodes
7235 vector< gp_XYZ > xyz;
7236 vector<const SMDS_MeshNode*> nodeList;
7238 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7239 if ( element->IsQuadratic() ) {
7240 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7241 nodeIt = f->interlacedNodesElemIterator();
7242 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7243 nodeIt = e->interlacedNodesElemIterator();
7245 while ( nodeIt->more() )
7247 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7248 xyz.push_back( SMESH_TNodeXYZ(node) );
7249 nodeList.push_back(node);
7252 int i, nbNodes = element->NbNodes();
7254 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7256 // compute face normal
7257 gp_Vec faceNorm(0,0,0);
7258 xyz.push_back( xyz.front() );
7259 nodeList.push_back( nodeList.front() );
7260 for ( i = 0; i < nbNodes; ++i )
7262 gp_Vec edge1( xyz[i+1], xyz[i]);
7263 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7264 faceNorm += edge1 ^ edge2;
7266 double normSize = faceNorm.Magnitude();
7267 if ( normSize <= tol )
7269 // degenerated face: point is out if it is out of all face edges
7270 for ( i = 0; i < nbNodes; ++i )
7272 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7273 if ( !IsOut( &edge, point, tol ))
7278 faceNorm /= normSize;
7280 // check if the point lays on face plane
7281 gp_Vec n2p( xyz[0], point );
7282 if ( fabs( n2p * faceNorm ) > tol )
7283 return true; // not on face plane
7285 // check if point is out of face boundary:
7286 // define it by closest transition of a ray point->infinity through face boundary
7287 // on the face plane.
7288 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7289 // to find intersections of the ray with the boundary.
7291 gp_Vec plnNorm = ray ^ faceNorm;
7292 normSize = plnNorm.Magnitude();
7293 if ( normSize <= tol ) return false; // point coincides with the first node
7294 plnNorm /= normSize;
7295 // for each node of the face, compute its signed distance to the plane
7296 vector<double> dist( nbNodes + 1);
7297 for ( i = 0; i < nbNodes; ++i )
7299 gp_Vec n2p( xyz[i], point );
7300 dist[i] = n2p * plnNorm;
7302 dist.back() = dist.front();
7303 // find the closest intersection
7305 double rClosest, distClosest = 1e100;;
7307 for ( i = 0; i < nbNodes; ++i )
7310 if ( fabs( dist[i]) < tol )
7312 else if ( fabs( dist[i+1]) < tol )
7314 else if ( dist[i] * dist[i+1] < 0 )
7315 r = dist[i] / ( dist[i] - dist[i+1] );
7317 continue; // no intersection
7318 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7319 gp_Vec p2int ( point, pInt);
7320 if ( p2int * ray > -tol ) // right half-space
7322 double intDist = p2int.SquareMagnitude();
7323 if ( intDist < distClosest )
7328 distClosest = intDist;
7333 return true; // no intesections - out
7335 // analyse transition
7336 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7337 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7338 gp_Vec p2int ( point, pClosest );
7339 bool out = (edgeNorm * p2int) < -tol;
7340 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7343 // ray pass through a face node; analyze transition through an adjacent edge
7344 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7345 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7346 gp_Vec edgeAdjacent( p1, p2 );
7347 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7348 bool out2 = (edgeNorm2 * p2int) < -tol;
7350 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7351 return covexCorner ? (out || out2) : (out && out2);
7353 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7355 // point is out of edge if it is NOT ON any straight part of edge
7356 // (we consider quadratic edge as being composed of two straight parts)
7357 for ( i = 1; i < nbNodes; ++i )
7359 gp_Vec edge( xyz[i-1], xyz[i]);
7360 gp_Vec n1p ( xyz[i-1], point);
7361 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7364 gp_Vec n2p( xyz[i], point );
7365 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7367 return false; // point is ON this part
7371 // Node or 0D element -------------------------------------------------------------------------
7373 gp_Vec n2p ( xyz[0], point );
7374 return n2p.Magnitude() <= tol;
7379 //=======================================================================
7383 // Position of a point relative to a segment
7387 // VERTEX 1 o----ON-----> VERTEX 2
7391 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7392 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7396 int _index; // index of vertex or segment
7398 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7399 bool operator < (const PointPos& other ) const
7401 if ( _name == other._name )
7402 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7403 return _name < other._name;
7407 //================================================================================
7409 * \brief Return of a point relative to a segment
7410 * \param point2D - the point to analyze position of
7411 * \param xyVec - end points of segments
7412 * \param index0 - 0-based index of the first point of segment
7413 * \param posToFindOut - flags of positions to detect
7414 * \retval PointPos - point position
7416 //================================================================================
7418 PointPos getPointPosition( const gp_XY& point2D,
7419 const gp_XY* segEnds,
7420 const int index0 = 0,
7421 const int posToFindOut = POS_ALL)
7423 const gp_XY& p1 = segEnds[ index0 ];
7424 const gp_XY& p2 = segEnds[ index0+1 ];
7425 const gp_XY grad = p2 - p1;
7427 if ( posToFindOut & POS_VERTEX )
7429 // check if the point2D is at "vertex 1" zone
7430 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7431 p1.Y() + grad.X() ) };
7432 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7433 return PointPos( POS_VERTEX, index0 );
7435 // check if the point2D is at "vertex 2" zone
7436 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7437 p2.Y() + grad.X() ) };
7438 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7439 return PointPos( POS_VERTEX, index0 + 1);
7441 double edgeEquation =
7442 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7443 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7447 //=======================================================================
7449 * \brief Return minimal distance from a point to a face
7451 * Currently we ignore non-planarity and 2nd order of face
7453 //=======================================================================
7455 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7456 const gp_Pnt& point )
7458 double badDistance = -1;
7459 if ( !face ) return badDistance;
7461 // coordinates of nodes (medium nodes, if any, ignored)
7462 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7463 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7464 xyz.resize( face->NbCornerNodes()+1 );
7466 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7467 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7469 gp_Vec OZ ( xyz[0], xyz[1] );
7470 gp_Vec OX ( xyz[0], xyz[2] );
7471 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7473 if ( xyz.size() < 4 ) return badDistance;
7474 OZ = gp_Vec ( xyz[0], xyz[2] );
7475 OX = gp_Vec ( xyz[0], xyz[3] );
7479 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7481 catch ( Standard_Failure ) {
7484 trsf.SetTransformation( tgtCS );
7486 // move all the nodes to 2D
7487 vector<gp_XY> xy( xyz.size() );
7488 for ( size_t i = 0;i < xyz.size()-1; ++i )
7490 gp_XYZ p3d = xyz[i];
7491 trsf.Transforms( p3d );
7492 xy[i].SetCoord( p3d.X(), p3d.Z() );
7494 xyz.back() = xyz.front();
7495 xy.back() = xy.front();
7497 // // move the point in 2D
7498 gp_XYZ tmpPnt = point.XYZ();
7499 trsf.Transforms( tmpPnt );
7500 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7502 // loop on segments of the face to analyze point position ralative to the face
7503 set< PointPos > pntPosSet;
7504 for ( size_t i = 1; i < xy.size(); ++i )
7506 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7507 pntPosSet.insert( pos );
7511 PointPos pos = *pntPosSet.begin();
7512 // cout << "Face " << face->GetID() << " DIST: ";
7513 switch ( pos._name )
7516 // point is most close to a segment
7517 gp_Vec p0p1( point, xyz[ pos._index ] );
7518 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7520 double projDist = p0p1 * p1p2; // distance projected to the segment
7521 gp_Vec projVec = p1p2 * projDist;
7522 gp_Vec distVec = p0p1 - projVec;
7523 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7524 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7525 return distVec.Magnitude();
7528 // point is inside the face
7529 double distToFacePlane = tmpPnt.Y();
7530 // cout << distToFacePlane << ", INSIDE " << endl;
7531 return Abs( distToFacePlane );
7534 // point is most close to a node
7535 gp_Vec distVec( point, xyz[ pos._index ]);
7536 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7537 return distVec.Magnitude();
7543 //=======================================================================
7544 //function : SimplifyFace
7546 //=======================================================================
7547 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7548 vector<const SMDS_MeshNode *>& poly_nodes,
7549 vector<int>& quantities) const
7551 int nbNodes = faceNodes.size();
7556 set<const SMDS_MeshNode*> nodeSet;
7558 // get simple seq of nodes
7559 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7560 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7561 int iSimple = 0, nbUnique = 0;
7563 simpleNodes[iSimple++] = faceNodes[0];
7565 for (int iCur = 1; iCur < nbNodes; iCur++) {
7566 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7567 simpleNodes[iSimple++] = faceNodes[iCur];
7568 if (nodeSet.insert( faceNodes[iCur] ).second)
7572 int nbSimple = iSimple;
7573 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7583 bool foundLoop = (nbSimple > nbUnique);
7586 set<const SMDS_MeshNode*> loopSet;
7587 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7588 const SMDS_MeshNode* n = simpleNodes[iSimple];
7589 if (!loopSet.insert( n ).second) {
7593 int iC = 0, curLast = iSimple;
7594 for (; iC < curLast; iC++) {
7595 if (simpleNodes[iC] == n) break;
7597 int loopLen = curLast - iC;
7599 // create sub-element
7601 quantities.push_back(loopLen);
7602 for (; iC < curLast; iC++) {
7603 poly_nodes.push_back(simpleNodes[iC]);
7606 // shift the rest nodes (place from the first loop position)
7607 for (iC = curLast + 1; iC < nbSimple; iC++) {
7608 simpleNodes[iC - loopLen] = simpleNodes[iC];
7610 nbSimple -= loopLen;
7613 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7614 } // while (foundLoop)
7618 quantities.push_back(iSimple);
7619 for (int i = 0; i < iSimple; i++)
7620 poly_nodes.push_back(simpleNodes[i]);
7626 //=======================================================================
7627 //function : MergeNodes
7628 //purpose : In each group, the cdr of nodes are substituted by the first one
7630 //=======================================================================
7632 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7634 MESSAGE("MergeNodes");
7635 myLastCreatedElems.Clear();
7636 myLastCreatedNodes.Clear();
7638 SMESHDS_Mesh* aMesh = GetMeshDS();
7640 TNodeNodeMap nodeNodeMap; // node to replace - new node
7641 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7642 list< int > rmElemIds, rmNodeIds;
7644 // Fill nodeNodeMap and elems
7646 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7647 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7648 list<const SMDS_MeshNode*>& nodes = *grIt;
7649 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7650 const SMDS_MeshNode* nToKeep = *nIt;
7651 //MESSAGE("node to keep " << nToKeep->GetID());
7652 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7653 const SMDS_MeshNode* nToRemove = *nIt;
7654 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7655 if ( nToRemove != nToKeep ) {
7656 //MESSAGE(" node to remove " << nToRemove->GetID());
7657 rmNodeIds.push_back( nToRemove->GetID() );
7658 AddToSameGroups( nToKeep, nToRemove, aMesh );
7659 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7660 // after MergeNodes() w/o creating node in place of merged ones.
7661 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7662 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7663 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7664 sm->SetIsAlwaysComputed( true );
7667 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7668 while ( invElemIt->more() ) {
7669 const SMDS_MeshElement* elem = invElemIt->next();
7674 // Change element nodes or remove an element
7676 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7677 for ( ; eIt != elems.end(); eIt++ ) {
7678 const SMDS_MeshElement* elem = *eIt;
7679 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7680 int nbNodes = elem->NbNodes();
7681 int aShapeId = FindShape( elem );
7683 set<const SMDS_MeshNode*> nodeSet;
7684 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7685 int iUnique = 0, iCur = 0, nbRepl = 0;
7686 vector<int> iRepl( nbNodes );
7688 // get new seq of nodes
7689 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7690 while ( itN->more() ) {
7691 const SMDS_MeshNode* n =
7692 static_cast<const SMDS_MeshNode*>( itN->next() );
7694 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7695 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7697 // BUG 0020185: begin
7699 bool stopRecur = false;
7700 set<const SMDS_MeshNode*> nodesRecur;
7701 nodesRecur.insert(n);
7702 while (!stopRecur) {
7703 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7704 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7705 n = (*nnIt_i).second;
7706 if (!nodesRecur.insert(n).second) {
7707 // error: recursive dependancy
7717 curNodes[ iCur ] = n;
7718 bool isUnique = nodeSet.insert( n ).second;
7720 uniqueNodes[ iUnique++ ] = n;
7722 iRepl[ nbRepl++ ] = iCur;
7726 // Analyse element topology after replacement
7729 int nbUniqueNodes = nodeSet.size();
7730 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7731 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7732 // Polygons and Polyhedral volumes
7733 if (elem->IsPoly()) {
7735 if (elem->GetType() == SMDSAbs_Face) {
7737 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7739 for (; inode < nbNodes; inode++) {
7740 face_nodes[inode] = curNodes[inode];
7743 vector<const SMDS_MeshNode *> polygons_nodes;
7744 vector<int> quantities;
7745 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7748 for (int iface = 0; iface < nbNew; iface++) {
7749 int nbNodes = quantities[iface];
7750 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7751 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7752 poly_nodes[ii] = polygons_nodes[inode];
7754 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7755 myLastCreatedElems.Append(newElem);
7757 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7760 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7761 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7762 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7764 if (nbNew > 0) quid = nbNew - 1;
7765 vector<int> newquant(quantities.begin()+quid, quantities.end());
7766 const SMDS_MeshElement* newElem = 0;
7767 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7768 myLastCreatedElems.Append(newElem);
7769 if ( aShapeId && newElem )
7770 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7771 rmElemIds.push_back(elem->GetID());
7774 rmElemIds.push_back(elem->GetID());
7778 else if (elem->GetType() == SMDSAbs_Volume) {
7779 // Polyhedral volume
7780 if (nbUniqueNodes < 4) {
7781 rmElemIds.push_back(elem->GetID());
7784 // each face has to be analyzed in order to check volume validity
7785 const SMDS_VtkVolume* aPolyedre =
7786 dynamic_cast<const SMDS_VtkVolume*>( elem );
7788 int nbFaces = aPolyedre->NbFaces();
7790 vector<const SMDS_MeshNode *> poly_nodes;
7791 vector<int> quantities;
7793 for (int iface = 1; iface <= nbFaces; iface++) {
7794 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7795 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7797 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7798 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7799 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7800 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7801 faceNode = (*nnIt).second;
7803 faceNodes[inode - 1] = faceNode;
7806 SimplifyFace(faceNodes, poly_nodes, quantities);
7809 if (quantities.size() > 3) {
7810 // to be done: remove coincident faces
7813 if (quantities.size() > 3)
7815 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7816 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7817 const SMDS_MeshElement* newElem = 0;
7818 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7819 myLastCreatedElems.Append(newElem);
7820 if ( aShapeId && newElem )
7821 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7822 rmElemIds.push_back(elem->GetID());
7826 rmElemIds.push_back(elem->GetID());
7837 // TODO not all the possible cases are solved. Find something more generic?
7838 switch ( nbNodes ) {
7839 case 2: ///////////////////////////////////// EDGE
7840 isOk = false; break;
7841 case 3: ///////////////////////////////////// TRIANGLE
7842 isOk = false; break;
7844 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7846 else { //////////////////////////////////// QUADRANGLE
7847 if ( nbUniqueNodes < 3 )
7849 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7850 isOk = false; // opposite nodes stick
7851 //MESSAGE("isOk " << isOk);
7854 case 6: ///////////////////////////////////// PENTAHEDRON
7855 if ( nbUniqueNodes == 4 ) {
7856 // ---------------------------------> tetrahedron
7858 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7859 // all top nodes stick: reverse a bottom
7860 uniqueNodes[ 0 ] = curNodes [ 1 ];
7861 uniqueNodes[ 1 ] = curNodes [ 0 ];
7863 else if (nbRepl == 3 &&
7864 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7865 // all bottom nodes stick: set a top before
7866 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7867 uniqueNodes[ 0 ] = curNodes [ 3 ];
7868 uniqueNodes[ 1 ] = curNodes [ 4 ];
7869 uniqueNodes[ 2 ] = curNodes [ 5 ];
7871 else if (nbRepl == 4 &&
7872 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7873 // a lateral face turns into a line: reverse a bottom
7874 uniqueNodes[ 0 ] = curNodes [ 1 ];
7875 uniqueNodes[ 1 ] = curNodes [ 0 ];
7880 else if ( nbUniqueNodes == 5 ) {
7881 // PENTAHEDRON --------------------> 2 tetrahedrons
7882 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7883 // a bottom node sticks with a linked top one
7885 SMDS_MeshElement* newElem =
7886 aMesh->AddVolume(curNodes[ 3 ],
7889 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7890 myLastCreatedElems.Append(newElem);
7892 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7893 // 2. : reverse a bottom
7894 uniqueNodes[ 0 ] = curNodes [ 1 ];
7895 uniqueNodes[ 1 ] = curNodes [ 0 ];
7905 if(elem->IsQuadratic()) { // Quadratic quadrangle
7917 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7920 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7922 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7923 uniqueNodes[0] = curNodes[0];
7924 uniqueNodes[1] = curNodes[2];
7925 uniqueNodes[2] = curNodes[3];
7926 uniqueNodes[3] = curNodes[5];
7927 uniqueNodes[4] = curNodes[6];
7928 uniqueNodes[5] = curNodes[7];
7931 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7932 uniqueNodes[0] = curNodes[0];
7933 uniqueNodes[1] = curNodes[1];
7934 uniqueNodes[2] = curNodes[2];
7935 uniqueNodes[3] = curNodes[4];
7936 uniqueNodes[4] = curNodes[5];
7937 uniqueNodes[5] = curNodes[6];
7940 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7941 uniqueNodes[0] = curNodes[1];
7942 uniqueNodes[1] = curNodes[2];
7943 uniqueNodes[2] = curNodes[3];
7944 uniqueNodes[3] = curNodes[5];
7945 uniqueNodes[4] = curNodes[6];
7946 uniqueNodes[5] = curNodes[0];
7949 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7950 uniqueNodes[0] = curNodes[0];
7951 uniqueNodes[1] = curNodes[1];
7952 uniqueNodes[2] = curNodes[3];
7953 uniqueNodes[3] = curNodes[4];
7954 uniqueNodes[4] = curNodes[6];
7955 uniqueNodes[5] = curNodes[7];
7958 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7959 uniqueNodes[0] = curNodes[0];
7960 uniqueNodes[1] = curNodes[2];
7961 uniqueNodes[2] = curNodes[3];
7962 uniqueNodes[3] = curNodes[1];
7963 uniqueNodes[4] = curNodes[6];
7964 uniqueNodes[5] = curNodes[7];
7967 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7968 uniqueNodes[0] = curNodes[0];
7969 uniqueNodes[1] = curNodes[1];
7970 uniqueNodes[2] = curNodes[2];
7971 uniqueNodes[3] = curNodes[4];
7972 uniqueNodes[4] = curNodes[5];
7973 uniqueNodes[5] = curNodes[7];
7976 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7977 uniqueNodes[0] = curNodes[0];
7978 uniqueNodes[1] = curNodes[1];
7979 uniqueNodes[2] = curNodes[3];
7980 uniqueNodes[3] = curNodes[4];
7981 uniqueNodes[4] = curNodes[2];
7982 uniqueNodes[5] = curNodes[7];
7985 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7986 uniqueNodes[0] = curNodes[0];
7987 uniqueNodes[1] = curNodes[1];
7988 uniqueNodes[2] = curNodes[2];
7989 uniqueNodes[3] = curNodes[4];
7990 uniqueNodes[4] = curNodes[5];
7991 uniqueNodes[5] = curNodes[3];
7996 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7999 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
8003 //////////////////////////////////// HEXAHEDRON
8005 SMDS_VolumeTool hexa (elem);
8006 hexa.SetExternalNormal();
8007 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8008 //////////////////////// HEX ---> 1 tetrahedron
8009 for ( int iFace = 0; iFace < 6; iFace++ ) {
8010 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8011 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8012 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8013 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8014 // one face turns into a point ...
8015 int iOppFace = hexa.GetOppFaceIndex( iFace );
8016 ind = hexa.GetFaceNodesIndices( iOppFace );
8018 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8019 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8022 if ( nbStick == 1 ) {
8023 // ... and the opposite one - into a triangle.
8025 ind = hexa.GetFaceNodesIndices( iFace );
8026 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8033 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8034 //////////////////////// HEX ---> 1 prism
8035 int nbTria = 0, iTria[3];
8036 const int *ind; // indices of face nodes
8037 // look for triangular faces
8038 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8039 ind = hexa.GetFaceNodesIndices( iFace );
8040 TIDSortedNodeSet faceNodes;
8041 for ( iCur = 0; iCur < 4; iCur++ )
8042 faceNodes.insert( curNodes[ind[iCur]] );
8043 if ( faceNodes.size() == 3 )
8044 iTria[ nbTria++ ] = iFace;
8046 // check if triangles are opposite
8047 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8050 // set nodes of the bottom triangle
8051 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8053 for ( iCur = 0; iCur < 4; iCur++ )
8054 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8055 indB.push_back( ind[iCur] );
8056 if ( !hexa.IsForward() )
8057 std::swap( indB[0], indB[2] );
8058 for ( iCur = 0; iCur < 3; iCur++ )
8059 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8060 // set nodes of the top triangle
8061 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8062 for ( iCur = 0; iCur < 3; ++iCur )
8063 for ( int j = 0; j < 4; ++j )
8064 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8066 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8072 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8073 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8074 for ( int iFace = 0; iFace < 6; iFace++ ) {
8075 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8076 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8077 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8078 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8079 // one face turns into a point ...
8080 int iOppFace = hexa.GetOppFaceIndex( iFace );
8081 ind = hexa.GetFaceNodesIndices( iOppFace );
8083 iUnique = 2; // reverse a tetrahedron 1 bottom
8084 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8085 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8087 else if ( iUnique >= 0 )
8088 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8090 if ( nbStick == 0 ) {
8091 // ... and the opposite one is a quadrangle
8093 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8094 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8097 SMDS_MeshElement* newElem =
8098 aMesh->AddVolume(curNodes[ind[ 0 ]],
8101 curNodes[indTop[ 0 ]]);
8102 myLastCreatedElems.Append(newElem);
8104 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8111 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8112 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8113 // find indices of quad and tri faces
8114 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8115 for ( iFace = 0; iFace < 6; iFace++ ) {
8116 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8118 for ( iCur = 0; iCur < 4; iCur++ )
8119 nodeSet.insert( curNodes[ind[ iCur ]] );
8120 nbUniqueNodes = nodeSet.size();
8121 if ( nbUniqueNodes == 3 )
8122 iTriFace[ nbTri++ ] = iFace;
8123 else if ( nbUniqueNodes == 4 )
8124 iQuadFace[ nbQuad++ ] = iFace;
8126 if (nbQuad == 2 && nbTri == 4 &&
8127 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8128 // 2 opposite quadrangles stuck with a diagonal;
8129 // sample groups of merged indices: (0-4)(2-6)
8130 // --------------------------------------------> 2 tetrahedrons
8131 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8132 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8133 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8134 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8135 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8136 // stuck with 0-2 diagonal
8144 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8145 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8146 // stuck with 1-3 diagonal
8158 uniqueNodes[ 0 ] = curNodes [ i0 ];
8159 uniqueNodes[ 1 ] = curNodes [ i1d ];
8160 uniqueNodes[ 2 ] = curNodes [ i3d ];
8161 uniqueNodes[ 3 ] = curNodes [ i0t ];
8164 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8168 myLastCreatedElems.Append(newElem);
8170 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8173 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8174 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8175 // --------------------------------------------> prism
8176 // find 2 opposite triangles
8178 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8179 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8180 // find indices of kept and replaced nodes
8181 // and fill unique nodes of 2 opposite triangles
8182 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8183 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8184 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8185 // fill unique nodes
8188 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8189 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8190 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8192 // iCur of a linked node of the opposite face (make normals co-directed):
8193 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8194 // check that correspondent corners of triangles are linked
8195 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8198 uniqueNodes[ iUnique ] = n;
8199 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8208 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8211 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8218 } // switch ( nbNodes )
8220 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8222 if ( isOk ) { // the elem remains valid after sticking nodes
8223 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8225 // Change nodes of polyedre
8226 const SMDS_VtkVolume* aPolyedre =
8227 dynamic_cast<const SMDS_VtkVolume*>( elem );
8229 int nbFaces = aPolyedre->NbFaces();
8231 vector<const SMDS_MeshNode *> poly_nodes;
8232 vector<int> quantities (nbFaces);
8234 for (int iface = 1; iface <= nbFaces; iface++) {
8235 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8236 quantities[iface - 1] = nbFaceNodes;
8238 for (inode = 1; inode <= nbFaceNodes; inode++) {
8239 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8241 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8242 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8243 curNode = (*nnIt).second;
8245 poly_nodes.push_back(curNode);
8248 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8251 else // replace non-polyhedron elements
8253 const SMDSAbs_ElementType etyp = elem->GetType();
8254 const int elemId = elem->GetID();
8255 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8256 uniqueNodes.resize(nbUniqueNodes);
8258 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8260 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8261 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8262 if ( sm && newElem )
8263 sm->AddElement( newElem );
8264 if ( elem != newElem )
8265 ReplaceElemInGroups( elem, newElem, aMesh );
8269 // Remove invalid regular element or invalid polygon
8270 rmElemIds.push_back( elem->GetID() );
8273 } // loop on elements
8275 // Remove bad elements, then equal nodes (order important)
8277 Remove( rmElemIds, false );
8278 Remove( rmNodeIds, true );
8283 // ========================================================
8284 // class : SortableElement
8285 // purpose : allow sorting elements basing on their nodes
8286 // ========================================================
8287 class SortableElement : public set <const SMDS_MeshElement*>
8291 SortableElement( const SMDS_MeshElement* theElem )
8294 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8295 while ( nodeIt->more() )
8296 this->insert( nodeIt->next() );
8299 const SMDS_MeshElement* Get() const
8302 void Set(const SMDS_MeshElement* e) const
8307 mutable const SMDS_MeshElement* myElem;
8310 //=======================================================================
8311 //function : FindEqualElements
8312 //purpose : Return list of group of elements built on the same nodes.
8313 // Search among theElements or in the whole mesh if theElements is empty
8314 //=======================================================================
8316 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8317 TListOfListOfElementsID & theGroupsOfElementsID)
8319 myLastCreatedElems.Clear();
8320 myLastCreatedNodes.Clear();
8322 typedef map< SortableElement, int > TMapOfNodeSet;
8323 typedef list<int> TGroupOfElems;
8325 if ( theElements.empty() )
8326 { // get all elements in the mesh
8327 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8328 while ( eIt->more() )
8329 theElements.insert( theElements.end(), eIt->next());
8332 vector< TGroupOfElems > arrayOfGroups;
8333 TGroupOfElems groupOfElems;
8334 TMapOfNodeSet mapOfNodeSet;
8336 TIDSortedElemSet::iterator elemIt = theElements.begin();
8337 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8338 const SMDS_MeshElement* curElem = *elemIt;
8339 SortableElement SE(curElem);
8342 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8343 if( !(pp.second) ) {
8344 TMapOfNodeSet::iterator& itSE = pp.first;
8345 ind = (*itSE).second;
8346 arrayOfGroups[ind].push_back(curElem->GetID());
8349 groupOfElems.clear();
8350 groupOfElems.push_back(curElem->GetID());
8351 arrayOfGroups.push_back(groupOfElems);
8356 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8357 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8358 groupOfElems = *groupIt;
8359 if ( groupOfElems.size() > 1 ) {
8360 groupOfElems.sort();
8361 theGroupsOfElementsID.push_back(groupOfElems);
8366 //=======================================================================
8367 //function : MergeElements
8368 //purpose : In each given group, substitute all elements by the first one.
8369 //=======================================================================
8371 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8373 myLastCreatedElems.Clear();
8374 myLastCreatedNodes.Clear();
8376 typedef list<int> TListOfIDs;
8377 TListOfIDs rmElemIds; // IDs of elems to remove
8379 SMESHDS_Mesh* aMesh = GetMeshDS();
8381 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8382 while ( groupsIt != theGroupsOfElementsID.end() ) {
8383 TListOfIDs& aGroupOfElemID = *groupsIt;
8384 aGroupOfElemID.sort();
8385 int elemIDToKeep = aGroupOfElemID.front();
8386 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8387 aGroupOfElemID.pop_front();
8388 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8389 while ( idIt != aGroupOfElemID.end() ) {
8390 int elemIDToRemove = *idIt;
8391 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8392 // add the kept element in groups of removed one (PAL15188)
8393 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8394 rmElemIds.push_back( elemIDToRemove );
8400 Remove( rmElemIds, false );
8403 //=======================================================================
8404 //function : MergeEqualElements
8405 //purpose : Remove all but one of elements built on the same nodes.
8406 //=======================================================================
8408 void SMESH_MeshEditor::MergeEqualElements()
8410 TIDSortedElemSet aMeshElements; /* empty input ==
8411 to merge equal elements in the whole mesh */
8412 TListOfListOfElementsID aGroupsOfElementsID;
8413 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8414 MergeElements(aGroupsOfElementsID);
8417 //=======================================================================
8418 //function : FindFaceInSet
8419 //purpose : Return a face having linked nodes n1 and n2 and which is
8420 // - not in avoidSet,
8421 // - in elemSet provided that !elemSet.empty()
8422 // i1 and i2 optionally returns indices of n1 and n2
8423 //=======================================================================
8425 const SMDS_MeshElement*
8426 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8427 const SMDS_MeshNode* n2,
8428 const TIDSortedElemSet& elemSet,
8429 const TIDSortedElemSet& avoidSet,
8435 const SMDS_MeshElement* face = 0;
8437 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8438 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8439 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8441 //MESSAGE("in while ( invElemIt->more() && !face )");
8442 const SMDS_MeshElement* elem = invElemIt->next();
8443 if (avoidSet.count( elem ))
8445 if ( !elemSet.empty() && !elemSet.count( elem ))
8448 i1 = elem->GetNodeIndex( n1 );
8449 // find a n2 linked to n1
8450 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8451 for ( int di = -1; di < 2 && !face; di += 2 )
8453 i2 = (i1+di+nbN) % nbN;
8454 if ( elem->GetNode( i2 ) == n2 )
8457 if ( !face && elem->IsQuadratic())
8459 // analysis for quadratic elements using all nodes
8460 const SMDS_VtkFace* F =
8461 dynamic_cast<const SMDS_VtkFace*>(elem);
8462 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8463 // use special nodes iterator
8464 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8465 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8466 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8468 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8469 if ( n1 == prevN && n2 == n )
8473 else if ( n2 == prevN && n1 == n )
8475 face = elem; swap( i1, i2 );
8481 if ( n1ind ) *n1ind = i1;
8482 if ( n2ind ) *n2ind = i2;
8486 //=======================================================================
8487 //function : findAdjacentFace
8489 //=======================================================================
8491 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8492 const SMDS_MeshNode* n2,
8493 const SMDS_MeshElement* elem)
8495 TIDSortedElemSet elemSet, avoidSet;
8497 avoidSet.insert ( elem );
8498 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8501 //=======================================================================
8502 //function : FindFreeBorder
8504 //=======================================================================
8506 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8508 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8509 const SMDS_MeshNode* theSecondNode,
8510 const SMDS_MeshNode* theLastNode,
8511 list< const SMDS_MeshNode* > & theNodes,
8512 list< const SMDS_MeshElement* >& theFaces)
8514 if ( !theFirstNode || !theSecondNode )
8516 // find border face between theFirstNode and theSecondNode
8517 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8521 theFaces.push_back( curElem );
8522 theNodes.push_back( theFirstNode );
8523 theNodes.push_back( theSecondNode );
8525 //vector<const SMDS_MeshNode*> nodes;
8526 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8527 TIDSortedElemSet foundElems;
8528 bool needTheLast = ( theLastNode != 0 );
8530 while ( nStart != theLastNode ) {
8531 if ( nStart == theFirstNode )
8532 return !needTheLast;
8534 // find all free border faces sharing form nStart
8536 list< const SMDS_MeshElement* > curElemList;
8537 list< const SMDS_MeshNode* > nStartList;
8538 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8539 while ( invElemIt->more() ) {
8540 const SMDS_MeshElement* e = invElemIt->next();
8541 if ( e == curElem || foundElems.insert( e ).second ) {
8543 int iNode = 0, nbNodes = e->NbNodes();
8544 //const SMDS_MeshNode* nodes[nbNodes+1];
8545 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8547 if(e->IsQuadratic()) {
8548 const SMDS_VtkFace* F =
8549 dynamic_cast<const SMDS_VtkFace*>(e);
8550 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8551 // use special nodes iterator
8552 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8553 while( anIter->more() ) {
8554 nodes[ iNode++ ] = cast2Node(anIter->next());
8558 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8559 while ( nIt->more() )
8560 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8562 nodes[ iNode ] = nodes[ 0 ];
8564 for ( iNode = 0; iNode < nbNodes; iNode++ )
8565 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8566 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8567 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8569 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8570 curElemList.push_back( e );
8574 // analyse the found
8576 int nbNewBorders = curElemList.size();
8577 if ( nbNewBorders == 0 ) {
8578 // no free border furthermore
8579 return !needTheLast;
8581 else if ( nbNewBorders == 1 ) {
8582 // one more element found
8584 nStart = nStartList.front();
8585 curElem = curElemList.front();
8586 theFaces.push_back( curElem );
8587 theNodes.push_back( nStart );
8590 // several continuations found
8591 list< const SMDS_MeshElement* >::iterator curElemIt;
8592 list< const SMDS_MeshNode* >::iterator nStartIt;
8593 // check if one of them reached the last node
8594 if ( needTheLast ) {
8595 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8596 curElemIt!= curElemList.end();
8597 curElemIt++, nStartIt++ )
8598 if ( *nStartIt == theLastNode ) {
8599 theFaces.push_back( *curElemIt );
8600 theNodes.push_back( *nStartIt );
8604 // find the best free border by the continuations
8605 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8606 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8607 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8608 curElemIt!= curElemList.end();
8609 curElemIt++, nStartIt++ )
8611 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8612 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8613 // find one more free border
8614 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8618 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8619 // choice: clear a worse one
8620 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8621 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8622 contNodes[ iWorse ].clear();
8623 contFaces[ iWorse ].clear();
8626 if ( contNodes[0].empty() && contNodes[1].empty() )
8629 // append the best free border
8630 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8631 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8632 theNodes.pop_back(); // remove nIgnore
8633 theNodes.pop_back(); // remove nStart
8634 theFaces.pop_back(); // remove curElem
8635 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8636 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8637 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8638 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8641 } // several continuations found
8642 } // while ( nStart != theLastNode )
8647 //=======================================================================
8648 //function : CheckFreeBorderNodes
8649 //purpose : Return true if the tree nodes are on a free border
8650 //=======================================================================
8652 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8653 const SMDS_MeshNode* theNode2,
8654 const SMDS_MeshNode* theNode3)
8656 list< const SMDS_MeshNode* > nodes;
8657 list< const SMDS_MeshElement* > faces;
8658 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8661 //=======================================================================
8662 //function : SewFreeBorder
8664 //=======================================================================
8666 SMESH_MeshEditor::Sew_Error
8667 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8668 const SMDS_MeshNode* theBordSecondNode,
8669 const SMDS_MeshNode* theBordLastNode,
8670 const SMDS_MeshNode* theSideFirstNode,
8671 const SMDS_MeshNode* theSideSecondNode,
8672 const SMDS_MeshNode* theSideThirdNode,
8673 const bool theSideIsFreeBorder,
8674 const bool toCreatePolygons,
8675 const bool toCreatePolyedrs)
8677 myLastCreatedElems.Clear();
8678 myLastCreatedNodes.Clear();
8680 MESSAGE("::SewFreeBorder()");
8681 Sew_Error aResult = SEW_OK;
8683 // ====================================
8684 // find side nodes and elements
8685 // ====================================
8687 list< const SMDS_MeshNode* > nSide[ 2 ];
8688 list< const SMDS_MeshElement* > eSide[ 2 ];
8689 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8690 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8694 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8695 nSide[0], eSide[0])) {
8696 MESSAGE(" Free Border 1 not found " );
8697 aResult = SEW_BORDER1_NOT_FOUND;
8699 if (theSideIsFreeBorder) {
8702 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8703 nSide[1], eSide[1])) {
8704 MESSAGE(" Free Border 2 not found " );
8705 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8708 if ( aResult != SEW_OK )
8711 if (!theSideIsFreeBorder) {
8715 // -------------------------------------------------------------------------
8717 // 1. If nodes to merge are not coincident, move nodes of the free border
8718 // from the coord sys defined by the direction from the first to last
8719 // nodes of the border to the correspondent sys of the side 2
8720 // 2. On the side 2, find the links most co-directed with the correspondent
8721 // links of the free border
8722 // -------------------------------------------------------------------------
8724 // 1. Since sewing may break if there are volumes to split on the side 2,
8725 // we wont move nodes but just compute new coordinates for them
8726 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8727 TNodeXYZMap nBordXYZ;
8728 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8729 list< const SMDS_MeshNode* >::iterator nBordIt;
8731 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8732 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8733 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8734 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8735 double tol2 = 1.e-8;
8736 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8737 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8738 // Need node movement.
8740 // find X and Z axes to create trsf
8741 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8743 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8745 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8748 gp_Ax3 toBordAx( Pb1, Zb, X );
8749 gp_Ax3 fromSideAx( Ps1, Zs, X );
8750 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8752 gp_Trsf toBordSys, fromSide2Sys;
8753 toBordSys.SetTransformation( toBordAx );
8754 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8755 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8758 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8759 const SMDS_MeshNode* n = *nBordIt;
8760 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8761 toBordSys.Transforms( xyz );
8762 fromSide2Sys.Transforms( xyz );
8763 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8767 // just insert nodes XYZ in the nBordXYZ map
8768 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8769 const SMDS_MeshNode* n = *nBordIt;
8770 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8774 // 2. On the side 2, find the links most co-directed with the correspondent
8775 // links of the free border
8777 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8778 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8779 sideNodes.push_back( theSideFirstNode );
8781 bool hasVolumes = false;
8782 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8783 set<long> foundSideLinkIDs, checkedLinkIDs;
8784 SMDS_VolumeTool volume;
8785 //const SMDS_MeshNode* faceNodes[ 4 ];
8787 const SMDS_MeshNode* sideNode;
8788 const SMDS_MeshElement* sideElem;
8789 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8790 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8791 nBordIt = bordNodes.begin();
8793 // border node position and border link direction to compare with
8794 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8795 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8796 // choose next side node by link direction or by closeness to
8797 // the current border node:
8798 bool searchByDir = ( *nBordIt != theBordLastNode );
8800 // find the next node on the Side 2
8802 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8804 checkedLinkIDs.clear();
8805 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8807 // loop on inverse elements of current node (prevSideNode) on the Side 2
8808 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8809 while ( invElemIt->more() )
8811 const SMDS_MeshElement* elem = invElemIt->next();
8812 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8813 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8814 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8815 bool isVolume = volume.Set( elem );
8816 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8817 if ( isVolume ) // --volume
8819 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8820 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8821 if(elem->IsQuadratic()) {
8822 const SMDS_VtkFace* F =
8823 dynamic_cast<const SMDS_VtkFace*>(elem);
8824 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8825 // use special nodes iterator
8826 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8827 while( anIter->more() ) {
8828 nodes[ iNode ] = cast2Node(anIter->next());
8829 if ( nodes[ iNode++ ] == prevSideNode )
8830 iPrevNode = iNode - 1;
8834 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8835 while ( nIt->more() ) {
8836 nodes[ iNode ] = cast2Node( nIt->next() );
8837 if ( nodes[ iNode++ ] == prevSideNode )
8838 iPrevNode = iNode - 1;
8841 // there are 2 links to check
8846 // loop on links, to be precise, on the second node of links
8847 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8848 const SMDS_MeshNode* n = nodes[ iNode ];
8850 if ( !volume.IsLinked( n, prevSideNode ))
8854 if ( iNode ) // a node before prevSideNode
8855 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8856 else // a node after prevSideNode
8857 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8859 // check if this link was already used
8860 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8861 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8862 if (!isJustChecked &&
8863 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8865 // test a link geometrically
8866 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8867 bool linkIsBetter = false;
8868 double dot = 0.0, dist = 0.0;
8869 if ( searchByDir ) { // choose most co-directed link
8870 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8871 linkIsBetter = ( dot > maxDot );
8873 else { // choose link with the node closest to bordPos
8874 dist = ( nextXYZ - bordPos ).SquareModulus();
8875 linkIsBetter = ( dist < minDist );
8877 if ( linkIsBetter ) {
8886 } // loop on inverse elements of prevSideNode
8889 MESSAGE(" Cant find path by links of the Side 2 ");
8890 return SEW_BAD_SIDE_NODES;
8892 sideNodes.push_back( sideNode );
8893 sideElems.push_back( sideElem );
8894 foundSideLinkIDs.insert ( linkID );
8895 prevSideNode = sideNode;
8897 if ( *nBordIt == theBordLastNode )
8898 searchByDir = false;
8900 // find the next border link to compare with
8901 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8902 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8903 // move to next border node if sideNode is before forward border node (bordPos)
8904 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8905 prevBordNode = *nBordIt;
8907 bordPos = nBordXYZ[ *nBordIt ];
8908 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8909 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8913 while ( sideNode != theSideSecondNode );
8915 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8916 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8917 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8919 } // end nodes search on the side 2
8921 // ============================
8922 // sew the border to the side 2
8923 // ============================
8925 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8926 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8928 TListOfListOfNodes nodeGroupsToMerge;
8929 if ( nbNodes[0] == nbNodes[1] ||
8930 ( theSideIsFreeBorder && !theSideThirdNode)) {
8932 // all nodes are to be merged
8934 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8935 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8936 nIt[0]++, nIt[1]++ )
8938 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8939 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8940 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8945 // insert new nodes into the border and the side to get equal nb of segments
8947 // get normalized parameters of nodes on the borders
8948 //double param[ 2 ][ maxNbNodes ];
8950 param[0] = new double [ maxNbNodes ];
8951 param[1] = new double [ maxNbNodes ];
8953 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8954 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8955 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8956 const SMDS_MeshNode* nPrev = *nIt;
8957 double bordLength = 0;
8958 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8959 const SMDS_MeshNode* nCur = *nIt;
8960 gp_XYZ segment (nCur->X() - nPrev->X(),
8961 nCur->Y() - nPrev->Y(),
8962 nCur->Z() - nPrev->Z());
8963 double segmentLen = segment.Modulus();
8964 bordLength += segmentLen;
8965 param[ iBord ][ iNode ] = bordLength;
8968 // normalize within [0,1]
8969 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8970 param[ iBord ][ iNode ] /= bordLength;
8974 // loop on border segments
8975 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8976 int i[ 2 ] = { 0, 0 };
8977 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8978 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8980 TElemOfNodeListMap insertMap;
8981 TElemOfNodeListMap::iterator insertMapIt;
8983 // key: elem to insert nodes into
8984 // value: 2 nodes to insert between + nodes to be inserted
8986 bool next[ 2 ] = { false, false };
8988 // find min adjacent segment length after sewing
8989 double nextParam = 10., prevParam = 0;
8990 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8991 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8992 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8993 if ( i[ iBord ] > 0 )
8994 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8996 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8997 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8998 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
9000 // choose to insert or to merge nodes
9001 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
9002 if ( Abs( du ) <= minSegLen * 0.2 ) {
9005 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9006 const SMDS_MeshNode* n0 = *nIt[0];
9007 const SMDS_MeshNode* n1 = *nIt[1];
9008 nodeGroupsToMerge.back().push_back( n1 );
9009 nodeGroupsToMerge.back().push_back( n0 );
9010 // position of node of the border changes due to merge
9011 param[ 0 ][ i[0] ] += du;
9012 // move n1 for the sake of elem shape evaluation during insertion.
9013 // n1 will be removed by MergeNodes() anyway
9014 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9015 next[0] = next[1] = true;
9020 int intoBord = ( du < 0 ) ? 0 : 1;
9021 const SMDS_MeshElement* elem = *eIt[ intoBord ];
9022 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
9023 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
9024 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
9025 if ( intoBord == 1 ) {
9026 // move node of the border to be on a link of elem of the side
9027 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9028 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9029 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9030 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9031 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9033 insertMapIt = insertMap.find( elem );
9034 bool notFound = ( insertMapIt == insertMap.end() );
9035 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9037 // insert into another link of the same element:
9038 // 1. perform insertion into the other link of the elem
9039 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9040 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9041 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9042 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9043 // 2. perform insertion into the link of adjacent faces
9045 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9047 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9051 if (toCreatePolyedrs) {
9052 // perform insertion into the links of adjacent volumes
9053 UpdateVolumes(n12, n22, nodeList);
9055 // 3. find an element appeared on n1 and n2 after the insertion
9056 insertMap.erase( elem );
9057 elem = findAdjacentFace( n1, n2, 0 );
9059 if ( notFound || otherLink ) {
9060 // add element and nodes of the side into the insertMap
9061 insertMapIt = insertMap.insert
9062 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9063 (*insertMapIt).second.push_back( n1 );
9064 (*insertMapIt).second.push_back( n2 );
9066 // add node to be inserted into elem
9067 (*insertMapIt).second.push_back( nIns );
9068 next[ 1 - intoBord ] = true;
9071 // go to the next segment
9072 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9073 if ( next[ iBord ] ) {
9074 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9076 nPrev[ iBord ] = *nIt[ iBord ];
9077 nIt[ iBord ]++; i[ iBord ]++;
9081 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9083 // perform insertion of nodes into elements
9085 for (insertMapIt = insertMap.begin();
9086 insertMapIt != insertMap.end();
9089 const SMDS_MeshElement* elem = (*insertMapIt).first;
9090 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9091 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9092 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9094 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9096 if ( !theSideIsFreeBorder ) {
9097 // look for and insert nodes into the faces adjacent to elem
9099 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9101 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9106 if (toCreatePolyedrs) {
9107 // perform insertion into the links of adjacent volumes
9108 UpdateVolumes(n1, n2, nodeList);
9114 } // end: insert new nodes
9116 MergeNodes ( nodeGroupsToMerge );
9121 //=======================================================================
9122 //function : InsertNodesIntoLink
9123 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9124 // and theBetweenNode2 and split theElement
9125 //=======================================================================
9127 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9128 const SMDS_MeshNode* theBetweenNode1,
9129 const SMDS_MeshNode* theBetweenNode2,
9130 list<const SMDS_MeshNode*>& theNodesToInsert,
9131 const bool toCreatePoly)
9133 if ( theFace->GetType() != SMDSAbs_Face ) return;
9135 // find indices of 2 link nodes and of the rest nodes
9136 int iNode = 0, il1, il2, i3, i4;
9137 il1 = il2 = i3 = i4 = -1;
9138 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9139 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9141 if(theFace->IsQuadratic()) {
9142 const SMDS_VtkFace* F =
9143 dynamic_cast<const SMDS_VtkFace*>(theFace);
9144 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9145 // use special nodes iterator
9146 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9147 while( anIter->more() ) {
9148 const SMDS_MeshNode* n = cast2Node(anIter->next());
9149 if ( n == theBetweenNode1 )
9151 else if ( n == theBetweenNode2 )
9157 nodes[ iNode++ ] = n;
9161 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9162 while ( nodeIt->more() ) {
9163 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9164 if ( n == theBetweenNode1 )
9166 else if ( n == theBetweenNode2 )
9172 nodes[ iNode++ ] = n;
9175 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9178 // arrange link nodes to go one after another regarding the face orientation
9179 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9180 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9185 aNodesToInsert.reverse();
9187 // check that not link nodes of a quadrangles are in good order
9188 int nbFaceNodes = theFace->NbNodes();
9189 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9195 if (toCreatePoly || theFace->IsPoly()) {
9198 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9200 // add nodes of face up to first node of link
9203 if(theFace->IsQuadratic()) {
9204 const SMDS_VtkFace* F =
9205 dynamic_cast<const SMDS_VtkFace*>(theFace);
9206 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9207 // use special nodes iterator
9208 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9209 while( anIter->more() && !isFLN ) {
9210 const SMDS_MeshNode* n = cast2Node(anIter->next());
9211 poly_nodes[iNode++] = n;
9212 if (n == nodes[il1]) {
9216 // add nodes to insert
9217 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9218 for (; nIt != aNodesToInsert.end(); nIt++) {
9219 poly_nodes[iNode++] = *nIt;
9221 // add nodes of face starting from last node of link
9222 while ( anIter->more() ) {
9223 poly_nodes[iNode++] = cast2Node(anIter->next());
9227 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9228 while ( nodeIt->more() && !isFLN ) {
9229 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9230 poly_nodes[iNode++] = n;
9231 if (n == nodes[il1]) {
9235 // add nodes to insert
9236 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9237 for (; nIt != aNodesToInsert.end(); nIt++) {
9238 poly_nodes[iNode++] = *nIt;
9240 // add nodes of face starting from last node of link
9241 while ( nodeIt->more() ) {
9242 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9243 poly_nodes[iNode++] = n;
9247 // edit or replace the face
9248 SMESHDS_Mesh *aMesh = GetMeshDS();
9250 if (theFace->IsPoly()) {
9251 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9254 int aShapeId = FindShape( theFace );
9256 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9257 myLastCreatedElems.Append(newElem);
9258 if ( aShapeId && newElem )
9259 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9261 aMesh->RemoveElement(theFace);
9266 SMESHDS_Mesh *aMesh = GetMeshDS();
9267 if( !theFace->IsQuadratic() ) {
9269 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9270 int nbLinkNodes = 2 + aNodesToInsert.size();
9271 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9272 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9273 linkNodes[ 0 ] = nodes[ il1 ];
9274 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9275 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9276 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9277 linkNodes[ iNode++ ] = *nIt;
9279 // decide how to split a quadrangle: compare possible variants
9280 // and choose which of splits to be a quadrangle
9281 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9282 if ( nbFaceNodes == 3 ) {
9283 iBestQuad = nbSplits;
9286 else if ( nbFaceNodes == 4 ) {
9287 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9288 double aBestRate = DBL_MAX;
9289 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9291 double aBadRate = 0;
9292 // evaluate elements quality
9293 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9294 if ( iSplit == iQuad ) {
9295 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9299 aBadRate += getBadRate( &quad, aCrit );
9302 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9304 nodes[ iSplit < iQuad ? i4 : i3 ]);
9305 aBadRate += getBadRate( &tria, aCrit );
9309 if ( aBadRate < aBestRate ) {
9311 aBestRate = aBadRate;
9316 // create new elements
9317 int aShapeId = FindShape( theFace );
9320 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9321 SMDS_MeshElement* newElem = 0;
9322 if ( iSplit == iBestQuad )
9323 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9328 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9330 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9331 myLastCreatedElems.Append(newElem);
9332 if ( aShapeId && newElem )
9333 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9336 // change nodes of theFace
9337 const SMDS_MeshNode* newNodes[ 4 ];
9338 newNodes[ 0 ] = linkNodes[ i1 ];
9339 newNodes[ 1 ] = linkNodes[ i2 ];
9340 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9341 newNodes[ 3 ] = nodes[ i4 ];
9342 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9343 const SMDS_MeshElement* newElem = 0;
9344 if (iSplit == iBestQuad)
9345 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9347 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9348 myLastCreatedElems.Append(newElem);
9349 if ( aShapeId && newElem )
9350 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9351 } // end if(!theFace->IsQuadratic())
9352 else { // theFace is quadratic
9353 // we have to split theFace on simple triangles and one simple quadrangle
9355 int nbshift = tmp*2;
9356 // shift nodes in nodes[] by nbshift
9358 for(i=0; i<nbshift; i++) {
9359 const SMDS_MeshNode* n = nodes[0];
9360 for(j=0; j<nbFaceNodes-1; j++) {
9361 nodes[j] = nodes[j+1];
9363 nodes[nbFaceNodes-1] = n;
9365 il1 = il1 - nbshift;
9366 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9367 // n0 n1 n2 n0 n1 n2
9368 // +-----+-----+ +-----+-----+
9377 // create new elements
9378 int aShapeId = FindShape( theFace );
9381 if(nbFaceNodes==6) { // quadratic triangle
9382 SMDS_MeshElement* newElem =
9383 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9384 myLastCreatedElems.Append(newElem);
9385 if ( aShapeId && newElem )
9386 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9387 if(theFace->IsMediumNode(nodes[il1])) {
9388 // create quadrangle
9389 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9390 myLastCreatedElems.Append(newElem);
9391 if ( aShapeId && newElem )
9392 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9398 // create quadrangle
9399 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9400 myLastCreatedElems.Append(newElem);
9401 if ( aShapeId && newElem )
9402 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9408 else { // nbFaceNodes==8 - quadratic quadrangle
9409 SMDS_MeshElement* newElem =
9410 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9411 myLastCreatedElems.Append(newElem);
9412 if ( aShapeId && newElem )
9413 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9414 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9415 myLastCreatedElems.Append(newElem);
9416 if ( aShapeId && newElem )
9417 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9418 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9419 myLastCreatedElems.Append(newElem);
9420 if ( aShapeId && newElem )
9421 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9422 if(theFace->IsMediumNode(nodes[il1])) {
9423 // create quadrangle
9424 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9425 myLastCreatedElems.Append(newElem);
9426 if ( aShapeId && newElem )
9427 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9433 // create quadrangle
9434 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9435 myLastCreatedElems.Append(newElem);
9436 if ( aShapeId && newElem )
9437 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9443 // create needed triangles using n1,n2,n3 and inserted nodes
9444 int nbn = 2 + aNodesToInsert.size();
9445 //const SMDS_MeshNode* aNodes[nbn];
9446 vector<const SMDS_MeshNode*> aNodes(nbn);
9447 aNodes[0] = nodes[n1];
9448 aNodes[nbn-1] = nodes[n2];
9449 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9450 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9451 aNodes[iNode++] = *nIt;
9453 for(i=1; i<nbn; i++) {
9454 SMDS_MeshElement* newElem =
9455 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9456 myLastCreatedElems.Append(newElem);
9457 if ( aShapeId && newElem )
9458 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9462 aMesh->RemoveElement(theFace);
9465 //=======================================================================
9466 //function : UpdateVolumes
9468 //=======================================================================
9469 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9470 const SMDS_MeshNode* theBetweenNode2,
9471 list<const SMDS_MeshNode*>& theNodesToInsert)
9473 myLastCreatedElems.Clear();
9474 myLastCreatedNodes.Clear();
9476 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9477 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9478 const SMDS_MeshElement* elem = invElemIt->next();
9480 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9481 SMDS_VolumeTool aVolume (elem);
9482 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9485 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9486 int iface, nbFaces = aVolume.NbFaces();
9487 vector<const SMDS_MeshNode *> poly_nodes;
9488 vector<int> quantities (nbFaces);
9490 for (iface = 0; iface < nbFaces; iface++) {
9491 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9492 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9493 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9495 for (int inode = 0; inode < nbFaceNodes; inode++) {
9496 poly_nodes.push_back(faceNodes[inode]);
9498 if (nbInserted == 0) {
9499 if (faceNodes[inode] == theBetweenNode1) {
9500 if (faceNodes[inode + 1] == theBetweenNode2) {
9501 nbInserted = theNodesToInsert.size();
9503 // add nodes to insert
9504 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9505 for (; nIt != theNodesToInsert.end(); nIt++) {
9506 poly_nodes.push_back(*nIt);
9510 else if (faceNodes[inode] == theBetweenNode2) {
9511 if (faceNodes[inode + 1] == theBetweenNode1) {
9512 nbInserted = theNodesToInsert.size();
9514 // add nodes to insert in reversed order
9515 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9517 for (; nIt != theNodesToInsert.begin(); nIt--) {
9518 poly_nodes.push_back(*nIt);
9520 poly_nodes.push_back(*nIt);
9527 quantities[iface] = nbFaceNodes + nbInserted;
9530 // Replace or update the volume
9531 SMESHDS_Mesh *aMesh = GetMeshDS();
9533 if (elem->IsPoly()) {
9534 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9538 int aShapeId = FindShape( elem );
9540 SMDS_MeshElement* newElem =
9541 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9542 myLastCreatedElems.Append(newElem);
9543 if (aShapeId && newElem)
9544 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9546 aMesh->RemoveElement(elem);
9553 //================================================================================
9555 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9557 //================================================================================
9559 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9560 vector<const SMDS_MeshNode *> & nodes,
9561 vector<int> & nbNodeInFaces )
9564 nbNodeInFaces.clear();
9565 SMDS_VolumeTool vTool ( elem );
9566 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9568 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9569 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9570 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9575 //=======================================================================
9577 * \brief Convert elements contained in a submesh to quadratic
9578 * \return int - nb of checked elements
9580 //=======================================================================
9582 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9583 SMESH_MesherHelper& theHelper,
9584 const bool theForce3d)
9587 if( !theSm ) return nbElem;
9589 vector<int> nbNodeInFaces;
9590 vector<const SMDS_MeshNode *> nodes;
9591 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9592 while(ElemItr->more())
9595 const SMDS_MeshElement* elem = ElemItr->next();
9596 if( !elem ) continue;
9598 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9599 if ( elem->IsQuadratic() )
9602 switch ( aGeomType ) {
9603 case SMDSEntity_Quad_Quadrangle:
9604 case SMDSEntity_Quad_Hexa: alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9605 case SMDSEntity_BiQuad_Quadrangle:
9606 case SMDSEntity_TriQuad_Hexa: alreadyOK = theHelper.GetIsBiQuadratic(); break;
9607 default: alreadyOK = true;
9609 if ( alreadyOK ) continue;
9611 // get elem data needed to re-create it
9613 const int id = elem->GetID();
9614 const int nbNodes = elem->NbCornerNodes();
9615 const SMDSAbs_ElementType aType = elem->GetType();
9616 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9617 if ( aGeomType == SMDSEntity_Polyhedra )
9618 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9619 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9620 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9622 // remove a linear element
9623 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9625 const SMDS_MeshElement* NewElem = 0;
9631 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9639 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9642 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9645 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9650 case SMDSAbs_Volume :
9654 case SMDSEntity_Tetra:
9655 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9657 case SMDSEntity_Pyramid:
9658 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9660 case SMDSEntity_Penta:
9661 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9663 case SMDSEntity_Hexa:
9664 case SMDSEntity_Quad_Hexa:
9665 case SMDSEntity_TriQuad_Hexa:
9666 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9667 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9669 case SMDSEntity_Hexagonal_Prism:
9671 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9678 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9680 theSm->AddElement( NewElem );
9684 //=======================================================================
9685 //function : ConvertToQuadratic
9687 //=======================================================================
9689 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9691 SMESHDS_Mesh* meshDS = GetMeshDS();
9693 SMESH_MesherHelper aHelper(*myMesh);
9695 aHelper.SetIsQuadratic( true );
9696 aHelper.SetIsBiQuadratic( theToBiQuad );
9697 aHelper.SetElementsOnShape(true);
9699 int nbCheckedElems = 0;
9700 if ( myMesh->HasShapeToMesh() )
9702 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9704 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9705 while ( smIt->more() ) {
9706 SMESH_subMesh* sm = smIt->next();
9707 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9708 aHelper.SetSubShape( sm->GetSubShape() );
9709 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9714 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9715 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9717 aHelper.SetElementsOnShape(false);
9718 SMESHDS_SubMesh *smDS = 0;
9719 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9720 while(aEdgeItr->more())
9722 const SMDS_MeshEdge* edge = aEdgeItr->next();
9723 if(edge && !edge->IsQuadratic())
9725 int id = edge->GetID();
9726 //MESSAGE("edge->GetID() " << id);
9727 const SMDS_MeshNode* n1 = edge->GetNode(0);
9728 const SMDS_MeshNode* n2 = edge->GetNode(1);
9730 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9732 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9733 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9736 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9737 while(aFaceItr->more())
9739 const SMDS_MeshFace* face = aFaceItr->next();
9740 if ( !face ) continue;
9742 const SMDSAbs_EntityType type = face->GetEntityType();
9743 if (( theToBiQuad && type == SMDSEntity_BiQuad_Quadrangle ) ||
9744 ( !theToBiQuad && type == SMDSEntity_Quad_Quadrangle ))
9747 const int id = face->GetID();
9748 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9750 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9752 SMDS_MeshFace * NewFace = 0;
9755 case SMDSEntity_Triangle:
9756 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9758 case SMDSEntity_Quadrangle:
9759 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9762 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9764 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9766 vector<int> nbNodeInFaces;
9767 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9768 while(aVolumeItr->more())
9770 const SMDS_MeshVolume* volume = aVolumeItr->next();
9771 if(!volume || volume->IsQuadratic() ) continue;
9773 const SMDSAbs_EntityType type = volume->GetEntityType();
9774 if (( theToBiQuad && type == SMDSEntity_TriQuad_Hexa ) ||
9775 ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
9778 const int id = volume->GetID();
9779 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9780 if ( type == SMDSEntity_Polyhedra )
9781 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9782 else if ( type == SMDSEntity_Hexagonal_Prism )
9783 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9785 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9787 SMDS_MeshVolume * NewVolume = 0;
9790 case SMDSEntity_Tetra:
9791 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9793 case SMDSEntity_Hexa:
9794 case SMDSEntity_Quad_Hexa:
9795 case SMDSEntity_TriQuad_Hexa:
9796 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9797 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9799 case SMDSEntity_Pyramid:
9800 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9801 nodes[3], nodes[4], id, theForce3d);
9803 case SMDSEntity_Penta:
9804 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9805 nodes[3], nodes[4], nodes[5], id, theForce3d);
9807 case SMDSEntity_Hexagonal_Prism:
9809 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9811 ReplaceElemInGroups(volume, NewVolume, meshDS);
9816 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9817 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9818 aHelper.FixQuadraticElements(myError);
9822 //================================================================================
9824 * \brief Makes given elements quadratic
9825 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9826 * \param theElements - elements to make quadratic
9828 //================================================================================
9830 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9831 TIDSortedElemSet& theElements,
9832 const bool theToBiQuad)
9834 if ( theElements.empty() ) return;
9836 // we believe that all theElements are of the same type
9837 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9839 // get all nodes shared by theElements
9840 TIDSortedNodeSet allNodes;
9841 TIDSortedElemSet::iterator eIt = theElements.begin();
9842 for ( ; eIt != theElements.end(); ++eIt )
9843 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9845 // complete theElements with elements of lower dim whose all nodes are in allNodes
9847 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9848 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9849 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9850 for ( ; nIt != allNodes.end(); ++nIt )
9852 const SMDS_MeshNode* n = *nIt;
9853 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9854 while ( invIt->more() )
9856 const SMDS_MeshElement* e = invIt->next();
9857 if ( e->IsQuadratic() )
9860 switch ( e->GetEntityType() ) {
9861 case SMDSEntity_Quad_Quadrangle:
9862 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9863 case SMDSEntity_BiQuad_Quadrangle:
9864 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9865 default: alreadyOK = true;
9869 quadAdjacentElems[ e->GetType() ].insert( e );
9873 if ( e->GetType() >= elemType )
9875 continue; // same type of more complex linear element
9878 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9879 continue; // e is already checked
9883 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9884 while ( nodeIt->more() && allIn )
9885 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9887 theElements.insert(e );
9891 SMESH_MesherHelper helper(*myMesh);
9892 helper.SetIsQuadratic( true );
9893 helper.SetIsBiQuadratic( theToBiQuad );
9895 // add links of quadratic adjacent elements to the helper
9897 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9898 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9899 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9901 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9903 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9904 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9905 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9907 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9909 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9910 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9911 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9913 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9916 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9918 SMESHDS_Mesh* meshDS = GetMeshDS();
9919 SMESHDS_SubMesh* smDS = 0;
9920 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9922 const SMDS_MeshElement* elem = *eIt;
9923 if( elem->NbNodes() < 2 || elem->IsPoly() )
9926 if ( elem->IsQuadratic() )
9929 switch ( elem->GetEntityType() ) {
9930 case SMDSEntity_Quad_Quadrangle:
9931 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9932 case SMDSEntity_BiQuad_Quadrangle:
9933 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9934 default: alreadyOK = true;
9936 if ( alreadyOK ) continue;
9939 const SMDSAbs_ElementType type = elem->GetType();
9940 const int id = elem->GetID();
9941 const int nbNodes = elem->NbCornerNodes();
9942 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9944 if ( !smDS || !smDS->Contains( elem ))
9945 smDS = meshDS->MeshElements( elem->getshapeId() );
9946 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9948 SMDS_MeshElement * newElem = 0;
9951 case 4: // cases for most frequently used element types go first (for optimization)
9952 if ( type == SMDSAbs_Volume )
9953 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9955 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9958 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9959 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9962 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9965 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9968 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9969 nodes[4], id, theForce3d);
9972 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9973 nodes[4], nodes[5], id, theForce3d);
9977 ReplaceElemInGroups( elem, newElem, meshDS);
9978 if( newElem && smDS )
9979 smDS->AddElement( newElem );
9982 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9983 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9984 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9985 helper.FixQuadraticElements( myError );
9989 //=======================================================================
9991 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9992 * \return int - nb of checked elements
9994 //=======================================================================
9996 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9997 SMDS_ElemIteratorPtr theItr,
9998 const int theShapeID)
10001 SMESHDS_Mesh* meshDS = GetMeshDS();
10003 while( theItr->more() )
10005 const SMDS_MeshElement* elem = theItr->next();
10007 if( elem && elem->IsQuadratic())
10009 int id = elem->GetID();
10010 int nbCornerNodes = elem->NbCornerNodes();
10011 SMDSAbs_ElementType aType = elem->GetType();
10013 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
10015 //remove a quadratic element
10016 if ( !theSm || !theSm->Contains( elem ))
10017 theSm = meshDS->MeshElements( elem->getshapeId() );
10018 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
10020 // remove medium nodes
10021 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
10022 if ( nodes[i]->NbInverseElements() == 0 )
10023 meshDS->RemoveFreeNode( nodes[i], theSm );
10025 // add a linear element
10026 nodes.resize( nbCornerNodes );
10027 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
10028 ReplaceElemInGroups(elem, newElem, meshDS);
10029 if( theSm && newElem )
10030 theSm->AddElement( newElem );
10036 //=======================================================================
10037 //function : ConvertFromQuadratic
10039 //=======================================================================
10041 bool SMESH_MeshEditor::ConvertFromQuadratic()
10043 int nbCheckedElems = 0;
10044 if ( myMesh->HasShapeToMesh() )
10046 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
10048 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
10049 while ( smIt->more() ) {
10050 SMESH_subMesh* sm = smIt->next();
10051 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
10052 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
10058 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
10059 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10061 SMESHDS_SubMesh *aSM = 0;
10062 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10070 //================================================================================
10072 * \brief Return true if all medium nodes of the element are in the node set
10074 //================================================================================
10076 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10078 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10079 if ( !nodeSet.count( elem->GetNode(i) ))
10085 //================================================================================
10087 * \brief Makes given elements linear
10089 //================================================================================
10091 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10093 if ( theElements.empty() ) return;
10095 // collect IDs of medium nodes of theElements; some of these nodes will be removed
10096 set<int> mediumNodeIDs;
10097 TIDSortedElemSet::iterator eIt = theElements.begin();
10098 for ( ; eIt != theElements.end(); ++eIt )
10100 const SMDS_MeshElement* e = *eIt;
10101 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10102 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10105 // replace given elements by linear ones
10106 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10107 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10108 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10110 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10111 // except those elements sharing medium nodes of quadratic element whose medium nodes
10112 // are not all in mediumNodeIDs
10114 // get remaining medium nodes
10115 TIDSortedNodeSet mediumNodes;
10116 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10117 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10118 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10119 mediumNodes.insert( mediumNodes.end(), n );
10121 // find more quadratic elements to convert
10122 TIDSortedElemSet moreElemsToConvert;
10123 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10124 for ( ; nIt != mediumNodes.end(); ++nIt )
10126 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10127 while ( invIt->more() )
10129 const SMDS_MeshElement* e = invIt->next();
10130 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10132 // find a more complex element including e and
10133 // whose medium nodes are not in mediumNodes
10134 bool complexFound = false;
10135 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10137 SMDS_ElemIteratorPtr invIt2 =
10138 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10139 while ( invIt2->more() )
10141 const SMDS_MeshElement* eComplex = invIt2->next();
10142 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10144 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10145 if ( nbCommonNodes == e->NbNodes())
10147 complexFound = true;
10148 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10154 if ( !complexFound )
10155 moreElemsToConvert.insert( e );
10159 elemIt = SMDS_ElemIteratorPtr
10160 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10161 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10164 //=======================================================================
10165 //function : SewSideElements
10167 //=======================================================================
10169 SMESH_MeshEditor::Sew_Error
10170 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10171 TIDSortedElemSet& theSide2,
10172 const SMDS_MeshNode* theFirstNode1,
10173 const SMDS_MeshNode* theFirstNode2,
10174 const SMDS_MeshNode* theSecondNode1,
10175 const SMDS_MeshNode* theSecondNode2)
10177 myLastCreatedElems.Clear();
10178 myLastCreatedNodes.Clear();
10180 MESSAGE ("::::SewSideElements()");
10181 if ( theSide1.size() != theSide2.size() )
10182 return SEW_DIFF_NB_OF_ELEMENTS;
10184 Sew_Error aResult = SEW_OK;
10186 // 1. Build set of faces representing each side
10187 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10188 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10190 // =======================================================================
10191 // 1. Build set of faces representing each side:
10192 // =======================================================================
10193 // a. build set of nodes belonging to faces
10194 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10195 // c. create temporary faces representing side of volumes if correspondent
10196 // face does not exist
10198 SMESHDS_Mesh* aMesh = GetMeshDS();
10199 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10200 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10201 TIDSortedElemSet faceSet1, faceSet2;
10202 set<const SMDS_MeshElement*> volSet1, volSet2;
10203 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10204 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10205 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10206 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10207 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10208 int iSide, iFace, iNode;
10210 list<const SMDS_MeshElement* > tempFaceList;
10211 for ( iSide = 0; iSide < 2; iSide++ ) {
10212 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10213 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10214 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10215 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10216 set<const SMDS_MeshElement*>::iterator vIt;
10217 TIDSortedElemSet::iterator eIt;
10218 set<const SMDS_MeshNode*>::iterator nIt;
10220 // check that given nodes belong to given elements
10221 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10222 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10223 int firstIndex = -1, secondIndex = -1;
10224 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10225 const SMDS_MeshElement* elem = *eIt;
10226 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10227 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10228 if ( firstIndex > -1 && secondIndex > -1 ) break;
10230 if ( firstIndex < 0 || secondIndex < 0 ) {
10231 // we can simply return until temporary faces created
10232 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10235 // -----------------------------------------------------------
10236 // 1a. Collect nodes of existing faces
10237 // and build set of face nodes in order to detect missing
10238 // faces corresponding to sides of volumes
10239 // -----------------------------------------------------------
10241 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10243 // loop on the given element of a side
10244 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10245 //const SMDS_MeshElement* elem = *eIt;
10246 const SMDS_MeshElement* elem = *eIt;
10247 if ( elem->GetType() == SMDSAbs_Face ) {
10248 faceSet->insert( elem );
10249 set <const SMDS_MeshNode*> faceNodeSet;
10250 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10251 while ( nodeIt->more() ) {
10252 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10253 nodeSet->insert( n );
10254 faceNodeSet.insert( n );
10256 setOfFaceNodeSet.insert( faceNodeSet );
10258 else if ( elem->GetType() == SMDSAbs_Volume )
10259 volSet->insert( elem );
10261 // ------------------------------------------------------------------------------
10262 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10263 // ------------------------------------------------------------------------------
10265 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10266 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10267 while ( fIt->more() ) { // loop on faces sharing a node
10268 const SMDS_MeshElement* f = fIt->next();
10269 if ( faceSet->find( f ) == faceSet->end() ) {
10270 // check if all nodes are in nodeSet and
10271 // complete setOfFaceNodeSet if they are
10272 set <const SMDS_MeshNode*> faceNodeSet;
10273 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10274 bool allInSet = true;
10275 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10276 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10277 if ( nodeSet->find( n ) == nodeSet->end() )
10280 faceNodeSet.insert( n );
10283 faceSet->insert( f );
10284 setOfFaceNodeSet.insert( faceNodeSet );
10290 // -------------------------------------------------------------------------
10291 // 1c. Create temporary faces representing sides of volumes if correspondent
10292 // face does not exist
10293 // -------------------------------------------------------------------------
10295 if ( !volSet->empty() ) {
10296 //int nodeSetSize = nodeSet->size();
10298 // loop on given volumes
10299 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10300 SMDS_VolumeTool vol (*vIt);
10301 // loop on volume faces: find free faces
10302 // --------------------------------------
10303 list<const SMDS_MeshElement* > freeFaceList;
10304 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10305 if ( !vol.IsFreeFace( iFace ))
10307 // check if there is already a face with same nodes in a face set
10308 const SMDS_MeshElement* aFreeFace = 0;
10309 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10310 int nbNodes = vol.NbFaceNodes( iFace );
10311 set <const SMDS_MeshNode*> faceNodeSet;
10312 vol.GetFaceNodes( iFace, faceNodeSet );
10313 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10315 // no such a face is given but it still can exist, check it
10316 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10317 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10319 if ( !aFreeFace ) {
10320 // create a temporary face
10321 if ( nbNodes == 3 ) {
10322 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10323 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10325 else if ( nbNodes == 4 ) {
10326 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10327 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10330 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10331 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10332 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10335 tempFaceList.push_back( aFreeFace );
10339 freeFaceList.push_back( aFreeFace );
10341 } // loop on faces of a volume
10343 // choose one of several free faces of a volume
10344 // --------------------------------------------
10345 if ( freeFaceList.size() > 1 ) {
10346 // choose a face having max nb of nodes shared by other elems of a side
10347 int maxNbNodes = -1;
10348 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10349 while ( fIt != freeFaceList.end() ) { // loop on free faces
10350 int nbSharedNodes = 0;
10351 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10352 while ( nodeIt->more() ) { // loop on free face nodes
10353 const SMDS_MeshNode* n =
10354 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10355 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10356 while ( invElemIt->more() ) {
10357 const SMDS_MeshElement* e = invElemIt->next();
10358 nbSharedNodes += faceSet->count( e );
10359 nbSharedNodes += elemSet->count( e );
10362 if ( nbSharedNodes > maxNbNodes ) {
10363 maxNbNodes = nbSharedNodes;
10364 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10366 else if ( nbSharedNodes == maxNbNodes ) {
10370 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10373 if ( freeFaceList.size() > 1 )
10375 // could not choose one face, use another way
10376 // choose a face most close to the bary center of the opposite side
10377 gp_XYZ aBC( 0., 0., 0. );
10378 set <const SMDS_MeshNode*> addedNodes;
10379 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10380 eIt = elemSet2->begin();
10381 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10382 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10383 while ( nodeIt->more() ) { // loop on free face nodes
10384 const SMDS_MeshNode* n =
10385 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10386 if ( addedNodes.insert( n ).second )
10387 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10390 aBC /= addedNodes.size();
10391 double minDist = DBL_MAX;
10392 fIt = freeFaceList.begin();
10393 while ( fIt != freeFaceList.end() ) { // loop on free faces
10395 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10396 while ( nodeIt->more() ) { // loop on free face nodes
10397 const SMDS_MeshNode* n =
10398 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10399 gp_XYZ p( n->X(),n->Y(),n->Z() );
10400 dist += ( aBC - p ).SquareModulus();
10402 if ( dist < minDist ) {
10404 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10407 fIt = freeFaceList.erase( fIt++ );
10410 } // choose one of several free faces of a volume
10412 if ( freeFaceList.size() == 1 ) {
10413 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10414 faceSet->insert( aFreeFace );
10415 // complete a node set with nodes of a found free face
10416 // for ( iNode = 0; iNode < ; iNode++ )
10417 // nodeSet->insert( fNodes[ iNode ] );
10420 } // loop on volumes of a side
10422 // // complete a set of faces if new nodes in a nodeSet appeared
10423 // // ----------------------------------------------------------
10424 // if ( nodeSetSize != nodeSet->size() ) {
10425 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10426 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10427 // while ( fIt->more() ) { // loop on faces sharing a node
10428 // const SMDS_MeshElement* f = fIt->next();
10429 // if ( faceSet->find( f ) == faceSet->end() ) {
10430 // // check if all nodes are in nodeSet and
10431 // // complete setOfFaceNodeSet if they are
10432 // set <const SMDS_MeshNode*> faceNodeSet;
10433 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10434 // bool allInSet = true;
10435 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10436 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10437 // if ( nodeSet->find( n ) == nodeSet->end() )
10438 // allInSet = false;
10440 // faceNodeSet.insert( n );
10442 // if ( allInSet ) {
10443 // faceSet->insert( f );
10444 // setOfFaceNodeSet.insert( faceNodeSet );
10450 } // Create temporary faces, if there are volumes given
10453 if ( faceSet1.size() != faceSet2.size() ) {
10454 // delete temporary faces: they are in reverseElements of actual nodes
10455 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10456 // while ( tmpFaceIt->more() )
10457 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10458 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10459 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10460 // aMesh->RemoveElement(*tmpFaceIt);
10461 MESSAGE("Diff nb of faces");
10462 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10465 // ============================================================
10466 // 2. Find nodes to merge:
10467 // bind a node to remove to a node to put instead
10468 // ============================================================
10470 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10471 if ( theFirstNode1 != theFirstNode2 )
10472 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10473 if ( theSecondNode1 != theSecondNode2 )
10474 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10476 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10477 set< long > linkIdSet; // links to process
10478 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10480 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10481 list< NLink > linkList[2];
10482 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10483 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10484 // loop on links in linkList; find faces by links and append links
10485 // of the found faces to linkList
10486 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10487 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10489 NLink link[] = { *linkIt[0], *linkIt[1] };
10490 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10491 if ( !linkIdSet.count( linkID ) )
10494 // by links, find faces in the face sets,
10495 // and find indices of link nodes in the found faces;
10496 // in a face set, there is only one or no face sharing a link
10497 // ---------------------------------------------------------------
10499 const SMDS_MeshElement* face[] = { 0, 0 };
10500 vector<const SMDS_MeshNode*> fnodes[2];
10501 int iLinkNode[2][2];
10502 TIDSortedElemSet avoidSet;
10503 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10504 const SMDS_MeshNode* n1 = link[iSide].first;
10505 const SMDS_MeshNode* n2 = link[iSide].second;
10506 //cout << "Side " << iSide << " ";
10507 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10508 // find a face by two link nodes
10509 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10510 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10511 if ( face[ iSide ])
10513 //cout << " F " << face[ iSide]->GetID() <<endl;
10514 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10515 // put face nodes to fnodes
10516 if ( face[ iSide ]->IsQuadratic() )
10518 // use interlaced nodes iterator
10519 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10520 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10521 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10522 while ( nIter->more() )
10523 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10527 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10528 face[ iSide ]->end_nodes() );
10530 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10534 // check similarity of elements of the sides
10535 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10536 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10537 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10538 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10541 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10543 break; // do not return because it's necessary to remove tmp faces
10546 // set nodes to merge
10547 // -------------------
10549 if ( face[0] && face[1] ) {
10550 const int nbNodes = face[0]->NbNodes();
10551 if ( nbNodes != face[1]->NbNodes() ) {
10552 MESSAGE("Diff nb of face nodes");
10553 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10554 break; // do not return because it s necessary to remove tmp faces
10556 bool reverse[] = { false, false }; // order of nodes in the link
10557 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10558 // analyse link orientation in faces
10559 int i1 = iLinkNode[ iSide ][ 0 ];
10560 int i2 = iLinkNode[ iSide ][ 1 ];
10561 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10563 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10564 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10565 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10567 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10568 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10571 // add other links of the faces to linkList
10572 // -----------------------------------------
10574 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10575 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10576 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10577 if ( !iter_isnew.second ) { // already in a set: no need to process
10578 linkIdSet.erase( iter_isnew.first );
10580 else // new in set == encountered for the first time: add
10582 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10583 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10584 linkList[0].push_back ( NLink( n1, n2 ));
10585 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10590 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10593 } // loop on link lists
10595 if ( aResult == SEW_OK &&
10596 ( //linkIt[0] != linkList[0].end() ||
10597 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10598 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10599 " " << (faceSetPtr[1]->empty()));
10600 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10603 // ====================================================================
10604 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10605 // ====================================================================
10607 // delete temporary faces
10608 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10609 // while ( tmpFaceIt->more() )
10610 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10611 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10612 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10613 aMesh->RemoveElement(*tmpFaceIt);
10615 if ( aResult != SEW_OK)
10618 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10619 // loop on nodes replacement map
10620 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10621 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10622 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10623 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10624 nodeIDsToRemove.push_back( nToRemove->GetID() );
10625 // loop on elements sharing nToRemove
10626 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10627 while ( invElemIt->more() ) {
10628 const SMDS_MeshElement* e = invElemIt->next();
10629 // get a new suite of nodes: make replacement
10630 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10631 vector< const SMDS_MeshNode*> nodes( nbNodes );
10632 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10633 while ( nIt->more() ) {
10634 const SMDS_MeshNode* n =
10635 static_cast<const SMDS_MeshNode*>( nIt->next() );
10636 nnIt = nReplaceMap.find( n );
10637 if ( nnIt != nReplaceMap.end() ) {
10639 n = (*nnIt).second;
10643 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10644 // elemIDsToRemove.push_back( e->GetID() );
10648 SMDSAbs_ElementType etyp = e->GetType();
10649 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10652 myLastCreatedElems.Append(newElem);
10653 AddToSameGroups(newElem, e, aMesh);
10654 int aShapeId = e->getshapeId();
10657 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10660 aMesh->RemoveElement(e);
10665 Remove( nodeIDsToRemove, true );
10670 //================================================================================
10672 * \brief Find corresponding nodes in two sets of faces
10673 * \param theSide1 - first face set
10674 * \param theSide2 - second first face
10675 * \param theFirstNode1 - a boundary node of set 1
10676 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10677 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10678 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10679 * \param nReplaceMap - output map of corresponding nodes
10680 * \return bool - is a success or not
10682 //================================================================================
10685 //#define DEBUG_MATCHING_NODES
10688 SMESH_MeshEditor::Sew_Error
10689 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10690 set<const SMDS_MeshElement*>& theSide2,
10691 const SMDS_MeshNode* theFirstNode1,
10692 const SMDS_MeshNode* theFirstNode2,
10693 const SMDS_MeshNode* theSecondNode1,
10694 const SMDS_MeshNode* theSecondNode2,
10695 TNodeNodeMap & nReplaceMap)
10697 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10699 nReplaceMap.clear();
10700 if ( theFirstNode1 != theFirstNode2 )
10701 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10702 if ( theSecondNode1 != theSecondNode2 )
10703 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10705 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10706 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10708 list< NLink > linkList[2];
10709 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10710 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10712 // loop on links in linkList; find faces by links and append links
10713 // of the found faces to linkList
10714 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10715 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10716 NLink link[] = { *linkIt[0], *linkIt[1] };
10717 if ( linkSet.find( link[0] ) == linkSet.end() )
10720 // by links, find faces in the face sets,
10721 // and find indices of link nodes in the found faces;
10722 // in a face set, there is only one or no face sharing a link
10723 // ---------------------------------------------------------------
10725 const SMDS_MeshElement* face[] = { 0, 0 };
10726 list<const SMDS_MeshNode*> notLinkNodes[2];
10727 //bool reverse[] = { false, false }; // order of notLinkNodes
10729 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10731 const SMDS_MeshNode* n1 = link[iSide].first;
10732 const SMDS_MeshNode* n2 = link[iSide].second;
10733 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10734 set< const SMDS_MeshElement* > facesOfNode1;
10735 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10737 // during a loop of the first node, we find all faces around n1,
10738 // during a loop of the second node, we find one face sharing both n1 and n2
10739 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10740 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10741 while ( fIt->more() ) { // loop on faces sharing a node
10742 const SMDS_MeshElement* f = fIt->next();
10743 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10744 ! facesOfNode1.insert( f ).second ) // f encounters twice
10746 if ( face[ iSide ] ) {
10747 MESSAGE( "2 faces per link " );
10748 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10751 faceSet->erase( f );
10753 // get not link nodes
10754 int nbN = f->NbNodes();
10755 if ( f->IsQuadratic() )
10757 nbNodes[ iSide ] = nbN;
10758 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10759 int i1 = f->GetNodeIndex( n1 );
10760 int i2 = f->GetNodeIndex( n2 );
10761 int iEnd = nbN, iBeg = -1, iDelta = 1;
10762 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10764 std::swap( iEnd, iBeg ); iDelta = -1;
10769 if ( i == iEnd ) i = iBeg + iDelta;
10770 if ( i == i1 ) break;
10771 nodes.push_back ( f->GetNode( i ) );
10777 // check similarity of elements of the sides
10778 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10779 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10780 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10781 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10784 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10788 // set nodes to merge
10789 // -------------------
10791 if ( face[0] && face[1] ) {
10792 if ( nbNodes[0] != nbNodes[1] ) {
10793 MESSAGE("Diff nb of face nodes");
10794 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10796 #ifdef DEBUG_MATCHING_NODES
10797 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10798 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10799 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10801 int nbN = nbNodes[0];
10803 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10804 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10805 for ( int i = 0 ; i < nbN - 2; ++i ) {
10806 #ifdef DEBUG_MATCHING_NODES
10807 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10809 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10813 // add other links of the face 1 to linkList
10814 // -----------------------------------------
10816 const SMDS_MeshElement* f0 = face[0];
10817 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10818 for ( int i = 0; i < nbN; i++ )
10820 const SMDS_MeshNode* n2 = f0->GetNode( i );
10821 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10822 linkSet.insert( SMESH_TLink( n1, n2 ));
10823 if ( !iter_isnew.second ) { // already in a set: no need to process
10824 linkSet.erase( iter_isnew.first );
10826 else // new in set == encountered for the first time: add
10828 #ifdef DEBUG_MATCHING_NODES
10829 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10830 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10832 linkList[0].push_back ( NLink( n1, n2 ));
10833 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10838 } // loop on link lists
10843 //================================================================================
10845 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10846 \param theElems - the list of elements (edges or faces) to be replicated
10847 The nodes for duplication could be found from these elements
10848 \param theNodesNot - list of nodes to NOT replicate
10849 \param theAffectedElems - the list of elements (cells and edges) to which the
10850 replicated nodes should be associated to.
10851 \return TRUE if operation has been completed successfully, FALSE otherwise
10853 //================================================================================
10855 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10856 const TIDSortedElemSet& theNodesNot,
10857 const TIDSortedElemSet& theAffectedElems )
10859 myLastCreatedElems.Clear();
10860 myLastCreatedNodes.Clear();
10862 if ( theElems.size() == 0 )
10865 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10870 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10871 // duplicate elements and nodes
10872 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10873 // replce nodes by duplications
10874 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10878 //================================================================================
10880 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10881 \param theMeshDS - mesh instance
10882 \param theElems - the elements replicated or modified (nodes should be changed)
10883 \param theNodesNot - nodes to NOT replicate
10884 \param theNodeNodeMap - relation of old node to new created node
10885 \param theIsDoubleElem - flag os to replicate element or modify
10886 \return TRUE if operation has been completed successfully, FALSE otherwise
10888 //================================================================================
10890 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10891 const TIDSortedElemSet& theElems,
10892 const TIDSortedElemSet& theNodesNot,
10893 std::map< const SMDS_MeshNode*,
10894 const SMDS_MeshNode* >& theNodeNodeMap,
10895 const bool theIsDoubleElem )
10897 MESSAGE("doubleNodes");
10898 // iterate on through element and duplicate them (by nodes duplication)
10900 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10901 for ( ; elemItr != theElems.end(); ++elemItr )
10903 const SMDS_MeshElement* anElem = *elemItr;
10907 bool isDuplicate = false;
10908 // duplicate nodes to duplicate element
10909 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10910 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10912 while ( anIter->more() )
10915 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10916 SMDS_MeshNode* aNewNode = aCurrNode;
10917 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10918 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10919 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10922 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10923 theNodeNodeMap[ aCurrNode ] = aNewNode;
10924 myLastCreatedNodes.Append( aNewNode );
10926 isDuplicate |= (aCurrNode != aNewNode);
10927 newNodes[ ind++ ] = aNewNode;
10929 if ( !isDuplicate )
10932 if ( theIsDoubleElem )
10933 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10936 MESSAGE("ChangeElementNodes");
10937 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10944 //================================================================================
10946 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10947 \param theNodes - identifiers of nodes to be doubled
10948 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10949 nodes. If list of element identifiers is empty then nodes are doubled but
10950 they not assigned to elements
10951 \return TRUE if operation has been completed successfully, FALSE otherwise
10953 //================================================================================
10955 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10956 const std::list< int >& theListOfModifiedElems )
10958 MESSAGE("DoubleNodes");
10959 myLastCreatedElems.Clear();
10960 myLastCreatedNodes.Clear();
10962 if ( theListOfNodes.size() == 0 )
10965 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10969 // iterate through nodes and duplicate them
10971 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10973 std::list< int >::const_iterator aNodeIter;
10974 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10976 int aCurr = *aNodeIter;
10977 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10983 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10986 anOldNodeToNewNode[ aNode ] = aNewNode;
10987 myLastCreatedNodes.Append( aNewNode );
10991 // Create map of new nodes for modified elements
10993 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10995 std::list< int >::const_iterator anElemIter;
10996 for ( anElemIter = theListOfModifiedElems.begin();
10997 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10999 int aCurr = *anElemIter;
11000 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
11004 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
11006 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11008 while ( anIter->more() )
11010 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
11011 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
11013 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
11014 aNodeArr[ ind++ ] = aNewNode;
11017 aNodeArr[ ind++ ] = aCurrNode;
11019 anElemToNodes[ anElem ] = aNodeArr;
11022 // Change nodes of elements
11024 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
11025 anElemToNodesIter = anElemToNodes.begin();
11026 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
11028 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
11029 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
11032 MESSAGE("ChangeElementNodes");
11033 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
11042 //================================================================================
11044 \brief Check if element located inside shape
11045 \return TRUE if IN or ON shape, FALSE otherwise
11047 //================================================================================
11049 template<class Classifier>
11050 bool isInside(const SMDS_MeshElement* theElem,
11051 Classifier& theClassifier,
11052 const double theTol)
11054 gp_XYZ centerXYZ (0, 0, 0);
11055 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11056 while (aNodeItr->more())
11057 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
11059 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11060 theClassifier.Perform(aPnt, theTol);
11061 TopAbs_State aState = theClassifier.State();
11062 return (aState == TopAbs_IN || aState == TopAbs_ON );
11065 //================================================================================
11067 * \brief Classifier of the 3D point on the TopoDS_Face
11068 * with interaface suitable for isInside()
11070 //================================================================================
11072 struct _FaceClassifier
11074 Extrema_ExtPS _extremum;
11075 BRepAdaptor_Surface _surface;
11076 TopAbs_State _state;
11078 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11080 _extremum.Initialize( _surface,
11081 _surface.FirstUParameter(), _surface.LastUParameter(),
11082 _surface.FirstVParameter(), _surface.LastVParameter(),
11083 _surface.Tolerance(), _surface.Tolerance() );
11085 void Perform(const gp_Pnt& aPnt, double theTol)
11087 _state = TopAbs_OUT;
11088 _extremum.Perform(aPnt);
11089 if ( _extremum.IsDone() )
11090 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11091 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11092 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11094 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11097 TopAbs_State State() const
11104 //================================================================================
11106 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11107 This method is the first step of DoubleNodeElemGroupsInRegion.
11108 \param theElems - list of groups of elements (edges or faces) to be replicated
11109 \param theNodesNot - list of groups of nodes not to replicated
11110 \param theShape - shape to detect affected elements (element which geometric center
11111 located on or inside shape).
11112 The replicated nodes should be associated to affected elements.
11113 \return groups of affected elements
11114 \sa DoubleNodeElemGroupsInRegion()
11116 //================================================================================
11118 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11119 const TIDSortedElemSet& theNodesNot,
11120 const TopoDS_Shape& theShape,
11121 TIDSortedElemSet& theAffectedElems)
11123 if ( theShape.IsNull() )
11126 const double aTol = Precision::Confusion();
11127 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11128 auto_ptr<_FaceClassifier> aFaceClassifier;
11129 if ( theShape.ShapeType() == TopAbs_SOLID )
11131 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11132 bsc3d->PerformInfinitePoint(aTol);
11134 else if (theShape.ShapeType() == TopAbs_FACE )
11136 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11139 // iterates on indicated elements and get elements by back references from their nodes
11140 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11141 for ( ; elemItr != theElems.end(); ++elemItr )
11143 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11147 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11148 while ( nodeItr->more() )
11150 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11151 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11153 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11154 while ( backElemItr->more() )
11156 const SMDS_MeshElement* curElem = backElemItr->next();
11157 if ( curElem && theElems.find(curElem) == theElems.end() &&
11159 isInside( curElem, *bsc3d, aTol ) :
11160 isInside( curElem, *aFaceClassifier, aTol )))
11161 theAffectedElems.insert( curElem );
11168 //================================================================================
11170 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11171 \param theElems - group of of elements (edges or faces) to be replicated
11172 \param theNodesNot - group of nodes not to replicate
11173 \param theShape - shape to detect affected elements (element which geometric center
11174 located on or inside shape).
11175 The replicated nodes should be associated to affected elements.
11176 \return TRUE if operation has been completed successfully, FALSE otherwise
11178 //================================================================================
11180 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11181 const TIDSortedElemSet& theNodesNot,
11182 const TopoDS_Shape& theShape )
11184 if ( theShape.IsNull() )
11187 const double aTol = Precision::Confusion();
11188 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11189 auto_ptr<_FaceClassifier> aFaceClassifier;
11190 if ( theShape.ShapeType() == TopAbs_SOLID )
11192 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11193 bsc3d->PerformInfinitePoint(aTol);
11195 else if (theShape.ShapeType() == TopAbs_FACE )
11197 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11200 // iterates on indicated elements and get elements by back references from their nodes
11201 TIDSortedElemSet anAffected;
11202 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11203 for ( ; elemItr != theElems.end(); ++elemItr )
11205 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11209 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11210 while ( nodeItr->more() )
11212 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11213 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11215 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11216 while ( backElemItr->more() )
11218 const SMDS_MeshElement* curElem = backElemItr->next();
11219 if ( curElem && theElems.find(curElem) == theElems.end() &&
11221 isInside( curElem, *bsc3d, aTol ) :
11222 isInside( curElem, *aFaceClassifier, aTol )))
11223 anAffected.insert( curElem );
11227 return DoubleNodes( theElems, theNodesNot, anAffected );
11231 * \brief compute an oriented angle between two planes defined by four points.
11232 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11233 * @param p0 base of the rotation axe
11234 * @param p1 extremity of the rotation axe
11235 * @param g1 belongs to the first plane
11236 * @param g2 belongs to the second plane
11238 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11240 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11241 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11242 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11243 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11244 gp_Vec vref(p0, p1);
11247 gp_Vec n1 = vref.Crossed(v1);
11248 gp_Vec n2 = vref.Crossed(v2);
11249 return n2.AngleWithRef(n1, vref);
11253 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11254 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11255 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11256 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11257 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11258 * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
11259 * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
11260 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11261 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11262 * @param theElems - list of groups of volumes, where a group of volume is a set of
11263 * SMDS_MeshElements sorted by Id.
11264 * @param createJointElems - if TRUE, create the elements
11265 * @return TRUE if operation has been completed successfully, FALSE otherwise
11267 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11268 bool createJointElems)
11270 MESSAGE("----------------------------------------------");
11271 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11272 MESSAGE("----------------------------------------------");
11274 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11275 meshDS->BuildDownWardConnectivity(true);
11277 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11279 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11280 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11281 // build the list of nodes shared by 2 or more domains, with their domain indexes
11283 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11284 std::map<int,int>celldom; // cell vtkId --> domain
11285 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11286 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11287 faceDomains.clear();
11289 cellDomains.clear();
11290 nodeDomains.clear();
11291 std::map<int,int> emptyMap;
11292 std::set<int> emptySet;
11295 MESSAGE(".. Number of domains :"<<theElems.size());
11297 // Check if the domains do not share an element
11298 for (int idom = 0; idom < theElems.size()-1; idom++)
11300 // MESSAGE("... Check of domain #" << idom);
11301 const TIDSortedElemSet& domain = theElems[idom];
11302 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11303 for (; elemItr != domain.end(); ++elemItr)
11305 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11306 int idombisdeb = idom + 1 ;
11307 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11309 const TIDSortedElemSet& domainbis = theElems[idombis];
11310 if ( domainbis.count(anElem) )
11312 MESSAGE(".... Domain #" << idom);
11313 MESSAGE(".... Domain #" << idombis);
11314 throw SALOME_Exception("The domains are not disjoint.");
11321 for (int idom = 0; idom < theElems.size(); idom++)
11324 // --- build a map (face to duplicate --> volume to modify)
11325 // with all the faces shared by 2 domains (group of elements)
11326 // and corresponding volume of this domain, for each shared face.
11327 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11329 MESSAGE("... Neighbors of domain #" << idom);
11330 const TIDSortedElemSet& domain = theElems[idom];
11331 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11332 for (; elemItr != domain.end(); ++elemItr)
11334 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11337 int vtkId = anElem->getVtkId();
11338 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11339 int neighborsVtkIds[NBMAXNEIGHBORS];
11340 int downIds[NBMAXNEIGHBORS];
11341 unsigned char downTypes[NBMAXNEIGHBORS];
11342 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11343 for (int n = 0; n < nbNeighbors; n++)
11345 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11346 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11347 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11350 for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
11352 // MESSAGE("Domain " << idombis);
11353 const TIDSortedElemSet& domainbis = theElems[idombis];
11354 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11356 if ( ok ) // the characteristics of the face is stored
11358 DownIdType face(downIds[n], downTypes[n]);
11359 if (!faceDomains.count(face))
11360 faceDomains[face] = emptyMap; // create an empty entry for face
11361 if (!faceDomains[face].count(idom))
11363 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11364 celldom[vtkId] = idom;
11365 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11373 //MESSAGE("Number of shared faces " << faceDomains.size());
11374 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11376 // --- explore the shared faces domain by domain,
11377 // explore the nodes of the face and see if they belong to a cell in the domain,
11378 // which has only a node or an edge on the border (not a shared face)
11380 for (int idomain = 0; idomain < theElems.size(); idomain++)
11382 //MESSAGE("Domain " << idomain);
11383 const TIDSortedElemSet& domain = theElems[idomain];
11384 itface = faceDomains.begin();
11385 for (; itface != faceDomains.end(); ++itface)
11387 std::map<int, int> domvol = itface->second;
11388 if (!domvol.count(idomain))
11390 DownIdType face = itface->first;
11391 //MESSAGE(" --- face " << face.cellId);
11392 std::set<int> oldNodes;
11394 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11395 std::set<int>::iterator itn = oldNodes.begin();
11396 for (; itn != oldNodes.end(); ++itn)
11399 //MESSAGE(" node " << oldId);
11400 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11401 for (int i=0; i<l.ncells; i++)
11403 int vtkId = l.cells[i];
11404 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11405 if (!domain.count(anElem))
11407 int vtkType = grid->GetCellType(vtkId);
11408 int downId = grid->CellIdToDownId(vtkId);
11411 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11412 continue; // not OK at this stage of the algorithm:
11413 //no cells created after BuildDownWardConnectivity
11415 DownIdType aCell(downId, vtkType);
11416 if (!cellDomains.count(aCell))
11417 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11418 cellDomains[aCell][idomain] = vtkId;
11419 celldom[vtkId] = idomain;
11420 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11426 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11427 // for each shared face, get the nodes
11428 // for each node, for each domain of the face, create a clone of the node
11430 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11431 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11432 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11434 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11435 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11436 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11438 MESSAGE(".. Duplication of the nodes");
11439 for (int idomain = 0; idomain < theElems.size(); idomain++)
11441 itface = faceDomains.begin();
11442 for (; itface != faceDomains.end(); ++itface)
11444 std::map<int, int> domvol = itface->second;
11445 if (!domvol.count(idomain))
11447 DownIdType face = itface->first;
11448 //MESSAGE(" --- face " << face.cellId);
11449 std::set<int> oldNodes;
11451 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11452 std::set<int>::iterator itn = oldNodes.begin();
11453 for (; itn != oldNodes.end(); ++itn)
11456 //MESSAGE("-+-+-a node " << oldId);
11457 if (!nodeDomains.count(oldId))
11458 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11459 if (nodeDomains[oldId].empty())
11461 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11462 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11464 std::map<int, int>::iterator itdom = domvol.begin();
11465 for (; itdom != domvol.end(); ++itdom)
11467 int idom = itdom->first;
11468 //MESSAGE(" domain " << idom);
11469 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11471 if (nodeDomains[oldId].size() >= 2) // a multiple node
11473 vector<int> orderedDoms;
11474 //MESSAGE("multiple node " << oldId);
11475 if (mutipleNodes.count(oldId))
11476 orderedDoms = mutipleNodes[oldId];
11479 map<int,int>::iterator it = nodeDomains[oldId].begin();
11480 for (; it != nodeDomains[oldId].end(); ++it)
11481 orderedDoms.push_back(it->first);
11483 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11484 //stringstream txt;
11485 //for (int i=0; i<orderedDoms.size(); i++)
11486 // txt << orderedDoms[i] << " ";
11487 //MESSAGE("orderedDoms " << txt.str());
11488 mutipleNodes[oldId] = orderedDoms;
11490 double *coords = grid->GetPoint(oldId);
11491 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11492 int newId = newNode->getVtkId();
11493 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11494 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11501 MESSAGE(".. Creation of elements");
11502 for (int idomain = 0; idomain < theElems.size(); idomain++)
11504 itface = faceDomains.begin();
11505 for (; itface != faceDomains.end(); ++itface)
11507 std::map<int, int> domvol = itface->second;
11508 if (!domvol.count(idomain))
11510 DownIdType face = itface->first;
11511 //MESSAGE(" --- face " << face.cellId);
11512 std::set<int> oldNodes;
11514 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11515 int nbMultipleNodes = 0;
11516 std::set<int>::iterator itn = oldNodes.begin();
11517 for (; itn != oldNodes.end(); ++itn)
11520 if (mutipleNodes.count(oldId))
11523 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11525 //MESSAGE("multiple Nodes detected on a shared face");
11526 int downId = itface->first.cellId;
11527 unsigned char cellType = itface->first.cellType;
11528 // --- shared edge or shared face ?
11529 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11532 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11533 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11534 if (mutipleNodes.count(nodes[i]))
11535 if (!mutipleNodesToFace.count(nodes[i]))
11536 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11538 else // shared face (between two volumes)
11540 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11541 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11542 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11543 for (int ie =0; ie < nbEdges; ie++)
11546 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11547 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11549 vector<int> vn0 = mutipleNodes[nodes[0]];
11550 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11552 for (int i0 = 0; i0 < vn0.size(); i0++)
11553 for (int i1 = 0; i1 < vn1.size(); i1++)
11554 if (vn0[i0] == vn1[i1])
11555 doms.push_back(vn0[i0]);
11556 if (doms.size() >2)
11558 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11559 double *coords = grid->GetPoint(nodes[0]);
11560 gp_Pnt p0(coords[0], coords[1], coords[2]);
11561 coords = grid->GetPoint(nodes[nbNodes - 1]);
11562 gp_Pnt p1(coords[0], coords[1], coords[2]);
11564 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11565 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11566 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11567 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11568 for (int id=0; id < doms.size(); id++)
11570 int idom = doms[id];
11571 for (int ivol=0; ivol<nbvol; ivol++)
11573 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11574 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11575 if (theElems[idom].count(elem))
11577 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11578 domvol[idom] = svol;
11579 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11581 vtkIdType npts = 0;
11582 vtkIdType* pts = 0;
11583 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11584 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11587 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11588 angleDom[idom] = 0;
11592 gp_Pnt g(values[0], values[1], values[2]);
11593 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11594 //MESSAGE(" angle=" << angleDom[idom]);
11600 map<double, int> sortedDom; // sort domains by angle
11601 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11602 sortedDom[ia->second] = ia->first;
11603 vector<int> vnodes;
11605 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11607 vdom.push_back(ib->second);
11608 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11610 for (int ino = 0; ino < nbNodes; ino++)
11611 vnodes.push_back(nodes[ino]);
11612 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11621 // --- iterate on shared faces (volumes to modify, face to extrude)
11622 // get node id's of the face (id SMDS = id VTK)
11623 // create flat element with old and new nodes if requested
11625 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11626 // (domain1 X domain2) = domain1 + MAXINT*domain2
11628 std::map<int, std::map<long,int> > nodeQuadDomains;
11629 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11631 MESSAGE(".. Creation of elements: simple junction");
11632 if (createJointElems)
11635 string joints2DName = "joints2D";
11636 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11637 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11638 string joints3DName = "joints3D";
11639 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11640 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11642 itface = faceDomains.begin();
11643 for (; itface != faceDomains.end(); ++itface)
11645 DownIdType face = itface->first;
11646 std::set<int> oldNodes;
11647 std::set<int>::iterator itn;
11649 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11651 std::map<int, int> domvol = itface->second;
11652 std::map<int, int>::iterator itdom = domvol.begin();
11653 int dom1 = itdom->first;
11654 int vtkVolId = itdom->second;
11656 int dom2 = itdom->first;
11657 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11659 stringstream grpname;
11662 grpname << dom1 << "_" << dom2;
11664 grpname << dom2 << "_" << dom1;
11665 string namegrp = grpname.str();
11666 if (!mapOfJunctionGroups.count(namegrp))
11667 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11668 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11670 sgrp->Add(vol->GetID());
11671 if (vol->GetType() == SMDSAbs_Volume)
11672 joints3DGrp->Add(vol->GetID());
11673 else if (vol->GetType() == SMDSAbs_Face)
11674 joints2DGrp->Add(vol->GetID());
11678 // --- create volumes on multiple domain intersection if requested
11679 // iterate on mutipleNodesToFace
11680 // iterate on edgesMultiDomains
11682 MESSAGE(".. Creation of elements: multiple junction");
11683 if (createJointElems)
11685 // --- iterate on mutipleNodesToFace
11687 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11688 for (; itn != mutipleNodesToFace.end(); ++itn)
11690 int node = itn->first;
11691 vector<int> orderDom = itn->second;
11692 vector<vtkIdType> orderedNodes;
11693 for (int idom = 0; idom <orderDom.size(); idom++)
11694 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11695 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11697 stringstream grpname;
11699 grpname << 0 << "_" << 0;
11701 string namegrp = grpname.str();
11702 if (!mapOfJunctionGroups.count(namegrp))
11703 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11704 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11706 sgrp->Add(face->GetID());
11709 // --- iterate on edgesMultiDomains
11711 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11712 for (; ite != edgesMultiDomains.end(); ++ite)
11714 vector<int> nodes = ite->first;
11715 vector<int> orderDom = ite->second;
11716 vector<vtkIdType> orderedNodes;
11717 if (nodes.size() == 2)
11719 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11720 for (int ino=0; ino < nodes.size(); ino++)
11721 if (orderDom.size() == 3)
11722 for (int idom = 0; idom <orderDom.size(); idom++)
11723 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11725 for (int idom = orderDom.size()-1; idom >=0; idom--)
11726 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11727 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11730 string namegrp = "jointsMultiples";
11731 if (!mapOfJunctionGroups.count(namegrp))
11732 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11733 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11735 sgrp->Add(vol->GetID());
11739 INFOS("Quadratic multiple joints not implemented");
11740 // TODO quadratic nodes
11745 // --- list the explicit faces and edges of the mesh that need to be modified,
11746 // i.e. faces and edges built with one or more duplicated nodes.
11747 // associate these faces or edges to their corresponding domain.
11748 // only the first domain found is kept when a face or edge is shared
11750 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11751 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11752 faceOrEdgeDom.clear();
11755 MESSAGE(".. Modification of elements");
11756 for (int idomain = 0; idomain < theElems.size(); idomain++)
11758 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11759 for (; itnod != nodeDomains.end(); ++itnod)
11761 int oldId = itnod->first;
11762 //MESSAGE(" node " << oldId);
11763 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11764 for (int i = 0; i < l.ncells; i++)
11766 int vtkId = l.cells[i];
11767 int vtkType = grid->GetCellType(vtkId);
11768 int downId = grid->CellIdToDownId(vtkId);
11770 continue; // new cells: not to be modified
11771 DownIdType aCell(downId, vtkType);
11772 int volParents[1000];
11773 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11774 for (int j = 0; j < nbvol; j++)
11775 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11776 if (!feDom.count(vtkId))
11778 feDom[vtkId] = idomain;
11779 faceOrEdgeDom[aCell] = emptyMap;
11780 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11781 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11782 // << " type " << vtkType << " downId " << downId);
11788 // --- iterate on shared faces (volumes to modify, face to extrude)
11789 // get node id's of the face
11790 // replace old nodes by new nodes in volumes, and update inverse connectivity
11792 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11793 for (int m=0; m<3; m++)
11795 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11796 itface = (*amap).begin();
11797 for (; itface != (*amap).end(); ++itface)
11799 DownIdType face = itface->first;
11800 std::set<int> oldNodes;
11801 std::set<int>::iterator itn;
11803 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11804 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11805 std::map<int, int> localClonedNodeIds;
11807 std::map<int, int> domvol = itface->second;
11808 std::map<int, int>::iterator itdom = domvol.begin();
11809 for (; itdom != domvol.end(); ++itdom)
11811 int idom = itdom->first;
11812 int vtkVolId = itdom->second;
11813 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11814 localClonedNodeIds.clear();
11815 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11818 if (nodeDomains[oldId].count(idom))
11820 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11821 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11824 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11829 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11830 grid->BuildLinks();
11838 * \brief Double nodes on some external faces and create flat elements.
11839 * Flat elements are mainly used by some types of mechanic calculations.
11841 * Each group of the list must be constituted of faces.
11842 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11843 * @param theElems - list of groups of faces, where a group of faces is a set of
11844 * SMDS_MeshElements sorted by Id.
11845 * @return TRUE if operation has been completed successfully, FALSE otherwise
11847 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11849 MESSAGE("-------------------------------------------------");
11850 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11851 MESSAGE("-------------------------------------------------");
11853 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11855 // --- For each group of faces
11856 // duplicate the nodes, create a flat element based on the face
11857 // replace the nodes of the faces by their clones
11859 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11860 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11861 clonedNodes.clear();
11862 intermediateNodes.clear();
11863 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11864 mapOfJunctionGroups.clear();
11866 for (int idom = 0; idom < theElems.size(); idom++)
11868 const TIDSortedElemSet& domain = theElems[idom];
11869 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11870 for (; elemItr != domain.end(); ++elemItr)
11872 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11873 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11876 // MESSAGE("aFace=" << aFace->GetID());
11877 bool isQuad = aFace->IsQuadratic();
11878 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11880 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11882 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11883 while (nodeIt->more())
11885 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11886 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11888 ln2.push_back(node);
11890 ln0.push_back(node);
11892 const SMDS_MeshNode* clone = 0;
11893 if (!clonedNodes.count(node))
11895 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11896 clonedNodes[node] = clone;
11899 clone = clonedNodes[node];
11902 ln3.push_back(clone);
11904 ln1.push_back(clone);
11906 const SMDS_MeshNode* inter = 0;
11907 if (isQuad && (!isMedium))
11909 if (!intermediateNodes.count(node))
11911 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11912 intermediateNodes[node] = inter;
11915 inter = intermediateNodes[node];
11916 ln4.push_back(inter);
11920 // --- extrude the face
11922 vector<const SMDS_MeshNode*> ln;
11923 SMDS_MeshVolume* vol = 0;
11924 vtkIdType aType = aFace->GetVtkType();
11928 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11929 // MESSAGE("vol prism " << vol->GetID());
11930 ln.push_back(ln1[0]);
11931 ln.push_back(ln1[1]);
11932 ln.push_back(ln1[2]);
11935 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11936 // MESSAGE("vol hexa " << vol->GetID());
11937 ln.push_back(ln1[0]);
11938 ln.push_back(ln1[1]);
11939 ln.push_back(ln1[2]);
11940 ln.push_back(ln1[3]);
11942 case VTK_QUADRATIC_TRIANGLE:
11943 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11944 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11945 // MESSAGE("vol quad prism " << vol->GetID());
11946 ln.push_back(ln1[0]);
11947 ln.push_back(ln1[1]);
11948 ln.push_back(ln1[2]);
11949 ln.push_back(ln3[0]);
11950 ln.push_back(ln3[1]);
11951 ln.push_back(ln3[2]);
11953 case VTK_QUADRATIC_QUAD:
11954 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11955 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11956 // ln4[0], ln4[1], ln4[2], ln4[3]);
11957 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11958 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11959 ln4[0], ln4[1], ln4[2], ln4[3]);
11960 // MESSAGE("vol quad hexa " << vol->GetID());
11961 ln.push_back(ln1[0]);
11962 ln.push_back(ln1[1]);
11963 ln.push_back(ln1[2]);
11964 ln.push_back(ln1[3]);
11965 ln.push_back(ln3[0]);
11966 ln.push_back(ln3[1]);
11967 ln.push_back(ln3[2]);
11968 ln.push_back(ln3[3]);
11978 stringstream grpname;
11982 string namegrp = grpname.str();
11983 if (!mapOfJunctionGroups.count(namegrp))
11984 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11985 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11987 sgrp->Add(vol->GetID());
11990 // --- modify the face
11992 aFace->ChangeNodes(&ln[0], ln.size());
11999 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12000 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12001 * groups of faces to remove inside the object, (idem edges).
12002 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12004 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12005 const TopoDS_Shape& theShape,
12006 SMESH_NodeSearcher* theNodeSearcher,
12007 const char* groupName,
12008 std::vector<double>& nodesCoords,
12009 std::vector<std::vector<int> >& listOfListOfNodes)
12011 MESSAGE("--------------------------------");
12012 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12013 MESSAGE("--------------------------------");
12015 // --- zone of volumes to remove is given :
12016 // 1 either by a geom shape (one or more vertices) and a radius,
12017 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12018 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12019 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12020 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12021 // defined by it's name.
12023 SMESHDS_GroupBase* groupDS = 0;
12024 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12025 while ( groupIt->more() )
12028 SMESH_Group * group = groupIt->next();
12029 if ( !group ) continue;
12030 groupDS = group->GetGroupDS();
12031 if ( !groupDS || groupDS->IsEmpty() ) continue;
12032 std::string grpName = group->GetName();
12033 //MESSAGE("grpName=" << grpName);
12034 if (grpName == groupName)
12040 bool isNodeGroup = false;
12041 bool isNodeCoords = false;
12044 if (groupDS->GetType() != SMDSAbs_Node)
12046 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12049 if (nodesCoords.size() > 0)
12050 isNodeCoords = true; // a list o nodes given by their coordinates
12051 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12053 // --- define groups to build
12055 int idg; // --- group of SMDS volumes
12056 string grpvName = groupName;
12057 grpvName += "_vol";
12058 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12061 MESSAGE("group not created " << grpvName);
12064 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12066 int idgs; // --- group of SMDS faces on the skin
12067 string grpsName = groupName;
12068 grpsName += "_skin";
12069 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12072 MESSAGE("group not created " << grpsName);
12075 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12077 int idgi; // --- group of SMDS faces internal (several shapes)
12078 string grpiName = groupName;
12079 grpiName += "_internalFaces";
12080 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12083 MESSAGE("group not created " << grpiName);
12086 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12088 int idgei; // --- group of SMDS faces internal (several shapes)
12089 string grpeiName = groupName;
12090 grpeiName += "_internalEdges";
12091 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12094 MESSAGE("group not created " << grpeiName);
12097 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12099 // --- build downward connectivity
12101 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12102 meshDS->BuildDownWardConnectivity(true);
12103 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12105 // --- set of volumes detected inside
12107 std::set<int> setOfInsideVol;
12108 std::set<int> setOfVolToCheck;
12110 std::vector<gp_Pnt> gpnts;
12113 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12115 MESSAGE("group of nodes provided");
12116 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12117 while ( elemIt->more() )
12119 const SMDS_MeshElement* elem = elemIt->next();
12122 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12125 SMDS_MeshElement* vol = 0;
12126 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12127 while (volItr->more())
12129 vol = (SMDS_MeshElement*)volItr->next();
12130 setOfInsideVol.insert(vol->getVtkId());
12131 sgrp->Add(vol->GetID());
12135 else if (isNodeCoords)
12137 MESSAGE("list of nodes coordinates provided");
12140 while (i < nodesCoords.size()-2)
12142 double x = nodesCoords[i++];
12143 double y = nodesCoords[i++];
12144 double z = nodesCoords[i++];
12145 gp_Pnt p = gp_Pnt(x, y ,z);
12146 gpnts.push_back(p);
12147 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12150 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12152 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12153 TopTools_IndexedMapOfShape vertexMap;
12154 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12155 gp_Pnt p = gp_Pnt(0,0,0);
12156 if (vertexMap.Extent() < 1)
12159 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12161 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12162 p = BRep_Tool::Pnt(vertex);
12163 gpnts.push_back(p);
12164 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12168 if (gpnts.size() > 0)
12171 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12173 nodeId = startNode->GetID();
12174 MESSAGE("nodeId " << nodeId);
12176 double radius2 = radius*radius;
12177 MESSAGE("radius2 " << radius2);
12179 // --- volumes on start node
12181 setOfVolToCheck.clear();
12182 SMDS_MeshElement* startVol = 0;
12183 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12184 while (volItr->more())
12186 startVol = (SMDS_MeshElement*)volItr->next();
12187 setOfVolToCheck.insert(startVol->getVtkId());
12189 if (setOfVolToCheck.empty())
12191 MESSAGE("No volumes found");
12195 // --- starting with central volumes then their neighbors, check if they are inside
12196 // or outside the domain, until no more new neighbor volume is inside.
12197 // Fill the group of inside volumes
12199 std::map<int, double> mapOfNodeDistance2;
12200 mapOfNodeDistance2.clear();
12201 std::set<int> setOfOutsideVol;
12202 while (!setOfVolToCheck.empty())
12204 std::set<int>::iterator it = setOfVolToCheck.begin();
12206 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12207 bool volInside = false;
12208 vtkIdType npts = 0;
12209 vtkIdType* pts = 0;
12210 grid->GetCellPoints(vtkId, npts, pts);
12211 for (int i=0; i<npts; i++)
12213 double distance2 = 0;
12214 if (mapOfNodeDistance2.count(pts[i]))
12216 distance2 = mapOfNodeDistance2[pts[i]];
12217 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12221 double *coords = grid->GetPoint(pts[i]);
12222 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12224 for (int j=0; j<gpnts.size(); j++)
12226 double d2 = aPoint.SquareDistance(gpnts[j]);
12227 if (d2 < distance2)
12230 if (distance2 < radius2)
12234 mapOfNodeDistance2[pts[i]] = distance2;
12235 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12237 if (distance2 < radius2)
12239 volInside = true; // one or more nodes inside the domain
12240 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12246 setOfInsideVol.insert(vtkId);
12247 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12248 int neighborsVtkIds[NBMAXNEIGHBORS];
12249 int downIds[NBMAXNEIGHBORS];
12250 unsigned char downTypes[NBMAXNEIGHBORS];
12251 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12252 for (int n = 0; n < nbNeighbors; n++)
12253 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12254 setOfVolToCheck.insert(neighborsVtkIds[n]);
12258 setOfOutsideVol.insert(vtkId);
12259 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12261 setOfVolToCheck.erase(vtkId);
12265 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12266 // If yes, add the volume to the inside set
12268 bool addedInside = true;
12269 std::set<int> setOfVolToReCheck;
12270 while (addedInside)
12272 MESSAGE(" --------------------------- re check");
12273 addedInside = false;
12274 std::set<int>::iterator itv = setOfInsideVol.begin();
12275 for (; itv != setOfInsideVol.end(); ++itv)
12278 int neighborsVtkIds[NBMAXNEIGHBORS];
12279 int downIds[NBMAXNEIGHBORS];
12280 unsigned char downTypes[NBMAXNEIGHBORS];
12281 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12282 for (int n = 0; n < nbNeighbors; n++)
12283 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12284 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12286 setOfVolToCheck = setOfVolToReCheck;
12287 setOfVolToReCheck.clear();
12288 while (!setOfVolToCheck.empty())
12290 std::set<int>::iterator it = setOfVolToCheck.begin();
12292 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12294 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12295 int countInside = 0;
12296 int neighborsVtkIds[NBMAXNEIGHBORS];
12297 int downIds[NBMAXNEIGHBORS];
12298 unsigned char downTypes[NBMAXNEIGHBORS];
12299 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12300 for (int n = 0; n < nbNeighbors; n++)
12301 if (setOfInsideVol.count(neighborsVtkIds[n]))
12303 MESSAGE("countInside " << countInside);
12304 if (countInside > 1)
12306 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12307 setOfInsideVol.insert(vtkId);
12308 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12309 addedInside = true;
12312 setOfVolToReCheck.insert(vtkId);
12314 setOfVolToCheck.erase(vtkId);
12318 // --- map of Downward faces at the boundary, inside the global volume
12319 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12320 // fill group of SMDS faces inside the volume (when several volume shapes)
12321 // fill group of SMDS faces on the skin of the global volume (if skin)
12323 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12324 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12325 std::set<int>::iterator it = setOfInsideVol.begin();
12326 for (; it != setOfInsideVol.end(); ++it)
12329 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12330 int neighborsVtkIds[NBMAXNEIGHBORS];
12331 int downIds[NBMAXNEIGHBORS];
12332 unsigned char downTypes[NBMAXNEIGHBORS];
12333 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12334 for (int n = 0; n < nbNeighbors; n++)
12336 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12337 if (neighborDim == 3)
12339 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12341 DownIdType face(downIds[n], downTypes[n]);
12342 boundaryFaces[face] = vtkId;
12344 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12345 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12346 if (vtkFaceId >= 0)
12348 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12349 // find also the smds edges on this face
12350 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12351 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12352 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12353 for (int i = 0; i < nbEdges; i++)
12355 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12356 if (vtkEdgeId >= 0)
12357 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12361 else if (neighborDim == 2) // skin of the volume
12363 DownIdType face(downIds[n], downTypes[n]);
12364 skinFaces[face] = vtkId;
12365 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12366 if (vtkFaceId >= 0)
12367 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12372 // --- identify the edges constituting the wire of each subshape on the skin
12373 // define polylines with the nodes of edges, equivalent to wires
12374 // project polylines on subshapes, and partition, to get geom faces
12376 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12377 std::set<int> emptySet;
12379 std::set<int> shapeIds;
12381 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12382 while (itelem->more())
12384 const SMDS_MeshElement *elem = itelem->next();
12385 int shapeId = elem->getshapeId();
12386 int vtkId = elem->getVtkId();
12387 if (!shapeIdToVtkIdSet.count(shapeId))
12389 shapeIdToVtkIdSet[shapeId] = emptySet;
12390 shapeIds.insert(shapeId);
12392 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12395 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12396 std::set<DownIdType, DownIdCompare> emptyEdges;
12397 emptyEdges.clear();
12399 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12400 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12402 int shapeId = itShape->first;
12403 MESSAGE(" --- Shape ID --- "<< shapeId);
12404 shapeIdToEdges[shapeId] = emptyEdges;
12406 std::vector<int> nodesEdges;
12408 std::set<int>::iterator its = itShape->second.begin();
12409 for (; its != itShape->second.end(); ++its)
12412 MESSAGE(" " << vtkId);
12413 int neighborsVtkIds[NBMAXNEIGHBORS];
12414 int downIds[NBMAXNEIGHBORS];
12415 unsigned char downTypes[NBMAXNEIGHBORS];
12416 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12417 for (int n = 0; n < nbNeighbors; n++)
12419 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12421 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12422 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12423 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12425 DownIdType edge(downIds[n], downTypes[n]);
12426 if (!shapeIdToEdges[shapeId].count(edge))
12428 shapeIdToEdges[shapeId].insert(edge);
12430 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12431 nodesEdges.push_back(vtkNodeId[0]);
12432 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12433 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12439 std::list<int> order;
12441 if (nodesEdges.size() > 0)
12443 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12444 nodesEdges[0] = -1;
12445 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12446 nodesEdges[1] = -1; // do not reuse this edge
12450 int nodeTofind = order.back(); // try first to push back
12452 for (i = 0; i<nodesEdges.size(); i++)
12453 if (nodesEdges[i] == nodeTofind)
12455 if (i == nodesEdges.size())
12456 found = false; // no follower found on back
12459 if (i%2) // odd ==> use the previous one
12460 if (nodesEdges[i-1] < 0)
12464 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12465 nodesEdges[i-1] = -1;
12467 else // even ==> use the next one
12468 if (nodesEdges[i+1] < 0)
12472 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12473 nodesEdges[i+1] = -1;
12478 // try to push front
12480 nodeTofind = order.front(); // try to push front
12481 for (i = 0; i<nodesEdges.size(); i++)
12482 if (nodesEdges[i] == nodeTofind)
12484 if (i == nodesEdges.size())
12486 found = false; // no predecessor found on front
12489 if (i%2) // odd ==> use the previous one
12490 if (nodesEdges[i-1] < 0)
12494 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12495 nodesEdges[i-1] = -1;
12497 else // even ==> use the next one
12498 if (nodesEdges[i+1] < 0)
12502 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12503 nodesEdges[i+1] = -1;
12509 std::vector<int> nodes;
12510 nodes.push_back(shapeId);
12511 std::list<int>::iterator itl = order.begin();
12512 for (; itl != order.end(); itl++)
12514 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12515 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12517 listOfListOfNodes.push_back(nodes);
12520 // partition geom faces with blocFissure
12521 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12522 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12528 //================================================================================
12530 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12531 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12532 * \return TRUE if operation has been completed successfully, FALSE otherwise
12534 //================================================================================
12536 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12538 // iterates on volume elements and detect all free faces on them
12539 SMESHDS_Mesh* aMesh = GetMeshDS();
12542 //bool res = false;
12543 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12544 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12547 const SMDS_MeshVolume* volume = vIt->next();
12548 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12549 vTool.SetExternalNormal();
12550 //const bool isPoly = volume->IsPoly();
12551 const int iQuad = volume->IsQuadratic();
12552 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12554 if (!vTool.IsFreeFace(iface))
12557 vector<const SMDS_MeshNode *> nodes;
12558 int nbFaceNodes = vTool.NbFaceNodes(iface);
12559 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12561 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12562 nodes.push_back(faceNodes[inode]);
12563 if (iQuad) { // add medium nodes
12564 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12565 nodes.push_back(faceNodes[inode]);
12566 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12567 nodes.push_back(faceNodes[8]);
12569 // add new face based on volume nodes
12570 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12572 continue; // face already exsist
12574 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12578 return ( nbFree==(nbExisted+nbCreated) );
12583 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12585 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12587 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12590 //================================================================================
12592 * \brief Creates missing boundary elements
12593 * \param elements - elements whose boundary is to be checked
12594 * \param dimension - defines type of boundary elements to create
12595 * \param group - a group to store created boundary elements in
12596 * \param targetMesh - a mesh to store created boundary elements in
12597 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12598 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12599 * boundary elements will be copied into the targetMesh
12600 * \param toAddExistingBondary - if true, not only new but also pre-existing
12601 * boundary elements will be added into the new group
12602 * \param aroundElements - if true, elements will be created on boundary of given
12603 * elements else, on boundary of the whole mesh.
12604 * \return nb of added boundary elements
12606 //================================================================================
12608 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12609 Bnd_Dimension dimension,
12610 SMESH_Group* group/*=0*/,
12611 SMESH_Mesh* targetMesh/*=0*/,
12612 bool toCopyElements/*=false*/,
12613 bool toCopyExistingBoundary/*=false*/,
12614 bool toAddExistingBondary/*= false*/,
12615 bool aroundElements/*= false*/)
12617 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12618 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12619 // hope that all elements are of the same type, do not check them all
12620 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12621 throw SALOME_Exception(LOCALIZED("wrong element type"));
12624 toCopyElements = toCopyExistingBoundary = false;
12626 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12627 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12628 int nbAddedBnd = 0;
12630 // editor adding present bnd elements and optionally holding elements to add to the group
12631 SMESH_MeshEditor* presentEditor;
12632 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12633 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12635 SMESH_MesherHelper helper( *myMesh );
12636 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12637 SMDS_VolumeTool vTool;
12638 TIDSortedElemSet avoidSet;
12639 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12642 typedef vector<const SMDS_MeshNode*> TConnectivity;
12644 SMDS_ElemIteratorPtr eIt;
12645 if (elements.empty())
12646 eIt = aMesh->elementsIterator(elemType);
12648 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12650 while (eIt->more())
12652 const SMDS_MeshElement* elem = eIt->next();
12653 const int iQuad = elem->IsQuadratic();
12655 // ------------------------------------------------------------------------------------
12656 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12657 // ------------------------------------------------------------------------------------
12658 vector<const SMDS_MeshElement*> presentBndElems;
12659 vector<TConnectivity> missingBndElems;
12660 TConnectivity nodes;
12661 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12663 vTool.SetExternalNormal();
12664 const SMDS_MeshElement* otherVol = 0;
12665 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12667 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12668 ( !aroundElements || elements.count( otherVol )))
12670 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12671 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12672 if ( missType == SMDSAbs_Edge ) // boundary edges
12674 nodes.resize( 2+iQuad );
12675 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12677 for ( int j = 0; j < nodes.size(); ++j )
12679 if ( const SMDS_MeshElement* edge =
12680 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12681 presentBndElems.push_back( edge );
12683 missingBndElems.push_back( nodes );
12686 else // boundary face
12689 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12690 nodes.push_back( nn[inode] );
12691 if (iQuad) // add medium nodes
12692 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12693 nodes.push_back( nn[inode] );
12694 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12696 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12698 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12699 SMDSAbs_Face, /*noMedium=*/false ))
12700 presentBndElems.push_back( f );
12702 missingBndElems.push_back( nodes );
12704 if ( targetMesh != myMesh )
12706 // add 1D elements on face boundary to be added to a new mesh
12707 const SMDS_MeshElement* edge;
12708 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12711 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12713 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12714 if ( edge && avoidSet.insert( edge ).second )
12715 presentBndElems.push_back( edge );
12721 else // elem is a face ------------------------------------------
12723 avoidSet.clear(), avoidSet.insert( elem );
12724 int nbNodes = elem->NbCornerNodes();
12725 nodes.resize( 2 /*+ iQuad*/);
12726 for ( int i = 0; i < nbNodes; i++ )
12728 nodes[0] = elem->GetNode(i);
12729 nodes[1] = elem->GetNode((i+1)%nbNodes);
12730 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12731 continue; // not free link
12734 //nodes[2] = elem->GetNode( i + nbNodes );
12735 if ( const SMDS_MeshElement* edge =
12736 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12737 presentBndElems.push_back( edge );
12739 missingBndElems.push_back( nodes );
12743 // ---------------------------------
12744 // 2. Add missing boundary elements
12745 // ---------------------------------
12746 if ( targetMesh != myMesh )
12747 // instead of making a map of nodes in this mesh and targetMesh,
12748 // we create nodes with same IDs.
12749 for ( int i = 0; i < missingBndElems.size(); ++i )
12751 TConnectivity& srcNodes = missingBndElems[i];
12752 TConnectivity nodes( srcNodes.size() );
12753 for ( inode = 0; inode < nodes.size(); ++inode )
12754 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12755 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12757 /*noMedium=*/false))
12759 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12763 for ( int i = 0; i < missingBndElems.size(); ++i )
12765 TConnectivity& nodes = missingBndElems[i];
12766 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12768 /*noMedium=*/false))
12770 SMDS_MeshElement* elem =
12771 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12774 // try to set a new element to a shape
12775 if ( myMesh->HasShapeToMesh() )
12778 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12779 const int nbN = nodes.size() / (iQuad+1 );
12780 for ( inode = 0; inode < nbN && ok; ++inode )
12782 pair<int, TopAbs_ShapeEnum> i_stype =
12783 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12784 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12785 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12787 if ( ok && mediumShapes.size() > 1 )
12789 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12790 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12791 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12793 if (( ok = ( stype_i->first != stype_i_0.first )))
12794 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12795 aMesh->IndexToShape( stype_i_0.second ));
12798 if ( ok && mediumShapes.begin()->first == missShapeType )
12799 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12803 // ----------------------------------
12804 // 3. Copy present boundary elements
12805 // ----------------------------------
12806 if ( toCopyExistingBoundary )
12807 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12809 const SMDS_MeshElement* e = presentBndElems[i];
12810 TConnectivity nodes( e->NbNodes() );
12811 for ( inode = 0; inode < nodes.size(); ++inode )
12812 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12813 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12815 else // store present elements to add them to a group
12816 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12818 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12821 } // loop on given elements
12823 // ---------------------------------------------
12824 // 4. Fill group with boundary elements
12825 // ---------------------------------------------
12828 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12829 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12830 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12832 tgtEditor.myLastCreatedElems.Clear();
12833 tgtEditor2.myLastCreatedElems.Clear();
12835 // -----------------------
12836 // 5. Copy given elements
12837 // -----------------------
12838 if ( toCopyElements && targetMesh != myMesh )
12840 if (elements.empty())
12841 eIt = aMesh->elementsIterator(elemType);
12843 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12844 while (eIt->more())
12846 const SMDS_MeshElement* elem = eIt->next();
12847 TConnectivity nodes( elem->NbNodes() );
12848 for ( inode = 0; inode < nodes.size(); ++inode )
12849 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12850 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12852 tgtEditor.myLastCreatedElems.Clear();