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 // there must be a top element
6069 const SMDS_MeshElement* topElem = 0;
6072 topElem = resultElems.back();
6073 resultElems.pop_back();
6077 list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6078 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6079 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6081 topElem = *resElemIt;
6082 resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6087 // add resultElems to groups originted from ones the sourceElem belongs to
6088 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6089 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6091 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6092 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6094 // fill in a new group
6095 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6096 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6097 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6098 newGroup.Add( *resElemIt );
6100 // fill a "top" group
6103 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6104 newTopGroup.Add( topElem );
6108 } // loop on created elements
6109 }// loop on nodes and elements
6111 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6113 list<int> topGrouIds;
6114 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6116 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6117 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6118 orderedOldNewGroups[i]->get<2>() };
6119 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6120 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6122 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6123 if ( newGroupDS->IsEmpty() )
6125 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6130 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6133 const bool isTop = ( nbNewGroups == 2 &&
6134 newGroupDS->GetType() == oldGroupDS->GetType() &&
6137 string name = oldGroupDS->GetStoreName();
6138 if ( !targetMesh ) {
6139 string suffix = ( isTop ? "top": postfix.c_str() );
6143 while ( !groupNames.insert( name ).second ) // name exists
6144 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6149 newGroupDS->SetStoreName( name.c_str() );
6151 // make a SMESH_Groups
6152 mesh->AddGroup( newGroupDS );
6154 topGrouIds.push_back( newGroupDS->GetID() );
6156 newGroupIDs->push_back( newGroupDS->GetID() );
6160 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6165 //================================================================================
6167 * \brief Return list of group of nodes close to each other within theTolerance
6168 * Search among theNodes or in the whole mesh if theNodes is empty using
6169 * an Octree algorithm
6171 //================================================================================
6173 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6174 const double theTolerance,
6175 TListOfListOfNodes & theGroupsOfNodes)
6177 myLastCreatedElems.Clear();
6178 myLastCreatedNodes.Clear();
6180 if ( theNodes.empty() )
6181 { // get all nodes in the mesh
6182 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6183 while ( nIt->more() )
6184 theNodes.insert( theNodes.end(),nIt->next());
6187 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6191 //=======================================================================
6193 * \brief Implementation of search for the node closest to point
6195 //=======================================================================
6197 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6199 //---------------------------------------------------------------------
6201 * \brief Constructor
6203 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6205 myMesh = ( SMESHDS_Mesh* ) theMesh;
6207 TIDSortedNodeSet nodes;
6209 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6210 while ( nIt->more() )
6211 nodes.insert( nodes.end(), nIt->next() );
6213 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6215 // get max size of a leaf box
6216 SMESH_OctreeNode* tree = myOctreeNode;
6217 while ( !tree->isLeaf() )
6219 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6223 myHalfLeafSize = tree->maxSize() / 2.;
6226 //---------------------------------------------------------------------
6228 * \brief Move node and update myOctreeNode accordingly
6230 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6232 myOctreeNode->UpdateByMoveNode( node, toPnt );
6233 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6236 //---------------------------------------------------------------------
6238 * \brief Do it's job
6240 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6242 map<double, const SMDS_MeshNode*> dist2Nodes;
6243 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6244 if ( !dist2Nodes.empty() )
6245 return dist2Nodes.begin()->second;
6246 list<const SMDS_MeshNode*> nodes;
6247 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6249 double minSqDist = DBL_MAX;
6250 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6252 // sort leafs by their distance from thePnt
6253 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6254 TDistTreeMap treeMap;
6255 list< SMESH_OctreeNode* > treeList;
6256 list< SMESH_OctreeNode* >::iterator trIt;
6257 treeList.push_back( myOctreeNode );
6259 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6260 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6261 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6263 SMESH_OctreeNode* tree = *trIt;
6264 if ( !tree->isLeaf() ) // put children to the queue
6266 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6267 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6268 while ( cIt->more() )
6269 treeList.push_back( cIt->next() );
6271 else if ( tree->NbNodes() ) // put a tree to the treeMap
6273 const Bnd_B3d& box = *tree->getBox();
6274 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6275 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6276 if ( !it_in.second ) // not unique distance to box center
6277 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6280 // find distance after which there is no sense to check tree's
6281 double sqLimit = DBL_MAX;
6282 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6283 if ( treeMap.size() > 5 ) {
6284 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6285 const Bnd_B3d& box = *closestTree->getBox();
6286 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6287 sqLimit = limit * limit;
6289 // get all nodes from trees
6290 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6291 if ( sqDist_tree->first > sqLimit )
6293 SMESH_OctreeNode* tree = sqDist_tree->second;
6294 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6297 // find closest among nodes
6298 minSqDist = DBL_MAX;
6299 const SMDS_MeshNode* closestNode = 0;
6300 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6301 for ( ; nIt != nodes.end(); ++nIt ) {
6302 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6303 if ( minSqDist > sqDist ) {
6311 //---------------------------------------------------------------------
6315 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6317 //---------------------------------------------------------------------
6319 * \brief Return the node tree
6321 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6324 SMESH_OctreeNode* myOctreeNode;
6325 SMESHDS_Mesh* myMesh;
6326 double myHalfLeafSize; // max size of a leaf box
6329 //=======================================================================
6331 * \brief Return SMESH_NodeSearcher
6333 //=======================================================================
6335 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6337 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6340 // ========================================================================
6341 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6343 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6344 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6345 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6347 //=======================================================================
6349 * \brief Octal tree of bounding boxes of elements
6351 //=======================================================================
6353 class ElementBndBoxTree : public SMESH_Octree
6357 ElementBndBoxTree(const SMDS_Mesh& mesh,
6358 SMDSAbs_ElementType elemType,
6359 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6360 double tolerance = NodeRadius );
6361 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6362 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6363 void getElementsInSphere ( const gp_XYZ& center,
6364 const double radius, TIDSortedElemSet& foundElems);
6365 size_t getSize() { return std::max( _size, _elements.size() ); }
6366 ~ElementBndBoxTree();
6369 ElementBndBoxTree():_size(0) {}
6370 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6371 void buildChildrenData();
6372 Bnd_B3d* buildRootBox();
6374 //!< Bounding box of element
6375 struct ElementBox : public Bnd_B3d
6377 const SMDS_MeshElement* _element;
6378 int _refCount; // an ElementBox can be included in several tree branches
6379 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6381 vector< ElementBox* > _elements;
6385 //================================================================================
6387 * \brief ElementBndBoxTree creation
6389 //================================================================================
6391 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6392 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6394 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6395 _elements.reserve( nbElems );
6397 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6398 while ( elemIt->more() )
6399 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6404 //================================================================================
6408 //================================================================================
6410 ElementBndBoxTree::~ElementBndBoxTree()
6412 for ( int i = 0; i < _elements.size(); ++i )
6413 if ( --_elements[i]->_refCount <= 0 )
6414 delete _elements[i];
6417 //================================================================================
6419 * \brief Return the maximal box
6421 //================================================================================
6423 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6425 Bnd_B3d* box = new Bnd_B3d;
6426 for ( int i = 0; i < _elements.size(); ++i )
6427 box->Add( *_elements[i] );
6431 //================================================================================
6433 * \brief Redistrubute element boxes among children
6435 //================================================================================
6437 void ElementBndBoxTree::buildChildrenData()
6439 for ( int i = 0; i < _elements.size(); ++i )
6441 for (int j = 0; j < 8; j++)
6443 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6445 _elements[i]->_refCount++;
6446 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6449 _elements[i]->_refCount--;
6451 _size = _elements.size();
6452 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6454 for (int j = 0; j < 8; j++)
6456 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6457 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6458 child->myIsLeaf = true;
6460 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6461 SMESHUtils::CompactVector( child->_elements );
6465 //================================================================================
6467 * \brief Return elements which can include the point
6469 //================================================================================
6471 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6472 TIDSortedElemSet& foundElems)
6474 if ( getBox()->IsOut( point.XYZ() ))
6479 for ( int i = 0; i < _elements.size(); ++i )
6480 if ( !_elements[i]->IsOut( point.XYZ() ))
6481 foundElems.insert( _elements[i]->_element );
6485 for (int i = 0; i < 8; i++)
6486 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6490 //================================================================================
6492 * \brief Return elements which can be intersected by the line
6494 //================================================================================
6496 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6497 TIDSortedElemSet& foundElems)
6499 if ( getBox()->IsOut( line ))
6504 for ( int i = 0; i < _elements.size(); ++i )
6505 if ( !_elements[i]->IsOut( line ))
6506 foundElems.insert( _elements[i]->_element );
6510 for (int i = 0; i < 8; i++)
6511 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6515 //================================================================================
6517 * \brief Return elements from leaves intersecting the sphere
6519 //================================================================================
6521 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6522 const double radius,
6523 TIDSortedElemSet& foundElems)
6525 if ( getBox()->IsOut( center, radius ))
6530 for ( int i = 0; i < _elements.size(); ++i )
6531 if ( !_elements[i]->IsOut( center, radius ))
6532 foundElems.insert( _elements[i]->_element );
6536 for (int i = 0; i < 8; i++)
6537 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6541 //================================================================================
6543 * \brief Construct the element box
6545 //================================================================================
6547 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6551 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6552 while ( nIt->more() )
6553 Add( SMESH_TNodeXYZ( nIt->next() ));
6554 Enlarge( tolerance );
6559 //=======================================================================
6561 * \brief Implementation of search for the elements by point and
6562 * of classification of point in 2D mesh
6564 //=======================================================================
6566 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6568 SMESHDS_Mesh* _mesh;
6569 SMDS_ElemIteratorPtr _meshPartIt;
6570 ElementBndBoxTree* _ebbTree;
6571 SMESH_NodeSearcherImpl* _nodeSearcher;
6572 SMDSAbs_ElementType _elementType;
6574 bool _outerFacesFound;
6575 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6577 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6578 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6579 ~SMESH_ElementSearcherImpl()
6581 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6582 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6584 virtual int FindElementsByPoint(const gp_Pnt& point,
6585 SMDSAbs_ElementType type,
6586 vector< const SMDS_MeshElement* >& foundElements);
6587 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6588 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6589 SMDSAbs_ElementType type );
6591 void GetElementsNearLine( const gp_Ax1& line,
6592 SMDSAbs_ElementType type,
6593 vector< const SMDS_MeshElement* >& foundElems);
6594 double getTolerance();
6595 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6596 const double tolerance, double & param);
6597 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6598 bool isOuterBoundary(const SMDS_MeshElement* face) const
6600 return _outerFaces.empty() || _outerFaces.count(face);
6602 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6604 const SMDS_MeshElement* _face;
6606 bool _coincides; //!< the line lays in face plane
6607 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6608 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6610 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6613 TIDSortedElemSet _faces;
6614 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6615 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6619 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6621 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6622 << ", _coincides="<<i._coincides << ")";
6625 //=======================================================================
6627 * \brief define tolerance for search
6629 //=======================================================================
6631 double SMESH_ElementSearcherImpl::getTolerance()
6633 if ( _tolerance < 0 )
6635 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6638 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6640 double boxSize = _nodeSearcher->getTree()->maxSize();
6641 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6643 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6645 double boxSize = _ebbTree->maxSize();
6646 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6648 if ( _tolerance == 0 )
6650 // define tolerance by size of a most complex element
6651 int complexType = SMDSAbs_Volume;
6652 while ( complexType > SMDSAbs_All &&
6653 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6655 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6657 if ( complexType == int( SMDSAbs_Node ))
6659 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6661 if ( meshInfo.NbNodes() > 2 )
6662 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6666 SMDS_ElemIteratorPtr elemIt =
6667 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6668 const SMDS_MeshElement* elem = elemIt->next();
6669 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6670 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6672 while ( nodeIt->more() )
6674 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6675 elemSize = max( dist, elemSize );
6678 _tolerance = 1e-4 * elemSize;
6684 //================================================================================
6686 * \brief Find intersection of the line and an edge of face and return parameter on line
6688 //================================================================================
6690 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6691 const SMDS_MeshElement* face,
6698 GeomAPI_ExtremaCurveCurve anExtCC;
6699 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6701 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6702 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6704 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6705 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6706 anExtCC.Init( lineCurve, edge);
6707 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6709 Quantity_Parameter pl, pe;
6710 anExtCC.LowerDistanceParameters( pl, pe );
6712 if ( ++nbInts == 2 )
6716 if ( nbInts > 0 ) param /= nbInts;
6719 //================================================================================
6721 * \brief Find all faces belonging to the outer boundary of mesh
6723 //================================================================================
6725 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6727 if ( _outerFacesFound ) return;
6729 // Collect all outer faces by passing from one outer face to another via their links
6730 // and BTW find out if there are internal faces at all.
6732 // checked links and links where outer boundary meets internal one
6733 set< SMESH_TLink > visitedLinks, seamLinks;
6735 // links to treat with already visited faces sharing them
6736 list < TFaceLink > startLinks;
6738 // load startLinks with the first outerFace
6739 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6740 _outerFaces.insert( outerFace );
6742 TIDSortedElemSet emptySet;
6743 while ( !startLinks.empty() )
6745 const SMESH_TLink& link = startLinks.front()._link;
6746 TIDSortedElemSet& faces = startLinks.front()._faces;
6748 outerFace = *faces.begin();
6749 // find other faces sharing the link
6750 const SMDS_MeshElement* f;
6751 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6754 // select another outer face among the found
6755 const SMDS_MeshElement* outerFace2 = 0;
6756 if ( faces.size() == 2 )
6758 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6760 else if ( faces.size() > 2 )
6762 seamLinks.insert( link );
6764 // link direction within the outerFace
6765 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6766 SMESH_TNodeXYZ( link.node2()));
6767 int i1 = outerFace->GetNodeIndex( link.node1() );
6768 int i2 = outerFace->GetNodeIndex( link.node2() );
6769 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6770 if ( rev ) n1n2.Reverse();
6772 gp_XYZ ofNorm, fNorm;
6773 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6775 // direction from the link inside outerFace
6776 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6777 // sort all other faces by angle with the dirInOF
6778 map< double, const SMDS_MeshElement* > angle2Face;
6779 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6780 for ( ; face != faces.end(); ++face )
6782 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6784 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6785 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6786 if ( angle < 0 ) angle += 2. * M_PI;
6787 angle2Face.insert( make_pair( angle, *face ));
6789 if ( !angle2Face.empty() )
6790 outerFace2 = angle2Face.begin()->second;
6793 // store the found outer face and add its links to continue seaching from
6796 _outerFaces.insert( outerFace );
6797 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6798 for ( int i = 0; i < nbNodes; ++i )
6800 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6801 if ( visitedLinks.insert( link2 ).second )
6802 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6805 startLinks.pop_front();
6807 _outerFacesFound = true;
6809 if ( !seamLinks.empty() )
6811 // There are internal boundaries touching the outher one,
6812 // find all faces of internal boundaries in order to find
6813 // faces of boundaries of holes, if any.
6818 _outerFaces.clear();
6822 //=======================================================================
6824 * \brief Find elements of given type where the given point is IN or ON.
6825 * Returns nb of found elements and elements them-selves.
6827 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6829 //=======================================================================
6831 int SMESH_ElementSearcherImpl::
6832 FindElementsByPoint(const gp_Pnt& point,
6833 SMDSAbs_ElementType type,
6834 vector< const SMDS_MeshElement* >& foundElements)
6836 foundElements.clear();
6838 double tolerance = getTolerance();
6840 // =================================================================================
6841 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6843 if ( !_nodeSearcher )
6844 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6846 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6847 if ( !closeNode ) return foundElements.size();
6849 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6850 return foundElements.size(); // to far from any node
6852 if ( type == SMDSAbs_Node )
6854 foundElements.push_back( closeNode );
6858 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6859 while ( elemIt->more() )
6860 foundElements.push_back( elemIt->next() );
6863 // =================================================================================
6864 else // elements more complex than 0D
6866 if ( !_ebbTree || _elementType != type )
6868 if ( _ebbTree ) delete _ebbTree;
6869 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6871 TIDSortedElemSet suspectElems;
6872 _ebbTree->getElementsNearPoint( point, suspectElems );
6873 TIDSortedElemSet::iterator elem = suspectElems.begin();
6874 for ( ; elem != suspectElems.end(); ++elem )
6875 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6876 foundElements.push_back( *elem );
6878 return foundElements.size();
6881 //=======================================================================
6883 * \brief Find an element of given type most close to the given point
6885 * WARNING: Only face search is implemeneted so far
6887 //=======================================================================
6889 const SMDS_MeshElement*
6890 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6891 SMDSAbs_ElementType type )
6893 const SMDS_MeshElement* closestElem = 0;
6895 if ( type == SMDSAbs_Face )
6897 if ( !_ebbTree || _elementType != type )
6899 if ( _ebbTree ) delete _ebbTree;
6900 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6902 TIDSortedElemSet suspectElems;
6903 _ebbTree->getElementsNearPoint( point, suspectElems );
6905 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6907 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6908 _ebbTree->getBox()->CornerMax() );
6910 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6911 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6913 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6914 while ( suspectElems.empty() )
6916 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6920 double minDist = std::numeric_limits<double>::max();
6921 multimap< double, const SMDS_MeshElement* > dist2face;
6922 TIDSortedElemSet::iterator elem = suspectElems.begin();
6923 for ( ; elem != suspectElems.end(); ++elem )
6925 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6927 if ( dist < minDist + 1e-10)
6930 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6933 if ( !dist2face.empty() )
6935 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6936 closestElem = d2f->second;
6937 // if there are several elements at the same distance, select one
6938 // with GC closest to the point
6939 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6940 double minDistToGC = 0;
6941 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6943 if ( minDistToGC == 0 )
6946 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6947 TXyzIterator(), gc ) / closestElem->NbNodes();
6948 minDistToGC = point.SquareDistance( gc );
6951 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6952 TXyzIterator(), gc ) / d2f->second->NbNodes();
6953 double d = point.SquareDistance( gc );
6954 if ( d < minDistToGC )
6957 closestElem = d2f->second;
6960 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6961 // <<closestElem->GetID() << " DIST " << minDist << endl;
6966 // NOT IMPLEMENTED SO FAR
6972 //================================================================================
6974 * \brief Classify the given point in the closed 2D mesh
6976 //================================================================================
6978 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6980 double tolerance = getTolerance();
6981 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6983 if ( _ebbTree ) delete _ebbTree;
6984 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6986 // Algo: analyse transition of a line starting at the point through mesh boundary;
6987 // try three lines parallel to axis of the coordinate system and perform rough
6988 // analysis. If solution is not clear perform thorough analysis.
6990 const int nbAxes = 3;
6991 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6992 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6993 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6994 multimap< int, int > nbInt2Axis; // to find the simplest case
6995 for ( int axis = 0; axis < nbAxes; ++axis )
6997 gp_Ax1 lineAxis( point, axisDir[axis]);
6998 gp_Lin line ( lineAxis );
7000 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
7001 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
7003 // Intersect faces with the line
7005 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7006 TIDSortedElemSet::iterator face = suspectFaces.begin();
7007 for ( ; face != suspectFaces.end(); ++face )
7011 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
7012 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
7014 // perform intersection
7015 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
7016 if ( !intersection.IsDone() )
7018 if ( intersection.IsInQuadric() )
7020 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7022 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7024 gp_Pnt intersectionPoint = intersection.Point(1);
7025 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7026 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7029 // Analyse intersections roughly
7031 int nbInter = u2inters.size();
7035 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7036 if ( nbInter == 1 ) // not closed mesh
7037 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7039 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7042 if ( (f<0) == (l<0) )
7045 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7046 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
7047 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7050 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7052 if ( _outerFacesFound ) break; // pass to thorough analysis
7054 } // three attempts - loop on CS axes
7056 // Analyse intersections thoroughly.
7057 // We make two loops maximum, on the first one we only exclude touching intersections,
7058 // on the second, if situation is still unclear, we gather and use information on
7059 // position of faces (internal or outer). If faces position is already gathered,
7060 // we make the second loop right away.
7062 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7064 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7065 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7067 int axis = nb_axis->second;
7068 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7070 gp_Ax1 lineAxis( point, axisDir[axis]);
7071 gp_Lin line ( lineAxis );
7073 // add tangent intersections to u2inters
7075 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7076 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7077 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7078 u2inters.insert(make_pair( param, *tgtInt ));
7079 tangentInters[ axis ].clear();
7081 // Count intersections before and after the point excluding touching ones.
7082 // If hasPositionInfo we count intersections of outer boundary only
7084 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7085 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7086 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7087 bool ok = ! u_int1->second._coincides;
7088 while ( ok && u_int1 != u2inters.end() )
7090 double u = u_int1->first;
7091 bool touchingInt = false;
7092 if ( ++u_int2 != u2inters.end() )
7094 // skip intersections at the same point (if the line passes through edge or node)
7096 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7102 // skip tangent intersections
7104 const SMDS_MeshElement* prevFace = u_int1->second._face;
7105 while ( ok && u_int2->second._coincides )
7107 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7113 ok = ( u_int2 != u2inters.end() );
7118 // skip intersections at the same point after tangent intersections
7121 double u2 = u_int2->first;
7123 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7129 // decide if we skipped a touching intersection
7130 if ( nbSamePnt + nbTgt > 0 )
7132 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7133 map< double, TInters >::iterator u_int = u_int1;
7134 for ( ; u_int != u_int2; ++u_int )
7136 if ( u_int->second._coincides ) continue;
7137 double dot = u_int->second._faceNorm * line.Direction();
7138 if ( dot > maxDot ) maxDot = dot;
7139 if ( dot < minDot ) minDot = dot;
7141 touchingInt = ( minDot*maxDot < 0 );
7146 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7157 u_int1 = u_int2; // to next intersection
7159 } // loop on intersections with one line
7163 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7166 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7169 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7170 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7172 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7175 if ( (f<0) == (l<0) )
7178 if ( hasPositionInfo )
7179 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7181 } // loop on intersections of the tree lines - thorough analysis
7183 if ( !hasPositionInfo )
7185 // gather info on faces position - is face in the outer boundary or not
7186 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7187 findOuterBoundary( u2inters.begin()->second._face );
7190 } // two attempts - with and w/o faces position info in the mesh
7192 return TopAbs_UNKNOWN;
7195 //=======================================================================
7197 * \brief Return elements possibly intersecting the line
7199 //=======================================================================
7201 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7202 SMDSAbs_ElementType type,
7203 vector< const SMDS_MeshElement* >& foundElems)
7205 if ( !_ebbTree || _elementType != type )
7207 if ( _ebbTree ) delete _ebbTree;
7208 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7210 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7211 _ebbTree->getElementsNearLine( line, suspectFaces );
7212 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7215 //=======================================================================
7217 * \brief Return SMESH_ElementSearcher
7219 //=======================================================================
7221 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7223 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7226 //=======================================================================
7228 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7230 //=======================================================================
7232 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7234 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7237 //=======================================================================
7239 * \brief Return true if the point is IN or ON of the element
7241 //=======================================================================
7243 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7245 if ( element->GetType() == SMDSAbs_Volume)
7247 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7250 // get ordered nodes
7252 vector< gp_XYZ > xyz;
7253 vector<const SMDS_MeshNode*> nodeList;
7255 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7256 if ( element->IsQuadratic() ) {
7257 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7258 nodeIt = f->interlacedNodesElemIterator();
7259 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7260 nodeIt = e->interlacedNodesElemIterator();
7262 while ( nodeIt->more() )
7264 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7265 xyz.push_back( SMESH_TNodeXYZ(node) );
7266 nodeList.push_back(node);
7269 int i, nbNodes = element->NbNodes();
7271 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7273 // compute face normal
7274 gp_Vec faceNorm(0,0,0);
7275 xyz.push_back( xyz.front() );
7276 nodeList.push_back( nodeList.front() );
7277 for ( i = 0; i < nbNodes; ++i )
7279 gp_Vec edge1( xyz[i+1], xyz[i]);
7280 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7281 faceNorm += edge1 ^ edge2;
7283 double normSize = faceNorm.Magnitude();
7284 if ( normSize <= tol )
7286 // degenerated face: point is out if it is out of all face edges
7287 for ( i = 0; i < nbNodes; ++i )
7289 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7290 if ( !IsOut( &edge, point, tol ))
7295 faceNorm /= normSize;
7297 // check if the point lays on face plane
7298 gp_Vec n2p( xyz[0], point );
7299 if ( fabs( n2p * faceNorm ) > tol )
7300 return true; // not on face plane
7302 // check if point is out of face boundary:
7303 // define it by closest transition of a ray point->infinity through face boundary
7304 // on the face plane.
7305 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7306 // to find intersections of the ray with the boundary.
7308 gp_Vec plnNorm = ray ^ faceNorm;
7309 normSize = plnNorm.Magnitude();
7310 if ( normSize <= tol ) return false; // point coincides with the first node
7311 plnNorm /= normSize;
7312 // for each node of the face, compute its signed distance to the plane
7313 vector<double> dist( nbNodes + 1);
7314 for ( i = 0; i < nbNodes; ++i )
7316 gp_Vec n2p( xyz[i], point );
7317 dist[i] = n2p * plnNorm;
7319 dist.back() = dist.front();
7320 // find the closest intersection
7322 double rClosest, distClosest = 1e100;;
7324 for ( i = 0; i < nbNodes; ++i )
7327 if ( fabs( dist[i]) < tol )
7329 else if ( fabs( dist[i+1]) < tol )
7331 else if ( dist[i] * dist[i+1] < 0 )
7332 r = dist[i] / ( dist[i] - dist[i+1] );
7334 continue; // no intersection
7335 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7336 gp_Vec p2int ( point, pInt);
7337 if ( p2int * ray > -tol ) // right half-space
7339 double intDist = p2int.SquareMagnitude();
7340 if ( intDist < distClosest )
7345 distClosest = intDist;
7350 return true; // no intesections - out
7352 // analyse transition
7353 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7354 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7355 gp_Vec p2int ( point, pClosest );
7356 bool out = (edgeNorm * p2int) < -tol;
7357 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7360 // ray pass through a face node; analyze transition through an adjacent edge
7361 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7362 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7363 gp_Vec edgeAdjacent( p1, p2 );
7364 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7365 bool out2 = (edgeNorm2 * p2int) < -tol;
7367 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7368 return covexCorner ? (out || out2) : (out && out2);
7370 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7372 // point is out of edge if it is NOT ON any straight part of edge
7373 // (we consider quadratic edge as being composed of two straight parts)
7374 for ( i = 1; i < nbNodes; ++i )
7376 gp_Vec edge( xyz[i-1], xyz[i]);
7377 gp_Vec n1p ( xyz[i-1], point);
7378 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7381 gp_Vec n2p( xyz[i], point );
7382 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7384 return false; // point is ON this part
7388 // Node or 0D element -------------------------------------------------------------------------
7390 gp_Vec n2p ( xyz[0], point );
7391 return n2p.Magnitude() <= tol;
7396 //=======================================================================
7400 // Position of a point relative to a segment
7404 // VERTEX 1 o----ON-----> VERTEX 2
7408 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7409 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7413 int _index; // index of vertex or segment
7415 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7416 bool operator < (const PointPos& other ) const
7418 if ( _name == other._name )
7419 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7420 return _name < other._name;
7424 //================================================================================
7426 * \brief Return of a point relative to a segment
7427 * \param point2D - the point to analyze position of
7428 * \param xyVec - end points of segments
7429 * \param index0 - 0-based index of the first point of segment
7430 * \param posToFindOut - flags of positions to detect
7431 * \retval PointPos - point position
7433 //================================================================================
7435 PointPos getPointPosition( const gp_XY& point2D,
7436 const gp_XY* segEnds,
7437 const int index0 = 0,
7438 const int posToFindOut = POS_ALL)
7440 const gp_XY& p1 = segEnds[ index0 ];
7441 const gp_XY& p2 = segEnds[ index0+1 ];
7442 const gp_XY grad = p2 - p1;
7444 if ( posToFindOut & POS_VERTEX )
7446 // check if the point2D is at "vertex 1" zone
7447 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7448 p1.Y() + grad.X() ) };
7449 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7450 return PointPos( POS_VERTEX, index0 );
7452 // check if the point2D is at "vertex 2" zone
7453 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7454 p2.Y() + grad.X() ) };
7455 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7456 return PointPos( POS_VERTEX, index0 + 1);
7458 double edgeEquation =
7459 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7460 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7464 //=======================================================================
7466 * \brief Return minimal distance from a point to a face
7468 * Currently we ignore non-planarity and 2nd order of face
7470 //=======================================================================
7472 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7473 const gp_Pnt& point )
7475 double badDistance = -1;
7476 if ( !face ) return badDistance;
7478 // coordinates of nodes (medium nodes, if any, ignored)
7479 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7480 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7481 xyz.resize( face->NbCornerNodes()+1 );
7483 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7484 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7486 gp_Vec OZ ( xyz[0], xyz[1] );
7487 gp_Vec OX ( xyz[0], xyz[2] );
7488 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7490 if ( xyz.size() < 4 ) return badDistance;
7491 OZ = gp_Vec ( xyz[0], xyz[2] );
7492 OX = gp_Vec ( xyz[0], xyz[3] );
7496 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7498 catch ( Standard_Failure ) {
7501 trsf.SetTransformation( tgtCS );
7503 // move all the nodes to 2D
7504 vector<gp_XY> xy( xyz.size() );
7505 for ( size_t i = 0;i < xyz.size()-1; ++i )
7507 gp_XYZ p3d = xyz[i];
7508 trsf.Transforms( p3d );
7509 xy[i].SetCoord( p3d.X(), p3d.Z() );
7511 xyz.back() = xyz.front();
7512 xy.back() = xy.front();
7514 // // move the point in 2D
7515 gp_XYZ tmpPnt = point.XYZ();
7516 trsf.Transforms( tmpPnt );
7517 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7519 // loop on segments of the face to analyze point position ralative to the face
7520 set< PointPos > pntPosSet;
7521 for ( size_t i = 1; i < xy.size(); ++i )
7523 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7524 pntPosSet.insert( pos );
7528 PointPos pos = *pntPosSet.begin();
7529 // cout << "Face " << face->GetID() << " DIST: ";
7530 switch ( pos._name )
7533 // point is most close to a segment
7534 gp_Vec p0p1( point, xyz[ pos._index ] );
7535 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7537 double projDist = p0p1 * p1p2; // distance projected to the segment
7538 gp_Vec projVec = p1p2 * projDist;
7539 gp_Vec distVec = p0p1 - projVec;
7540 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7541 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7542 return distVec.Magnitude();
7545 // point is inside the face
7546 double distToFacePlane = tmpPnt.Y();
7547 // cout << distToFacePlane << ", INSIDE " << endl;
7548 return Abs( distToFacePlane );
7551 // point is most close to a node
7552 gp_Vec distVec( point, xyz[ pos._index ]);
7553 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7554 return distVec.Magnitude();
7560 //=======================================================================
7561 //function : SimplifyFace
7563 //=======================================================================
7564 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7565 vector<const SMDS_MeshNode *>& poly_nodes,
7566 vector<int>& quantities) const
7568 int nbNodes = faceNodes.size();
7573 set<const SMDS_MeshNode*> nodeSet;
7575 // get simple seq of nodes
7576 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7577 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7578 int iSimple = 0, nbUnique = 0;
7580 simpleNodes[iSimple++] = faceNodes[0];
7582 for (int iCur = 1; iCur < nbNodes; iCur++) {
7583 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7584 simpleNodes[iSimple++] = faceNodes[iCur];
7585 if (nodeSet.insert( faceNodes[iCur] ).second)
7589 int nbSimple = iSimple;
7590 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7600 bool foundLoop = (nbSimple > nbUnique);
7603 set<const SMDS_MeshNode*> loopSet;
7604 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7605 const SMDS_MeshNode* n = simpleNodes[iSimple];
7606 if (!loopSet.insert( n ).second) {
7610 int iC = 0, curLast = iSimple;
7611 for (; iC < curLast; iC++) {
7612 if (simpleNodes[iC] == n) break;
7614 int loopLen = curLast - iC;
7616 // create sub-element
7618 quantities.push_back(loopLen);
7619 for (; iC < curLast; iC++) {
7620 poly_nodes.push_back(simpleNodes[iC]);
7623 // shift the rest nodes (place from the first loop position)
7624 for (iC = curLast + 1; iC < nbSimple; iC++) {
7625 simpleNodes[iC - loopLen] = simpleNodes[iC];
7627 nbSimple -= loopLen;
7630 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7631 } // while (foundLoop)
7635 quantities.push_back(iSimple);
7636 for (int i = 0; i < iSimple; i++)
7637 poly_nodes.push_back(simpleNodes[i]);
7643 //=======================================================================
7644 //function : MergeNodes
7645 //purpose : In each group, the cdr of nodes are substituted by the first one
7647 //=======================================================================
7649 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7651 MESSAGE("MergeNodes");
7652 myLastCreatedElems.Clear();
7653 myLastCreatedNodes.Clear();
7655 SMESHDS_Mesh* aMesh = GetMeshDS();
7657 TNodeNodeMap nodeNodeMap; // node to replace - new node
7658 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7659 list< int > rmElemIds, rmNodeIds;
7661 // Fill nodeNodeMap and elems
7663 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7664 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7665 list<const SMDS_MeshNode*>& nodes = *grIt;
7666 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7667 const SMDS_MeshNode* nToKeep = *nIt;
7668 //MESSAGE("node to keep " << nToKeep->GetID());
7669 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7670 const SMDS_MeshNode* nToRemove = *nIt;
7671 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7672 if ( nToRemove != nToKeep ) {
7673 //MESSAGE(" node to remove " << nToRemove->GetID());
7674 rmNodeIds.push_back( nToRemove->GetID() );
7675 AddToSameGroups( nToKeep, nToRemove, aMesh );
7676 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7677 // after MergeNodes() w/o creating node in place of merged ones.
7678 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7679 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7680 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7681 sm->SetIsAlwaysComputed( true );
7684 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7685 while ( invElemIt->more() ) {
7686 const SMDS_MeshElement* elem = invElemIt->next();
7691 // Change element nodes or remove an element
7693 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7694 for ( ; eIt != elems.end(); eIt++ ) {
7695 const SMDS_MeshElement* elem = *eIt;
7696 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7697 int nbNodes = elem->NbNodes();
7698 int aShapeId = FindShape( elem );
7700 set<const SMDS_MeshNode*> nodeSet;
7701 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7702 int iUnique = 0, iCur = 0, nbRepl = 0;
7703 vector<int> iRepl( nbNodes );
7705 // get new seq of nodes
7706 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7707 while ( itN->more() ) {
7708 const SMDS_MeshNode* n =
7709 static_cast<const SMDS_MeshNode*>( itN->next() );
7711 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7712 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7714 // BUG 0020185: begin
7716 bool stopRecur = false;
7717 set<const SMDS_MeshNode*> nodesRecur;
7718 nodesRecur.insert(n);
7719 while (!stopRecur) {
7720 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7721 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7722 n = (*nnIt_i).second;
7723 if (!nodesRecur.insert(n).second) {
7724 // error: recursive dependancy
7734 curNodes[ iCur ] = n;
7735 bool isUnique = nodeSet.insert( n ).second;
7737 uniqueNodes[ iUnique++ ] = n;
7739 iRepl[ nbRepl++ ] = iCur;
7743 // Analyse element topology after replacement
7746 int nbUniqueNodes = nodeSet.size();
7747 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7748 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7749 // Polygons and Polyhedral volumes
7750 if (elem->IsPoly()) {
7752 if (elem->GetType() == SMDSAbs_Face) {
7754 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7756 for (; inode < nbNodes; inode++) {
7757 face_nodes[inode] = curNodes[inode];
7760 vector<const SMDS_MeshNode *> polygons_nodes;
7761 vector<int> quantities;
7762 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7765 for (int iface = 0; iface < nbNew; iface++) {
7766 int nbNodes = quantities[iface];
7767 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7768 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7769 poly_nodes[ii] = polygons_nodes[inode];
7771 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7772 myLastCreatedElems.Append(newElem);
7774 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7777 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7778 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7779 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7781 if (nbNew > 0) quid = nbNew - 1;
7782 vector<int> newquant(quantities.begin()+quid, quantities.end());
7783 const SMDS_MeshElement* newElem = 0;
7784 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7785 myLastCreatedElems.Append(newElem);
7786 if ( aShapeId && newElem )
7787 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7788 rmElemIds.push_back(elem->GetID());
7791 rmElemIds.push_back(elem->GetID());
7795 else if (elem->GetType() == SMDSAbs_Volume) {
7796 // Polyhedral volume
7797 if (nbUniqueNodes < 4) {
7798 rmElemIds.push_back(elem->GetID());
7801 // each face has to be analyzed in order to check volume validity
7802 const SMDS_VtkVolume* aPolyedre =
7803 dynamic_cast<const SMDS_VtkVolume*>( elem );
7805 int nbFaces = aPolyedre->NbFaces();
7807 vector<const SMDS_MeshNode *> poly_nodes;
7808 vector<int> quantities;
7810 for (int iface = 1; iface <= nbFaces; iface++) {
7811 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7812 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7814 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7815 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7816 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7817 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7818 faceNode = (*nnIt).second;
7820 faceNodes[inode - 1] = faceNode;
7823 SimplifyFace(faceNodes, poly_nodes, quantities);
7826 if (quantities.size() > 3) {
7827 // to be done: remove coincident faces
7830 if (quantities.size() > 3)
7832 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7833 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7834 const SMDS_MeshElement* newElem = 0;
7835 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7836 myLastCreatedElems.Append(newElem);
7837 if ( aShapeId && newElem )
7838 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7839 rmElemIds.push_back(elem->GetID());
7843 rmElemIds.push_back(elem->GetID());
7854 // TODO not all the possible cases are solved. Find something more generic?
7855 switch ( nbNodes ) {
7856 case 2: ///////////////////////////////////// EDGE
7857 isOk = false; break;
7858 case 3: ///////////////////////////////////// TRIANGLE
7859 isOk = false; break;
7861 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7863 else { //////////////////////////////////// QUADRANGLE
7864 if ( nbUniqueNodes < 3 )
7866 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7867 isOk = false; // opposite nodes stick
7868 //MESSAGE("isOk " << isOk);
7871 case 6: ///////////////////////////////////// PENTAHEDRON
7872 if ( nbUniqueNodes == 4 ) {
7873 // ---------------------------------> tetrahedron
7875 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7876 // all top nodes stick: reverse a bottom
7877 uniqueNodes[ 0 ] = curNodes [ 1 ];
7878 uniqueNodes[ 1 ] = curNodes [ 0 ];
7880 else if (nbRepl == 3 &&
7881 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7882 // all bottom nodes stick: set a top before
7883 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7884 uniqueNodes[ 0 ] = curNodes [ 3 ];
7885 uniqueNodes[ 1 ] = curNodes [ 4 ];
7886 uniqueNodes[ 2 ] = curNodes [ 5 ];
7888 else if (nbRepl == 4 &&
7889 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7890 // a lateral face turns into a line: reverse a bottom
7891 uniqueNodes[ 0 ] = curNodes [ 1 ];
7892 uniqueNodes[ 1 ] = curNodes [ 0 ];
7897 else if ( nbUniqueNodes == 5 ) {
7898 // PENTAHEDRON --------------------> 2 tetrahedrons
7899 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7900 // a bottom node sticks with a linked top one
7902 SMDS_MeshElement* newElem =
7903 aMesh->AddVolume(curNodes[ 3 ],
7906 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7907 myLastCreatedElems.Append(newElem);
7909 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7910 // 2. : reverse a bottom
7911 uniqueNodes[ 0 ] = curNodes [ 1 ];
7912 uniqueNodes[ 1 ] = curNodes [ 0 ];
7922 if(elem->IsQuadratic()) { // Quadratic quadrangle
7934 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7937 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7939 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7940 uniqueNodes[0] = curNodes[0];
7941 uniqueNodes[1] = curNodes[2];
7942 uniqueNodes[2] = curNodes[3];
7943 uniqueNodes[3] = curNodes[5];
7944 uniqueNodes[4] = curNodes[6];
7945 uniqueNodes[5] = curNodes[7];
7948 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7949 uniqueNodes[0] = curNodes[0];
7950 uniqueNodes[1] = curNodes[1];
7951 uniqueNodes[2] = curNodes[2];
7952 uniqueNodes[3] = curNodes[4];
7953 uniqueNodes[4] = curNodes[5];
7954 uniqueNodes[5] = curNodes[6];
7957 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7958 uniqueNodes[0] = curNodes[1];
7959 uniqueNodes[1] = curNodes[2];
7960 uniqueNodes[2] = curNodes[3];
7961 uniqueNodes[3] = curNodes[5];
7962 uniqueNodes[4] = curNodes[6];
7963 uniqueNodes[5] = curNodes[0];
7966 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7967 uniqueNodes[0] = curNodes[0];
7968 uniqueNodes[1] = curNodes[1];
7969 uniqueNodes[2] = curNodes[3];
7970 uniqueNodes[3] = curNodes[4];
7971 uniqueNodes[4] = curNodes[6];
7972 uniqueNodes[5] = curNodes[7];
7975 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7976 uniqueNodes[0] = curNodes[0];
7977 uniqueNodes[1] = curNodes[2];
7978 uniqueNodes[2] = curNodes[3];
7979 uniqueNodes[3] = curNodes[1];
7980 uniqueNodes[4] = curNodes[6];
7981 uniqueNodes[5] = curNodes[7];
7984 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7985 uniqueNodes[0] = curNodes[0];
7986 uniqueNodes[1] = curNodes[1];
7987 uniqueNodes[2] = curNodes[2];
7988 uniqueNodes[3] = curNodes[4];
7989 uniqueNodes[4] = curNodes[5];
7990 uniqueNodes[5] = curNodes[7];
7993 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7994 uniqueNodes[0] = curNodes[0];
7995 uniqueNodes[1] = curNodes[1];
7996 uniqueNodes[2] = curNodes[3];
7997 uniqueNodes[3] = curNodes[4];
7998 uniqueNodes[4] = curNodes[2];
7999 uniqueNodes[5] = curNodes[7];
8002 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
8003 uniqueNodes[0] = curNodes[0];
8004 uniqueNodes[1] = curNodes[1];
8005 uniqueNodes[2] = curNodes[2];
8006 uniqueNodes[3] = curNodes[4];
8007 uniqueNodes[4] = curNodes[5];
8008 uniqueNodes[5] = curNodes[3];
8013 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
8016 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
8020 //////////////////////////////////// HEXAHEDRON
8022 SMDS_VolumeTool hexa (elem);
8023 hexa.SetExternalNormal();
8024 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8025 //////////////////////// HEX ---> 1 tetrahedron
8026 for ( int iFace = 0; iFace < 6; iFace++ ) {
8027 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8028 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8029 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8030 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8031 // one face turns into a point ...
8032 int iOppFace = hexa.GetOppFaceIndex( iFace );
8033 ind = hexa.GetFaceNodesIndices( iOppFace );
8035 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8036 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8039 if ( nbStick == 1 ) {
8040 // ... and the opposite one - into a triangle.
8042 ind = hexa.GetFaceNodesIndices( iFace );
8043 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8050 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8051 //////////////////////// HEX ---> 1 prism
8052 int nbTria = 0, iTria[3];
8053 const int *ind; // indices of face nodes
8054 // look for triangular faces
8055 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8056 ind = hexa.GetFaceNodesIndices( iFace );
8057 TIDSortedNodeSet faceNodes;
8058 for ( iCur = 0; iCur < 4; iCur++ )
8059 faceNodes.insert( curNodes[ind[iCur]] );
8060 if ( faceNodes.size() == 3 )
8061 iTria[ nbTria++ ] = iFace;
8063 // check if triangles are opposite
8064 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8067 // set nodes of the bottom triangle
8068 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8070 for ( iCur = 0; iCur < 4; iCur++ )
8071 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8072 indB.push_back( ind[iCur] );
8073 if ( !hexa.IsForward() )
8074 std::swap( indB[0], indB[2] );
8075 for ( iCur = 0; iCur < 3; iCur++ )
8076 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8077 // set nodes of the top triangle
8078 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8079 for ( iCur = 0; iCur < 3; ++iCur )
8080 for ( int j = 0; j < 4; ++j )
8081 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8083 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8089 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8090 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8091 for ( int iFace = 0; iFace < 6; iFace++ ) {
8092 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8093 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8094 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8095 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8096 // one face turns into a point ...
8097 int iOppFace = hexa.GetOppFaceIndex( iFace );
8098 ind = hexa.GetFaceNodesIndices( iOppFace );
8100 iUnique = 2; // reverse a tetrahedron 1 bottom
8101 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8102 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8104 else if ( iUnique >= 0 )
8105 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8107 if ( nbStick == 0 ) {
8108 // ... and the opposite one is a quadrangle
8110 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8111 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8114 SMDS_MeshElement* newElem =
8115 aMesh->AddVolume(curNodes[ind[ 0 ]],
8118 curNodes[indTop[ 0 ]]);
8119 myLastCreatedElems.Append(newElem);
8121 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8128 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8129 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8130 // find indices of quad and tri faces
8131 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8132 for ( iFace = 0; iFace < 6; iFace++ ) {
8133 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8135 for ( iCur = 0; iCur < 4; iCur++ )
8136 nodeSet.insert( curNodes[ind[ iCur ]] );
8137 nbUniqueNodes = nodeSet.size();
8138 if ( nbUniqueNodes == 3 )
8139 iTriFace[ nbTri++ ] = iFace;
8140 else if ( nbUniqueNodes == 4 )
8141 iQuadFace[ nbQuad++ ] = iFace;
8143 if (nbQuad == 2 && nbTri == 4 &&
8144 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8145 // 2 opposite quadrangles stuck with a diagonal;
8146 // sample groups of merged indices: (0-4)(2-6)
8147 // --------------------------------------------> 2 tetrahedrons
8148 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8149 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8150 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8151 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8152 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8153 // stuck with 0-2 diagonal
8161 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8162 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8163 // stuck with 1-3 diagonal
8175 uniqueNodes[ 0 ] = curNodes [ i0 ];
8176 uniqueNodes[ 1 ] = curNodes [ i1d ];
8177 uniqueNodes[ 2 ] = curNodes [ i3d ];
8178 uniqueNodes[ 3 ] = curNodes [ i0t ];
8181 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8185 myLastCreatedElems.Append(newElem);
8187 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8190 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8191 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8192 // --------------------------------------------> prism
8193 // find 2 opposite triangles
8195 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8196 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8197 // find indices of kept and replaced nodes
8198 // and fill unique nodes of 2 opposite triangles
8199 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8200 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8201 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8202 // fill unique nodes
8205 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8206 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8207 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8209 // iCur of a linked node of the opposite face (make normals co-directed):
8210 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8211 // check that correspondent corners of triangles are linked
8212 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8215 uniqueNodes[ iUnique ] = n;
8216 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8225 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8228 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8235 } // switch ( nbNodes )
8237 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8239 if ( isOk ) { // the elem remains valid after sticking nodes
8240 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8242 // Change nodes of polyedre
8243 const SMDS_VtkVolume* aPolyedre =
8244 dynamic_cast<const SMDS_VtkVolume*>( elem );
8246 int nbFaces = aPolyedre->NbFaces();
8248 vector<const SMDS_MeshNode *> poly_nodes;
8249 vector<int> quantities (nbFaces);
8251 for (int iface = 1; iface <= nbFaces; iface++) {
8252 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8253 quantities[iface - 1] = nbFaceNodes;
8255 for (inode = 1; inode <= nbFaceNodes; inode++) {
8256 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8258 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8259 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8260 curNode = (*nnIt).second;
8262 poly_nodes.push_back(curNode);
8265 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8268 else // replace non-polyhedron elements
8270 const SMDSAbs_ElementType etyp = elem->GetType();
8271 const int elemId = elem->GetID();
8272 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8273 uniqueNodes.resize(nbUniqueNodes);
8275 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8277 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8278 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8279 if ( sm && newElem )
8280 sm->AddElement( newElem );
8281 if ( elem != newElem )
8282 ReplaceElemInGroups( elem, newElem, aMesh );
8286 // Remove invalid regular element or invalid polygon
8287 rmElemIds.push_back( elem->GetID() );
8290 } // loop on elements
8292 // Remove bad elements, then equal nodes (order important)
8294 Remove( rmElemIds, false );
8295 Remove( rmNodeIds, true );
8300 // ========================================================
8301 // class : SortableElement
8302 // purpose : allow sorting elements basing on their nodes
8303 // ========================================================
8304 class SortableElement : public set <const SMDS_MeshElement*>
8308 SortableElement( const SMDS_MeshElement* theElem )
8311 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8312 while ( nodeIt->more() )
8313 this->insert( nodeIt->next() );
8316 const SMDS_MeshElement* Get() const
8319 void Set(const SMDS_MeshElement* e) const
8324 mutable const SMDS_MeshElement* myElem;
8327 //=======================================================================
8328 //function : FindEqualElements
8329 //purpose : Return list of group of elements built on the same nodes.
8330 // Search among theElements or in the whole mesh if theElements is empty
8331 //=======================================================================
8333 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8334 TListOfListOfElementsID & theGroupsOfElementsID)
8336 myLastCreatedElems.Clear();
8337 myLastCreatedNodes.Clear();
8339 typedef map< SortableElement, int > TMapOfNodeSet;
8340 typedef list<int> TGroupOfElems;
8342 if ( theElements.empty() )
8343 { // get all elements in the mesh
8344 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8345 while ( eIt->more() )
8346 theElements.insert( theElements.end(), eIt->next());
8349 vector< TGroupOfElems > arrayOfGroups;
8350 TGroupOfElems groupOfElems;
8351 TMapOfNodeSet mapOfNodeSet;
8353 TIDSortedElemSet::iterator elemIt = theElements.begin();
8354 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8355 const SMDS_MeshElement* curElem = *elemIt;
8356 SortableElement SE(curElem);
8359 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8360 if( !(pp.second) ) {
8361 TMapOfNodeSet::iterator& itSE = pp.first;
8362 ind = (*itSE).second;
8363 arrayOfGroups[ind].push_back(curElem->GetID());
8366 groupOfElems.clear();
8367 groupOfElems.push_back(curElem->GetID());
8368 arrayOfGroups.push_back(groupOfElems);
8373 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8374 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8375 groupOfElems = *groupIt;
8376 if ( groupOfElems.size() > 1 ) {
8377 groupOfElems.sort();
8378 theGroupsOfElementsID.push_back(groupOfElems);
8383 //=======================================================================
8384 //function : MergeElements
8385 //purpose : In each given group, substitute all elements by the first one.
8386 //=======================================================================
8388 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8390 myLastCreatedElems.Clear();
8391 myLastCreatedNodes.Clear();
8393 typedef list<int> TListOfIDs;
8394 TListOfIDs rmElemIds; // IDs of elems to remove
8396 SMESHDS_Mesh* aMesh = GetMeshDS();
8398 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8399 while ( groupsIt != theGroupsOfElementsID.end() ) {
8400 TListOfIDs& aGroupOfElemID = *groupsIt;
8401 aGroupOfElemID.sort();
8402 int elemIDToKeep = aGroupOfElemID.front();
8403 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8404 aGroupOfElemID.pop_front();
8405 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8406 while ( idIt != aGroupOfElemID.end() ) {
8407 int elemIDToRemove = *idIt;
8408 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8409 // add the kept element in groups of removed one (PAL15188)
8410 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8411 rmElemIds.push_back( elemIDToRemove );
8417 Remove( rmElemIds, false );
8420 //=======================================================================
8421 //function : MergeEqualElements
8422 //purpose : Remove all but one of elements built on the same nodes.
8423 //=======================================================================
8425 void SMESH_MeshEditor::MergeEqualElements()
8427 TIDSortedElemSet aMeshElements; /* empty input ==
8428 to merge equal elements in the whole mesh */
8429 TListOfListOfElementsID aGroupsOfElementsID;
8430 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8431 MergeElements(aGroupsOfElementsID);
8434 //=======================================================================
8435 //function : FindFaceInSet
8436 //purpose : Return a face having linked nodes n1 and n2 and which is
8437 // - not in avoidSet,
8438 // - in elemSet provided that !elemSet.empty()
8439 // i1 and i2 optionally returns indices of n1 and n2
8440 //=======================================================================
8442 const SMDS_MeshElement*
8443 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8444 const SMDS_MeshNode* n2,
8445 const TIDSortedElemSet& elemSet,
8446 const TIDSortedElemSet& avoidSet,
8452 const SMDS_MeshElement* face = 0;
8454 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8455 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8456 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8458 //MESSAGE("in while ( invElemIt->more() && !face )");
8459 const SMDS_MeshElement* elem = invElemIt->next();
8460 if (avoidSet.count( elem ))
8462 if ( !elemSet.empty() && !elemSet.count( elem ))
8465 i1 = elem->GetNodeIndex( n1 );
8466 // find a n2 linked to n1
8467 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8468 for ( int di = -1; di < 2 && !face; di += 2 )
8470 i2 = (i1+di+nbN) % nbN;
8471 if ( elem->GetNode( i2 ) == n2 )
8474 if ( !face && elem->IsQuadratic())
8476 // analysis for quadratic elements using all nodes
8477 const SMDS_VtkFace* F =
8478 dynamic_cast<const SMDS_VtkFace*>(elem);
8479 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8480 // use special nodes iterator
8481 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8482 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8483 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8485 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8486 if ( n1 == prevN && n2 == n )
8490 else if ( n2 == prevN && n1 == n )
8492 face = elem; swap( i1, i2 );
8498 if ( n1ind ) *n1ind = i1;
8499 if ( n2ind ) *n2ind = i2;
8503 //=======================================================================
8504 //function : findAdjacentFace
8506 //=======================================================================
8508 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8509 const SMDS_MeshNode* n2,
8510 const SMDS_MeshElement* elem)
8512 TIDSortedElemSet elemSet, avoidSet;
8514 avoidSet.insert ( elem );
8515 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8518 //=======================================================================
8519 //function : FindFreeBorder
8521 //=======================================================================
8523 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8525 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8526 const SMDS_MeshNode* theSecondNode,
8527 const SMDS_MeshNode* theLastNode,
8528 list< const SMDS_MeshNode* > & theNodes,
8529 list< const SMDS_MeshElement* >& theFaces)
8531 if ( !theFirstNode || !theSecondNode )
8533 // find border face between theFirstNode and theSecondNode
8534 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8538 theFaces.push_back( curElem );
8539 theNodes.push_back( theFirstNode );
8540 theNodes.push_back( theSecondNode );
8542 //vector<const SMDS_MeshNode*> nodes;
8543 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8544 TIDSortedElemSet foundElems;
8545 bool needTheLast = ( theLastNode != 0 );
8547 while ( nStart != theLastNode ) {
8548 if ( nStart == theFirstNode )
8549 return !needTheLast;
8551 // find all free border faces sharing form nStart
8553 list< const SMDS_MeshElement* > curElemList;
8554 list< const SMDS_MeshNode* > nStartList;
8555 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8556 while ( invElemIt->more() ) {
8557 const SMDS_MeshElement* e = invElemIt->next();
8558 if ( e == curElem || foundElems.insert( e ).second ) {
8560 int iNode = 0, nbNodes = e->NbNodes();
8561 //const SMDS_MeshNode* nodes[nbNodes+1];
8562 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8564 if(e->IsQuadratic()) {
8565 const SMDS_VtkFace* F =
8566 dynamic_cast<const SMDS_VtkFace*>(e);
8567 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8568 // use special nodes iterator
8569 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8570 while( anIter->more() ) {
8571 nodes[ iNode++ ] = cast2Node(anIter->next());
8575 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8576 while ( nIt->more() )
8577 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8579 nodes[ iNode ] = nodes[ 0 ];
8581 for ( iNode = 0; iNode < nbNodes; iNode++ )
8582 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8583 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8584 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8586 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8587 curElemList.push_back( e );
8591 // analyse the found
8593 int nbNewBorders = curElemList.size();
8594 if ( nbNewBorders == 0 ) {
8595 // no free border furthermore
8596 return !needTheLast;
8598 else if ( nbNewBorders == 1 ) {
8599 // one more element found
8601 nStart = nStartList.front();
8602 curElem = curElemList.front();
8603 theFaces.push_back( curElem );
8604 theNodes.push_back( nStart );
8607 // several continuations found
8608 list< const SMDS_MeshElement* >::iterator curElemIt;
8609 list< const SMDS_MeshNode* >::iterator nStartIt;
8610 // check if one of them reached the last node
8611 if ( needTheLast ) {
8612 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8613 curElemIt!= curElemList.end();
8614 curElemIt++, nStartIt++ )
8615 if ( *nStartIt == theLastNode ) {
8616 theFaces.push_back( *curElemIt );
8617 theNodes.push_back( *nStartIt );
8621 // find the best free border by the continuations
8622 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8623 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8624 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8625 curElemIt!= curElemList.end();
8626 curElemIt++, nStartIt++ )
8628 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8629 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8630 // find one more free border
8631 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8635 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8636 // choice: clear a worse one
8637 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8638 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8639 contNodes[ iWorse ].clear();
8640 contFaces[ iWorse ].clear();
8643 if ( contNodes[0].empty() && contNodes[1].empty() )
8646 // append the best free border
8647 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8648 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8649 theNodes.pop_back(); // remove nIgnore
8650 theNodes.pop_back(); // remove nStart
8651 theFaces.pop_back(); // remove curElem
8652 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8653 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8654 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8655 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8658 } // several continuations found
8659 } // while ( nStart != theLastNode )
8664 //=======================================================================
8665 //function : CheckFreeBorderNodes
8666 //purpose : Return true if the tree nodes are on a free border
8667 //=======================================================================
8669 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8670 const SMDS_MeshNode* theNode2,
8671 const SMDS_MeshNode* theNode3)
8673 list< const SMDS_MeshNode* > nodes;
8674 list< const SMDS_MeshElement* > faces;
8675 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8678 //=======================================================================
8679 //function : SewFreeBorder
8681 //=======================================================================
8683 SMESH_MeshEditor::Sew_Error
8684 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8685 const SMDS_MeshNode* theBordSecondNode,
8686 const SMDS_MeshNode* theBordLastNode,
8687 const SMDS_MeshNode* theSideFirstNode,
8688 const SMDS_MeshNode* theSideSecondNode,
8689 const SMDS_MeshNode* theSideThirdNode,
8690 const bool theSideIsFreeBorder,
8691 const bool toCreatePolygons,
8692 const bool toCreatePolyedrs)
8694 myLastCreatedElems.Clear();
8695 myLastCreatedNodes.Clear();
8697 MESSAGE("::SewFreeBorder()");
8698 Sew_Error aResult = SEW_OK;
8700 // ====================================
8701 // find side nodes and elements
8702 // ====================================
8704 list< const SMDS_MeshNode* > nSide[ 2 ];
8705 list< const SMDS_MeshElement* > eSide[ 2 ];
8706 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8707 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8711 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8712 nSide[0], eSide[0])) {
8713 MESSAGE(" Free Border 1 not found " );
8714 aResult = SEW_BORDER1_NOT_FOUND;
8716 if (theSideIsFreeBorder) {
8719 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8720 nSide[1], eSide[1])) {
8721 MESSAGE(" Free Border 2 not found " );
8722 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8725 if ( aResult != SEW_OK )
8728 if (!theSideIsFreeBorder) {
8732 // -------------------------------------------------------------------------
8734 // 1. If nodes to merge are not coincident, move nodes of the free border
8735 // from the coord sys defined by the direction from the first to last
8736 // nodes of the border to the correspondent sys of the side 2
8737 // 2. On the side 2, find the links most co-directed with the correspondent
8738 // links of the free border
8739 // -------------------------------------------------------------------------
8741 // 1. Since sewing may break if there are volumes to split on the side 2,
8742 // we wont move nodes but just compute new coordinates for them
8743 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8744 TNodeXYZMap nBordXYZ;
8745 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8746 list< const SMDS_MeshNode* >::iterator nBordIt;
8748 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8749 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8750 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8751 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8752 double tol2 = 1.e-8;
8753 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8754 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8755 // Need node movement.
8757 // find X and Z axes to create trsf
8758 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8760 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8762 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8765 gp_Ax3 toBordAx( Pb1, Zb, X );
8766 gp_Ax3 fromSideAx( Ps1, Zs, X );
8767 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8769 gp_Trsf toBordSys, fromSide2Sys;
8770 toBordSys.SetTransformation( toBordAx );
8771 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8772 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8775 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8776 const SMDS_MeshNode* n = *nBordIt;
8777 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8778 toBordSys.Transforms( xyz );
8779 fromSide2Sys.Transforms( xyz );
8780 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8784 // just insert nodes XYZ in the nBordXYZ map
8785 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8786 const SMDS_MeshNode* n = *nBordIt;
8787 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8791 // 2. On the side 2, find the links most co-directed with the correspondent
8792 // links of the free border
8794 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8795 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8796 sideNodes.push_back( theSideFirstNode );
8798 bool hasVolumes = false;
8799 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8800 set<long> foundSideLinkIDs, checkedLinkIDs;
8801 SMDS_VolumeTool volume;
8802 //const SMDS_MeshNode* faceNodes[ 4 ];
8804 const SMDS_MeshNode* sideNode;
8805 const SMDS_MeshElement* sideElem;
8806 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8807 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8808 nBordIt = bordNodes.begin();
8810 // border node position and border link direction to compare with
8811 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8812 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8813 // choose next side node by link direction or by closeness to
8814 // the current border node:
8815 bool searchByDir = ( *nBordIt != theBordLastNode );
8817 // find the next node on the Side 2
8819 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8821 checkedLinkIDs.clear();
8822 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8824 // loop on inverse elements of current node (prevSideNode) on the Side 2
8825 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8826 while ( invElemIt->more() )
8828 const SMDS_MeshElement* elem = invElemIt->next();
8829 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8830 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8831 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8832 bool isVolume = volume.Set( elem );
8833 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8834 if ( isVolume ) // --volume
8836 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8837 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8838 if(elem->IsQuadratic()) {
8839 const SMDS_VtkFace* F =
8840 dynamic_cast<const SMDS_VtkFace*>(elem);
8841 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8842 // use special nodes iterator
8843 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8844 while( anIter->more() ) {
8845 nodes[ iNode ] = cast2Node(anIter->next());
8846 if ( nodes[ iNode++ ] == prevSideNode )
8847 iPrevNode = iNode - 1;
8851 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8852 while ( nIt->more() ) {
8853 nodes[ iNode ] = cast2Node( nIt->next() );
8854 if ( nodes[ iNode++ ] == prevSideNode )
8855 iPrevNode = iNode - 1;
8858 // there are 2 links to check
8863 // loop on links, to be precise, on the second node of links
8864 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8865 const SMDS_MeshNode* n = nodes[ iNode ];
8867 if ( !volume.IsLinked( n, prevSideNode ))
8871 if ( iNode ) // a node before prevSideNode
8872 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8873 else // a node after prevSideNode
8874 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8876 // check if this link was already used
8877 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8878 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8879 if (!isJustChecked &&
8880 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8882 // test a link geometrically
8883 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8884 bool linkIsBetter = false;
8885 double dot = 0.0, dist = 0.0;
8886 if ( searchByDir ) { // choose most co-directed link
8887 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8888 linkIsBetter = ( dot > maxDot );
8890 else { // choose link with the node closest to bordPos
8891 dist = ( nextXYZ - bordPos ).SquareModulus();
8892 linkIsBetter = ( dist < minDist );
8894 if ( linkIsBetter ) {
8903 } // loop on inverse elements of prevSideNode
8906 MESSAGE(" Cant find path by links of the Side 2 ");
8907 return SEW_BAD_SIDE_NODES;
8909 sideNodes.push_back( sideNode );
8910 sideElems.push_back( sideElem );
8911 foundSideLinkIDs.insert ( linkID );
8912 prevSideNode = sideNode;
8914 if ( *nBordIt == theBordLastNode )
8915 searchByDir = false;
8917 // find the next border link to compare with
8918 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8919 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8920 // move to next border node if sideNode is before forward border node (bordPos)
8921 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8922 prevBordNode = *nBordIt;
8924 bordPos = nBordXYZ[ *nBordIt ];
8925 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8926 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8930 while ( sideNode != theSideSecondNode );
8932 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8933 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8934 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8936 } // end nodes search on the side 2
8938 // ============================
8939 // sew the border to the side 2
8940 // ============================
8942 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8943 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8945 TListOfListOfNodes nodeGroupsToMerge;
8946 if ( nbNodes[0] == nbNodes[1] ||
8947 ( theSideIsFreeBorder && !theSideThirdNode)) {
8949 // all nodes are to be merged
8951 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8952 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8953 nIt[0]++, nIt[1]++ )
8955 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8956 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8957 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8962 // insert new nodes into the border and the side to get equal nb of segments
8964 // get normalized parameters of nodes on the borders
8965 //double param[ 2 ][ maxNbNodes ];
8967 param[0] = new double [ maxNbNodes ];
8968 param[1] = new double [ maxNbNodes ];
8970 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8971 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8972 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8973 const SMDS_MeshNode* nPrev = *nIt;
8974 double bordLength = 0;
8975 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8976 const SMDS_MeshNode* nCur = *nIt;
8977 gp_XYZ segment (nCur->X() - nPrev->X(),
8978 nCur->Y() - nPrev->Y(),
8979 nCur->Z() - nPrev->Z());
8980 double segmentLen = segment.Modulus();
8981 bordLength += segmentLen;
8982 param[ iBord ][ iNode ] = bordLength;
8985 // normalize within [0,1]
8986 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8987 param[ iBord ][ iNode ] /= bordLength;
8991 // loop on border segments
8992 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8993 int i[ 2 ] = { 0, 0 };
8994 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8995 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8997 TElemOfNodeListMap insertMap;
8998 TElemOfNodeListMap::iterator insertMapIt;
9000 // key: elem to insert nodes into
9001 // value: 2 nodes to insert between + nodes to be inserted
9003 bool next[ 2 ] = { false, false };
9005 // find min adjacent segment length after sewing
9006 double nextParam = 10., prevParam = 0;
9007 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9008 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
9009 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
9010 if ( i[ iBord ] > 0 )
9011 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
9013 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
9014 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
9015 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
9017 // choose to insert or to merge nodes
9018 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
9019 if ( Abs( du ) <= minSegLen * 0.2 ) {
9022 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9023 const SMDS_MeshNode* n0 = *nIt[0];
9024 const SMDS_MeshNode* n1 = *nIt[1];
9025 nodeGroupsToMerge.back().push_back( n1 );
9026 nodeGroupsToMerge.back().push_back( n0 );
9027 // position of node of the border changes due to merge
9028 param[ 0 ][ i[0] ] += du;
9029 // move n1 for the sake of elem shape evaluation during insertion.
9030 // n1 will be removed by MergeNodes() anyway
9031 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9032 next[0] = next[1] = true;
9037 int intoBord = ( du < 0 ) ? 0 : 1;
9038 const SMDS_MeshElement* elem = *eIt[ intoBord ];
9039 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
9040 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
9041 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
9042 if ( intoBord == 1 ) {
9043 // move node of the border to be on a link of elem of the side
9044 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9045 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9046 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9047 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9048 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9050 insertMapIt = insertMap.find( elem );
9051 bool notFound = ( insertMapIt == insertMap.end() );
9052 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9054 // insert into another link of the same element:
9055 // 1. perform insertion into the other link of the elem
9056 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9057 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9058 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9059 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9060 // 2. perform insertion into the link of adjacent faces
9062 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9064 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9068 if (toCreatePolyedrs) {
9069 // perform insertion into the links of adjacent volumes
9070 UpdateVolumes(n12, n22, nodeList);
9072 // 3. find an element appeared on n1 and n2 after the insertion
9073 insertMap.erase( elem );
9074 elem = findAdjacentFace( n1, n2, 0 );
9076 if ( notFound || otherLink ) {
9077 // add element and nodes of the side into the insertMap
9078 insertMapIt = insertMap.insert
9079 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9080 (*insertMapIt).second.push_back( n1 );
9081 (*insertMapIt).second.push_back( n2 );
9083 // add node to be inserted into elem
9084 (*insertMapIt).second.push_back( nIns );
9085 next[ 1 - intoBord ] = true;
9088 // go to the next segment
9089 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9090 if ( next[ iBord ] ) {
9091 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9093 nPrev[ iBord ] = *nIt[ iBord ];
9094 nIt[ iBord ]++; i[ iBord ]++;
9098 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9100 // perform insertion of nodes into elements
9102 for (insertMapIt = insertMap.begin();
9103 insertMapIt != insertMap.end();
9106 const SMDS_MeshElement* elem = (*insertMapIt).first;
9107 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9108 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9109 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9111 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9113 if ( !theSideIsFreeBorder ) {
9114 // look for and insert nodes into the faces adjacent to elem
9116 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9118 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9123 if (toCreatePolyedrs) {
9124 // perform insertion into the links of adjacent volumes
9125 UpdateVolumes(n1, n2, nodeList);
9131 } // end: insert new nodes
9133 MergeNodes ( nodeGroupsToMerge );
9138 //=======================================================================
9139 //function : InsertNodesIntoLink
9140 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9141 // and theBetweenNode2 and split theElement
9142 //=======================================================================
9144 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9145 const SMDS_MeshNode* theBetweenNode1,
9146 const SMDS_MeshNode* theBetweenNode2,
9147 list<const SMDS_MeshNode*>& theNodesToInsert,
9148 const bool toCreatePoly)
9150 if ( theFace->GetType() != SMDSAbs_Face ) return;
9152 // find indices of 2 link nodes and of the rest nodes
9153 int iNode = 0, il1, il2, i3, i4;
9154 il1 = il2 = i3 = i4 = -1;
9155 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9156 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9158 if(theFace->IsQuadratic()) {
9159 const SMDS_VtkFace* F =
9160 dynamic_cast<const SMDS_VtkFace*>(theFace);
9161 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9162 // use special nodes iterator
9163 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9164 while( anIter->more() ) {
9165 const SMDS_MeshNode* n = cast2Node(anIter->next());
9166 if ( n == theBetweenNode1 )
9168 else if ( n == theBetweenNode2 )
9174 nodes[ iNode++ ] = n;
9178 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9179 while ( nodeIt->more() ) {
9180 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9181 if ( n == theBetweenNode1 )
9183 else if ( n == theBetweenNode2 )
9189 nodes[ iNode++ ] = n;
9192 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9195 // arrange link nodes to go one after another regarding the face orientation
9196 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9197 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9202 aNodesToInsert.reverse();
9204 // check that not link nodes of a quadrangles are in good order
9205 int nbFaceNodes = theFace->NbNodes();
9206 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9212 if (toCreatePoly || theFace->IsPoly()) {
9215 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9217 // add nodes of face up to first node of link
9220 if(theFace->IsQuadratic()) {
9221 const SMDS_VtkFace* F =
9222 dynamic_cast<const SMDS_VtkFace*>(theFace);
9223 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9224 // use special nodes iterator
9225 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9226 while( anIter->more() && !isFLN ) {
9227 const SMDS_MeshNode* n = cast2Node(anIter->next());
9228 poly_nodes[iNode++] = n;
9229 if (n == nodes[il1]) {
9233 // add nodes to insert
9234 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9235 for (; nIt != aNodesToInsert.end(); nIt++) {
9236 poly_nodes[iNode++] = *nIt;
9238 // add nodes of face starting from last node of link
9239 while ( anIter->more() ) {
9240 poly_nodes[iNode++] = cast2Node(anIter->next());
9244 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9245 while ( nodeIt->more() && !isFLN ) {
9246 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9247 poly_nodes[iNode++] = n;
9248 if (n == nodes[il1]) {
9252 // add nodes to insert
9253 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9254 for (; nIt != aNodesToInsert.end(); nIt++) {
9255 poly_nodes[iNode++] = *nIt;
9257 // add nodes of face starting from last node of link
9258 while ( nodeIt->more() ) {
9259 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9260 poly_nodes[iNode++] = n;
9264 // edit or replace the face
9265 SMESHDS_Mesh *aMesh = GetMeshDS();
9267 if (theFace->IsPoly()) {
9268 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9271 int aShapeId = FindShape( theFace );
9273 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9274 myLastCreatedElems.Append(newElem);
9275 if ( aShapeId && newElem )
9276 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9278 aMesh->RemoveElement(theFace);
9283 SMESHDS_Mesh *aMesh = GetMeshDS();
9284 if( !theFace->IsQuadratic() ) {
9286 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9287 int nbLinkNodes = 2 + aNodesToInsert.size();
9288 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9289 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9290 linkNodes[ 0 ] = nodes[ il1 ];
9291 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9292 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9293 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9294 linkNodes[ iNode++ ] = *nIt;
9296 // decide how to split a quadrangle: compare possible variants
9297 // and choose which of splits to be a quadrangle
9298 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9299 if ( nbFaceNodes == 3 ) {
9300 iBestQuad = nbSplits;
9303 else if ( nbFaceNodes == 4 ) {
9304 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9305 double aBestRate = DBL_MAX;
9306 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9308 double aBadRate = 0;
9309 // evaluate elements quality
9310 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9311 if ( iSplit == iQuad ) {
9312 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9316 aBadRate += getBadRate( &quad, aCrit );
9319 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9321 nodes[ iSplit < iQuad ? i4 : i3 ]);
9322 aBadRate += getBadRate( &tria, aCrit );
9326 if ( aBadRate < aBestRate ) {
9328 aBestRate = aBadRate;
9333 // create new elements
9334 int aShapeId = FindShape( theFace );
9337 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9338 SMDS_MeshElement* newElem = 0;
9339 if ( iSplit == iBestQuad )
9340 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9345 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9347 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9348 myLastCreatedElems.Append(newElem);
9349 if ( aShapeId && newElem )
9350 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9353 // change nodes of theFace
9354 const SMDS_MeshNode* newNodes[ 4 ];
9355 newNodes[ 0 ] = linkNodes[ i1 ];
9356 newNodes[ 1 ] = linkNodes[ i2 ];
9357 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9358 newNodes[ 3 ] = nodes[ i4 ];
9359 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9360 const SMDS_MeshElement* newElem = 0;
9361 if (iSplit == iBestQuad)
9362 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9364 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9365 myLastCreatedElems.Append(newElem);
9366 if ( aShapeId && newElem )
9367 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9368 } // end if(!theFace->IsQuadratic())
9369 else { // theFace is quadratic
9370 // we have to split theFace on simple triangles and one simple quadrangle
9372 int nbshift = tmp*2;
9373 // shift nodes in nodes[] by nbshift
9375 for(i=0; i<nbshift; i++) {
9376 const SMDS_MeshNode* n = nodes[0];
9377 for(j=0; j<nbFaceNodes-1; j++) {
9378 nodes[j] = nodes[j+1];
9380 nodes[nbFaceNodes-1] = n;
9382 il1 = il1 - nbshift;
9383 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9384 // n0 n1 n2 n0 n1 n2
9385 // +-----+-----+ +-----+-----+
9394 // create new elements
9395 int aShapeId = FindShape( theFace );
9398 if(nbFaceNodes==6) { // quadratic triangle
9399 SMDS_MeshElement* newElem =
9400 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9401 myLastCreatedElems.Append(newElem);
9402 if ( aShapeId && newElem )
9403 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9404 if(theFace->IsMediumNode(nodes[il1])) {
9405 // create quadrangle
9406 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9407 myLastCreatedElems.Append(newElem);
9408 if ( aShapeId && newElem )
9409 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9415 // create quadrangle
9416 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9417 myLastCreatedElems.Append(newElem);
9418 if ( aShapeId && newElem )
9419 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9425 else { // nbFaceNodes==8 - quadratic quadrangle
9426 SMDS_MeshElement* newElem =
9427 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9428 myLastCreatedElems.Append(newElem);
9429 if ( aShapeId && newElem )
9430 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9431 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9432 myLastCreatedElems.Append(newElem);
9433 if ( aShapeId && newElem )
9434 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9435 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9436 myLastCreatedElems.Append(newElem);
9437 if ( aShapeId && newElem )
9438 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9439 if(theFace->IsMediumNode(nodes[il1])) {
9440 // create quadrangle
9441 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9442 myLastCreatedElems.Append(newElem);
9443 if ( aShapeId && newElem )
9444 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9450 // create quadrangle
9451 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9452 myLastCreatedElems.Append(newElem);
9453 if ( aShapeId && newElem )
9454 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9460 // create needed triangles using n1,n2,n3 and inserted nodes
9461 int nbn = 2 + aNodesToInsert.size();
9462 //const SMDS_MeshNode* aNodes[nbn];
9463 vector<const SMDS_MeshNode*> aNodes(nbn);
9464 aNodes[0] = nodes[n1];
9465 aNodes[nbn-1] = nodes[n2];
9466 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9467 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9468 aNodes[iNode++] = *nIt;
9470 for(i=1; i<nbn; i++) {
9471 SMDS_MeshElement* newElem =
9472 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9473 myLastCreatedElems.Append(newElem);
9474 if ( aShapeId && newElem )
9475 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9479 aMesh->RemoveElement(theFace);
9482 //=======================================================================
9483 //function : UpdateVolumes
9485 //=======================================================================
9486 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9487 const SMDS_MeshNode* theBetweenNode2,
9488 list<const SMDS_MeshNode*>& theNodesToInsert)
9490 myLastCreatedElems.Clear();
9491 myLastCreatedNodes.Clear();
9493 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9494 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9495 const SMDS_MeshElement* elem = invElemIt->next();
9497 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9498 SMDS_VolumeTool aVolume (elem);
9499 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9502 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9503 int iface, nbFaces = aVolume.NbFaces();
9504 vector<const SMDS_MeshNode *> poly_nodes;
9505 vector<int> quantities (nbFaces);
9507 for (iface = 0; iface < nbFaces; iface++) {
9508 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9509 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9510 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9512 for (int inode = 0; inode < nbFaceNodes; inode++) {
9513 poly_nodes.push_back(faceNodes[inode]);
9515 if (nbInserted == 0) {
9516 if (faceNodes[inode] == theBetweenNode1) {
9517 if (faceNodes[inode + 1] == theBetweenNode2) {
9518 nbInserted = theNodesToInsert.size();
9520 // add nodes to insert
9521 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9522 for (; nIt != theNodesToInsert.end(); nIt++) {
9523 poly_nodes.push_back(*nIt);
9527 else if (faceNodes[inode] == theBetweenNode2) {
9528 if (faceNodes[inode + 1] == theBetweenNode1) {
9529 nbInserted = theNodesToInsert.size();
9531 // add nodes to insert in reversed order
9532 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9534 for (; nIt != theNodesToInsert.begin(); nIt--) {
9535 poly_nodes.push_back(*nIt);
9537 poly_nodes.push_back(*nIt);
9544 quantities[iface] = nbFaceNodes + nbInserted;
9547 // Replace or update the volume
9548 SMESHDS_Mesh *aMesh = GetMeshDS();
9550 if (elem->IsPoly()) {
9551 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9555 int aShapeId = FindShape( elem );
9557 SMDS_MeshElement* newElem =
9558 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9559 myLastCreatedElems.Append(newElem);
9560 if (aShapeId && newElem)
9561 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9563 aMesh->RemoveElement(elem);
9570 //================================================================================
9572 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9574 //================================================================================
9576 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9577 vector<const SMDS_MeshNode *> & nodes,
9578 vector<int> & nbNodeInFaces )
9581 nbNodeInFaces.clear();
9582 SMDS_VolumeTool vTool ( elem );
9583 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9585 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9586 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9587 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9592 //=======================================================================
9594 * \brief Convert elements contained in a submesh to quadratic
9595 * \return int - nb of checked elements
9597 //=======================================================================
9599 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9600 SMESH_MesherHelper& theHelper,
9601 const bool theForce3d)
9604 if( !theSm ) return nbElem;
9606 vector<int> nbNodeInFaces;
9607 vector<const SMDS_MeshNode *> nodes;
9608 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9609 while(ElemItr->more())
9612 const SMDS_MeshElement* elem = ElemItr->next();
9613 if( !elem ) continue;
9615 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9616 if ( elem->IsQuadratic() )
9619 switch ( aGeomType ) {
9620 case SMDSEntity_Quad_Quadrangle:
9621 case SMDSEntity_Quad_Hexa: alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9622 case SMDSEntity_BiQuad_Quadrangle:
9623 case SMDSEntity_TriQuad_Hexa: alreadyOK = theHelper.GetIsBiQuadratic(); break;
9624 default: alreadyOK = true;
9626 if ( alreadyOK ) continue;
9628 // get elem data needed to re-create it
9630 const int id = elem->GetID();
9631 const int nbNodes = elem->NbCornerNodes();
9632 const SMDSAbs_ElementType aType = elem->GetType();
9633 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9634 if ( aGeomType == SMDSEntity_Polyhedra )
9635 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9636 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9637 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9639 // remove a linear element
9640 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9642 const SMDS_MeshElement* NewElem = 0;
9648 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9656 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9659 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9662 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9667 case SMDSAbs_Volume :
9671 case SMDSEntity_Tetra:
9672 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9674 case SMDSEntity_Pyramid:
9675 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9677 case SMDSEntity_Penta:
9678 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9680 case SMDSEntity_Hexa:
9681 case SMDSEntity_Quad_Hexa:
9682 case SMDSEntity_TriQuad_Hexa:
9683 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9684 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9686 case SMDSEntity_Hexagonal_Prism:
9688 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9695 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9697 theSm->AddElement( NewElem );
9701 //=======================================================================
9702 //function : ConvertToQuadratic
9704 //=======================================================================
9706 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9708 SMESHDS_Mesh* meshDS = GetMeshDS();
9710 SMESH_MesherHelper aHelper(*myMesh);
9712 aHelper.SetIsQuadratic( true );
9713 aHelper.SetIsBiQuadratic( theToBiQuad );
9714 aHelper.SetElementsOnShape(true);
9716 int nbCheckedElems = 0;
9717 if ( myMesh->HasShapeToMesh() )
9719 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9721 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9722 while ( smIt->more() ) {
9723 SMESH_subMesh* sm = smIt->next();
9724 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9725 aHelper.SetSubShape( sm->GetSubShape() );
9726 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9731 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9732 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9734 aHelper.SetElementsOnShape(false);
9735 SMESHDS_SubMesh *smDS = 0;
9736 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9737 while(aEdgeItr->more())
9739 const SMDS_MeshEdge* edge = aEdgeItr->next();
9740 if(edge && !edge->IsQuadratic())
9742 int id = edge->GetID();
9743 //MESSAGE("edge->GetID() " << id);
9744 const SMDS_MeshNode* n1 = edge->GetNode(0);
9745 const SMDS_MeshNode* n2 = edge->GetNode(1);
9747 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9749 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9750 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9753 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9754 while(aFaceItr->more())
9756 const SMDS_MeshFace* face = aFaceItr->next();
9757 if ( !face ) continue;
9759 const SMDSAbs_EntityType type = face->GetEntityType();
9760 if (( theToBiQuad && type == SMDSEntity_BiQuad_Quadrangle ) ||
9761 ( !theToBiQuad && type == SMDSEntity_Quad_Quadrangle ))
9764 const int id = face->GetID();
9765 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9767 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9769 SMDS_MeshFace * NewFace = 0;
9772 case SMDSEntity_Triangle:
9773 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9775 case SMDSEntity_Quadrangle:
9776 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9779 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9781 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9783 vector<int> nbNodeInFaces;
9784 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9785 while(aVolumeItr->more())
9787 const SMDS_MeshVolume* volume = aVolumeItr->next();
9788 if(!volume || volume->IsQuadratic() ) continue;
9790 const SMDSAbs_EntityType type = volume->GetEntityType();
9791 if (( theToBiQuad && type == SMDSEntity_TriQuad_Hexa ) ||
9792 ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
9795 const int id = volume->GetID();
9796 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9797 if ( type == SMDSEntity_Polyhedra )
9798 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9799 else if ( type == SMDSEntity_Hexagonal_Prism )
9800 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9802 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9804 SMDS_MeshVolume * NewVolume = 0;
9807 case SMDSEntity_Tetra:
9808 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9810 case SMDSEntity_Hexa:
9811 case SMDSEntity_Quad_Hexa:
9812 case SMDSEntity_TriQuad_Hexa:
9813 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9814 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9816 case SMDSEntity_Pyramid:
9817 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9818 nodes[3], nodes[4], id, theForce3d);
9820 case SMDSEntity_Penta:
9821 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9822 nodes[3], nodes[4], nodes[5], id, theForce3d);
9824 case SMDSEntity_Hexagonal_Prism:
9826 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9828 ReplaceElemInGroups(volume, NewVolume, meshDS);
9833 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9834 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9835 aHelper.FixQuadraticElements(myError);
9839 //================================================================================
9841 * \brief Makes given elements quadratic
9842 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9843 * \param theElements - elements to make quadratic
9845 //================================================================================
9847 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9848 TIDSortedElemSet& theElements,
9849 const bool theToBiQuad)
9851 if ( theElements.empty() ) return;
9853 // we believe that all theElements are of the same type
9854 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9856 // get all nodes shared by theElements
9857 TIDSortedNodeSet allNodes;
9858 TIDSortedElemSet::iterator eIt = theElements.begin();
9859 for ( ; eIt != theElements.end(); ++eIt )
9860 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9862 // complete theElements with elements of lower dim whose all nodes are in allNodes
9864 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9865 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9866 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9867 for ( ; nIt != allNodes.end(); ++nIt )
9869 const SMDS_MeshNode* n = *nIt;
9870 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9871 while ( invIt->more() )
9873 const SMDS_MeshElement* e = invIt->next();
9874 if ( e->IsQuadratic() )
9877 switch ( e->GetEntityType() ) {
9878 case SMDSEntity_Quad_Quadrangle:
9879 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9880 case SMDSEntity_BiQuad_Quadrangle:
9881 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9882 default: alreadyOK = true;
9886 quadAdjacentElems[ e->GetType() ].insert( e );
9890 if ( e->GetType() >= elemType )
9892 continue; // same type of more complex linear element
9895 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9896 continue; // e is already checked
9900 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9901 while ( nodeIt->more() && allIn )
9902 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9904 theElements.insert(e );
9908 SMESH_MesherHelper helper(*myMesh);
9909 helper.SetIsQuadratic( true );
9910 helper.SetIsBiQuadratic( theToBiQuad );
9912 // add links of quadratic adjacent elements to the helper
9914 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9915 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9916 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9918 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9920 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9921 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9922 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9924 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9926 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9927 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9928 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9930 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9933 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9935 SMESHDS_Mesh* meshDS = GetMeshDS();
9936 SMESHDS_SubMesh* smDS = 0;
9937 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9939 const SMDS_MeshElement* elem = *eIt;
9940 if( elem->NbNodes() < 2 || elem->IsPoly() )
9943 if ( elem->IsQuadratic() )
9946 switch ( elem->GetEntityType() ) {
9947 case SMDSEntity_Quad_Quadrangle:
9948 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9949 case SMDSEntity_BiQuad_Quadrangle:
9950 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9951 default: alreadyOK = true;
9953 if ( alreadyOK ) continue;
9956 const SMDSAbs_ElementType type = elem->GetType();
9957 const int id = elem->GetID();
9958 const int nbNodes = elem->NbCornerNodes();
9959 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9961 if ( !smDS || !smDS->Contains( elem ))
9962 smDS = meshDS->MeshElements( elem->getshapeId() );
9963 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9965 SMDS_MeshElement * newElem = 0;
9968 case 4: // cases for most frequently used element types go first (for optimization)
9969 if ( type == SMDSAbs_Volume )
9970 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9972 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9975 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9976 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9979 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9982 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9985 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9986 nodes[4], id, theForce3d);
9989 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9990 nodes[4], nodes[5], id, theForce3d);
9994 ReplaceElemInGroups( elem, newElem, meshDS);
9995 if( newElem && smDS )
9996 smDS->AddElement( newElem );
9999 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
10000 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
10001 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
10002 helper.FixQuadraticElements( myError );
10006 //=======================================================================
10008 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
10009 * \return int - nb of checked elements
10011 //=======================================================================
10013 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
10014 SMDS_ElemIteratorPtr theItr,
10015 const int theShapeID)
10018 SMESHDS_Mesh* meshDS = GetMeshDS();
10020 while( theItr->more() )
10022 const SMDS_MeshElement* elem = theItr->next();
10024 if( elem && elem->IsQuadratic())
10026 int id = elem->GetID();
10027 int nbCornerNodes = elem->NbCornerNodes();
10028 SMDSAbs_ElementType aType = elem->GetType();
10030 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
10032 //remove a quadratic element
10033 if ( !theSm || !theSm->Contains( elem ))
10034 theSm = meshDS->MeshElements( elem->getshapeId() );
10035 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
10037 // remove medium nodes
10038 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
10039 if ( nodes[i]->NbInverseElements() == 0 )
10040 meshDS->RemoveFreeNode( nodes[i], theSm );
10042 // add a linear element
10043 nodes.resize( nbCornerNodes );
10044 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
10045 ReplaceElemInGroups(elem, newElem, meshDS);
10046 if( theSm && newElem )
10047 theSm->AddElement( newElem );
10053 //=======================================================================
10054 //function : ConvertFromQuadratic
10056 //=======================================================================
10058 bool SMESH_MeshEditor::ConvertFromQuadratic()
10060 int nbCheckedElems = 0;
10061 if ( myMesh->HasShapeToMesh() )
10063 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
10065 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
10066 while ( smIt->more() ) {
10067 SMESH_subMesh* sm = smIt->next();
10068 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
10069 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
10075 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
10076 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10078 SMESHDS_SubMesh *aSM = 0;
10079 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10087 //================================================================================
10089 * \brief Return true if all medium nodes of the element are in the node set
10091 //================================================================================
10093 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10095 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10096 if ( !nodeSet.count( elem->GetNode(i) ))
10102 //================================================================================
10104 * \brief Makes given elements linear
10106 //================================================================================
10108 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10110 if ( theElements.empty() ) return;
10112 // collect IDs of medium nodes of theElements; some of these nodes will be removed
10113 set<int> mediumNodeIDs;
10114 TIDSortedElemSet::iterator eIt = theElements.begin();
10115 for ( ; eIt != theElements.end(); ++eIt )
10117 const SMDS_MeshElement* e = *eIt;
10118 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10119 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10122 // replace given elements by linear ones
10123 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10124 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10125 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10127 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10128 // except those elements sharing medium nodes of quadratic element whose medium nodes
10129 // are not all in mediumNodeIDs
10131 // get remaining medium nodes
10132 TIDSortedNodeSet mediumNodes;
10133 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10134 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10135 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10136 mediumNodes.insert( mediumNodes.end(), n );
10138 // find more quadratic elements to convert
10139 TIDSortedElemSet moreElemsToConvert;
10140 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10141 for ( ; nIt != mediumNodes.end(); ++nIt )
10143 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10144 while ( invIt->more() )
10146 const SMDS_MeshElement* e = invIt->next();
10147 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10149 // find a more complex element including e and
10150 // whose medium nodes are not in mediumNodes
10151 bool complexFound = false;
10152 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10154 SMDS_ElemIteratorPtr invIt2 =
10155 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10156 while ( invIt2->more() )
10158 const SMDS_MeshElement* eComplex = invIt2->next();
10159 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10161 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10162 if ( nbCommonNodes == e->NbNodes())
10164 complexFound = true;
10165 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10171 if ( !complexFound )
10172 moreElemsToConvert.insert( e );
10176 elemIt = SMDS_ElemIteratorPtr
10177 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10178 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10181 //=======================================================================
10182 //function : SewSideElements
10184 //=======================================================================
10186 SMESH_MeshEditor::Sew_Error
10187 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10188 TIDSortedElemSet& theSide2,
10189 const SMDS_MeshNode* theFirstNode1,
10190 const SMDS_MeshNode* theFirstNode2,
10191 const SMDS_MeshNode* theSecondNode1,
10192 const SMDS_MeshNode* theSecondNode2)
10194 myLastCreatedElems.Clear();
10195 myLastCreatedNodes.Clear();
10197 MESSAGE ("::::SewSideElements()");
10198 if ( theSide1.size() != theSide2.size() )
10199 return SEW_DIFF_NB_OF_ELEMENTS;
10201 Sew_Error aResult = SEW_OK;
10203 // 1. Build set of faces representing each side
10204 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10205 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10207 // =======================================================================
10208 // 1. Build set of faces representing each side:
10209 // =======================================================================
10210 // a. build set of nodes belonging to faces
10211 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10212 // c. create temporary faces representing side of volumes if correspondent
10213 // face does not exist
10215 SMESHDS_Mesh* aMesh = GetMeshDS();
10216 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10217 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10218 TIDSortedElemSet faceSet1, faceSet2;
10219 set<const SMDS_MeshElement*> volSet1, volSet2;
10220 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10221 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10222 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10223 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10224 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10225 int iSide, iFace, iNode;
10227 list<const SMDS_MeshElement* > tempFaceList;
10228 for ( iSide = 0; iSide < 2; iSide++ ) {
10229 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10230 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10231 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10232 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10233 set<const SMDS_MeshElement*>::iterator vIt;
10234 TIDSortedElemSet::iterator eIt;
10235 set<const SMDS_MeshNode*>::iterator nIt;
10237 // check that given nodes belong to given elements
10238 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10239 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10240 int firstIndex = -1, secondIndex = -1;
10241 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10242 const SMDS_MeshElement* elem = *eIt;
10243 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10244 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10245 if ( firstIndex > -1 && secondIndex > -1 ) break;
10247 if ( firstIndex < 0 || secondIndex < 0 ) {
10248 // we can simply return until temporary faces created
10249 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10252 // -----------------------------------------------------------
10253 // 1a. Collect nodes of existing faces
10254 // and build set of face nodes in order to detect missing
10255 // faces corresponding to sides of volumes
10256 // -----------------------------------------------------------
10258 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10260 // loop on the given element of a side
10261 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10262 //const SMDS_MeshElement* elem = *eIt;
10263 const SMDS_MeshElement* elem = *eIt;
10264 if ( elem->GetType() == SMDSAbs_Face ) {
10265 faceSet->insert( elem );
10266 set <const SMDS_MeshNode*> faceNodeSet;
10267 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10268 while ( nodeIt->more() ) {
10269 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10270 nodeSet->insert( n );
10271 faceNodeSet.insert( n );
10273 setOfFaceNodeSet.insert( faceNodeSet );
10275 else if ( elem->GetType() == SMDSAbs_Volume )
10276 volSet->insert( elem );
10278 // ------------------------------------------------------------------------------
10279 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10280 // ------------------------------------------------------------------------------
10282 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10283 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10284 while ( fIt->more() ) { // loop on faces sharing a node
10285 const SMDS_MeshElement* f = fIt->next();
10286 if ( faceSet->find( f ) == faceSet->end() ) {
10287 // check if all nodes are in nodeSet and
10288 // complete setOfFaceNodeSet if they are
10289 set <const SMDS_MeshNode*> faceNodeSet;
10290 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10291 bool allInSet = true;
10292 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10293 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10294 if ( nodeSet->find( n ) == nodeSet->end() )
10297 faceNodeSet.insert( n );
10300 faceSet->insert( f );
10301 setOfFaceNodeSet.insert( faceNodeSet );
10307 // -------------------------------------------------------------------------
10308 // 1c. Create temporary faces representing sides of volumes if correspondent
10309 // face does not exist
10310 // -------------------------------------------------------------------------
10312 if ( !volSet->empty() ) {
10313 //int nodeSetSize = nodeSet->size();
10315 // loop on given volumes
10316 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10317 SMDS_VolumeTool vol (*vIt);
10318 // loop on volume faces: find free faces
10319 // --------------------------------------
10320 list<const SMDS_MeshElement* > freeFaceList;
10321 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10322 if ( !vol.IsFreeFace( iFace ))
10324 // check if there is already a face with same nodes in a face set
10325 const SMDS_MeshElement* aFreeFace = 0;
10326 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10327 int nbNodes = vol.NbFaceNodes( iFace );
10328 set <const SMDS_MeshNode*> faceNodeSet;
10329 vol.GetFaceNodes( iFace, faceNodeSet );
10330 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10332 // no such a face is given but it still can exist, check it
10333 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10334 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10336 if ( !aFreeFace ) {
10337 // create a temporary face
10338 if ( nbNodes == 3 ) {
10339 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10340 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10342 else if ( nbNodes == 4 ) {
10343 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10344 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10347 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10348 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10349 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10352 tempFaceList.push_back( aFreeFace );
10356 freeFaceList.push_back( aFreeFace );
10358 } // loop on faces of a volume
10360 // choose one of several free faces of a volume
10361 // --------------------------------------------
10362 if ( freeFaceList.size() > 1 ) {
10363 // choose a face having max nb of nodes shared by other elems of a side
10364 int maxNbNodes = -1;
10365 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10366 while ( fIt != freeFaceList.end() ) { // loop on free faces
10367 int nbSharedNodes = 0;
10368 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10369 while ( nodeIt->more() ) { // loop on free face nodes
10370 const SMDS_MeshNode* n =
10371 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10372 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10373 while ( invElemIt->more() ) {
10374 const SMDS_MeshElement* e = invElemIt->next();
10375 nbSharedNodes += faceSet->count( e );
10376 nbSharedNodes += elemSet->count( e );
10379 if ( nbSharedNodes > maxNbNodes ) {
10380 maxNbNodes = nbSharedNodes;
10381 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10383 else if ( nbSharedNodes == maxNbNodes ) {
10387 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10390 if ( freeFaceList.size() > 1 )
10392 // could not choose one face, use another way
10393 // choose a face most close to the bary center of the opposite side
10394 gp_XYZ aBC( 0., 0., 0. );
10395 set <const SMDS_MeshNode*> addedNodes;
10396 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10397 eIt = elemSet2->begin();
10398 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10399 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10400 while ( nodeIt->more() ) { // loop on free face nodes
10401 const SMDS_MeshNode* n =
10402 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10403 if ( addedNodes.insert( n ).second )
10404 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10407 aBC /= addedNodes.size();
10408 double minDist = DBL_MAX;
10409 fIt = freeFaceList.begin();
10410 while ( fIt != freeFaceList.end() ) { // loop on free faces
10412 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10413 while ( nodeIt->more() ) { // loop on free face nodes
10414 const SMDS_MeshNode* n =
10415 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10416 gp_XYZ p( n->X(),n->Y(),n->Z() );
10417 dist += ( aBC - p ).SquareModulus();
10419 if ( dist < minDist ) {
10421 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10424 fIt = freeFaceList.erase( fIt++ );
10427 } // choose one of several free faces of a volume
10429 if ( freeFaceList.size() == 1 ) {
10430 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10431 faceSet->insert( aFreeFace );
10432 // complete a node set with nodes of a found free face
10433 // for ( iNode = 0; iNode < ; iNode++ )
10434 // nodeSet->insert( fNodes[ iNode ] );
10437 } // loop on volumes of a side
10439 // // complete a set of faces if new nodes in a nodeSet appeared
10440 // // ----------------------------------------------------------
10441 // if ( nodeSetSize != nodeSet->size() ) {
10442 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10443 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10444 // while ( fIt->more() ) { // loop on faces sharing a node
10445 // const SMDS_MeshElement* f = fIt->next();
10446 // if ( faceSet->find( f ) == faceSet->end() ) {
10447 // // check if all nodes are in nodeSet and
10448 // // complete setOfFaceNodeSet if they are
10449 // set <const SMDS_MeshNode*> faceNodeSet;
10450 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10451 // bool allInSet = true;
10452 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10453 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10454 // if ( nodeSet->find( n ) == nodeSet->end() )
10455 // allInSet = false;
10457 // faceNodeSet.insert( n );
10459 // if ( allInSet ) {
10460 // faceSet->insert( f );
10461 // setOfFaceNodeSet.insert( faceNodeSet );
10467 } // Create temporary faces, if there are volumes given
10470 if ( faceSet1.size() != faceSet2.size() ) {
10471 // delete temporary faces: they are in reverseElements of actual nodes
10472 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10473 // while ( tmpFaceIt->more() )
10474 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10475 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10476 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10477 // aMesh->RemoveElement(*tmpFaceIt);
10478 MESSAGE("Diff nb of faces");
10479 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10482 // ============================================================
10483 // 2. Find nodes to merge:
10484 // bind a node to remove to a node to put instead
10485 // ============================================================
10487 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10488 if ( theFirstNode1 != theFirstNode2 )
10489 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10490 if ( theSecondNode1 != theSecondNode2 )
10491 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10493 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10494 set< long > linkIdSet; // links to process
10495 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10497 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10498 list< NLink > linkList[2];
10499 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10500 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10501 // loop on links in linkList; find faces by links and append links
10502 // of the found faces to linkList
10503 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10504 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10506 NLink link[] = { *linkIt[0], *linkIt[1] };
10507 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10508 if ( !linkIdSet.count( linkID ) )
10511 // by links, find faces in the face sets,
10512 // and find indices of link nodes in the found faces;
10513 // in a face set, there is only one or no face sharing a link
10514 // ---------------------------------------------------------------
10516 const SMDS_MeshElement* face[] = { 0, 0 };
10517 vector<const SMDS_MeshNode*> fnodes[2];
10518 int iLinkNode[2][2];
10519 TIDSortedElemSet avoidSet;
10520 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10521 const SMDS_MeshNode* n1 = link[iSide].first;
10522 const SMDS_MeshNode* n2 = link[iSide].second;
10523 //cout << "Side " << iSide << " ";
10524 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10525 // find a face by two link nodes
10526 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10527 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10528 if ( face[ iSide ])
10530 //cout << " F " << face[ iSide]->GetID() <<endl;
10531 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10532 // put face nodes to fnodes
10533 if ( face[ iSide ]->IsQuadratic() )
10535 // use interlaced nodes iterator
10536 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10537 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10538 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10539 while ( nIter->more() )
10540 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10544 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10545 face[ iSide ]->end_nodes() );
10547 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10551 // check similarity of elements of the sides
10552 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10553 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10554 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10555 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10558 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10560 break; // do not return because it's necessary to remove tmp faces
10563 // set nodes to merge
10564 // -------------------
10566 if ( face[0] && face[1] ) {
10567 const int nbNodes = face[0]->NbNodes();
10568 if ( nbNodes != face[1]->NbNodes() ) {
10569 MESSAGE("Diff nb of face nodes");
10570 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10571 break; // do not return because it s necessary to remove tmp faces
10573 bool reverse[] = { false, false }; // order of nodes in the link
10574 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10575 // analyse link orientation in faces
10576 int i1 = iLinkNode[ iSide ][ 0 ];
10577 int i2 = iLinkNode[ iSide ][ 1 ];
10578 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10580 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10581 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10582 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10584 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10585 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10588 // add other links of the faces to linkList
10589 // -----------------------------------------
10591 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10592 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10593 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10594 if ( !iter_isnew.second ) { // already in a set: no need to process
10595 linkIdSet.erase( iter_isnew.first );
10597 else // new in set == encountered for the first time: add
10599 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10600 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10601 linkList[0].push_back ( NLink( n1, n2 ));
10602 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10607 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10610 } // loop on link lists
10612 if ( aResult == SEW_OK &&
10613 ( //linkIt[0] != linkList[0].end() ||
10614 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10615 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10616 " " << (faceSetPtr[1]->empty()));
10617 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10620 // ====================================================================
10621 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10622 // ====================================================================
10624 // delete temporary faces
10625 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10626 // while ( tmpFaceIt->more() )
10627 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10628 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10629 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10630 aMesh->RemoveElement(*tmpFaceIt);
10632 if ( aResult != SEW_OK)
10635 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10636 // loop on nodes replacement map
10637 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10638 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10639 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10640 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10641 nodeIDsToRemove.push_back( nToRemove->GetID() );
10642 // loop on elements sharing nToRemove
10643 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10644 while ( invElemIt->more() ) {
10645 const SMDS_MeshElement* e = invElemIt->next();
10646 // get a new suite of nodes: make replacement
10647 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10648 vector< const SMDS_MeshNode*> nodes( nbNodes );
10649 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10650 while ( nIt->more() ) {
10651 const SMDS_MeshNode* n =
10652 static_cast<const SMDS_MeshNode*>( nIt->next() );
10653 nnIt = nReplaceMap.find( n );
10654 if ( nnIt != nReplaceMap.end() ) {
10656 n = (*nnIt).second;
10660 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10661 // elemIDsToRemove.push_back( e->GetID() );
10665 SMDSAbs_ElementType etyp = e->GetType();
10666 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10669 myLastCreatedElems.Append(newElem);
10670 AddToSameGroups(newElem, e, aMesh);
10671 int aShapeId = e->getshapeId();
10674 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10677 aMesh->RemoveElement(e);
10682 Remove( nodeIDsToRemove, true );
10687 //================================================================================
10689 * \brief Find corresponding nodes in two sets of faces
10690 * \param theSide1 - first face set
10691 * \param theSide2 - second first face
10692 * \param theFirstNode1 - a boundary node of set 1
10693 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10694 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10695 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10696 * \param nReplaceMap - output map of corresponding nodes
10697 * \return bool - is a success or not
10699 //================================================================================
10702 //#define DEBUG_MATCHING_NODES
10705 SMESH_MeshEditor::Sew_Error
10706 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10707 set<const SMDS_MeshElement*>& theSide2,
10708 const SMDS_MeshNode* theFirstNode1,
10709 const SMDS_MeshNode* theFirstNode2,
10710 const SMDS_MeshNode* theSecondNode1,
10711 const SMDS_MeshNode* theSecondNode2,
10712 TNodeNodeMap & nReplaceMap)
10714 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10716 nReplaceMap.clear();
10717 if ( theFirstNode1 != theFirstNode2 )
10718 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10719 if ( theSecondNode1 != theSecondNode2 )
10720 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10722 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10723 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10725 list< NLink > linkList[2];
10726 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10727 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10729 // loop on links in linkList; find faces by links and append links
10730 // of the found faces to linkList
10731 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10732 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10733 NLink link[] = { *linkIt[0], *linkIt[1] };
10734 if ( linkSet.find( link[0] ) == linkSet.end() )
10737 // by links, find faces in the face sets,
10738 // and find indices of link nodes in the found faces;
10739 // in a face set, there is only one or no face sharing a link
10740 // ---------------------------------------------------------------
10742 const SMDS_MeshElement* face[] = { 0, 0 };
10743 list<const SMDS_MeshNode*> notLinkNodes[2];
10744 //bool reverse[] = { false, false }; // order of notLinkNodes
10746 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10748 const SMDS_MeshNode* n1 = link[iSide].first;
10749 const SMDS_MeshNode* n2 = link[iSide].second;
10750 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10751 set< const SMDS_MeshElement* > facesOfNode1;
10752 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10754 // during a loop of the first node, we find all faces around n1,
10755 // during a loop of the second node, we find one face sharing both n1 and n2
10756 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10757 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10758 while ( fIt->more() ) { // loop on faces sharing a node
10759 const SMDS_MeshElement* f = fIt->next();
10760 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10761 ! facesOfNode1.insert( f ).second ) // f encounters twice
10763 if ( face[ iSide ] ) {
10764 MESSAGE( "2 faces per link " );
10765 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10768 faceSet->erase( f );
10770 // get not link nodes
10771 int nbN = f->NbNodes();
10772 if ( f->IsQuadratic() )
10774 nbNodes[ iSide ] = nbN;
10775 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10776 int i1 = f->GetNodeIndex( n1 );
10777 int i2 = f->GetNodeIndex( n2 );
10778 int iEnd = nbN, iBeg = -1, iDelta = 1;
10779 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10781 std::swap( iEnd, iBeg ); iDelta = -1;
10786 if ( i == iEnd ) i = iBeg + iDelta;
10787 if ( i == i1 ) break;
10788 nodes.push_back ( f->GetNode( i ) );
10794 // check similarity of elements of the sides
10795 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10796 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10797 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10798 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10801 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10805 // set nodes to merge
10806 // -------------------
10808 if ( face[0] && face[1] ) {
10809 if ( nbNodes[0] != nbNodes[1] ) {
10810 MESSAGE("Diff nb of face nodes");
10811 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10813 #ifdef DEBUG_MATCHING_NODES
10814 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10815 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10816 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10818 int nbN = nbNodes[0];
10820 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10821 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10822 for ( int i = 0 ; i < nbN - 2; ++i ) {
10823 #ifdef DEBUG_MATCHING_NODES
10824 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10826 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10830 // add other links of the face 1 to linkList
10831 // -----------------------------------------
10833 const SMDS_MeshElement* f0 = face[0];
10834 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10835 for ( int i = 0; i < nbN; i++ )
10837 const SMDS_MeshNode* n2 = f0->GetNode( i );
10838 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10839 linkSet.insert( SMESH_TLink( n1, n2 ));
10840 if ( !iter_isnew.second ) { // already in a set: no need to process
10841 linkSet.erase( iter_isnew.first );
10843 else // new in set == encountered for the first time: add
10845 #ifdef DEBUG_MATCHING_NODES
10846 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10847 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10849 linkList[0].push_back ( NLink( n1, n2 ));
10850 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10855 } // loop on link lists
10860 //================================================================================
10862 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10863 \param theElems - the list of elements (edges or faces) to be replicated
10864 The nodes for duplication could be found from these elements
10865 \param theNodesNot - list of nodes to NOT replicate
10866 \param theAffectedElems - the list of elements (cells and edges) to which the
10867 replicated nodes should be associated to.
10868 \return TRUE if operation has been completed successfully, FALSE otherwise
10870 //================================================================================
10872 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10873 const TIDSortedElemSet& theNodesNot,
10874 const TIDSortedElemSet& theAffectedElems )
10876 myLastCreatedElems.Clear();
10877 myLastCreatedNodes.Clear();
10879 if ( theElems.size() == 0 )
10882 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10887 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10888 // duplicate elements and nodes
10889 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10890 // replce nodes by duplications
10891 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10895 //================================================================================
10897 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10898 \param theMeshDS - mesh instance
10899 \param theElems - the elements replicated or modified (nodes should be changed)
10900 \param theNodesNot - nodes to NOT replicate
10901 \param theNodeNodeMap - relation of old node to new created node
10902 \param theIsDoubleElem - flag os to replicate element or modify
10903 \return TRUE if operation has been completed successfully, FALSE otherwise
10905 //================================================================================
10907 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10908 const TIDSortedElemSet& theElems,
10909 const TIDSortedElemSet& theNodesNot,
10910 std::map< const SMDS_MeshNode*,
10911 const SMDS_MeshNode* >& theNodeNodeMap,
10912 const bool theIsDoubleElem )
10914 MESSAGE("doubleNodes");
10915 // iterate on through element and duplicate them (by nodes duplication)
10917 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10918 for ( ; elemItr != theElems.end(); ++elemItr )
10920 const SMDS_MeshElement* anElem = *elemItr;
10924 bool isDuplicate = false;
10925 // duplicate nodes to duplicate element
10926 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10927 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10929 while ( anIter->more() )
10932 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10933 SMDS_MeshNode* aNewNode = aCurrNode;
10934 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10935 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10936 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10939 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10940 theNodeNodeMap[ aCurrNode ] = aNewNode;
10941 myLastCreatedNodes.Append( aNewNode );
10943 isDuplicate |= (aCurrNode != aNewNode);
10944 newNodes[ ind++ ] = aNewNode;
10946 if ( !isDuplicate )
10949 if ( theIsDoubleElem )
10950 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10953 MESSAGE("ChangeElementNodes");
10954 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10961 //================================================================================
10963 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10964 \param theNodes - identifiers of nodes to be doubled
10965 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10966 nodes. If list of element identifiers is empty then nodes are doubled but
10967 they not assigned to elements
10968 \return TRUE if operation has been completed successfully, FALSE otherwise
10970 //================================================================================
10972 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10973 const std::list< int >& theListOfModifiedElems )
10975 MESSAGE("DoubleNodes");
10976 myLastCreatedElems.Clear();
10977 myLastCreatedNodes.Clear();
10979 if ( theListOfNodes.size() == 0 )
10982 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10986 // iterate through nodes and duplicate them
10988 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10990 std::list< int >::const_iterator aNodeIter;
10991 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10993 int aCurr = *aNodeIter;
10994 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
11000 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11003 anOldNodeToNewNode[ aNode ] = aNewNode;
11004 myLastCreatedNodes.Append( aNewNode );
11008 // Create map of new nodes for modified elements
11010 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
11012 std::list< int >::const_iterator anElemIter;
11013 for ( anElemIter = theListOfModifiedElems.begin();
11014 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
11016 int aCurr = *anElemIter;
11017 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
11021 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
11023 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11025 while ( anIter->more() )
11027 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
11028 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
11030 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
11031 aNodeArr[ ind++ ] = aNewNode;
11034 aNodeArr[ ind++ ] = aCurrNode;
11036 anElemToNodes[ anElem ] = aNodeArr;
11039 // Change nodes of elements
11041 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
11042 anElemToNodesIter = anElemToNodes.begin();
11043 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
11045 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
11046 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
11049 MESSAGE("ChangeElementNodes");
11050 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
11059 //================================================================================
11061 \brief Check if element located inside shape
11062 \return TRUE if IN or ON shape, FALSE otherwise
11064 //================================================================================
11066 template<class Classifier>
11067 bool isInside(const SMDS_MeshElement* theElem,
11068 Classifier& theClassifier,
11069 const double theTol)
11071 gp_XYZ centerXYZ (0, 0, 0);
11072 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11073 while (aNodeItr->more())
11074 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
11076 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11077 theClassifier.Perform(aPnt, theTol);
11078 TopAbs_State aState = theClassifier.State();
11079 return (aState == TopAbs_IN || aState == TopAbs_ON );
11082 //================================================================================
11084 * \brief Classifier of the 3D point on the TopoDS_Face
11085 * with interaface suitable for isInside()
11087 //================================================================================
11089 struct _FaceClassifier
11091 Extrema_ExtPS _extremum;
11092 BRepAdaptor_Surface _surface;
11093 TopAbs_State _state;
11095 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11097 _extremum.Initialize( _surface,
11098 _surface.FirstUParameter(), _surface.LastUParameter(),
11099 _surface.FirstVParameter(), _surface.LastVParameter(),
11100 _surface.Tolerance(), _surface.Tolerance() );
11102 void Perform(const gp_Pnt& aPnt, double theTol)
11104 _state = TopAbs_OUT;
11105 _extremum.Perform(aPnt);
11106 if ( _extremum.IsDone() )
11107 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11108 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11109 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11111 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11114 TopAbs_State State() const
11121 //================================================================================
11123 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11124 This method is the first step of DoubleNodeElemGroupsInRegion.
11125 \param theElems - list of groups of elements (edges or faces) to be replicated
11126 \param theNodesNot - list of groups of nodes not to replicated
11127 \param theShape - shape to detect affected elements (element which geometric center
11128 located on or inside shape).
11129 The replicated nodes should be associated to affected elements.
11130 \return groups of affected elements
11131 \sa DoubleNodeElemGroupsInRegion()
11133 //================================================================================
11135 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11136 const TIDSortedElemSet& theNodesNot,
11137 const TopoDS_Shape& theShape,
11138 TIDSortedElemSet& theAffectedElems)
11140 if ( theShape.IsNull() )
11143 const double aTol = Precision::Confusion();
11144 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11145 auto_ptr<_FaceClassifier> aFaceClassifier;
11146 if ( theShape.ShapeType() == TopAbs_SOLID )
11148 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11149 bsc3d->PerformInfinitePoint(aTol);
11151 else if (theShape.ShapeType() == TopAbs_FACE )
11153 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11156 // iterates on indicated elements and get elements by back references from their nodes
11157 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11158 for ( ; elemItr != theElems.end(); ++elemItr )
11160 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11164 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11165 while ( nodeItr->more() )
11167 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11168 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11170 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11171 while ( backElemItr->more() )
11173 const SMDS_MeshElement* curElem = backElemItr->next();
11174 if ( curElem && theElems.find(curElem) == theElems.end() &&
11176 isInside( curElem, *bsc3d, aTol ) :
11177 isInside( curElem, *aFaceClassifier, aTol )))
11178 theAffectedElems.insert( curElem );
11185 //================================================================================
11187 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11188 \param theElems - group of of elements (edges or faces) to be replicated
11189 \param theNodesNot - group of nodes not to replicate
11190 \param theShape - shape to detect affected elements (element which geometric center
11191 located on or inside shape).
11192 The replicated nodes should be associated to affected elements.
11193 \return TRUE if operation has been completed successfully, FALSE otherwise
11195 //================================================================================
11197 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11198 const TIDSortedElemSet& theNodesNot,
11199 const TopoDS_Shape& theShape )
11201 if ( theShape.IsNull() )
11204 const double aTol = Precision::Confusion();
11205 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11206 auto_ptr<_FaceClassifier> aFaceClassifier;
11207 if ( theShape.ShapeType() == TopAbs_SOLID )
11209 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11210 bsc3d->PerformInfinitePoint(aTol);
11212 else if (theShape.ShapeType() == TopAbs_FACE )
11214 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11217 // iterates on indicated elements and get elements by back references from their nodes
11218 TIDSortedElemSet anAffected;
11219 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11220 for ( ; elemItr != theElems.end(); ++elemItr )
11222 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11226 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11227 while ( nodeItr->more() )
11229 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11230 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11232 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11233 while ( backElemItr->more() )
11235 const SMDS_MeshElement* curElem = backElemItr->next();
11236 if ( curElem && theElems.find(curElem) == theElems.end() &&
11238 isInside( curElem, *bsc3d, aTol ) :
11239 isInside( curElem, *aFaceClassifier, aTol )))
11240 anAffected.insert( curElem );
11244 return DoubleNodes( theElems, theNodesNot, anAffected );
11248 * \brief compute an oriented angle between two planes defined by four points.
11249 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11250 * @param p0 base of the rotation axe
11251 * @param p1 extremity of the rotation axe
11252 * @param g1 belongs to the first plane
11253 * @param g2 belongs to the second plane
11255 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11257 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11258 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11259 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11260 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11261 gp_Vec vref(p0, p1);
11264 gp_Vec n1 = vref.Crossed(v1);
11265 gp_Vec n2 = vref.Crossed(v2);
11266 return n2.AngleWithRef(n1, vref);
11270 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11271 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11272 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11273 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11274 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11275 * 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.
11276 * 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.
11277 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11278 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11279 * @param theElems - list of groups of volumes, where a group of volume is a set of
11280 * SMDS_MeshElements sorted by Id.
11281 * @param createJointElems - if TRUE, create the elements
11282 * @return TRUE if operation has been completed successfully, FALSE otherwise
11284 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11285 bool createJointElems)
11287 MESSAGE("----------------------------------------------");
11288 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11289 MESSAGE("----------------------------------------------");
11291 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11292 meshDS->BuildDownWardConnectivity(true);
11294 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11296 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11297 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11298 // build the list of nodes shared by 2 or more domains, with their domain indexes
11300 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11301 std::map<int,int>celldom; // cell vtkId --> domain
11302 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11303 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11304 faceDomains.clear();
11306 cellDomains.clear();
11307 nodeDomains.clear();
11308 std::map<int,int> emptyMap;
11309 std::set<int> emptySet;
11312 MESSAGE(".. Number of domains :"<<theElems.size());
11314 // Check if the domains do not share an element
11315 for (int idom = 0; idom < theElems.size()-1; idom++)
11317 // MESSAGE("... Check of domain #" << idom);
11318 const TIDSortedElemSet& domain = theElems[idom];
11319 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11320 for (; elemItr != domain.end(); ++elemItr)
11322 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11323 int idombisdeb = idom + 1 ;
11324 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11326 const TIDSortedElemSet& domainbis = theElems[idombis];
11327 if ( domainbis.count(anElem) )
11329 MESSAGE(".... Domain #" << idom);
11330 MESSAGE(".... Domain #" << idombis);
11331 throw SALOME_Exception("The domains are not disjoint.");
11338 for (int idom = 0; idom < theElems.size(); idom++)
11341 // --- build a map (face to duplicate --> volume to modify)
11342 // with all the faces shared by 2 domains (group of elements)
11343 // and corresponding volume of this domain, for each shared face.
11344 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11346 MESSAGE("... Neighbors of domain #" << idom);
11347 const TIDSortedElemSet& domain = theElems[idom];
11348 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11349 for (; elemItr != domain.end(); ++elemItr)
11351 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11354 int vtkId = anElem->getVtkId();
11355 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11356 int neighborsVtkIds[NBMAXNEIGHBORS];
11357 int downIds[NBMAXNEIGHBORS];
11358 unsigned char downTypes[NBMAXNEIGHBORS];
11359 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11360 for (int n = 0; n < nbNeighbors; n++)
11362 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11363 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11364 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11367 for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
11369 // MESSAGE("Domain " << idombis);
11370 const TIDSortedElemSet& domainbis = theElems[idombis];
11371 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11373 if ( ok ) // the characteristics of the face is stored
11375 DownIdType face(downIds[n], downTypes[n]);
11376 if (!faceDomains.count(face))
11377 faceDomains[face] = emptyMap; // create an empty entry for face
11378 if (!faceDomains[face].count(idom))
11380 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11381 celldom[vtkId] = idom;
11382 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11390 //MESSAGE("Number of shared faces " << faceDomains.size());
11391 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11393 // --- explore the shared faces domain by domain,
11394 // explore the nodes of the face and see if they belong to a cell in the domain,
11395 // which has only a node or an edge on the border (not a shared face)
11397 for (int idomain = 0; idomain < theElems.size(); idomain++)
11399 //MESSAGE("Domain " << idomain);
11400 const TIDSortedElemSet& domain = theElems[idomain];
11401 itface = faceDomains.begin();
11402 for (; itface != faceDomains.end(); ++itface)
11404 std::map<int, int> domvol = itface->second;
11405 if (!domvol.count(idomain))
11407 DownIdType face = itface->first;
11408 //MESSAGE(" --- face " << face.cellId);
11409 std::set<int> oldNodes;
11411 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11412 std::set<int>::iterator itn = oldNodes.begin();
11413 for (; itn != oldNodes.end(); ++itn)
11416 //MESSAGE(" node " << oldId);
11417 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11418 for (int i=0; i<l.ncells; i++)
11420 int vtkId = l.cells[i];
11421 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11422 if (!domain.count(anElem))
11424 int vtkType = grid->GetCellType(vtkId);
11425 int downId = grid->CellIdToDownId(vtkId);
11428 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11429 continue; // not OK at this stage of the algorithm:
11430 //no cells created after BuildDownWardConnectivity
11432 DownIdType aCell(downId, vtkType);
11433 if (!cellDomains.count(aCell))
11434 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11435 cellDomains[aCell][idomain] = vtkId;
11436 celldom[vtkId] = idomain;
11437 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11443 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11444 // for each shared face, get the nodes
11445 // for each node, for each domain of the face, create a clone of the node
11447 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11448 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11449 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11451 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11452 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11453 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11455 MESSAGE(".. Duplication of the nodes");
11456 for (int idomain = 0; idomain < theElems.size(); idomain++)
11458 itface = faceDomains.begin();
11459 for (; itface != faceDomains.end(); ++itface)
11461 std::map<int, int> domvol = itface->second;
11462 if (!domvol.count(idomain))
11464 DownIdType face = itface->first;
11465 //MESSAGE(" --- face " << face.cellId);
11466 std::set<int> oldNodes;
11468 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11469 std::set<int>::iterator itn = oldNodes.begin();
11470 for (; itn != oldNodes.end(); ++itn)
11473 //MESSAGE("-+-+-a node " << oldId);
11474 if (!nodeDomains.count(oldId))
11475 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11476 if (nodeDomains[oldId].empty())
11478 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11479 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11481 std::map<int, int>::iterator itdom = domvol.begin();
11482 for (; itdom != domvol.end(); ++itdom)
11484 int idom = itdom->first;
11485 //MESSAGE(" domain " << idom);
11486 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11488 if (nodeDomains[oldId].size() >= 2) // a multiple node
11490 vector<int> orderedDoms;
11491 //MESSAGE("multiple node " << oldId);
11492 if (mutipleNodes.count(oldId))
11493 orderedDoms = mutipleNodes[oldId];
11496 map<int,int>::iterator it = nodeDomains[oldId].begin();
11497 for (; it != nodeDomains[oldId].end(); ++it)
11498 orderedDoms.push_back(it->first);
11500 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11501 //stringstream txt;
11502 //for (int i=0; i<orderedDoms.size(); i++)
11503 // txt << orderedDoms[i] << " ";
11504 //MESSAGE("orderedDoms " << txt.str());
11505 mutipleNodes[oldId] = orderedDoms;
11507 double *coords = grid->GetPoint(oldId);
11508 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11509 int newId = newNode->getVtkId();
11510 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11511 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11518 MESSAGE(".. Creation of elements");
11519 for (int idomain = 0; idomain < theElems.size(); idomain++)
11521 itface = faceDomains.begin();
11522 for (; itface != faceDomains.end(); ++itface)
11524 std::map<int, int> domvol = itface->second;
11525 if (!domvol.count(idomain))
11527 DownIdType face = itface->first;
11528 //MESSAGE(" --- face " << face.cellId);
11529 std::set<int> oldNodes;
11531 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11532 int nbMultipleNodes = 0;
11533 std::set<int>::iterator itn = oldNodes.begin();
11534 for (; itn != oldNodes.end(); ++itn)
11537 if (mutipleNodes.count(oldId))
11540 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11542 //MESSAGE("multiple Nodes detected on a shared face");
11543 int downId = itface->first.cellId;
11544 unsigned char cellType = itface->first.cellType;
11545 // --- shared edge or shared face ?
11546 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11549 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11550 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11551 if (mutipleNodes.count(nodes[i]))
11552 if (!mutipleNodesToFace.count(nodes[i]))
11553 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11555 else // shared face (between two volumes)
11557 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11558 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11559 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11560 for (int ie =0; ie < nbEdges; ie++)
11563 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11564 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11566 vector<int> vn0 = mutipleNodes[nodes[0]];
11567 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11569 for (int i0 = 0; i0 < vn0.size(); i0++)
11570 for (int i1 = 0; i1 < vn1.size(); i1++)
11571 if (vn0[i0] == vn1[i1])
11572 doms.push_back(vn0[i0]);
11573 if (doms.size() >2)
11575 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11576 double *coords = grid->GetPoint(nodes[0]);
11577 gp_Pnt p0(coords[0], coords[1], coords[2]);
11578 coords = grid->GetPoint(nodes[nbNodes - 1]);
11579 gp_Pnt p1(coords[0], coords[1], coords[2]);
11581 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11582 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11583 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11584 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11585 for (int id=0; id < doms.size(); id++)
11587 int idom = doms[id];
11588 for (int ivol=0; ivol<nbvol; ivol++)
11590 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11591 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11592 if (theElems[idom].count(elem))
11594 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11595 domvol[idom] = svol;
11596 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11598 vtkIdType npts = 0;
11599 vtkIdType* pts = 0;
11600 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11601 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11604 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11605 angleDom[idom] = 0;
11609 gp_Pnt g(values[0], values[1], values[2]);
11610 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11611 //MESSAGE(" angle=" << angleDom[idom]);
11617 map<double, int> sortedDom; // sort domains by angle
11618 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11619 sortedDom[ia->second] = ia->first;
11620 vector<int> vnodes;
11622 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11624 vdom.push_back(ib->second);
11625 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11627 for (int ino = 0; ino < nbNodes; ino++)
11628 vnodes.push_back(nodes[ino]);
11629 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11638 // --- iterate on shared faces (volumes to modify, face to extrude)
11639 // get node id's of the face (id SMDS = id VTK)
11640 // create flat element with old and new nodes if requested
11642 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11643 // (domain1 X domain2) = domain1 + MAXINT*domain2
11645 std::map<int, std::map<long,int> > nodeQuadDomains;
11646 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11648 MESSAGE(".. Creation of elements: simple junction");
11649 if (createJointElems)
11652 string joints2DName = "joints2D";
11653 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11654 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11655 string joints3DName = "joints3D";
11656 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11657 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11659 itface = faceDomains.begin();
11660 for (; itface != faceDomains.end(); ++itface)
11662 DownIdType face = itface->first;
11663 std::set<int> oldNodes;
11664 std::set<int>::iterator itn;
11666 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11668 std::map<int, int> domvol = itface->second;
11669 std::map<int, int>::iterator itdom = domvol.begin();
11670 int dom1 = itdom->first;
11671 int vtkVolId = itdom->second;
11673 int dom2 = itdom->first;
11674 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11676 stringstream grpname;
11679 grpname << dom1 << "_" << dom2;
11681 grpname << dom2 << "_" << dom1;
11682 string namegrp = grpname.str();
11683 if (!mapOfJunctionGroups.count(namegrp))
11684 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11685 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11687 sgrp->Add(vol->GetID());
11688 if (vol->GetType() == SMDSAbs_Volume)
11689 joints3DGrp->Add(vol->GetID());
11690 else if (vol->GetType() == SMDSAbs_Face)
11691 joints2DGrp->Add(vol->GetID());
11695 // --- create volumes on multiple domain intersection if requested
11696 // iterate on mutipleNodesToFace
11697 // iterate on edgesMultiDomains
11699 MESSAGE(".. Creation of elements: multiple junction");
11700 if (createJointElems)
11702 // --- iterate on mutipleNodesToFace
11704 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11705 for (; itn != mutipleNodesToFace.end(); ++itn)
11707 int node = itn->first;
11708 vector<int> orderDom = itn->second;
11709 vector<vtkIdType> orderedNodes;
11710 for (int idom = 0; idom <orderDom.size(); idom++)
11711 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11712 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11714 stringstream grpname;
11716 grpname << 0 << "_" << 0;
11718 string namegrp = grpname.str();
11719 if (!mapOfJunctionGroups.count(namegrp))
11720 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11721 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11723 sgrp->Add(face->GetID());
11726 // --- iterate on edgesMultiDomains
11728 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11729 for (; ite != edgesMultiDomains.end(); ++ite)
11731 vector<int> nodes = ite->first;
11732 vector<int> orderDom = ite->second;
11733 vector<vtkIdType> orderedNodes;
11734 if (nodes.size() == 2)
11736 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11737 for (int ino=0; ino < nodes.size(); ino++)
11738 if (orderDom.size() == 3)
11739 for (int idom = 0; idom <orderDom.size(); idom++)
11740 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11742 for (int idom = orderDom.size()-1; idom >=0; idom--)
11743 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11744 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11747 string namegrp = "jointsMultiples";
11748 if (!mapOfJunctionGroups.count(namegrp))
11749 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11750 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11752 sgrp->Add(vol->GetID());
11756 INFOS("Quadratic multiple joints not implemented");
11757 // TODO quadratic nodes
11762 // --- list the explicit faces and edges of the mesh that need to be modified,
11763 // i.e. faces and edges built with one or more duplicated nodes.
11764 // associate these faces or edges to their corresponding domain.
11765 // only the first domain found is kept when a face or edge is shared
11767 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11768 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11769 faceOrEdgeDom.clear();
11772 MESSAGE(".. Modification of elements");
11773 for (int idomain = 0; idomain < theElems.size(); idomain++)
11775 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11776 for (; itnod != nodeDomains.end(); ++itnod)
11778 int oldId = itnod->first;
11779 //MESSAGE(" node " << oldId);
11780 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11781 for (int i = 0; i < l.ncells; i++)
11783 int vtkId = l.cells[i];
11784 int vtkType = grid->GetCellType(vtkId);
11785 int downId = grid->CellIdToDownId(vtkId);
11787 continue; // new cells: not to be modified
11788 DownIdType aCell(downId, vtkType);
11789 int volParents[1000];
11790 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11791 for (int j = 0; j < nbvol; j++)
11792 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11793 if (!feDom.count(vtkId))
11795 feDom[vtkId] = idomain;
11796 faceOrEdgeDom[aCell] = emptyMap;
11797 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11798 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11799 // << " type " << vtkType << " downId " << downId);
11805 // --- iterate on shared faces (volumes to modify, face to extrude)
11806 // get node id's of the face
11807 // replace old nodes by new nodes in volumes, and update inverse connectivity
11809 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11810 for (int m=0; m<3; m++)
11812 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11813 itface = (*amap).begin();
11814 for (; itface != (*amap).end(); ++itface)
11816 DownIdType face = itface->first;
11817 std::set<int> oldNodes;
11818 std::set<int>::iterator itn;
11820 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11821 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11822 std::map<int, int> localClonedNodeIds;
11824 std::map<int, int> domvol = itface->second;
11825 std::map<int, int>::iterator itdom = domvol.begin();
11826 for (; itdom != domvol.end(); ++itdom)
11828 int idom = itdom->first;
11829 int vtkVolId = itdom->second;
11830 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11831 localClonedNodeIds.clear();
11832 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11835 if (nodeDomains[oldId].count(idom))
11837 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11838 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11841 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11846 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11847 grid->BuildLinks();
11855 * \brief Double nodes on some external faces and create flat elements.
11856 * Flat elements are mainly used by some types of mechanic calculations.
11858 * Each group of the list must be constituted of faces.
11859 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11860 * @param theElems - list of groups of faces, where a group of faces is a set of
11861 * SMDS_MeshElements sorted by Id.
11862 * @return TRUE if operation has been completed successfully, FALSE otherwise
11864 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11866 MESSAGE("-------------------------------------------------");
11867 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11868 MESSAGE("-------------------------------------------------");
11870 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11872 // --- For each group of faces
11873 // duplicate the nodes, create a flat element based on the face
11874 // replace the nodes of the faces by their clones
11876 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11877 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11878 clonedNodes.clear();
11879 intermediateNodes.clear();
11880 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11881 mapOfJunctionGroups.clear();
11883 for (int idom = 0; idom < theElems.size(); idom++)
11885 const TIDSortedElemSet& domain = theElems[idom];
11886 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11887 for (; elemItr != domain.end(); ++elemItr)
11889 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11890 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11893 // MESSAGE("aFace=" << aFace->GetID());
11894 bool isQuad = aFace->IsQuadratic();
11895 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11897 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11899 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11900 while (nodeIt->more())
11902 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11903 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11905 ln2.push_back(node);
11907 ln0.push_back(node);
11909 const SMDS_MeshNode* clone = 0;
11910 if (!clonedNodes.count(node))
11912 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11913 clonedNodes[node] = clone;
11916 clone = clonedNodes[node];
11919 ln3.push_back(clone);
11921 ln1.push_back(clone);
11923 const SMDS_MeshNode* inter = 0;
11924 if (isQuad && (!isMedium))
11926 if (!intermediateNodes.count(node))
11928 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11929 intermediateNodes[node] = inter;
11932 inter = intermediateNodes[node];
11933 ln4.push_back(inter);
11937 // --- extrude the face
11939 vector<const SMDS_MeshNode*> ln;
11940 SMDS_MeshVolume* vol = 0;
11941 vtkIdType aType = aFace->GetVtkType();
11945 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11946 // MESSAGE("vol prism " << vol->GetID());
11947 ln.push_back(ln1[0]);
11948 ln.push_back(ln1[1]);
11949 ln.push_back(ln1[2]);
11952 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11953 // MESSAGE("vol hexa " << vol->GetID());
11954 ln.push_back(ln1[0]);
11955 ln.push_back(ln1[1]);
11956 ln.push_back(ln1[2]);
11957 ln.push_back(ln1[3]);
11959 case VTK_QUADRATIC_TRIANGLE:
11960 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11961 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11962 // MESSAGE("vol quad prism " << vol->GetID());
11963 ln.push_back(ln1[0]);
11964 ln.push_back(ln1[1]);
11965 ln.push_back(ln1[2]);
11966 ln.push_back(ln3[0]);
11967 ln.push_back(ln3[1]);
11968 ln.push_back(ln3[2]);
11970 case VTK_QUADRATIC_QUAD:
11971 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11972 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11973 // ln4[0], ln4[1], ln4[2], ln4[3]);
11974 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11975 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11976 ln4[0], ln4[1], ln4[2], ln4[3]);
11977 // MESSAGE("vol quad hexa " << vol->GetID());
11978 ln.push_back(ln1[0]);
11979 ln.push_back(ln1[1]);
11980 ln.push_back(ln1[2]);
11981 ln.push_back(ln1[3]);
11982 ln.push_back(ln3[0]);
11983 ln.push_back(ln3[1]);
11984 ln.push_back(ln3[2]);
11985 ln.push_back(ln3[3]);
11995 stringstream grpname;
11999 string namegrp = grpname.str();
12000 if (!mapOfJunctionGroups.count(namegrp))
12001 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12002 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12004 sgrp->Add(vol->GetID());
12007 // --- modify the face
12009 aFace->ChangeNodes(&ln[0], ln.size());
12016 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12017 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12018 * groups of faces to remove inside the object, (idem edges).
12019 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12021 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12022 const TopoDS_Shape& theShape,
12023 SMESH_NodeSearcher* theNodeSearcher,
12024 const char* groupName,
12025 std::vector<double>& nodesCoords,
12026 std::vector<std::vector<int> >& listOfListOfNodes)
12028 MESSAGE("--------------------------------");
12029 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12030 MESSAGE("--------------------------------");
12032 // --- zone of volumes to remove is given :
12033 // 1 either by a geom shape (one or more vertices) and a radius,
12034 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12035 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12036 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12037 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12038 // defined by it's name.
12040 SMESHDS_GroupBase* groupDS = 0;
12041 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12042 while ( groupIt->more() )
12045 SMESH_Group * group = groupIt->next();
12046 if ( !group ) continue;
12047 groupDS = group->GetGroupDS();
12048 if ( !groupDS || groupDS->IsEmpty() ) continue;
12049 std::string grpName = group->GetName();
12050 //MESSAGE("grpName=" << grpName);
12051 if (grpName == groupName)
12057 bool isNodeGroup = false;
12058 bool isNodeCoords = false;
12061 if (groupDS->GetType() != SMDSAbs_Node)
12063 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12066 if (nodesCoords.size() > 0)
12067 isNodeCoords = true; // a list o nodes given by their coordinates
12068 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12070 // --- define groups to build
12072 int idg; // --- group of SMDS volumes
12073 string grpvName = groupName;
12074 grpvName += "_vol";
12075 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12078 MESSAGE("group not created " << grpvName);
12081 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12083 int idgs; // --- group of SMDS faces on the skin
12084 string grpsName = groupName;
12085 grpsName += "_skin";
12086 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12089 MESSAGE("group not created " << grpsName);
12092 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12094 int idgi; // --- group of SMDS faces internal (several shapes)
12095 string grpiName = groupName;
12096 grpiName += "_internalFaces";
12097 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12100 MESSAGE("group not created " << grpiName);
12103 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12105 int idgei; // --- group of SMDS faces internal (several shapes)
12106 string grpeiName = groupName;
12107 grpeiName += "_internalEdges";
12108 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12111 MESSAGE("group not created " << grpeiName);
12114 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12116 // --- build downward connectivity
12118 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12119 meshDS->BuildDownWardConnectivity(true);
12120 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12122 // --- set of volumes detected inside
12124 std::set<int> setOfInsideVol;
12125 std::set<int> setOfVolToCheck;
12127 std::vector<gp_Pnt> gpnts;
12130 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12132 MESSAGE("group of nodes provided");
12133 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12134 while ( elemIt->more() )
12136 const SMDS_MeshElement* elem = elemIt->next();
12139 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12142 SMDS_MeshElement* vol = 0;
12143 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12144 while (volItr->more())
12146 vol = (SMDS_MeshElement*)volItr->next();
12147 setOfInsideVol.insert(vol->getVtkId());
12148 sgrp->Add(vol->GetID());
12152 else if (isNodeCoords)
12154 MESSAGE("list of nodes coordinates provided");
12157 while (i < nodesCoords.size()-2)
12159 double x = nodesCoords[i++];
12160 double y = nodesCoords[i++];
12161 double z = nodesCoords[i++];
12162 gp_Pnt p = gp_Pnt(x, y ,z);
12163 gpnts.push_back(p);
12164 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12167 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12169 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12170 TopTools_IndexedMapOfShape vertexMap;
12171 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12172 gp_Pnt p = gp_Pnt(0,0,0);
12173 if (vertexMap.Extent() < 1)
12176 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12178 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12179 p = BRep_Tool::Pnt(vertex);
12180 gpnts.push_back(p);
12181 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12185 if (gpnts.size() > 0)
12188 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12190 nodeId = startNode->GetID();
12191 MESSAGE("nodeId " << nodeId);
12193 double radius2 = radius*radius;
12194 MESSAGE("radius2 " << radius2);
12196 // --- volumes on start node
12198 setOfVolToCheck.clear();
12199 SMDS_MeshElement* startVol = 0;
12200 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12201 while (volItr->more())
12203 startVol = (SMDS_MeshElement*)volItr->next();
12204 setOfVolToCheck.insert(startVol->getVtkId());
12206 if (setOfVolToCheck.empty())
12208 MESSAGE("No volumes found");
12212 // --- starting with central volumes then their neighbors, check if they are inside
12213 // or outside the domain, until no more new neighbor volume is inside.
12214 // Fill the group of inside volumes
12216 std::map<int, double> mapOfNodeDistance2;
12217 mapOfNodeDistance2.clear();
12218 std::set<int> setOfOutsideVol;
12219 while (!setOfVolToCheck.empty())
12221 std::set<int>::iterator it = setOfVolToCheck.begin();
12223 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12224 bool volInside = false;
12225 vtkIdType npts = 0;
12226 vtkIdType* pts = 0;
12227 grid->GetCellPoints(vtkId, npts, pts);
12228 for (int i=0; i<npts; i++)
12230 double distance2 = 0;
12231 if (mapOfNodeDistance2.count(pts[i]))
12233 distance2 = mapOfNodeDistance2[pts[i]];
12234 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12238 double *coords = grid->GetPoint(pts[i]);
12239 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12241 for (int j=0; j<gpnts.size(); j++)
12243 double d2 = aPoint.SquareDistance(gpnts[j]);
12244 if (d2 < distance2)
12247 if (distance2 < radius2)
12251 mapOfNodeDistance2[pts[i]] = distance2;
12252 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12254 if (distance2 < radius2)
12256 volInside = true; // one or more nodes inside the domain
12257 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12263 setOfInsideVol.insert(vtkId);
12264 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12265 int neighborsVtkIds[NBMAXNEIGHBORS];
12266 int downIds[NBMAXNEIGHBORS];
12267 unsigned char downTypes[NBMAXNEIGHBORS];
12268 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12269 for (int n = 0; n < nbNeighbors; n++)
12270 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12271 setOfVolToCheck.insert(neighborsVtkIds[n]);
12275 setOfOutsideVol.insert(vtkId);
12276 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12278 setOfVolToCheck.erase(vtkId);
12282 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12283 // If yes, add the volume to the inside set
12285 bool addedInside = true;
12286 std::set<int> setOfVolToReCheck;
12287 while (addedInside)
12289 MESSAGE(" --------------------------- re check");
12290 addedInside = false;
12291 std::set<int>::iterator itv = setOfInsideVol.begin();
12292 for (; itv != setOfInsideVol.end(); ++itv)
12295 int neighborsVtkIds[NBMAXNEIGHBORS];
12296 int downIds[NBMAXNEIGHBORS];
12297 unsigned char downTypes[NBMAXNEIGHBORS];
12298 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12299 for (int n = 0; n < nbNeighbors; n++)
12300 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12301 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12303 setOfVolToCheck = setOfVolToReCheck;
12304 setOfVolToReCheck.clear();
12305 while (!setOfVolToCheck.empty())
12307 std::set<int>::iterator it = setOfVolToCheck.begin();
12309 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12311 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12312 int countInside = 0;
12313 int neighborsVtkIds[NBMAXNEIGHBORS];
12314 int downIds[NBMAXNEIGHBORS];
12315 unsigned char downTypes[NBMAXNEIGHBORS];
12316 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12317 for (int n = 0; n < nbNeighbors; n++)
12318 if (setOfInsideVol.count(neighborsVtkIds[n]))
12320 MESSAGE("countInside " << countInside);
12321 if (countInside > 1)
12323 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12324 setOfInsideVol.insert(vtkId);
12325 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12326 addedInside = true;
12329 setOfVolToReCheck.insert(vtkId);
12331 setOfVolToCheck.erase(vtkId);
12335 // --- map of Downward faces at the boundary, inside the global volume
12336 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12337 // fill group of SMDS faces inside the volume (when several volume shapes)
12338 // fill group of SMDS faces on the skin of the global volume (if skin)
12340 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12341 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12342 std::set<int>::iterator it = setOfInsideVol.begin();
12343 for (; it != setOfInsideVol.end(); ++it)
12346 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12347 int neighborsVtkIds[NBMAXNEIGHBORS];
12348 int downIds[NBMAXNEIGHBORS];
12349 unsigned char downTypes[NBMAXNEIGHBORS];
12350 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12351 for (int n = 0; n < nbNeighbors; n++)
12353 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12354 if (neighborDim == 3)
12356 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12358 DownIdType face(downIds[n], downTypes[n]);
12359 boundaryFaces[face] = vtkId;
12361 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12362 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12363 if (vtkFaceId >= 0)
12365 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12366 // find also the smds edges on this face
12367 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12368 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12369 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12370 for (int i = 0; i < nbEdges; i++)
12372 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12373 if (vtkEdgeId >= 0)
12374 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12378 else if (neighborDim == 2) // skin of the volume
12380 DownIdType face(downIds[n], downTypes[n]);
12381 skinFaces[face] = vtkId;
12382 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12383 if (vtkFaceId >= 0)
12384 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12389 // --- identify the edges constituting the wire of each subshape on the skin
12390 // define polylines with the nodes of edges, equivalent to wires
12391 // project polylines on subshapes, and partition, to get geom faces
12393 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12394 std::set<int> emptySet;
12396 std::set<int> shapeIds;
12398 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12399 while (itelem->more())
12401 const SMDS_MeshElement *elem = itelem->next();
12402 int shapeId = elem->getshapeId();
12403 int vtkId = elem->getVtkId();
12404 if (!shapeIdToVtkIdSet.count(shapeId))
12406 shapeIdToVtkIdSet[shapeId] = emptySet;
12407 shapeIds.insert(shapeId);
12409 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12412 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12413 std::set<DownIdType, DownIdCompare> emptyEdges;
12414 emptyEdges.clear();
12416 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12417 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12419 int shapeId = itShape->first;
12420 MESSAGE(" --- Shape ID --- "<< shapeId);
12421 shapeIdToEdges[shapeId] = emptyEdges;
12423 std::vector<int> nodesEdges;
12425 std::set<int>::iterator its = itShape->second.begin();
12426 for (; its != itShape->second.end(); ++its)
12429 MESSAGE(" " << vtkId);
12430 int neighborsVtkIds[NBMAXNEIGHBORS];
12431 int downIds[NBMAXNEIGHBORS];
12432 unsigned char downTypes[NBMAXNEIGHBORS];
12433 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12434 for (int n = 0; n < nbNeighbors; n++)
12436 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12438 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12439 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12440 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12442 DownIdType edge(downIds[n], downTypes[n]);
12443 if (!shapeIdToEdges[shapeId].count(edge))
12445 shapeIdToEdges[shapeId].insert(edge);
12447 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12448 nodesEdges.push_back(vtkNodeId[0]);
12449 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12450 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12456 std::list<int> order;
12458 if (nodesEdges.size() > 0)
12460 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12461 nodesEdges[0] = -1;
12462 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12463 nodesEdges[1] = -1; // do not reuse this edge
12467 int nodeTofind = order.back(); // try first to push back
12469 for (i = 0; i<nodesEdges.size(); i++)
12470 if (nodesEdges[i] == nodeTofind)
12472 if (i == nodesEdges.size())
12473 found = false; // no follower found on back
12476 if (i%2) // odd ==> use the previous one
12477 if (nodesEdges[i-1] < 0)
12481 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12482 nodesEdges[i-1] = -1;
12484 else // even ==> use the next one
12485 if (nodesEdges[i+1] < 0)
12489 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12490 nodesEdges[i+1] = -1;
12495 // try to push front
12497 nodeTofind = order.front(); // try to push front
12498 for (i = 0; i<nodesEdges.size(); i++)
12499 if (nodesEdges[i] == nodeTofind)
12501 if (i == nodesEdges.size())
12503 found = false; // no predecessor found on front
12506 if (i%2) // odd ==> use the previous one
12507 if (nodesEdges[i-1] < 0)
12511 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12512 nodesEdges[i-1] = -1;
12514 else // even ==> use the next one
12515 if (nodesEdges[i+1] < 0)
12519 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12520 nodesEdges[i+1] = -1;
12526 std::vector<int> nodes;
12527 nodes.push_back(shapeId);
12528 std::list<int>::iterator itl = order.begin();
12529 for (; itl != order.end(); itl++)
12531 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12532 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12534 listOfListOfNodes.push_back(nodes);
12537 // partition geom faces with blocFissure
12538 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12539 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12545 //================================================================================
12547 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12548 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12549 * \return TRUE if operation has been completed successfully, FALSE otherwise
12551 //================================================================================
12553 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12555 // iterates on volume elements and detect all free faces on them
12556 SMESHDS_Mesh* aMesh = GetMeshDS();
12559 //bool res = false;
12560 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12561 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12564 const SMDS_MeshVolume* volume = vIt->next();
12565 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12566 vTool.SetExternalNormal();
12567 //const bool isPoly = volume->IsPoly();
12568 const int iQuad = volume->IsQuadratic();
12569 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12571 if (!vTool.IsFreeFace(iface))
12574 vector<const SMDS_MeshNode *> nodes;
12575 int nbFaceNodes = vTool.NbFaceNodes(iface);
12576 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12578 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12579 nodes.push_back(faceNodes[inode]);
12580 if (iQuad) { // add medium nodes
12581 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12582 nodes.push_back(faceNodes[inode]);
12583 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12584 nodes.push_back(faceNodes[8]);
12586 // add new face based on volume nodes
12587 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12589 continue; // face already exsist
12591 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12595 return ( nbFree==(nbExisted+nbCreated) );
12600 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12602 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12604 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12607 //================================================================================
12609 * \brief Creates missing boundary elements
12610 * \param elements - elements whose boundary is to be checked
12611 * \param dimension - defines type of boundary elements to create
12612 * \param group - a group to store created boundary elements in
12613 * \param targetMesh - a mesh to store created boundary elements in
12614 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12615 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12616 * boundary elements will be copied into the targetMesh
12617 * \param toAddExistingBondary - if true, not only new but also pre-existing
12618 * boundary elements will be added into the new group
12619 * \param aroundElements - if true, elements will be created on boundary of given
12620 * elements else, on boundary of the whole mesh.
12621 * \return nb of added boundary elements
12623 //================================================================================
12625 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12626 Bnd_Dimension dimension,
12627 SMESH_Group* group/*=0*/,
12628 SMESH_Mesh* targetMesh/*=0*/,
12629 bool toCopyElements/*=false*/,
12630 bool toCopyExistingBoundary/*=false*/,
12631 bool toAddExistingBondary/*= false*/,
12632 bool aroundElements/*= false*/)
12634 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12635 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12636 // hope that all elements are of the same type, do not check them all
12637 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12638 throw SALOME_Exception(LOCALIZED("wrong element type"));
12641 toCopyElements = toCopyExistingBoundary = false;
12643 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12644 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12645 int nbAddedBnd = 0;
12647 // editor adding present bnd elements and optionally holding elements to add to the group
12648 SMESH_MeshEditor* presentEditor;
12649 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12650 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12652 SMESH_MesherHelper helper( *myMesh );
12653 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12654 SMDS_VolumeTool vTool;
12655 TIDSortedElemSet avoidSet;
12656 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12659 typedef vector<const SMDS_MeshNode*> TConnectivity;
12661 SMDS_ElemIteratorPtr eIt;
12662 if (elements.empty())
12663 eIt = aMesh->elementsIterator(elemType);
12665 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12667 while (eIt->more())
12669 const SMDS_MeshElement* elem = eIt->next();
12670 const int iQuad = elem->IsQuadratic();
12672 // ------------------------------------------------------------------------------------
12673 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12674 // ------------------------------------------------------------------------------------
12675 vector<const SMDS_MeshElement*> presentBndElems;
12676 vector<TConnectivity> missingBndElems;
12677 TConnectivity nodes;
12678 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12680 vTool.SetExternalNormal();
12681 const SMDS_MeshElement* otherVol = 0;
12682 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12684 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12685 ( !aroundElements || elements.count( otherVol )))
12687 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12688 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12689 if ( missType == SMDSAbs_Edge ) // boundary edges
12691 nodes.resize( 2+iQuad );
12692 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12694 for ( int j = 0; j < nodes.size(); ++j )
12696 if ( const SMDS_MeshElement* edge =
12697 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12698 presentBndElems.push_back( edge );
12700 missingBndElems.push_back( nodes );
12703 else // boundary face
12706 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12707 nodes.push_back( nn[inode] );
12708 if (iQuad) // add medium nodes
12709 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12710 nodes.push_back( nn[inode] );
12711 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12713 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12715 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12716 SMDSAbs_Face, /*noMedium=*/false ))
12717 presentBndElems.push_back( f );
12719 missingBndElems.push_back( nodes );
12721 if ( targetMesh != myMesh )
12723 // add 1D elements on face boundary to be added to a new mesh
12724 const SMDS_MeshElement* edge;
12725 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12728 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12730 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12731 if ( edge && avoidSet.insert( edge ).second )
12732 presentBndElems.push_back( edge );
12738 else // elem is a face ------------------------------------------
12740 avoidSet.clear(), avoidSet.insert( elem );
12741 int nbNodes = elem->NbCornerNodes();
12742 nodes.resize( 2 /*+ iQuad*/);
12743 for ( int i = 0; i < nbNodes; i++ )
12745 nodes[0] = elem->GetNode(i);
12746 nodes[1] = elem->GetNode((i+1)%nbNodes);
12747 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12748 continue; // not free link
12751 //nodes[2] = elem->GetNode( i + nbNodes );
12752 if ( const SMDS_MeshElement* edge =
12753 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12754 presentBndElems.push_back( edge );
12756 missingBndElems.push_back( nodes );
12760 // ---------------------------------
12761 // 2. Add missing boundary elements
12762 // ---------------------------------
12763 if ( targetMesh != myMesh )
12764 // instead of making a map of nodes in this mesh and targetMesh,
12765 // we create nodes with same IDs.
12766 for ( int i = 0; i < missingBndElems.size(); ++i )
12768 TConnectivity& srcNodes = missingBndElems[i];
12769 TConnectivity nodes( srcNodes.size() );
12770 for ( inode = 0; inode < nodes.size(); ++inode )
12771 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12772 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12774 /*noMedium=*/false))
12776 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12780 for ( int i = 0; i < missingBndElems.size(); ++i )
12782 TConnectivity& nodes = missingBndElems[i];
12783 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12785 /*noMedium=*/false))
12787 SMDS_MeshElement* elem =
12788 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12791 // try to set a new element to a shape
12792 if ( myMesh->HasShapeToMesh() )
12795 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12796 const int nbN = nodes.size() / (iQuad+1 );
12797 for ( inode = 0; inode < nbN && ok; ++inode )
12799 pair<int, TopAbs_ShapeEnum> i_stype =
12800 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12801 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12802 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12804 if ( ok && mediumShapes.size() > 1 )
12806 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12807 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12808 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12810 if (( ok = ( stype_i->first != stype_i_0.first )))
12811 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12812 aMesh->IndexToShape( stype_i_0.second ));
12815 if ( ok && mediumShapes.begin()->first == missShapeType )
12816 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12820 // ----------------------------------
12821 // 3. Copy present boundary elements
12822 // ----------------------------------
12823 if ( toCopyExistingBoundary )
12824 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12826 const SMDS_MeshElement* e = presentBndElems[i];
12827 TConnectivity nodes( e->NbNodes() );
12828 for ( inode = 0; inode < nodes.size(); ++inode )
12829 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12830 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12832 else // store present elements to add them to a group
12833 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12835 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12838 } // loop on given elements
12840 // ---------------------------------------------
12841 // 4. Fill group with boundary elements
12842 // ---------------------------------------------
12845 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12846 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12847 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12849 tgtEditor.myLastCreatedElems.Clear();
12850 tgtEditor2.myLastCreatedElems.Clear();
12852 // -----------------------
12853 // 5. Copy given elements
12854 // -----------------------
12855 if ( toCopyElements && targetMesh != myMesh )
12857 if (elements.empty())
12858 eIt = aMesh->elementsIterator(elemType);
12860 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12861 while (eIt->more())
12863 const SMDS_MeshElement* elem = eIt->next();
12864 TConnectivity nodes( elem->NbNodes() );
12865 for ( inode = 0; inode < nodes.size(); ++inode )
12866 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12867 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12869 tgtEditor.myLastCreatedElems.Clear();