1 // Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
86 #include <gp_Trsf.hxx>
100 #include <boost/tuple/tuple.hpp>
102 #include <Standard_Failure.hxx>
103 #include <Standard_ErrorHandler.hxx>
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
110 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
111 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
113 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
115 //=======================================================================
116 //function : SMESH_MeshEditor
118 //=======================================================================
120 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
121 :myMesh( theMesh ) // theMesh may be NULL
125 //================================================================================
127 * \brief Clears myLastCreatedNodes and myLastCreatedElems
129 //================================================================================
131 void SMESH_MeshEditor::CrearLastCreated()
133 myLastCreatedNodes.Clear();
134 myLastCreatedElems.Clear();
138 //=======================================================================
142 //=======================================================================
145 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
146 const SMDSAbs_ElementType type,
149 const double ballDiameter)
151 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
152 SMDS_MeshElement* e = 0;
153 int nbnode = node.size();
154 SMESHDS_Mesh* mesh = GetMeshDS();
159 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
160 else e = mesh->AddFace (node[0], node[1], node[2] );
162 else if (nbnode == 4) {
163 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
164 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
166 else if (nbnode == 6) {
167 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
168 node[4], node[5], ID);
169 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
172 else if (nbnode == 8) {
173 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
174 node[4], node[5], node[6], node[7], ID);
175 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
176 node[4], node[5], node[6], node[7] );
178 else if (nbnode == 9) {
179 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
180 node[4], node[5], node[6], node[7], node[8], ID);
181 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
182 node[4], node[5], node[6], node[7], node[8] );
185 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
186 else e = mesh->AddPolygonalFace (node );
193 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
194 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
196 else if (nbnode == 5) {
197 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
199 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
202 else if (nbnode == 6) {
203 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
204 node[4], node[5], ID);
205 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
208 else if (nbnode == 8) {
209 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], node[7], ID);
211 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], node[7] );
214 else if (nbnode == 10) {
215 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7],
217 node[8], node[9], ID);
218 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7],
222 else if (nbnode == 12) {
223 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7],
225 node[8], node[9], node[10], node[11], ID);
226 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7],
228 node[8], node[9], node[10], node[11] );
230 else if (nbnode == 13) {
231 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232 node[4], node[5], node[6], node[7],
233 node[8], node[9], node[10],node[11],
235 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
236 node[4], node[5], node[6], node[7],
237 node[8], node[9], node[10],node[11],
240 else if (nbnode == 15) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
242 node[4], node[5], node[6], node[7],
243 node[8], node[9], node[10],node[11],
244 node[12],node[13],node[14],ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
246 node[4], node[5], node[6], node[7],
247 node[8], node[9], node[10],node[11],
248 node[12],node[13],node[14] );
250 else if (nbnode == 20) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], node[6], node[7],
253 node[8], node[9], node[10],node[11],
254 node[12],node[13],node[14],node[15],
255 node[16],node[17],node[18],node[19],ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
257 node[4], node[5], node[6], node[7],
258 node[8], node[9], node[10],node[11],
259 node[12],node[13],node[14],node[15],
260 node[16],node[17],node[18],node[19] );
262 else if (nbnode == 27) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], node[10],node[11],
266 node[12],node[13],node[14],node[15],
267 node[16],node[17],node[18],node[19],
268 node[20],node[21],node[22],node[23],
269 node[24],node[25],node[26], ID);
270 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
271 node[4], node[5], node[6], node[7],
272 node[8], node[9], node[10],node[11],
273 node[12],node[13],node[14],node[15],
274 node[16],node[17],node[18],node[19],
275 node[20],node[21],node[22],node[23],
276 node[24],node[25],node[26] );
283 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
284 else e = mesh->AddEdge (node[0], node[1] );
286 else if ( nbnode == 3 ) {
287 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
288 else e = mesh->AddEdge (node[0], node[1], node[2] );
292 case SMDSAbs_0DElement:
294 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
295 else e = mesh->Add0DElement (node[0] );
300 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
301 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
305 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
306 else e = mesh->AddBall (node[0], ballDiameter);
311 if ( e ) myLastCreatedElems.Append( e );
315 //=======================================================================
319 //=======================================================================
321 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
322 const SMDSAbs_ElementType type,
326 vector<const SMDS_MeshNode*> nodes;
327 nodes.reserve( nodeIDs.size() );
328 vector<int>::const_iterator id = nodeIDs.begin();
329 while ( id != nodeIDs.end() ) {
330 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
331 nodes.push_back( node );
335 return AddElement( nodes, type, isPoly, ID );
338 //=======================================================================
340 //purpose : Remove a node or an element.
341 // Modify a compute state of sub-meshes which become empty
342 //=======================================================================
344 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
347 myLastCreatedElems.Clear();
348 myLastCreatedNodes.Clear();
350 SMESHDS_Mesh* aMesh = GetMeshDS();
351 set< SMESH_subMesh *> smmap;
354 list<int>::const_iterator it = theIDs.begin();
355 for ( ; it != theIDs.end(); it++ ) {
356 const SMDS_MeshElement * elem;
358 elem = aMesh->FindNode( *it );
360 elem = aMesh->FindElement( *it );
364 // Notify VERTEX sub-meshes about modification
366 const SMDS_MeshNode* node = cast2Node( elem );
367 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
368 if ( int aShapeID = node->getshapeId() )
369 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
372 // Find sub-meshes to notify about modification
373 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
374 // while ( nodeIt->more() ) {
375 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
376 // const SMDS_PositionPtr& aPosition = node->GetPosition();
377 // if ( aPosition.get() ) {
378 // if ( int aShapeID = aPosition->GetShapeId() ) {
379 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
380 // smmap.insert( sm );
387 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
389 aMesh->RemoveElement( elem );
393 // Notify sub-meshes about modification
394 if ( !smmap.empty() ) {
395 set< SMESH_subMesh *>::iterator smIt;
396 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
397 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
400 // // Check if the whole mesh becomes empty
401 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
402 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
407 //================================================================================
409 * \brief Create 0D elements on all nodes of the given object except those
410 * nodes on which a 0D element already exists.
411 * \param elements - Elements on whose nodes to create 0D elements; if empty,
412 * the all mesh is treated
413 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
415 //================================================================================
417 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
418 TIDSortedElemSet& all0DElems )
420 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
421 SMDS_ElemIteratorPtr elemIt;
422 if ( elements.empty() )
423 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
425 elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
427 while ( elemIt->more() )
429 const SMDS_MeshElement* e = elemIt->next();
430 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
431 while ( nodeIt->more() )
433 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
434 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
436 all0DElems.insert( it0D->next() );
438 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
439 all0DElems.insert( myLastCreatedElems.Last() );
445 //=======================================================================
446 //function : FindShape
447 //purpose : Return an index of the shape theElem is on
448 // or zero if a shape not found
449 //=======================================================================
451 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
453 myLastCreatedElems.Clear();
454 myLastCreatedNodes.Clear();
456 SMESHDS_Mesh * aMesh = GetMeshDS();
457 if ( aMesh->ShapeToMesh().IsNull() )
460 int aShapeID = theElem->getshapeId();
464 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
465 if ( sm->Contains( theElem ))
468 if ( theElem->GetType() == SMDSAbs_Node ) {
469 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
472 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
475 TopoDS_Shape aShape; // the shape a node of theElem is on
476 if ( theElem->GetType() != SMDSAbs_Node )
478 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
479 while ( nodeIt->more() ) {
480 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
481 if ((aShapeID = node->getshapeId()) > 0) {
482 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
483 if ( sm->Contains( theElem ))
485 if ( aShape.IsNull() )
486 aShape = aMesh->IndexToShape( aShapeID );
492 // None of nodes is on a proper shape,
493 // find the shape among ancestors of aShape on which a node is
494 if ( !aShape.IsNull() ) {
495 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
496 for ( ; ancIt.More(); ancIt.Next() ) {
497 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
498 if ( sm && sm->Contains( theElem ))
499 return aMesh->ShapeToIndex( ancIt.Value() );
504 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
505 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
506 for ( ; id_sm != id2sm.end(); ++id_sm )
507 if ( id_sm->second->Contains( theElem ))
511 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
515 //=======================================================================
516 //function : IsMedium
518 //=======================================================================
520 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
521 const SMDSAbs_ElementType typeToCheck)
523 bool isMedium = false;
524 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
525 while (it->more() && !isMedium ) {
526 const SMDS_MeshElement* elem = it->next();
527 isMedium = elem->IsMediumNode(node);
532 //=======================================================================
533 //function : ShiftNodesQuadTria
535 // Shift nodes in the array corresponded to quadratic triangle
536 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
537 //=======================================================================
538 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
540 const SMDS_MeshNode* nd1 = aNodes[0];
541 aNodes[0] = aNodes[1];
542 aNodes[1] = aNodes[2];
544 const SMDS_MeshNode* nd2 = aNodes[3];
545 aNodes[3] = aNodes[4];
546 aNodes[4] = aNodes[5];
550 //=======================================================================
551 //function : edgeConnectivity
553 // return number of the edges connected with the theNode.
554 // if theEdges has connections with the other type of the
555 // elements, return -1
556 //=======================================================================
557 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
559 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
561 while(elemIt->more()) {
569 //=======================================================================
570 //function : GetNodesFromTwoTria
572 // Shift nodes in the array corresponded to quadratic triangle
573 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
574 //=======================================================================
575 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
576 const SMDS_MeshElement * theTria2,
577 const SMDS_MeshNode* N1[],
578 const SMDS_MeshNode* N2[])
580 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
583 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
586 if(it->more()) return false;
587 it = theTria2->nodesIterator();
590 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
593 if(it->more()) return false;
595 int sames[3] = {-1,-1,-1};
607 if(nbsames!=2) return false;
609 ShiftNodesQuadTria(N1);
611 ShiftNodesQuadTria(N1);
614 i = sames[0] + sames[1] + sames[2];
616 ShiftNodesQuadTria(N2);
618 // now we receive following N1 and N2 (using numeration as above image)
619 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
620 // i.e. first nodes from both arrays determ new diagonal
624 //=======================================================================
625 //function : InverseDiag
626 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
627 // but having other common link.
628 // Return False if args are improper
629 //=======================================================================
631 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
632 const SMDS_MeshElement * theTria2 )
634 MESSAGE("InverseDiag");
635 myLastCreatedElems.Clear();
636 myLastCreatedNodes.Clear();
638 if (!theTria1 || !theTria2)
641 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
642 if (!F1) return false;
643 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
644 if (!F2) return false;
645 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
646 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
648 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
649 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
653 // put nodes in array and find out indices of the same ones
654 const SMDS_MeshNode* aNodes [6];
655 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
657 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
658 while ( it->more() ) {
659 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
661 if ( i > 2 ) // theTria2
662 // find same node of theTria1
663 for ( int j = 0; j < 3; j++ )
664 if ( aNodes[ i ] == aNodes[ j ]) {
673 return false; // theTria1 is not a triangle
674 it = theTria2->nodesIterator();
676 if ( i == 6 && it->more() )
677 return false; // theTria2 is not a triangle
680 // find indices of 1,2 and of A,B in theTria1
681 int iA = 0, iB = 0, i1 = 0, i2 = 0;
682 for ( i = 0; i < 6; i++ ) {
683 if ( sameInd [ i ] == 0 ) {
692 // nodes 1 and 2 should not be the same
693 if ( aNodes[ i1 ] == aNodes[ i2 ] )
697 aNodes[ iA ] = aNodes[ i2 ];
699 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
701 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
702 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
706 } // end if(F1 && F2)
708 // check case of quadratic faces
709 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
711 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
715 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
716 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
724 const SMDS_MeshNode* N1 [6];
725 const SMDS_MeshNode* N2 [6];
726 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
728 // now we receive following N1 and N2 (using numeration as above image)
729 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
730 // i.e. first nodes from both arrays determ new diagonal
732 const SMDS_MeshNode* N1new [6];
733 const SMDS_MeshNode* N2new [6];
746 // replaces nodes in faces
747 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
748 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
753 //=======================================================================
754 //function : findTriangles
755 //purpose : find triangles sharing theNode1-theNode2 link
756 //=======================================================================
758 static bool findTriangles(const SMDS_MeshNode * theNode1,
759 const SMDS_MeshNode * theNode2,
760 const SMDS_MeshElement*& theTria1,
761 const SMDS_MeshElement*& theTria2)
763 if ( !theNode1 || !theNode2 ) return false;
765 theTria1 = theTria2 = 0;
767 set< const SMDS_MeshElement* > emap;
768 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
770 const SMDS_MeshElement* elem = it->next();
771 if ( elem->NbNodes() == 3 )
774 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
776 const SMDS_MeshElement* elem = it->next();
777 if ( emap.find( elem ) != emap.end() ) {
779 // theTria1 must be element with minimum ID
780 if( theTria1->GetID() < elem->GetID() ) {
794 return ( theTria1 && theTria2 );
797 //=======================================================================
798 //function : InverseDiag
799 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
800 // with ones built on the same 4 nodes but having other common link.
801 // Return false if proper faces not found
802 //=======================================================================
804 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
805 const SMDS_MeshNode * theNode2)
807 myLastCreatedElems.Clear();
808 myLastCreatedNodes.Clear();
810 MESSAGE( "::InverseDiag()" );
812 const SMDS_MeshElement *tr1, *tr2;
813 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
816 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
817 if (!F1) return false;
818 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
819 if (!F2) return false;
820 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
821 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
823 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
824 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
828 // put nodes in array
829 // and find indices of 1,2 and of A in tr1 and of B in tr2
830 int i, iA1 = 0, i1 = 0;
831 const SMDS_MeshNode* aNodes1 [3];
832 SMDS_ElemIteratorPtr it;
833 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
834 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
835 if ( aNodes1[ i ] == theNode1 )
836 iA1 = i; // node A in tr1
837 else if ( aNodes1[ i ] != theNode2 )
841 const SMDS_MeshNode* aNodes2 [3];
842 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
843 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
844 if ( aNodes2[ i ] == theNode2 )
845 iB2 = i; // node B in tr2
846 else if ( aNodes2[ i ] != theNode1 )
850 // nodes 1 and 2 should not be the same
851 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
855 aNodes1[ iA1 ] = aNodes2[ i2 ];
857 aNodes2[ iB2 ] = aNodes1[ i1 ];
859 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
860 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
865 // check case of quadratic faces
866 return InverseDiag(tr1,tr2);
869 //=======================================================================
870 //function : getQuadrangleNodes
871 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
872 // fusion of triangles tr1 and tr2 having shared link on
873 // theNode1 and theNode2
874 //=======================================================================
876 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
877 const SMDS_MeshNode * theNode1,
878 const SMDS_MeshNode * theNode2,
879 const SMDS_MeshElement * tr1,
880 const SMDS_MeshElement * tr2 )
882 if( tr1->NbNodes() != tr2->NbNodes() )
884 // find the 4-th node to insert into tr1
885 const SMDS_MeshNode* n4 = 0;
886 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
888 while ( !n4 && i<3 ) {
889 const SMDS_MeshNode * n = cast2Node( it->next() );
891 bool isDiag = ( n == theNode1 || n == theNode2 );
895 // Make an array of nodes to be in a quadrangle
896 int iNode = 0, iFirstDiag = -1;
897 it = tr1->nodesIterator();
900 const SMDS_MeshNode * n = cast2Node( it->next() );
902 bool isDiag = ( n == theNode1 || n == theNode2 );
904 if ( iFirstDiag < 0 )
906 else if ( iNode - iFirstDiag == 1 )
907 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
909 else if ( n == n4 ) {
910 return false; // tr1 and tr2 should not have all the same nodes
912 theQuadNodes[ iNode++ ] = n;
914 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
915 theQuadNodes[ iNode ] = n4;
920 //=======================================================================
921 //function : DeleteDiag
922 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
923 // with a quadrangle built on the same 4 nodes.
924 // Return false if proper faces not found
925 //=======================================================================
927 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
928 const SMDS_MeshNode * theNode2)
930 myLastCreatedElems.Clear();
931 myLastCreatedNodes.Clear();
933 MESSAGE( "::DeleteDiag()" );
935 const SMDS_MeshElement *tr1, *tr2;
936 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
939 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
940 if (!F1) return false;
941 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
942 if (!F2) return false;
943 SMESHDS_Mesh * aMesh = GetMeshDS();
945 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
946 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
948 const SMDS_MeshNode* aNodes [ 4 ];
949 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
952 const SMDS_MeshElement* newElem = 0;
953 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
954 myLastCreatedElems.Append(newElem);
955 AddToSameGroups( newElem, tr1, aMesh );
956 int aShapeId = tr1->getshapeId();
959 aMesh->SetMeshElementOnShape( newElem, aShapeId );
961 aMesh->RemoveElement( tr1 );
962 aMesh->RemoveElement( tr2 );
967 // check case of quadratic faces
968 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
970 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
974 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
975 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
983 const SMDS_MeshNode* N1 [6];
984 const SMDS_MeshNode* N2 [6];
985 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
987 // now we receive following N1 and N2 (using numeration as above image)
988 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
989 // i.e. first nodes from both arrays determ new diagonal
991 const SMDS_MeshNode* aNodes[8];
1001 const SMDS_MeshElement* newElem = 0;
1002 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1003 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1004 myLastCreatedElems.Append(newElem);
1005 AddToSameGroups( newElem, tr1, aMesh );
1006 int aShapeId = tr1->getshapeId();
1009 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1011 aMesh->RemoveElement( tr1 );
1012 aMesh->RemoveElement( tr2 );
1014 // remove middle node (9)
1015 GetMeshDS()->RemoveNode( N1[4] );
1020 //=======================================================================
1021 //function : Reorient
1022 //purpose : Reverse theElement orientation
1023 //=======================================================================
1025 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1027 MESSAGE("Reorient");
1028 myLastCreatedElems.Clear();
1029 myLastCreatedNodes.Clear();
1033 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1034 if ( !it || !it->more() )
1037 switch ( theElem->GetType() ) {
1040 case SMDSAbs_Face: {
1041 if(!theElem->IsQuadratic()) {
1042 int i = theElem->NbNodes();
1043 vector<const SMDS_MeshNode*> aNodes( i );
1044 while ( it->more() )
1045 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1046 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1049 // quadratic elements
1050 if(theElem->GetType()==SMDSAbs_Edge) {
1051 vector<const SMDS_MeshNode*> aNodes(3);
1052 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1053 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1054 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1055 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1058 int nbn = theElem->NbNodes();
1059 vector<const SMDS_MeshNode*> aNodes(nbn);
1060 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1062 for(; i<nbn/2; i++) {
1063 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1065 for(i=0; i<nbn/2; i++) {
1066 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1068 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1072 case SMDSAbs_Volume: {
1073 if (theElem->IsPoly()) {
1074 // TODO reorient vtk polyhedron
1075 MESSAGE("reorient vtk polyhedron ?");
1076 const SMDS_VtkVolume* aPolyedre =
1077 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1079 MESSAGE("Warning: bad volumic element");
1083 int nbFaces = aPolyedre->NbFaces();
1084 vector<const SMDS_MeshNode *> poly_nodes;
1085 vector<int> quantities (nbFaces);
1087 // reverse each face of the polyedre
1088 for (int iface = 1; iface <= nbFaces; iface++) {
1089 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1090 quantities[iface - 1] = nbFaceNodes;
1092 for (inode = nbFaceNodes; inode >= 1; inode--) {
1093 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1094 poly_nodes.push_back(curNode);
1098 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1102 SMDS_VolumeTool vTool;
1103 if ( !vTool.Set( theElem ))
1106 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1107 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1116 //================================================================================
1118 * \brief Reorient faces.
1119 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1120 * \param theDirection - desired direction of normal of \a theFace
1121 * \param theFace - one of \a theFaces that sould be oriented according to
1122 * \a theDirection and whose orientation defines orientation of other faces
1123 * \return number of reoriented faces.
1125 //================================================================================
1127 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1128 const gp_Dir& theDirection,
1129 const SMDS_MeshElement * theFace)
1132 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1134 if ( theFaces.empty() )
1136 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1137 while ( fIt->more() )
1138 theFaces.insert( theFaces.end(), fIt->next() );
1141 // orient theFace according to theDirection
1143 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1144 if ( normal * theDirection.XYZ() < 0 )
1145 nbReori += Reorient( theFace );
1147 // Orient other faces
1149 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1150 TIDSortedElemSet avoidSet;
1151 set< SMESH_TLink > checkedLinks;
1152 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1154 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1155 theFaces.erase( theFace );
1156 startFaces.insert( theFace );
1158 int nodeInd1, nodeInd2;
1159 const SMDS_MeshElement* otherFace;
1160 vector< const SMDS_MeshElement* > facesNearLink;
1161 vector< std::pair< int, int > > nodeIndsOfFace;
1163 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1164 while ( !startFaces.empty() )
1166 startFace = startFaces.begin();
1167 theFace = *startFace;
1168 startFaces.erase( startFace );
1169 if ( !visitedFaces.insert( theFace ).second )
1173 avoidSet.insert(theFace);
1175 NLink link( theFace->GetNode( 0 ), 0 );
1177 const int nbNodes = theFace->NbCornerNodes();
1178 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1180 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1181 linkIt_isNew = checkedLinks.insert( link );
1182 if ( !linkIt_isNew.second )
1184 // link has already been checked and won't be encountered more
1185 // if the group (theFaces) is manifold
1186 //checkedLinks.erase( linkIt_isNew.first );
1190 facesNearLink.clear();
1191 nodeIndsOfFace.clear();
1192 while (( otherFace = FindFaceInSet( link.first, link.second,
1193 theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1194 if ( otherFace != theFace)
1196 facesNearLink.push_back( otherFace );
1197 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1198 avoidSet.insert( otherFace );
1200 if ( facesNearLink.size() > 1 )
1202 // NON-MANIFOLD mesh shell !
1203 // select a face most co-directed with theFace,
1204 // other faces won't be visited this time
1206 SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1207 double proj, maxProj = -1;
1208 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1209 SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1210 if (( proj = Abs( NF * NOF )) > maxProj ) {
1212 otherFace = facesNearLink[i];
1213 nodeInd1 = nodeIndsOfFace[i].first;
1214 nodeInd2 = nodeIndsOfFace[i].second;
1217 // not to visit rejected faces
1218 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1219 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1220 visitedFaces.insert( facesNearLink[i] );
1222 else if ( facesNearLink.size() == 1 )
1224 otherFace = facesNearLink[0];
1225 nodeInd1 = nodeIndsOfFace.back().first;
1226 nodeInd2 = nodeIndsOfFace.back().second;
1228 if ( otherFace && otherFace != theFace)
1230 // link must be reverse in otherFace if orientation ot otherFace
1231 // is same as that of theFace
1232 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1234 nbReori += Reorient( otherFace );
1236 startFaces.insert( otherFace );
1239 std::swap( link.first, link.second ); // reverse the link
1245 //=======================================================================
1246 //function : getBadRate
1248 //=======================================================================
1250 static double getBadRate (const SMDS_MeshElement* theElem,
1251 SMESH::Controls::NumericalFunctorPtr& theCrit)
1253 SMESH::Controls::TSequenceOfXYZ P;
1254 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1256 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1257 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1260 //=======================================================================
1261 //function : QuadToTri
1262 //purpose : Cut quadrangles into triangles.
1263 // theCrit is used to select a diagonal to cut
1264 //=======================================================================
1266 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1267 SMESH::Controls::NumericalFunctorPtr theCrit)
1269 myLastCreatedElems.Clear();
1270 myLastCreatedNodes.Clear();
1272 MESSAGE( "::QuadToTri()" );
1274 if ( !theCrit.get() )
1277 SMESHDS_Mesh * aMesh = GetMeshDS();
1279 Handle(Geom_Surface) surface;
1280 SMESH_MesherHelper helper( *GetMesh() );
1282 TIDSortedElemSet::iterator itElem;
1283 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1284 const SMDS_MeshElement* elem = *itElem;
1285 if ( !elem || elem->GetType() != SMDSAbs_Face )
1287 if ( elem->NbCornerNodes() != 4 )
1290 // retrieve element nodes
1291 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1293 // compare two sets of possible triangles
1294 double aBadRate1, aBadRate2; // to what extent a set is bad
1295 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1296 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1297 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1299 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1300 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1301 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1303 int aShapeId = FindShape( elem );
1304 const SMDS_MeshElement* newElem1 = 0;
1305 const SMDS_MeshElement* newElem2 = 0;
1307 if( !elem->IsQuadratic() ) {
1309 // split liner quadrangle
1310 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1311 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1312 if ( aBadRate1 <= aBadRate2 ) {
1313 // tr1 + tr2 is better
1314 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1315 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1318 // tr3 + tr4 is better
1319 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1320 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1325 // split quadratic quadrangle
1327 // get surface elem is on
1328 if ( aShapeId != helper.GetSubShapeID() ) {
1332 shape = aMesh->IndexToShape( aShapeId );
1333 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1334 TopoDS_Face face = TopoDS::Face( shape );
1335 surface = BRep_Tool::Surface( face );
1336 if ( !surface.IsNull() )
1337 helper.SetSubShape( shape );
1340 // find middle point for (0,1,2,3)
1341 // and create a node in this point;
1342 const SMDS_MeshNode* newN = 0;
1343 if ( aNodes.size() == 9 )
1345 // SMDSEntity_BiQuad_Quadrangle
1346 newN = aNodes.back();
1351 if ( surface.IsNull() )
1353 for ( int i = 0; i < 4; i++ )
1354 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1359 const SMDS_MeshNode* inFaceNode = 0;
1360 if ( helper.GetNodeUVneedInFaceNode() )
1361 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1362 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1363 inFaceNode = aNodes[ i ];
1365 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1367 for ( int i = 0; i < 4; i++ )
1368 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1370 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1372 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1373 myLastCreatedNodes.Append(newN);
1375 // create a new element
1376 if ( aBadRate1 <= aBadRate2 ) {
1377 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1378 aNodes[6], aNodes[7], newN );
1379 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1380 newN, aNodes[4], aNodes[5] );
1383 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1384 aNodes[7], aNodes[4], newN );
1385 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1386 newN, aNodes[5], aNodes[6] );
1390 // care of a new element
1392 myLastCreatedElems.Append(newElem1);
1393 myLastCreatedElems.Append(newElem2);
1394 AddToSameGroups( newElem1, elem, aMesh );
1395 AddToSameGroups( newElem2, elem, aMesh );
1397 // put a new triangle on the same shape
1400 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1401 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1403 aMesh->RemoveElement( elem );
1408 //=======================================================================
1409 //function : BestSplit
1410 //purpose : Find better diagonal for cutting.
1411 //=======================================================================
1413 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1414 SMESH::Controls::NumericalFunctorPtr theCrit)
1416 myLastCreatedElems.Clear();
1417 myLastCreatedNodes.Clear();
1422 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1425 if( theQuad->NbNodes()==4 ||
1426 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1428 // retrieve element nodes
1429 const SMDS_MeshNode* aNodes [4];
1430 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1432 //while (itN->more())
1434 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1436 // compare two sets of possible triangles
1437 double aBadRate1, aBadRate2; // to what extent a set is bad
1438 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1439 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1440 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1442 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1443 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1444 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1445 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1448 return 1; // diagonal 1-3
1450 return 2; // diagonal 2-4
1457 // Methods of splitting volumes into tetra
1459 const int theHexTo5_1[5*4+1] =
1461 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1463 const int theHexTo5_2[5*4+1] =
1465 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1467 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1469 const int theHexTo6_1[6*4+1] =
1471 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1473 const int theHexTo6_2[6*4+1] =
1475 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1477 const int theHexTo6_3[6*4+1] =
1479 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1481 const int theHexTo6_4[6*4+1] =
1483 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1485 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1487 const int thePyraTo2_1[2*4+1] =
1489 0, 1, 2, 4, 0, 2, 3, 4, -1
1491 const int thePyraTo2_2[2*4+1] =
1493 1, 2, 3, 4, 1, 3, 0, 4, -1
1495 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1497 const int thePentaTo3_1[3*4+1] =
1499 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1501 const int thePentaTo3_2[3*4+1] =
1503 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1505 const int thePentaTo3_3[3*4+1] =
1507 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1509 const int thePentaTo3_4[3*4+1] =
1511 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1513 const int thePentaTo3_5[3*4+1] =
1515 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1517 const int thePentaTo3_6[3*4+1] =
1519 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1521 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1522 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1524 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1527 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1528 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1529 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1534 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1535 bool _baryNode; //!< additional node is to be created at cell barycenter
1536 bool _ownConn; //!< to delete _connectivity in destructor
1537 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1539 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1540 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1541 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1542 bool hasFacet( const TTriangleFacet& facet ) const
1544 const int* tetConn = _connectivity;
1545 for ( ; tetConn[0] >= 0; tetConn += 4 )
1546 if (( facet.contains( tetConn[0] ) +
1547 facet.contains( tetConn[1] ) +
1548 facet.contains( tetConn[2] ) +
1549 facet.contains( tetConn[3] )) == 3 )
1555 //=======================================================================
1557 * \brief return TSplitMethod for the given element
1559 //=======================================================================
1561 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1563 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1565 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1566 // an edge and a face barycenter; tertaherdons are based on triangles and
1567 // a volume barycenter
1568 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1570 // Find out how adjacent volumes are split
1572 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1573 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1574 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1576 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1577 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1578 if ( nbNodes < 4 ) continue;
1580 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1581 const int* nInd = vol.GetFaceNodesIndices( iF );
1584 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1585 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1586 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1587 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1591 int iCom = 0; // common node of triangle faces to split into
1592 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1594 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1595 nInd[ iQ * ( (iCom+1)%nbNodes )],
1596 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1597 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1598 nInd[ iQ * ( (iCom+2)%nbNodes )],
1599 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1600 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1602 triaSplits.push_back( t012 );
1603 triaSplits.push_back( t023 );
1608 if ( !triaSplits.empty() )
1609 hasAdjacentSplits = true;
1612 // Among variants of split method select one compliant with adjacent volumes
1614 TSplitMethod method;
1615 if ( !vol.Element()->IsPoly() && !is24TetMode )
1617 int nbVariants = 2, nbTet = 0;
1618 const int** connVariants = 0;
1619 switch ( vol.Element()->GetEntityType() )
1621 case SMDSEntity_Hexa:
1622 case SMDSEntity_Quad_Hexa:
1623 case SMDSEntity_TriQuad_Hexa:
1624 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1625 connVariants = theHexTo5, nbTet = 5;
1627 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1629 case SMDSEntity_Pyramid:
1630 case SMDSEntity_Quad_Pyramid:
1631 connVariants = thePyraTo2; nbTet = 2;
1633 case SMDSEntity_Penta:
1634 case SMDSEntity_Quad_Penta:
1635 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1640 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1642 // check method compliancy with adjacent tetras,
1643 // all found splits must be among facets of tetras described by this method
1644 method = TSplitMethod( nbTet, connVariants[variant] );
1645 if ( hasAdjacentSplits && method._nbTetra > 0 )
1647 bool facetCreated = true;
1648 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1650 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1651 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1652 facetCreated = method.hasFacet( *facet );
1654 if ( !facetCreated )
1655 method = TSplitMethod(0); // incompatible method
1659 if ( method._nbTetra < 1 )
1661 // No standard method is applicable, use a generic solution:
1662 // each facet of a volume is split into triangles and
1663 // each of triangles and a volume barycenter form a tetrahedron.
1665 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1667 int* connectivity = new int[ maxTetConnSize + 1 ];
1668 method._connectivity = connectivity;
1669 method._ownConn = true;
1670 method._baryNode = !isHex27; // to create central node or not
1673 int baryCenInd = vol.NbNodes() - int( isHex27 );
1674 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1676 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1677 const int* nInd = vol.GetFaceNodesIndices( iF );
1678 // find common node of triangle facets of tetra to create
1679 int iCommon = 0; // index in linear numeration
1680 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1681 if ( !triaSplits.empty() )
1684 const TTriangleFacet* facet = &triaSplits.front();
1685 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1686 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1687 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1690 else if ( nbNodes > 3 && !is24TetMode )
1692 // find the best method of splitting into triangles by aspect ratio
1693 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1694 map< double, int > badness2iCommon;
1695 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1696 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1697 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1700 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1702 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1703 nodes[ iQ*((iLast-1)%nbNodes)],
1704 nodes[ iQ*((iLast )%nbNodes)]);
1705 badness += getBadRate( &tria, aspectRatio );
1707 badness2iCommon.insert( make_pair( badness, iCommon ));
1709 // use iCommon with lowest badness
1710 iCommon = badness2iCommon.begin()->second;
1712 if ( iCommon >= nbNodes )
1713 iCommon = 0; // something wrong
1715 // fill connectivity of tetrahedra based on a current face
1716 int nbTet = nbNodes - 2;
1717 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1722 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1723 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1727 method._faceBaryNode[ iF ] = 0;
1728 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1731 for ( int i = 0; i < nbTet; ++i )
1733 int i1 = i, i2 = (i+1) % nbNodes;
1734 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1735 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1736 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1737 connectivity[ connSize++ ] = faceBaryCenInd;
1738 connectivity[ connSize++ ] = baryCenInd;
1743 for ( int i = 0; i < nbTet; ++i )
1745 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1746 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1747 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1748 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1749 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1750 connectivity[ connSize++ ] = baryCenInd;
1753 method._nbTetra += nbTet;
1755 } // loop on volume faces
1757 connectivity[ connSize++ ] = -1;
1759 } // end of generic solution
1763 //================================================================================
1765 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1767 //================================================================================
1769 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1771 // find the tetrahedron including the three nodes of facet
1772 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1773 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1774 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1775 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1776 while ( volIt1->more() )
1778 const SMDS_MeshElement* v = volIt1->next();
1779 SMDSAbs_EntityType type = v->GetEntityType();
1780 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1782 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1783 continue; // medium node not allowed
1784 const int ind2 = v->GetNodeIndex( n2 );
1785 if ( ind2 < 0 || 3 < ind2 )
1787 const int ind3 = v->GetNodeIndex( n3 );
1788 if ( ind3 < 0 || 3 < ind3 )
1795 //=======================================================================
1797 * \brief A key of a face of volume
1799 //=======================================================================
1801 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1803 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1805 TIDSortedNodeSet sortedNodes;
1806 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1807 int nbNodes = vol.NbFaceNodes( iF );
1808 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1809 for ( int i = 0; i < nbNodes; i += iQ )
1810 sortedNodes.insert( fNodes[i] );
1811 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1812 first.first = (*(n++))->GetID();
1813 first.second = (*(n++))->GetID();
1814 second.first = (*(n++))->GetID();
1815 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1820 //=======================================================================
1821 //function : SplitVolumesIntoTetra
1822 //purpose : Split volume elements into tetrahedra.
1823 //=======================================================================
1825 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1826 const int theMethodFlags)
1828 // std-like iterator on coordinates of nodes of mesh element
1829 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1830 NXyzIterator xyzEnd;
1832 SMDS_VolumeTool volTool;
1833 SMESH_MesherHelper helper( *GetMesh());
1835 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1836 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1838 SMESH_SequenceOfElemPtr newNodes, newElems;
1840 // map face of volume to it's baricenrtic node
1841 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1844 TIDSortedElemSet::const_iterator elem = theElems.begin();
1845 for ( ; elem != theElems.end(); ++elem )
1847 if ( (*elem)->GetType() != SMDSAbs_Volume )
1849 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1850 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1853 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1855 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1856 if ( splitMethod._nbTetra < 1 ) continue;
1858 // find submesh to add new tetras to
1859 if ( !subMesh || !subMesh->Contains( *elem ))
1861 int shapeID = FindShape( *elem );
1862 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1863 subMesh = GetMeshDS()->MeshElements( shapeID );
1866 if ( (*elem)->IsQuadratic() )
1869 // add quadratic links to the helper
1870 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1872 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1873 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1874 for ( int iN = 0; iN < nbN; iN += iQ )
1875 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1877 helper.SetIsQuadratic( true );
1882 helper.SetIsQuadratic( false );
1884 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1885 helper.SetElementsOnShape( true );
1886 if ( splitMethod._baryNode )
1888 // make a node at barycenter
1889 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1890 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1891 nodes.push_back( gcNode );
1892 newNodes.Append( gcNode );
1894 if ( !splitMethod._faceBaryNode.empty() )
1896 // make or find baricentric nodes of faces
1897 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1898 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1900 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1901 volFace2BaryNode.insert
1902 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1905 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1906 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1908 nodes.push_back( iF_n->second = f_n->second );
1913 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1914 const int* tetConn = splitMethod._connectivity;
1915 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1916 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1917 nodes[ tetConn[1] ],
1918 nodes[ tetConn[2] ],
1919 nodes[ tetConn[3] ]));
1921 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1923 // Split faces on sides of the split volume
1925 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1926 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1928 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1929 if ( nbNodes < 4 ) continue;
1931 // find an existing face
1932 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1933 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1934 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1935 /*noMedium=*/false))
1938 helper.SetElementsOnShape( false );
1939 vector< const SMDS_MeshElement* > triangles;
1941 // find submesh to add new triangles in
1942 if ( !fSubMesh || !fSubMesh->Contains( face ))
1944 int shapeID = FindShape( face );
1945 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1947 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1948 if ( iF_n != splitMethod._faceBaryNode.end() )
1950 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1952 const SMDS_MeshNode* n1 = fNodes[iN];
1953 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1954 const SMDS_MeshNode *n3 = iF_n->second;
1955 if ( !volTool.IsFaceExternal( iF ))
1957 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1959 if ( fSubMesh && n3->getshapeId() < 1 )
1960 fSubMesh->AddNode( n3 );
1965 // among possible triangles create ones discribed by split method
1966 const int* nInd = volTool.GetFaceNodesIndices( iF );
1967 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1968 int iCom = 0; // common node of triangle faces to split into
1969 list< TTriangleFacet > facets;
1970 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1972 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1973 nInd[ iQ * ( (iCom+1)%nbNodes )],
1974 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1975 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1976 nInd[ iQ * ( (iCom+2)%nbNodes )],
1977 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1978 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1980 facets.push_back( t012 );
1981 facets.push_back( t023 );
1982 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1983 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1984 nInd[ iQ * ((iLast-1)%nbNodes )],
1985 nInd[ iQ * ((iLast )%nbNodes )]));
1989 list< TTriangleFacet >::iterator facet = facets.begin();
1990 for ( ; facet != facets.end(); ++facet )
1992 if ( !volTool.IsFaceExternal( iF ))
1993 swap( facet->_n2, facet->_n3 );
1994 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1995 volNodes[ facet->_n2 ],
1996 volNodes[ facet->_n3 ]));
1999 for ( int i = 0; i < triangles.size(); ++i )
2001 if ( !triangles[i] ) continue;
2003 fSubMesh->AddElement( triangles[i]);
2004 newElems.Append( triangles[i] );
2006 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2007 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2010 } // loop on volume faces to split them into triangles
2012 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2014 if ( geomType == SMDSEntity_TriQuad_Hexa )
2016 // remove medium nodes that could become free
2017 for ( int i = 20; i < volTool.NbNodes(); ++i )
2018 if ( volNodes[i]->NbInverseElements() == 0 )
2019 GetMeshDS()->RemoveNode( volNodes[i] );
2021 } // loop on volumes to split
2023 myLastCreatedNodes = newNodes;
2024 myLastCreatedElems = newElems;
2027 //=======================================================================
2028 //function : AddToSameGroups
2029 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2030 //=======================================================================
2032 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2033 const SMDS_MeshElement* elemInGroups,
2034 SMESHDS_Mesh * aMesh)
2036 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2037 if (!groups.empty()) {
2038 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2039 for ( ; grIt != groups.end(); grIt++ ) {
2040 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2041 if ( group && group->Contains( elemInGroups ))
2042 group->SMDSGroup().Add( elemToAdd );
2048 //=======================================================================
2049 //function : RemoveElemFromGroups
2050 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2051 //=======================================================================
2052 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2053 SMESHDS_Mesh * aMesh)
2055 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2056 if (!groups.empty())
2058 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2059 for (; GrIt != groups.end(); GrIt++)
2061 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2062 if (!grp || grp->IsEmpty()) continue;
2063 grp->SMDSGroup().Remove(removeelem);
2068 //================================================================================
2070 * \brief Replace elemToRm by elemToAdd in the all groups
2072 //================================================================================
2074 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2075 const SMDS_MeshElement* elemToAdd,
2076 SMESHDS_Mesh * aMesh)
2078 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2079 if (!groups.empty()) {
2080 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2081 for ( ; grIt != groups.end(); grIt++ ) {
2082 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2083 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2084 group->SMDSGroup().Add( elemToAdd );
2089 //================================================================================
2091 * \brief Replace elemToRm by elemToAdd in the all groups
2093 //================================================================================
2095 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2096 const vector<const SMDS_MeshElement*>& elemToAdd,
2097 SMESHDS_Mesh * aMesh)
2099 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2100 if (!groups.empty())
2102 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2103 for ( ; grIt != groups.end(); grIt++ ) {
2104 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2105 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2106 for ( int i = 0; i < elemToAdd.size(); ++i )
2107 group->SMDSGroup().Add( elemToAdd[ i ] );
2112 //=======================================================================
2113 //function : QuadToTri
2114 //purpose : Cut quadrangles into triangles.
2115 // theCrit is used to select a diagonal to cut
2116 //=======================================================================
2118 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2119 const bool the13Diag)
2121 myLastCreatedElems.Clear();
2122 myLastCreatedNodes.Clear();
2124 MESSAGE( "::QuadToTri()" );
2126 SMESHDS_Mesh * aMesh = GetMeshDS();
2128 Handle(Geom_Surface) surface;
2129 SMESH_MesherHelper helper( *GetMesh() );
2131 TIDSortedElemSet::iterator itElem;
2132 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2133 const SMDS_MeshElement* elem = *itElem;
2134 if ( !elem || elem->GetType() != SMDSAbs_Face )
2136 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2137 if(!isquad) continue;
2139 if(elem->NbNodes()==4) {
2140 // retrieve element nodes
2141 const SMDS_MeshNode* aNodes [4];
2142 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2144 while ( itN->more() )
2145 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2147 int aShapeId = FindShape( elem );
2148 const SMDS_MeshElement* newElem1 = 0;
2149 const SMDS_MeshElement* newElem2 = 0;
2151 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2152 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2155 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2156 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2158 myLastCreatedElems.Append(newElem1);
2159 myLastCreatedElems.Append(newElem2);
2160 // put a new triangle on the same shape and add to the same groups
2163 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2164 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2166 AddToSameGroups( newElem1, elem, aMesh );
2167 AddToSameGroups( newElem2, elem, aMesh );
2168 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2169 aMesh->RemoveElement( elem );
2172 // Quadratic quadrangle
2174 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2176 // get surface elem is on
2177 int aShapeId = FindShape( elem );
2178 if ( aShapeId != helper.GetSubShapeID() ) {
2182 shape = aMesh->IndexToShape( aShapeId );
2183 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2184 TopoDS_Face face = TopoDS::Face( shape );
2185 surface = BRep_Tool::Surface( face );
2186 if ( !surface.IsNull() )
2187 helper.SetSubShape( shape );
2191 const SMDS_MeshNode* aNodes [8];
2192 const SMDS_MeshNode* inFaceNode = 0;
2193 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2195 while ( itN->more() ) {
2196 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2197 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2198 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2200 inFaceNode = aNodes[ i-1 ];
2204 // find middle point for (0,1,2,3)
2205 // and create a node in this point;
2207 if ( surface.IsNull() ) {
2209 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2213 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2216 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2218 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2220 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2221 myLastCreatedNodes.Append(newN);
2223 // create a new element
2224 const SMDS_MeshElement* newElem1 = 0;
2225 const SMDS_MeshElement* newElem2 = 0;
2227 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2228 aNodes[6], aNodes[7], newN );
2229 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2230 newN, aNodes[4], aNodes[5] );
2233 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2234 aNodes[7], aNodes[4], newN );
2235 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2236 newN, aNodes[5], aNodes[6] );
2238 myLastCreatedElems.Append(newElem1);
2239 myLastCreatedElems.Append(newElem2);
2240 // put a new triangle on the same shape and add to the same groups
2243 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2244 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2246 AddToSameGroups( newElem1, elem, aMesh );
2247 AddToSameGroups( newElem2, elem, aMesh );
2248 aMesh->RemoveElement( elem );
2255 //=======================================================================
2256 //function : getAngle
2258 //=======================================================================
2260 double getAngle(const SMDS_MeshElement * tr1,
2261 const SMDS_MeshElement * tr2,
2262 const SMDS_MeshNode * n1,
2263 const SMDS_MeshNode * n2)
2265 double angle = 2. * M_PI; // bad angle
2268 SMESH::Controls::TSequenceOfXYZ P1, P2;
2269 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2270 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2273 if(!tr1->IsQuadratic())
2274 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2276 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2277 if ( N1.SquareMagnitude() <= gp::Resolution() )
2279 if(!tr2->IsQuadratic())
2280 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2282 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2283 if ( N2.SquareMagnitude() <= gp::Resolution() )
2286 // find the first diagonal node n1 in the triangles:
2287 // take in account a diagonal link orientation
2288 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2289 for ( int t = 0; t < 2; t++ ) {
2290 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2291 int i = 0, iDiag = -1;
2292 while ( it->more()) {
2293 const SMDS_MeshElement *n = it->next();
2294 if ( n == n1 || n == n2 ) {
2298 if ( i - iDiag == 1 )
2299 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2308 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2311 angle = N1.Angle( N2 );
2316 // =================================================
2317 // class generating a unique ID for a pair of nodes
2318 // and able to return nodes by that ID
2319 // =================================================
2323 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2324 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2327 long GetLinkID (const SMDS_MeshNode * n1,
2328 const SMDS_MeshNode * n2) const
2330 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2333 bool GetNodes (const long theLinkID,
2334 const SMDS_MeshNode* & theNode1,
2335 const SMDS_MeshNode* & theNode2) const
2337 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2338 if ( !theNode1 ) return false;
2339 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2340 if ( !theNode2 ) return false;
2346 const SMESHDS_Mesh* myMesh;
2351 //=======================================================================
2352 //function : TriToQuad
2353 //purpose : Fuse neighbour triangles into quadrangles.
2354 // theCrit is used to select a neighbour to fuse with.
2355 // theMaxAngle is a max angle between element normals at which
2356 // fusion is still performed.
2357 //=======================================================================
2359 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2360 SMESH::Controls::NumericalFunctorPtr theCrit,
2361 const double theMaxAngle)
2363 myLastCreatedElems.Clear();
2364 myLastCreatedNodes.Clear();
2366 MESSAGE( "::TriToQuad()" );
2368 if ( !theCrit.get() )
2371 SMESHDS_Mesh * aMesh = GetMeshDS();
2373 // Prepare data for algo: build
2374 // 1. map of elements with their linkIDs
2375 // 2. map of linkIDs with their elements
2377 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2378 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2379 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2380 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2382 TIDSortedElemSet::iterator itElem;
2383 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2384 const SMDS_MeshElement* elem = *itElem;
2385 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2386 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2387 if(!IsTria) continue;
2389 // retrieve element nodes
2390 const SMDS_MeshNode* aNodes [4];
2391 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2394 aNodes[ i++ ] = cast2Node( itN->next() );
2395 aNodes[ 3 ] = aNodes[ 0 ];
2398 for ( i = 0; i < 3; i++ ) {
2399 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2400 // check if elements sharing a link can be fused
2401 itLE = mapLi_listEl.find( link );
2402 if ( itLE != mapLi_listEl.end() ) {
2403 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2405 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2406 //if ( FindShape( elem ) != FindShape( elem2 ))
2407 // continue; // do not fuse triangles laying on different shapes
2408 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2409 continue; // avoid making badly shaped quads
2410 (*itLE).second.push_back( elem );
2413 mapLi_listEl[ link ].push_back( elem );
2415 mapEl_setLi [ elem ].insert( link );
2418 // Clean the maps from the links shared by a sole element, ie
2419 // links to which only one element is bound in mapLi_listEl
2421 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2422 int nbElems = (*itLE).second.size();
2423 if ( nbElems < 2 ) {
2424 const SMDS_MeshElement* elem = (*itLE).second.front();
2425 SMESH_TLink link = (*itLE).first;
2426 mapEl_setLi[ elem ].erase( link );
2427 if ( mapEl_setLi[ elem ].empty() )
2428 mapEl_setLi.erase( elem );
2432 // Algo: fuse triangles into quadrangles
2434 while ( ! mapEl_setLi.empty() ) {
2435 // Look for the start element:
2436 // the element having the least nb of shared links
2437 const SMDS_MeshElement* startElem = 0;
2439 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2440 int nbLinks = (*itEL).second.size();
2441 if ( nbLinks < minNbLinks ) {
2442 startElem = (*itEL).first;
2443 minNbLinks = nbLinks;
2444 if ( minNbLinks == 1 )
2449 // search elements to fuse starting from startElem or links of elements
2450 // fused earlyer - startLinks
2451 list< SMESH_TLink > startLinks;
2452 while ( startElem || !startLinks.empty() ) {
2453 while ( !startElem && !startLinks.empty() ) {
2454 // Get an element to start, by a link
2455 SMESH_TLink linkId = startLinks.front();
2456 startLinks.pop_front();
2457 itLE = mapLi_listEl.find( linkId );
2458 if ( itLE != mapLi_listEl.end() ) {
2459 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2460 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2461 for ( ; itE != listElem.end() ; itE++ )
2462 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2464 mapLi_listEl.erase( itLE );
2469 // Get candidates to be fused
2470 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2471 const SMESH_TLink *link12, *link13;
2473 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2474 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2475 ASSERT( !setLi.empty() );
2476 set< SMESH_TLink >::iterator itLi;
2477 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2479 const SMESH_TLink & link = (*itLi);
2480 itLE = mapLi_listEl.find( link );
2481 if ( itLE == mapLi_listEl.end() )
2484 const SMDS_MeshElement* elem = (*itLE).second.front();
2486 elem = (*itLE).second.back();
2487 mapLi_listEl.erase( itLE );
2488 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2499 // add other links of elem to list of links to re-start from
2500 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2501 set< SMESH_TLink >::iterator it;
2502 for ( it = links.begin(); it != links.end(); it++ ) {
2503 const SMESH_TLink& link2 = (*it);
2504 if ( link2 != link )
2505 startLinks.push_back( link2 );
2509 // Get nodes of possible quadrangles
2510 const SMDS_MeshNode *n12 [4], *n13 [4];
2511 bool Ok12 = false, Ok13 = false;
2512 const SMDS_MeshNode *linkNode1, *linkNode2;
2514 linkNode1 = link12->first;
2515 linkNode2 = link12->second;
2516 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2520 linkNode1 = link13->first;
2521 linkNode2 = link13->second;
2522 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2526 // Choose a pair to fuse
2527 if ( Ok12 && Ok13 ) {
2528 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2529 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2530 double aBadRate12 = getBadRate( &quad12, theCrit );
2531 double aBadRate13 = getBadRate( &quad13, theCrit );
2532 if ( aBadRate13 < aBadRate12 )
2539 // and remove fused elems and removed links from the maps
2540 mapEl_setLi.erase( tr1 );
2542 mapEl_setLi.erase( tr2 );
2543 mapLi_listEl.erase( *link12 );
2544 if(tr1->NbNodes()==3) {
2545 const SMDS_MeshElement* newElem = 0;
2546 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2547 myLastCreatedElems.Append(newElem);
2548 AddToSameGroups( newElem, tr1, aMesh );
2549 int aShapeId = tr1->getshapeId();
2552 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2554 aMesh->RemoveElement( tr1 );
2555 aMesh->RemoveElement( tr2 );
2558 const SMDS_MeshNode* N1 [6];
2559 const SMDS_MeshNode* N2 [6];
2560 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2561 // now we receive following N1 and N2 (using numeration as above image)
2562 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2563 // i.e. first nodes from both arrays determ new diagonal
2564 const SMDS_MeshNode* aNodes[8];
2573 const SMDS_MeshElement* newElem = 0;
2574 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2575 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2576 myLastCreatedElems.Append(newElem);
2577 AddToSameGroups( newElem, tr1, aMesh );
2578 int aShapeId = tr1->getshapeId();
2581 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2583 aMesh->RemoveElement( tr1 );
2584 aMesh->RemoveElement( tr2 );
2585 // remove middle node (9)
2586 GetMeshDS()->RemoveNode( N1[4] );
2590 mapEl_setLi.erase( tr3 );
2591 mapLi_listEl.erase( *link13 );
2592 if(tr1->NbNodes()==3) {
2593 const SMDS_MeshElement* newElem = 0;
2594 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2595 myLastCreatedElems.Append(newElem);
2596 AddToSameGroups( newElem, tr1, aMesh );
2597 int aShapeId = tr1->getshapeId();
2600 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2602 aMesh->RemoveElement( tr1 );
2603 aMesh->RemoveElement( tr3 );
2606 const SMDS_MeshNode* N1 [6];
2607 const SMDS_MeshNode* N2 [6];
2608 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2609 // now we receive following N1 and N2 (using numeration as above image)
2610 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2611 // i.e. first nodes from both arrays determ new diagonal
2612 const SMDS_MeshNode* aNodes[8];
2621 const SMDS_MeshElement* newElem = 0;
2622 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2623 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2624 myLastCreatedElems.Append(newElem);
2625 AddToSameGroups( newElem, tr1, aMesh );
2626 int aShapeId = tr1->getshapeId();
2629 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2631 aMesh->RemoveElement( tr1 );
2632 aMesh->RemoveElement( tr3 );
2633 // remove middle node (9)
2634 GetMeshDS()->RemoveNode( N1[4] );
2638 // Next element to fuse: the rejected one
2640 startElem = Ok12 ? tr3 : tr2;
2642 } // if ( startElem )
2643 } // while ( startElem || !startLinks.empty() )
2644 } // while ( ! mapEl_setLi.empty() )
2650 /*#define DUMPSO(txt) \
2651 // cout << txt << endl;
2652 //=============================================================================
2656 //=============================================================================
2657 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2661 int tmp = idNodes[ i1 ];
2662 idNodes[ i1 ] = idNodes[ i2 ];
2663 idNodes[ i2 ] = tmp;
2664 gp_Pnt Ptmp = P[ i1 ];
2667 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2670 //=======================================================================
2671 //function : SortQuadNodes
2672 //purpose : Set 4 nodes of a quadrangle face in a good order.
2673 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2675 //=======================================================================
2677 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2682 for ( i = 0; i < 4; i++ ) {
2683 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2685 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2688 gp_Vec V1(P[0], P[1]);
2689 gp_Vec V2(P[0], P[2]);
2690 gp_Vec V3(P[0], P[3]);
2692 gp_Vec Cross1 = V1 ^ V2;
2693 gp_Vec Cross2 = V2 ^ V3;
2696 if (Cross1.Dot(Cross2) < 0)
2701 if (Cross1.Dot(Cross2) < 0)
2705 swap ( i, i + 1, idNodes, P );
2707 // for ( int ii = 0; ii < 4; ii++ ) {
2708 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2709 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2715 //=======================================================================
2716 //function : SortHexaNodes
2717 //purpose : Set 8 nodes of a hexahedron in a good order.
2718 // Return success status
2719 //=======================================================================
2721 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2726 DUMPSO( "INPUT: ========================================");
2727 for ( i = 0; i < 8; i++ ) {
2728 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2729 if ( !n ) return false;
2730 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2731 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2733 DUMPSO( "========================================");
2736 set<int> faceNodes; // ids of bottom face nodes, to be found
2737 set<int> checkedId1; // ids of tried 2-nd nodes
2738 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2739 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2740 int iMin, iLoop1 = 0;
2742 // Loop to try the 2-nd nodes
2744 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2746 // Find not checked 2-nd node
2747 for ( i = 1; i < 8; i++ )
2748 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2749 int id1 = idNodes[i];
2750 swap ( 1, i, idNodes, P );
2751 checkedId1.insert ( id1 );
2755 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2756 // ie that all but meybe one (id3 which is on the same face) nodes
2757 // lay on the same side from the triangle plane.
2759 bool manyInPlane = false; // more than 4 nodes lay in plane
2761 while ( ++iLoop2 < 6 ) {
2763 // get 1-2-3 plane coeffs
2764 Standard_Real A, B, C, D;
2765 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2766 if ( N.SquareMagnitude() > gp::Resolution() )
2768 gp_Pln pln ( P[0], N );
2769 pln.Coefficients( A, B, C, D );
2771 // find the node (iMin) closest to pln
2772 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2774 for ( i = 3; i < 8; i++ ) {
2775 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2776 if ( fabs( dist[i] ) < minDist ) {
2777 minDist = fabs( dist[i] );
2780 if ( fabs( dist[i] ) <= tol )
2781 idInPln.insert( idNodes[i] );
2784 // there should not be more than 4 nodes in bottom plane
2785 if ( idInPln.size() > 1 )
2787 DUMPSO( "### idInPln.size() = " << idInPln.size());
2788 // idInPlane does not contain the first 3 nodes
2789 if ( manyInPlane || idInPln.size() == 5)
2790 return false; // all nodes in one plane
2793 // set the 1-st node to be not in plane
2794 for ( i = 3; i < 8; i++ ) {
2795 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2796 DUMPSO( "### Reset 0-th node");
2797 swap( 0, i, idNodes, P );
2802 // reset to re-check second nodes
2803 leastDist = DBL_MAX;
2807 break; // from iLoop2;
2810 // check that the other 4 nodes are on the same side
2811 bool sameSide = true;
2812 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2813 for ( i = 3; sameSide && i < 8; i++ ) {
2815 sameSide = ( isNeg == dist[i] <= 0.);
2818 // keep best solution
2819 if ( sameSide && minDist < leastDist ) {
2820 leastDist = minDist;
2822 faceNodes.insert( idNodes[ 1 ] );
2823 faceNodes.insert( idNodes[ 2 ] );
2824 faceNodes.insert( idNodes[ iMin ] );
2825 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2826 << " leastDist = " << leastDist);
2827 if ( leastDist <= DBL_MIN )
2832 // set next 3-d node to check
2833 int iNext = 2 + iLoop2;
2835 DUMPSO( "Try 2-nd");
2836 swap ( 2, iNext, idNodes, P );
2838 } // while ( iLoop2 < 6 )
2841 if ( faceNodes.empty() ) return false;
2843 // Put the faceNodes in proper places
2844 for ( i = 4; i < 8; i++ ) {
2845 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2846 // find a place to put
2848 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2850 DUMPSO( "Set faceNodes");
2851 swap ( iTo, i, idNodes, P );
2856 // Set nodes of the found bottom face in good order
2857 DUMPSO( " Found bottom face: ");
2858 i = SortQuadNodes( theMesh, idNodes );
2860 gp_Pnt Ptmp = P[ i ];
2865 // for ( int ii = 0; ii < 4; ii++ ) {
2866 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2867 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2870 // Gravity center of the top and bottom faces
2871 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2872 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2874 // Get direction from the bottom to the top face
2875 gp_Vec upDir ( aGCb, aGCt );
2876 Standard_Real upDirSize = upDir.Magnitude();
2877 if ( upDirSize <= gp::Resolution() ) return false;
2880 // Assure that the bottom face normal points up
2881 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2882 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2883 if ( Nb.Dot( upDir ) < 0 ) {
2884 DUMPSO( "Reverse bottom face");
2885 swap( 1, 3, idNodes, P );
2888 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2889 Standard_Real minDist = DBL_MAX;
2890 for ( i = 4; i < 8; i++ ) {
2891 // projection of P[i] to the plane defined by P[0] and upDir
2892 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2893 Standard_Real sqDist = P[0].SquareDistance( Pp );
2894 if ( sqDist < minDist ) {
2899 DUMPSO( "Set 4-th");
2900 swap ( 4, iMin, idNodes, P );
2902 // Set nodes of the top face in good order
2903 DUMPSO( "Sort top face");
2904 i = SortQuadNodes( theMesh, &idNodes[4] );
2907 gp_Pnt Ptmp = P[ i ];
2912 // Assure that direction of the top face normal is from the bottom face
2913 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2914 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2915 if ( Nt.Dot( upDir ) < 0 ) {
2916 DUMPSO( "Reverse top face");
2917 swap( 5, 7, idNodes, P );
2920 // DUMPSO( "OUTPUT: ========================================");
2921 // for ( i = 0; i < 8; i++ ) {
2922 // float *p = ugrid->GetPoint(idNodes[i]);
2923 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2929 //================================================================================
2931 * \brief Return nodes linked to the given one
2932 * \param theNode - the node
2933 * \param linkedNodes - the found nodes
2934 * \param type - the type of elements to check
2936 * Medium nodes are ignored
2938 //================================================================================
2940 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2941 TIDSortedElemSet & linkedNodes,
2942 SMDSAbs_ElementType type )
2944 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2945 while ( elemIt->more() )
2947 const SMDS_MeshElement* elem = elemIt->next();
2948 if(elem->GetType() == SMDSAbs_0DElement)
2951 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2952 if ( elem->GetType() == SMDSAbs_Volume )
2954 SMDS_VolumeTool vol( elem );
2955 while ( nodeIt->more() ) {
2956 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2957 if ( theNode != n && vol.IsLinked( theNode, n ))
2958 linkedNodes.insert( n );
2963 for ( int i = 0; nodeIt->more(); ++i ) {
2964 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2965 if ( n == theNode ) {
2966 int iBefore = i - 1;
2968 if ( elem->IsQuadratic() ) {
2969 int nb = elem->NbNodes() / 2;
2970 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2971 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2973 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2974 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2981 //=======================================================================
2982 //function : laplacianSmooth
2983 //purpose : pulls theNode toward the center of surrounding nodes directly
2984 // connected to that node along an element edge
2985 //=======================================================================
2987 void laplacianSmooth(const SMDS_MeshNode* theNode,
2988 const Handle(Geom_Surface)& theSurface,
2989 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2991 // find surrounding nodes
2993 TIDSortedElemSet nodeSet;
2994 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2996 // compute new coodrs
2998 double coord[] = { 0., 0., 0. };
2999 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3000 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3001 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3002 if ( theSurface.IsNull() ) { // smooth in 3D
3003 coord[0] += node->X();
3004 coord[1] += node->Y();
3005 coord[2] += node->Z();
3007 else { // smooth in 2D
3008 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3009 gp_XY* uv = theUVMap[ node ];
3010 coord[0] += uv->X();
3011 coord[1] += uv->Y();
3014 int nbNodes = nodeSet.size();
3017 coord[0] /= nbNodes;
3018 coord[1] /= nbNodes;
3020 if ( !theSurface.IsNull() ) {
3021 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3022 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3023 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3029 coord[2] /= nbNodes;
3033 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3036 //=======================================================================
3037 //function : centroidalSmooth
3038 //purpose : pulls theNode toward the element-area-weighted centroid of the
3039 // surrounding elements
3040 //=======================================================================
3042 void centroidalSmooth(const SMDS_MeshNode* theNode,
3043 const Handle(Geom_Surface)& theSurface,
3044 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3046 gp_XYZ aNewXYZ(0.,0.,0.);
3047 SMESH::Controls::Area anAreaFunc;
3048 double totalArea = 0.;
3053 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3054 while ( elemIt->more() )
3056 const SMDS_MeshElement* elem = elemIt->next();
3059 gp_XYZ elemCenter(0.,0.,0.);
3060 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3061 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3062 int nn = elem->NbNodes();
3063 if(elem->IsQuadratic()) nn = nn/2;
3065 //while ( itN->more() ) {
3067 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3069 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3070 aNodePoints.push_back( aP );
3071 if ( !theSurface.IsNull() ) { // smooth in 2D
3072 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3073 gp_XY* uv = theUVMap[ aNode ];
3074 aP.SetCoord( uv->X(), uv->Y(), 0. );
3078 double elemArea = anAreaFunc.GetValue( aNodePoints );
3079 totalArea += elemArea;
3081 aNewXYZ += elemCenter * elemArea;
3083 aNewXYZ /= totalArea;
3084 if ( !theSurface.IsNull() ) {
3085 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3086 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3091 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3094 //=======================================================================
3095 //function : getClosestUV
3096 //purpose : return UV of closest projection
3097 //=======================================================================
3099 static bool getClosestUV (Extrema_GenExtPS& projector,
3100 const gp_Pnt& point,
3103 projector.Perform( point );
3104 if ( projector.IsDone() ) {
3105 double u, v, minVal = DBL_MAX;
3106 for ( int i = projector.NbExt(); i > 0; i-- )
3107 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3108 if ( projector.SquareDistance( i ) < minVal ) {
3109 minVal = projector.SquareDistance( i );
3111 if ( projector.Value( i ) < minVal ) {
3112 minVal = projector.Value( i );
3114 projector.Point( i ).Parameter( u, v );
3116 result.SetCoord( u, v );
3122 //=======================================================================
3124 //purpose : Smooth theElements during theNbIterations or until a worst
3125 // element has aspect ratio <= theTgtAspectRatio.
3126 // Aspect Ratio varies in range [1.0, inf].
3127 // If theElements is empty, the whole mesh is smoothed.
3128 // theFixedNodes contains additionally fixed nodes. Nodes built
3129 // on edges and boundary nodes are always fixed.
3130 //=======================================================================
3132 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3133 set<const SMDS_MeshNode*> & theFixedNodes,
3134 const SmoothMethod theSmoothMethod,
3135 const int theNbIterations,
3136 double theTgtAspectRatio,
3139 myLastCreatedElems.Clear();
3140 myLastCreatedNodes.Clear();
3142 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3144 if ( theTgtAspectRatio < 1.0 )
3145 theTgtAspectRatio = 1.0;
3147 const double disttol = 1.e-16;
3149 SMESH::Controls::AspectRatio aQualityFunc;
3151 SMESHDS_Mesh* aMesh = GetMeshDS();
3153 if ( theElems.empty() ) {
3154 // add all faces to theElems
3155 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3156 while ( fIt->more() ) {
3157 const SMDS_MeshElement* face = fIt->next();
3158 theElems.insert( theElems.end(), face );
3161 // get all face ids theElems are on
3162 set< int > faceIdSet;
3163 TIDSortedElemSet::iterator itElem;
3165 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3166 int fId = FindShape( *itElem );
3167 // check that corresponding submesh exists and a shape is face
3169 faceIdSet.find( fId ) == faceIdSet.end() &&
3170 aMesh->MeshElements( fId )) {
3171 TopoDS_Shape F = aMesh->IndexToShape( fId );
3172 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3173 faceIdSet.insert( fId );
3176 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3178 // ===============================================
3179 // smooth elements on each TopoDS_Face separately
3180 // ===============================================
3182 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3183 for ( ; fId != faceIdSet.rend(); ++fId ) {
3184 // get face surface and submesh
3185 Handle(Geom_Surface) surface;
3186 SMESHDS_SubMesh* faceSubMesh = 0;
3188 double fToler2 = 0, f,l;
3189 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3190 bool isUPeriodic = false, isVPeriodic = false;
3192 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3193 surface = BRep_Tool::Surface( face );
3194 faceSubMesh = aMesh->MeshElements( *fId );
3195 fToler2 = BRep_Tool::Tolerance( face );
3196 fToler2 *= fToler2 * 10.;
3197 isUPeriodic = surface->IsUPeriodic();
3200 isVPeriodic = surface->IsVPeriodic();
3203 surface->Bounds( u1, u2, v1, v2 );
3205 // ---------------------------------------------------------
3206 // for elements on a face, find movable and fixed nodes and
3207 // compute UV for them
3208 // ---------------------------------------------------------
3209 bool checkBoundaryNodes = false;
3210 bool isQuadratic = false;
3211 set<const SMDS_MeshNode*> setMovableNodes;
3212 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3213 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3214 list< const SMDS_MeshElement* > elemsOnFace;
3216 Extrema_GenExtPS projector;
3217 GeomAdaptor_Surface surfAdaptor;
3218 if ( !surface.IsNull() ) {
3219 surfAdaptor.Load( surface );
3220 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3222 int nbElemOnFace = 0;
3223 itElem = theElems.begin();
3224 // loop on not yet smoothed elements: look for elems on a face
3225 while ( itElem != theElems.end() ) {
3226 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3227 break; // all elements found
3229 const SMDS_MeshElement* elem = *itElem;
3230 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3231 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3235 elemsOnFace.push_back( elem );
3236 theElems.erase( itElem++ );
3240 isQuadratic = elem->IsQuadratic();
3242 // get movable nodes of elem
3243 const SMDS_MeshNode* node;
3244 SMDS_TypeOfPosition posType;
3245 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3246 int nn = 0, nbn = elem->NbNodes();
3247 if(elem->IsQuadratic())
3249 while ( nn++ < nbn ) {
3250 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3251 const SMDS_PositionPtr& pos = node->GetPosition();
3252 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3253 if (posType != SMDS_TOP_EDGE &&
3254 posType != SMDS_TOP_VERTEX &&
3255 theFixedNodes.find( node ) == theFixedNodes.end())
3257 // check if all faces around the node are on faceSubMesh
3258 // because a node on edge may be bound to face
3259 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3261 if ( faceSubMesh ) {
3262 while ( eIt->more() && all ) {
3263 const SMDS_MeshElement* e = eIt->next();
3264 all = faceSubMesh->Contains( e );
3268 setMovableNodes.insert( node );
3270 checkBoundaryNodes = true;
3272 if ( posType == SMDS_TOP_3DSPACE )
3273 checkBoundaryNodes = true;
3276 if ( surface.IsNull() )
3279 // get nodes to check UV
3280 list< const SMDS_MeshNode* > uvCheckNodes;
3281 itN = elem->nodesIterator();
3282 nn = 0; nbn = elem->NbNodes();
3283 if(elem->IsQuadratic())
3285 while ( nn++ < nbn ) {
3286 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3287 if ( uvMap.find( node ) == uvMap.end() )
3288 uvCheckNodes.push_back( node );
3289 // add nodes of elems sharing node
3290 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3291 // while ( eIt->more() ) {
3292 // const SMDS_MeshElement* e = eIt->next();
3293 // if ( e != elem ) {
3294 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3295 // while ( nIt->more() ) {
3296 // const SMDS_MeshNode* n =
3297 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3298 // if ( uvMap.find( n ) == uvMap.end() )
3299 // uvCheckNodes.push_back( n );
3305 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3306 for ( ; n != uvCheckNodes.end(); ++n ) {
3309 const SMDS_PositionPtr& pos = node->GetPosition();
3310 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3312 switch ( posType ) {
3313 case SMDS_TOP_FACE: {
3314 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3315 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3318 case SMDS_TOP_EDGE: {
3319 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3320 Handle(Geom2d_Curve) pcurve;
3321 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3322 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3323 if ( !pcurve.IsNull() ) {
3324 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3325 uv = pcurve->Value( u ).XY();
3329 case SMDS_TOP_VERTEX: {
3330 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3331 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3332 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3337 // check existing UV
3338 bool project = true;
3339 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3340 double dist1 = DBL_MAX, dist2 = 0;
3341 if ( posType != SMDS_TOP_3DSPACE ) {
3342 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3343 project = dist1 > fToler2;
3345 if ( project ) { // compute new UV
3347 if ( !getClosestUV( projector, pNode, newUV )) {
3348 MESSAGE("Node Projection Failed " << node);
3352 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3354 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3356 if ( posType != SMDS_TOP_3DSPACE )
3357 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3358 if ( dist2 < dist1 )
3362 // store UV in the map
3363 listUV.push_back( uv );
3364 uvMap.insert( make_pair( node, &listUV.back() ));
3366 } // loop on not yet smoothed elements
3368 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3369 checkBoundaryNodes = true;
3371 // fix nodes on mesh boundary
3373 if ( checkBoundaryNodes ) {
3374 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3375 map< SMESH_TLink, int >::iterator link_nb;
3376 // put all elements links to linkNbMap
3377 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3378 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3379 const SMDS_MeshElement* elem = (*elemIt);
3380 int nbn = elem->NbCornerNodes();
3381 // loop on elem links: insert them in linkNbMap
3382 for ( int iN = 0; iN < nbn; ++iN ) {
3383 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3384 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3385 SMESH_TLink link( n1, n2 );
3386 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3390 // remove nodes that are in links encountered only once from setMovableNodes
3391 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3392 if ( link_nb->second == 1 ) {
3393 setMovableNodes.erase( link_nb->first.node1() );
3394 setMovableNodes.erase( link_nb->first.node2() );
3399 // -----------------------------------------------------
3400 // for nodes on seam edge, compute one more UV ( uvMap2 );
3401 // find movable nodes linked to nodes on seam and which
3402 // are to be smoothed using the second UV ( uvMap2 )
3403 // -----------------------------------------------------
3405 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3406 if ( !surface.IsNull() ) {
3407 TopExp_Explorer eExp( face, TopAbs_EDGE );
3408 for ( ; eExp.More(); eExp.Next() ) {
3409 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3410 if ( !BRep_Tool::IsClosed( edge, face ))
3412 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3413 if ( !sm ) continue;
3414 // find out which parameter varies for a node on seam
3417 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3418 if ( pcurve.IsNull() ) continue;
3419 uv1 = pcurve->Value( f );
3421 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3422 if ( pcurve.IsNull() ) continue;
3423 uv2 = pcurve->Value( f );
3424 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3426 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3427 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3429 // get nodes on seam and its vertices
3430 list< const SMDS_MeshNode* > seamNodes;
3431 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3432 while ( nSeamIt->more() ) {
3433 const SMDS_MeshNode* node = nSeamIt->next();
3434 if ( !isQuadratic || !IsMedium( node ))
3435 seamNodes.push_back( node );
3437 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3438 for ( ; vExp.More(); vExp.Next() ) {
3439 sm = aMesh->MeshElements( vExp.Current() );
3441 nSeamIt = sm->GetNodes();
3442 while ( nSeamIt->more() )
3443 seamNodes.push_back( nSeamIt->next() );
3446 // loop on nodes on seam
3447 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3448 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3449 const SMDS_MeshNode* nSeam = *noSeIt;
3450 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3451 if ( n_uv == uvMap.end() )
3454 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3455 // set the second UV
3456 listUV.push_back( *n_uv->second );
3457 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3458 if ( uvMap2.empty() )
3459 uvMap2 = uvMap; // copy the uvMap contents
3460 uvMap2[ nSeam ] = &listUV.back();
3462 // collect movable nodes linked to ones on seam in nodesNearSeam
3463 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3464 while ( eIt->more() ) {
3465 const SMDS_MeshElement* e = eIt->next();
3466 int nbUseMap1 = 0, nbUseMap2 = 0;
3467 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3468 int nn = 0, nbn = e->NbNodes();
3469 if(e->IsQuadratic()) nbn = nbn/2;
3470 while ( nn++ < nbn )
3472 const SMDS_MeshNode* n =
3473 static_cast<const SMDS_MeshNode*>( nIt->next() );
3475 setMovableNodes.find( n ) == setMovableNodes.end() )
3477 // add only nodes being closer to uv2 than to uv1
3478 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3479 0.5 * ( n->Y() + nSeam->Y() ),
3480 0.5 * ( n->Z() + nSeam->Z() ));
3482 getClosestUV( projector, pMid, uv );
3483 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3484 nodesNearSeam.insert( n );
3490 // for centroidalSmooth all element nodes must
3491 // be on one side of a seam
3492 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3493 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3495 while ( nn++ < nbn ) {
3496 const SMDS_MeshNode* n =
3497 static_cast<const SMDS_MeshNode*>( nIt->next() );
3498 setMovableNodes.erase( n );
3502 } // loop on nodes on seam
3503 } // loop on edge of a face
3504 } // if ( !face.IsNull() )
3506 if ( setMovableNodes.empty() ) {
3507 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3508 continue; // goto next face
3516 double maxRatio = -1., maxDisplacement = -1.;
3517 set<const SMDS_MeshNode*>::iterator nodeToMove;
3518 for ( it = 0; it < theNbIterations; it++ ) {
3519 maxDisplacement = 0.;
3520 nodeToMove = setMovableNodes.begin();
3521 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3522 const SMDS_MeshNode* node = (*nodeToMove);
3523 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3526 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3527 if ( theSmoothMethod == LAPLACIAN )
3528 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3530 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3532 // node displacement
3533 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3534 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3535 if ( aDispl > maxDisplacement )
3536 maxDisplacement = aDispl;
3538 // no node movement => exit
3539 //if ( maxDisplacement < 1.e-16 ) {
3540 if ( maxDisplacement < disttol ) {
3541 MESSAGE("-- no node movement --");
3545 // check elements quality
3547 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3548 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3549 const SMDS_MeshElement* elem = (*elemIt);
3550 if ( !elem || elem->GetType() != SMDSAbs_Face )
3552 SMESH::Controls::TSequenceOfXYZ aPoints;
3553 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3554 double aValue = aQualityFunc.GetValue( aPoints );
3555 if ( aValue > maxRatio )
3559 if ( maxRatio <= theTgtAspectRatio ) {
3560 MESSAGE("-- quality achived --");
3563 if (it+1 == theNbIterations) {
3564 MESSAGE("-- Iteration limit exceeded --");
3566 } // smoothing iterations
3568 MESSAGE(" Face id: " << *fId <<
3569 " Nb iterstions: " << it <<
3570 " Displacement: " << maxDisplacement <<
3571 " Aspect Ratio " << maxRatio);
3573 // ---------------------------------------
3574 // new nodes positions are computed,
3575 // record movement in DS and set new UV
3576 // ---------------------------------------
3577 nodeToMove = setMovableNodes.begin();
3578 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3579 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3580 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3581 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3582 if ( node_uv != uvMap.end() ) {
3583 gp_XY* uv = node_uv->second;
3585 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3589 // move medium nodes of quadratic elements
3592 SMESH_MesherHelper helper( *GetMesh() );
3593 if ( !face.IsNull() )
3594 helper.SetSubShape( face );
3595 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3596 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3597 const SMDS_VtkFace* QF =
3598 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3599 if(QF && QF->IsQuadratic()) {
3600 vector<const SMDS_MeshNode*> Ns;
3601 Ns.reserve(QF->NbNodes()+1);
3602 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3603 while ( anIter->more() )
3604 Ns.push_back( cast2Node(anIter->next()) );
3605 Ns.push_back( Ns[0] );
3607 for(int i=0; i<QF->NbNodes(); i=i+2) {
3608 if ( !surface.IsNull() ) {
3609 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3610 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3611 gp_XY uv = ( uv1 + uv2 ) / 2.;
3612 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3613 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3616 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3617 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3618 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3620 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3621 fabs( Ns[i+1]->Y() - y ) > disttol ||
3622 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3623 // we have to move i+1 node
3624 aMesh->MoveNode( Ns[i+1], x, y, z );
3631 } // loop on face ids
3635 //=======================================================================
3636 //function : isReverse
3637 //purpose : Return true if normal of prevNodes is not co-directied with
3638 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3639 // iNotSame is where prevNodes and nextNodes are different.
3640 // If result is true then future volume orientation is OK
3641 //=======================================================================
3643 static bool isReverse(const SMDS_MeshElement* face,
3644 const vector<const SMDS_MeshNode*>& prevNodes,
3645 const vector<const SMDS_MeshNode*>& nextNodes,
3649 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3650 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3651 gp_XYZ extrDir( pN - pP ), faceNorm;
3652 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3654 return faceNorm * extrDir < 0.0;
3657 //=======================================================================
3659 * \brief Create elements by sweeping an element
3660 * \param elem - element to sweep
3661 * \param newNodesItVec - nodes generated from each node of the element
3662 * \param newElems - generated elements
3663 * \param nbSteps - number of sweeping steps
3664 * \param srcElements - to append elem for each generated element
3666 //=======================================================================
3668 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3669 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3670 list<const SMDS_MeshElement*>& newElems,
3672 SMESH_SequenceOfElemPtr& srcElements)
3674 //MESSAGE("sweepElement " << nbSteps);
3675 SMESHDS_Mesh* aMesh = GetMeshDS();
3677 const int nbNodes = elem->NbNodes();
3678 const int nbCorners = elem->NbCornerNodes();
3679 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3680 polyhedron creation !!! */
3681 // Loop on elem nodes:
3682 // find new nodes and detect same nodes indices
3683 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3684 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3685 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3686 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3688 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3689 vector<int> sames(nbNodes);
3690 vector<bool> isSingleNode(nbNodes);
3692 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3693 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3694 const SMDS_MeshNode* node = nnIt->first;
3695 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3696 if ( listNewNodes.empty() )
3699 itNN [ iNode ] = listNewNodes.begin();
3700 prevNod[ iNode ] = node;
3701 nextNod[ iNode ] = listNewNodes.front();
3703 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3704 corner node of linear */
3705 if ( prevNod[ iNode ] != nextNod [ iNode ])
3706 nbDouble += !isSingleNode[iNode];
3708 if( iNode < nbCorners ) { // check corners only
3709 if ( prevNod[ iNode ] == nextNod [ iNode ])
3710 sames[nbSame++] = iNode;
3712 iNotSameNode = iNode;
3716 if ( nbSame == nbNodes || nbSame > 2) {
3717 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3721 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3723 // fix nodes order to have bottom normal external
3724 if ( baseType == SMDSEntity_Polygon )
3726 std::reverse( itNN.begin(), itNN.end() );
3727 std::reverse( prevNod.begin(), prevNod.end() );
3728 std::reverse( midlNod.begin(), midlNod.end() );
3729 std::reverse( nextNod.begin(), nextNod.end() );
3730 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3734 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3735 SMDS_MeshCell::applyInterlace( ind, itNN );
3736 SMDS_MeshCell::applyInterlace( ind, prevNod );
3737 SMDS_MeshCell::applyInterlace( ind, nextNod );
3738 SMDS_MeshCell::applyInterlace( ind, midlNod );
3739 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3742 sames[nbSame] = iNotSameNode;
3743 for ( int j = 0; j <= nbSame; ++j )
3744 for ( size_t i = 0; i < ind.size(); ++i )
3745 if ( ind[i] == sames[j] )
3750 iNotSameNode = sames[nbSame];
3755 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3757 iSameNode = sames[ nbSame-1 ];
3758 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3759 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3760 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3763 // make new elements
3764 for (int iStep = 0; iStep < nbSteps; iStep++ )
3767 for ( iNode = 0; iNode < nbNodes; iNode++ )
3769 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3770 nextNod[ iNode ] = *itNN[ iNode ]++;
3773 SMDS_MeshElement* aNewElem = 0;
3774 /*if(!elem->IsPoly())*/ {
3775 switch ( baseType ) {
3777 case SMDSEntity_Node: { // sweep NODE
3778 if ( nbSame == 0 ) {
3779 if ( isSingleNode[0] )
3780 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3782 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3788 case SMDSEntity_Edge: { // sweep EDGE
3789 if ( nbDouble == 0 )
3791 if ( nbSame == 0 ) // ---> quadrangle
3792 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3793 nextNod[ 1 ], nextNod[ 0 ] );
3794 else // ---> triangle
3795 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3796 nextNod[ iNotSameNode ] );
3798 else // ---> polygon
3800 vector<const SMDS_MeshNode*> poly_nodes;
3801 poly_nodes.push_back( prevNod[0] );
3802 poly_nodes.push_back( prevNod[1] );
3803 if ( prevNod[1] != nextNod[1] )
3805 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3806 poly_nodes.push_back( nextNod[1] );
3808 if ( prevNod[0] != nextNod[0] )
3810 poly_nodes.push_back( nextNod[0] );
3811 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3813 switch ( poly_nodes.size() ) {
3815 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3818 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3819 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3822 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3827 case SMDSEntity_Triangle: // TRIANGLE --->
3829 if ( nbDouble > 0 ) break;
3830 if ( nbSame == 0 ) // ---> pentahedron
3831 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3832 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3834 else if ( nbSame == 1 ) // ---> pyramid
3835 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3836 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3837 nextNod[ iSameNode ]);
3839 else // 2 same nodes: ---> tetrahedron
3840 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3841 nextNod[ iNotSameNode ]);
3844 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3848 if ( nbDouble+nbSame == 2 )
3850 if(nbSame==0) { // ---> quadratic quadrangle
3851 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3852 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3854 else { //(nbSame==1) // ---> quadratic triangle
3856 return; // medium node on axis
3858 else if(sames[0]==0)
3859 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3860 nextNod[2], midlNod[1], prevNod[2]);
3862 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3863 midlNod[0], nextNod[2], prevNod[2]);
3866 else if ( nbDouble == 3 )
3868 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3869 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3870 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3877 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3878 if ( nbDouble > 0 ) break;
3880 if ( nbSame == 0 ) // ---> hexahedron
3881 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3882 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3884 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3885 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3886 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3887 nextNod[ iSameNode ]);
3888 newElems.push_back( aNewElem );
3889 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3890 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3891 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3893 else if ( nbSame == 2 ) { // ---> pentahedron
3894 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3895 // iBeforeSame is same too
3896 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3897 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3898 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3900 // iAfterSame is same too
3901 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3902 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3903 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3907 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3908 if ( nbDouble+nbSame != 3 ) break;
3910 // ---> pentahedron with 15 nodes
3911 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3912 nextNod[0], nextNod[1], nextNod[2],
3913 prevNod[3], prevNod[4], prevNod[5],
3914 nextNod[3], nextNod[4], nextNod[5],
3915 midlNod[0], midlNod[1], midlNod[2]);
3917 else if(nbSame==1) {
3918 // ---> 2d order pyramid of 13 nodes
3919 int apex = iSameNode;
3920 int i0 = ( apex + 1 ) % nbCorners;
3921 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3925 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3926 nextNod[i0], nextNod[i1], prevNod[apex],
3927 prevNod[i01], midlNod[i0],
3928 nextNod[i01], midlNod[i1],
3929 prevNod[i1a], prevNod[i0a],
3930 nextNod[i0a], nextNod[i1a]);
3932 else if(nbSame==2) {
3933 // ---> 2d order tetrahedron of 10 nodes
3934 int n1 = iNotSameNode;
3935 int n2 = ( n1 + 1 ) % nbCorners;
3936 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3940 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3941 prevNod[n12], prevNod[n23], prevNod[n31],
3942 midlNod[n1], nextNod[n12], nextNod[n31]);
3946 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3948 if ( nbDouble != 4 ) break;
3949 // ---> hexahedron with 20 nodes
3950 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3951 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3952 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3953 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3954 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3956 else if(nbSame==1) {
3957 // ---> pyramid + pentahedron - can not be created since it is needed
3958 // additional middle node at the center of face
3959 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3962 else if( nbSame == 2 ) {
3963 if ( nbDouble != 2 ) break;
3964 // ---> 2d order Pentahedron with 15 nodes
3966 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3967 // iBeforeSame is same too
3974 // iAfterSame is same too
3984 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3985 prevNod[n4], prevNod[n5], nextNod[n5],
3986 prevNod[n12], midlNod[n2], nextNod[n12],
3987 prevNod[n45], midlNod[n5], nextNod[n45],
3988 prevNod[n14], prevNod[n25], nextNod[n25]);
3992 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3994 if( nbSame == 0 && nbDouble == 9 ) {
3995 // ---> tri-quadratic hexahedron with 27 nodes
3996 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3997 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3998 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3999 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4000 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4001 prevNod[8], // bottom center
4002 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4003 nextNod[8], // top center
4004 midlNod[8]);// elem center
4012 case SMDSEntity_Polygon: { // sweep POLYGON
4014 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4015 // ---> hexagonal prism
4016 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4017 prevNod[3], prevNod[4], prevNod[5],
4018 nextNod[0], nextNod[1], nextNod[2],
4019 nextNod[3], nextNod[4], nextNod[5]);
4023 case SMDSEntity_Ball:
4031 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4033 if ( baseType != SMDSEntity_Polygon )
4035 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4036 SMDS_MeshCell::applyInterlace( ind, prevNod );
4037 SMDS_MeshCell::applyInterlace( ind, nextNod );
4038 SMDS_MeshCell::applyInterlace( ind, midlNod );
4039 SMDS_MeshCell::applyInterlace( ind, itNN );
4040 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4041 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4043 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4044 vector<int> quantities (nbNodes + 2);
4045 polyedre_nodes.clear();
4049 for (int inode = 0; inode < nbNodes; inode++)
4050 polyedre_nodes.push_back( prevNod[inode] );
4051 quantities.push_back( nbNodes );
4054 polyedre_nodes.push_back( nextNod[0] );
4055 for (int inode = nbNodes; inode-1; --inode )
4056 polyedre_nodes.push_back( nextNod[inode-1] );
4057 quantities.push_back( nbNodes );
4060 for (int iface = 0; iface < nbNodes; iface++)
4062 const int prevNbNodes = polyedre_nodes.size();
4063 int inextface = (iface+1) % nbNodes;
4064 polyedre_nodes.push_back( prevNod[inextface] );
4065 polyedre_nodes.push_back( prevNod[iface] );
4066 if ( prevNod[iface] != nextNod[iface] )
4068 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4069 polyedre_nodes.push_back( nextNod[iface] );
4071 if ( prevNod[inextface] != nextNod[inextface] )
4073 polyedre_nodes.push_back( nextNod[inextface] );
4074 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4076 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4077 if ( nbFaceNodes > 2 )
4078 quantities.push_back( nbFaceNodes );
4079 else // degenerated face
4080 polyedre_nodes.resize( prevNbNodes );
4082 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4086 newElems.push_back( aNewElem );
4087 myLastCreatedElems.Append(aNewElem);
4088 srcElements.Append( elem );
4091 // set new prev nodes
4092 for ( iNode = 0; iNode < nbNodes; iNode++ )
4093 prevNod[ iNode ] = nextNod[ iNode ];
4098 //=======================================================================
4100 * \brief Create 1D and 2D elements around swept elements
4101 * \param mapNewNodes - source nodes and ones generated from them
4102 * \param newElemsMap - source elements and ones generated from them
4103 * \param elemNewNodesMap - nodes generated from each node of each element
4104 * \param elemSet - all swept elements
4105 * \param nbSteps - number of sweeping steps
4106 * \param srcElements - to append elem for each generated element
4108 //=======================================================================
4110 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4111 TElemOfElemListMap & newElemsMap,
4112 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4113 TIDSortedElemSet& elemSet,
4115 SMESH_SequenceOfElemPtr& srcElements)
4117 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4118 SMESHDS_Mesh* aMesh = GetMeshDS();
4120 // Find nodes belonging to only one initial element - sweep them to get edges.
4122 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4123 for ( ; nList != mapNewNodes.end(); nList++ )
4125 const SMDS_MeshNode* node =
4126 static_cast<const SMDS_MeshNode*>( nList->first );
4127 if ( newElemsMap.count( node ))
4128 continue; // node was extruded into edge
4129 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4130 int nbInitElems = 0;
4131 const SMDS_MeshElement* el = 0;
4132 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4133 while ( eIt->more() && nbInitElems < 2 ) {
4135 SMDSAbs_ElementType type = el->GetType();
4136 if ( type == SMDSAbs_Volume || type < highType ) continue;
4137 if ( type > highType ) {
4141 nbInitElems += elemSet.count(el);
4143 if ( nbInitElems < 2 ) {
4144 bool NotCreateEdge = el && el->IsMediumNode(node);
4145 if(!NotCreateEdge) {
4146 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4147 list<const SMDS_MeshElement*> newEdges;
4148 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4153 // Make a ceiling for each element ie an equal element of last new nodes.
4154 // Find free links of faces - make edges and sweep them into faces.
4156 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4157 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4158 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4160 const SMDS_MeshElement* elem = itElem->first;
4161 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4163 if(itElem->second.size()==0) continue;
4165 const bool isQuadratic = elem->IsQuadratic();
4167 if ( elem->GetType() == SMDSAbs_Edge ) {
4168 // create a ceiling edge
4169 if ( !isQuadratic ) {
4170 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4171 vecNewNodes[ 1 ]->second.back())) {
4172 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4173 vecNewNodes[ 1 ]->second.back()));
4174 srcElements.Append( elem );
4178 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4179 vecNewNodes[ 1 ]->second.back(),
4180 vecNewNodes[ 2 ]->second.back())) {
4181 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4182 vecNewNodes[ 1 ]->second.back(),
4183 vecNewNodes[ 2 ]->second.back()));
4184 srcElements.Append( elem );
4188 if ( elem->GetType() != SMDSAbs_Face )
4191 bool hasFreeLinks = false;
4193 TIDSortedElemSet avoidSet;
4194 avoidSet.insert( elem );
4196 set<const SMDS_MeshNode*> aFaceLastNodes;
4197 int iNode, nbNodes = vecNewNodes.size();
4198 if ( !isQuadratic ) {
4199 // loop on the face nodes
4200 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4202 // look for free links of the face
4203 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4204 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4205 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4206 // check if a link n1-n2 is free
4207 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4208 hasFreeLinks = true;
4209 // make a new edge and a ceiling for a new edge
4210 const SMDS_MeshElement* edge;
4211 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4212 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4213 srcElements.Append( myLastCreatedElems.Last() );
4215 n1 = vecNewNodes[ iNode ]->second.back();
4216 n2 = vecNewNodes[ iNext ]->second.back();
4217 if ( !aMesh->FindEdge( n1, n2 )) {
4218 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4219 srcElements.Append( edge );
4224 else { // elem is quadratic face
4225 int nbn = nbNodes/2;
4226 for ( iNode = 0; iNode < nbn; iNode++ ) {
4227 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4228 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4229 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4230 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4231 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4232 // check if a link is free
4233 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4234 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4235 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4236 hasFreeLinks = true;
4237 // make an edge and a ceiling for a new edge
4239 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4240 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4241 srcElements.Append( elem );
4243 n1 = vecNewNodes[ iNode ]->second.back();
4244 n2 = vecNewNodes[ iNext ]->second.back();
4245 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4246 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4247 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4248 srcElements.Append( elem );
4252 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4253 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4257 // sweep free links into faces
4259 if ( hasFreeLinks ) {
4260 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4261 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4263 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4264 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4265 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4266 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4268 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4269 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4270 std::advance( v, volNb );
4271 // find indices of free faces of a volume and their source edges
4272 list< int > freeInd;
4273 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4274 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4275 int iF, nbF = vTool.NbFaces();
4276 for ( iF = 0; iF < nbF; iF ++ ) {
4277 if (vTool.IsFreeFace( iF ) &&
4278 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4279 initNodeSet != faceNodeSet) // except an initial face
4281 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4283 freeInd.push_back( iF );
4284 // find source edge of a free face iF
4285 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4286 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4287 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4288 initNodeSet.begin(), initNodeSet.end(),
4289 commonNodes.begin());
4290 if ( (*v)->IsQuadratic() )
4291 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4293 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4295 if ( !srcEdges.back() )
4297 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4298 << iF << " of volume #" << vTool.ID() << endl;
4303 if ( freeInd.empty() )
4306 // create faces for all steps;
4307 // if such a face has been already created by sweep of edge,
4308 // assure that its orientation is OK
4309 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4310 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4311 vTool.SetExternalNormal();
4312 const int nextShift = vTool.IsForward() ? +1 : -1;
4313 list< int >::iterator ind = freeInd.begin();
4314 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4315 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4317 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4318 int nbn = vTool.NbFaceNodes( *ind );
4319 const SMDS_MeshElement * f = 0;
4320 if ( nbn == 3 ) ///// triangle
4322 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4324 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4326 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4328 nodes[ 1 + nextShift ] };
4330 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4332 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4336 else if ( nbn == 4 ) ///// quadrangle
4338 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4340 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4342 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4343 nodes[ 2 ], nodes[ 2+nextShift ] };
4345 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4347 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4348 newOrder[ 2 ], newOrder[ 3 ]));
4351 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4353 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4355 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4357 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4359 nodes[2 + 2*nextShift],
4360 nodes[3 - 2*nextShift],
4362 nodes[3 + 2*nextShift]};
4364 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4366 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4374 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4376 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4377 nodes[1], nodes[3], nodes[5], nodes[7] );
4379 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4381 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4382 nodes[4 - 2*nextShift],
4384 nodes[4 + 2*nextShift],
4386 nodes[5 - 2*nextShift],
4388 nodes[5 + 2*nextShift] };
4390 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4392 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4393 newOrder[ 2 ], newOrder[ 3 ],
4394 newOrder[ 4 ], newOrder[ 5 ],
4395 newOrder[ 6 ], newOrder[ 7 ]));
4398 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4400 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4401 SMDSAbs_Face, /*noMedium=*/false);
4403 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4405 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4406 nodes[4 - 2*nextShift],
4408 nodes[4 + 2*nextShift],
4410 nodes[5 - 2*nextShift],
4412 nodes[5 + 2*nextShift],
4415 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4417 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4418 newOrder[ 2 ], newOrder[ 3 ],
4419 newOrder[ 4 ], newOrder[ 5 ],
4420 newOrder[ 6 ], newOrder[ 7 ],
4424 else //////// polygon
4426 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4427 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4429 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4431 if ( !vTool.IsForward() )
4432 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4434 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4436 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4440 while ( srcElements.Length() < myLastCreatedElems.Length() )
4441 srcElements.Append( *srcEdge );
4443 } // loop on free faces
4445 // go to the next volume
4447 while ( iVol++ < nbVolumesByStep ) v++;
4450 } // loop on volumes of one step
4451 } // sweep free links into faces
4453 // Make a ceiling face with a normal external to a volume
4455 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4457 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4459 lastVol.SetExternalNormal();
4460 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4461 int nbn = lastVol.NbFaceNodes( iF );
4463 if (!hasFreeLinks ||
4464 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4465 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4467 else if ( nbn == 4 )
4469 if (!hasFreeLinks ||
4470 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4471 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4473 else if ( nbn == 6 && isQuadratic )
4475 if (!hasFreeLinks ||
4476 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4477 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4478 nodes[1], nodes[3], nodes[5]));
4480 else if ( nbn == 8 && isQuadratic )
4482 if (!hasFreeLinks ||
4483 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4484 nodes[1], nodes[3], nodes[5], nodes[7]) )
4485 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4486 nodes[1], nodes[3], nodes[5], nodes[7]));
4488 else if ( nbn == 9 && isQuadratic )
4490 if (!hasFreeLinks ||
4491 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4492 SMDSAbs_Face, /*noMedium=*/false) )
4493 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4494 nodes[1], nodes[3], nodes[5], nodes[7],
4498 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4499 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4500 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4503 while ( srcElements.Length() < myLastCreatedElems.Length() )
4504 srcElements.Append( elem );
4506 } // loop on swept elements
4509 //=======================================================================
4510 //function : RotationSweep
4512 //=======================================================================
4514 SMESH_MeshEditor::PGroupIDs
4515 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4516 const gp_Ax1& theAxis,
4517 const double theAngle,
4518 const int theNbSteps,
4519 const double theTol,
4520 const bool theMakeGroups,
4521 const bool theMakeWalls)
4523 myLastCreatedElems.Clear();
4524 myLastCreatedNodes.Clear();
4526 // source elements for each generated one
4527 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4529 MESSAGE( "RotationSweep()");
4531 aTrsf.SetRotation( theAxis, theAngle );
4533 aTrsf2.SetRotation( theAxis, theAngle/2. );
4535 gp_Lin aLine( theAxis );
4536 double aSqTol = theTol * theTol;
4538 SMESHDS_Mesh* aMesh = GetMeshDS();
4540 TNodeOfNodeListMap mapNewNodes;
4541 TElemOfVecOfNnlmiMap mapElemNewNodes;
4542 TElemOfElemListMap newElemsMap;
4544 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4545 myMesh->NbFaces(ORDER_QUADRATIC) +
4546 myMesh->NbVolumes(ORDER_QUADRATIC) );
4548 TIDSortedElemSet::iterator itElem;
4549 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4550 const SMDS_MeshElement* elem = *itElem;
4551 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4553 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4554 newNodesItVec.reserve( elem->NbNodes() );
4556 // loop on elem nodes
4557 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4558 while ( itN->more() )
4560 // check if a node has been already sweeped
4561 const SMDS_MeshNode* node = cast2Node( itN->next() );
4563 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4565 aXYZ.Coord( coord[0], coord[1], coord[2] );
4566 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4568 TNodeOfNodeListMapItr nIt =
4569 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4570 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4571 if ( listNewNodes.empty() )
4573 // check if we are to create medium nodes between corner ones
4574 bool needMediumNodes = false;
4575 if ( isQuadraticMesh )
4577 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4578 while (it->more() && !needMediumNodes )
4580 const SMDS_MeshElement* invElem = it->next();
4581 if ( invElem != elem && !theElems.count( invElem )) continue;
4582 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4583 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4584 needMediumNodes = true;
4589 const SMDS_MeshNode * newNode = node;
4590 for ( int i = 0; i < theNbSteps; i++ ) {
4592 if ( needMediumNodes ) // create a medium node
4594 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4595 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4596 myLastCreatedNodes.Append(newNode);
4597 srcNodes.Append( node );
4598 listNewNodes.push_back( newNode );
4599 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4602 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4604 // create a corner node
4605 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4606 myLastCreatedNodes.Append(newNode);
4607 srcNodes.Append( node );
4608 listNewNodes.push_back( newNode );
4611 listNewNodes.push_back( newNode );
4612 // if ( needMediumNodes )
4613 // listNewNodes.push_back( newNode );
4617 newNodesItVec.push_back( nIt );
4619 // make new elements
4620 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4624 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4626 PGroupIDs newGroupIDs;
4627 if ( theMakeGroups )
4628 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4634 //=======================================================================
4635 //function : CreateNode
4637 //=======================================================================
4638 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4641 const double tolnode,
4642 SMESH_SequenceOfNode& aNodes)
4644 // myLastCreatedElems.Clear();
4645 // myLastCreatedNodes.Clear();
4648 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4650 // try to search in sequence of existing nodes
4651 // if aNodes.Length()>0 we 'nave to use given sequence
4652 // else - use all nodes of mesh
4653 if(aNodes.Length()>0) {
4655 for(i=1; i<=aNodes.Length(); i++) {
4656 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4657 if(P1.Distance(P2)<tolnode)
4658 return aNodes.Value(i);
4662 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4663 while(itn->more()) {
4664 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4665 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4666 if(P1.Distance(P2)<tolnode)
4671 // create new node and return it
4672 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4673 //myLastCreatedNodes.Append(NewNode);
4678 //=======================================================================
4679 //function : ExtrusionSweep
4681 //=======================================================================
4683 SMESH_MeshEditor::PGroupIDs
4684 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4685 const gp_Vec& theStep,
4686 const int theNbSteps,
4687 TElemOfElemListMap& newElemsMap,
4688 const bool theMakeGroups,
4690 const double theTolerance)
4692 ExtrusParam aParams;
4693 aParams.myDir = gp_Dir(theStep);
4694 aParams.myNodes.Clear();
4695 aParams.mySteps = new TColStd_HSequenceOfReal;
4697 for(i=1; i<=theNbSteps; i++)
4698 aParams.mySteps->Append(theStep.Magnitude());
4701 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4705 //=======================================================================
4706 //function : ExtrusionSweep
4708 //=======================================================================
4710 SMESH_MeshEditor::PGroupIDs
4711 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4712 ExtrusParam& theParams,
4713 TElemOfElemListMap& newElemsMap,
4714 const bool theMakeGroups,
4716 const double theTolerance)
4718 myLastCreatedElems.Clear();
4719 myLastCreatedNodes.Clear();
4721 // source elements for each generated one
4722 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4724 SMESHDS_Mesh* aMesh = GetMeshDS();
4726 int nbsteps = theParams.mySteps->Length();
4728 TNodeOfNodeListMap mapNewNodes;
4729 //TNodeOfNodeVecMap mapNewNodes;
4730 TElemOfVecOfNnlmiMap mapElemNewNodes;
4731 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4733 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4734 myMesh->NbFaces(ORDER_QUADRATIC) +
4735 myMesh->NbVolumes(ORDER_QUADRATIC) );
4737 TIDSortedElemSet::iterator itElem;
4738 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4739 // check element type
4740 const SMDS_MeshElement* elem = *itElem;
4741 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4744 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4745 newNodesItVec.reserve( elem->NbNodes() );
4747 // loop on elem nodes
4748 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4749 while ( itN->more() )
4751 // check if a node has been already sweeped
4752 const SMDS_MeshNode* node = cast2Node( itN->next() );
4753 TNodeOfNodeListMap::iterator nIt =
4754 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4755 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4756 if ( listNewNodes.empty() )
4760 // check if we are to create medium nodes between corner ones
4761 bool needMediumNodes = false;
4762 if ( isQuadraticMesh )
4764 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4765 while (it->more() && !needMediumNodes )
4767 const SMDS_MeshElement* invElem = it->next();
4768 if ( invElem != elem && !theElems.count( invElem )) continue;
4769 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4770 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4771 needMediumNodes = true;
4775 double coord[] = { node->X(), node->Y(), node->Z() };
4776 for ( int i = 0; i < nbsteps; i++ )
4778 if ( needMediumNodes ) // create a medium node
4780 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4781 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4782 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4783 if( theFlags & EXTRUSION_FLAG_SEW ) {
4784 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4785 theTolerance, theParams.myNodes);
4786 listNewNodes.push_back( newNode );
4789 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4790 myLastCreatedNodes.Append(newNode);
4791 srcNodes.Append( node );
4792 listNewNodes.push_back( newNode );
4795 // create a corner node
4796 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4797 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4798 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4799 if( theFlags & EXTRUSION_FLAG_SEW ) {
4800 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4801 theTolerance, theParams.myNodes);
4802 listNewNodes.push_back( newNode );
4805 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4806 myLastCreatedNodes.Append(newNode);
4807 srcNodes.Append( node );
4808 listNewNodes.push_back( newNode );
4812 newNodesItVec.push_back( nIt );
4814 // make new elements
4815 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4818 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4819 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4821 PGroupIDs newGroupIDs;
4822 if ( theMakeGroups )
4823 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4828 //=======================================================================
4829 //function : ExtrusionAlongTrack
4831 //=======================================================================
4832 SMESH_MeshEditor::Extrusion_Error
4833 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4834 SMESH_subMesh* theTrack,
4835 const SMDS_MeshNode* theN1,
4836 const bool theHasAngles,
4837 list<double>& theAngles,
4838 const bool theLinearVariation,
4839 const bool theHasRefPoint,
4840 const gp_Pnt& theRefPoint,
4841 const bool theMakeGroups)
4843 MESSAGE("ExtrusionAlongTrack");
4844 myLastCreatedElems.Clear();
4845 myLastCreatedNodes.Clear();
4848 std::list<double> aPrms;
4849 TIDSortedElemSet::iterator itElem;
4852 TopoDS_Edge aTrackEdge;
4853 TopoDS_Vertex aV1, aV2;
4855 SMDS_ElemIteratorPtr aItE;
4856 SMDS_NodeIteratorPtr aItN;
4857 SMDSAbs_ElementType aTypeE;
4859 TNodeOfNodeListMap mapNewNodes;
4862 aNbE = theElements.size();
4865 return EXTR_NO_ELEMENTS;
4867 // 1.1 Track Pattern
4870 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4872 aItE = pSubMeshDS->GetElements();
4873 while ( aItE->more() ) {
4874 const SMDS_MeshElement* pE = aItE->next();
4875 aTypeE = pE->GetType();
4876 // Pattern must contain links only
4877 if ( aTypeE != SMDSAbs_Edge )
4878 return EXTR_PATH_NOT_EDGE;
4881 list<SMESH_MeshEditor_PathPoint> fullList;
4883 const TopoDS_Shape& aS = theTrack->GetSubShape();
4884 // Sub-shape for the Pattern must be an Edge or Wire
4885 if( aS.ShapeType() == TopAbs_EDGE ) {
4886 aTrackEdge = TopoDS::Edge( aS );
4887 // the Edge must not be degenerated
4888 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4889 return EXTR_BAD_PATH_SHAPE;
4890 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4891 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4892 const SMDS_MeshNode* aN1 = aItN->next();
4893 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4894 const SMDS_MeshNode* aN2 = aItN->next();
4895 // starting node must be aN1 or aN2
4896 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4897 return EXTR_BAD_STARTING_NODE;
4898 aItN = pSubMeshDS->GetNodes();
4899 while ( aItN->more() ) {
4900 const SMDS_MeshNode* pNode = aItN->next();
4901 const SMDS_EdgePosition* pEPos =
4902 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4903 double aT = pEPos->GetUParameter();
4904 aPrms.push_back( aT );
4906 //Extrusion_Error err =
4907 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4908 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4909 list< SMESH_subMesh* > LSM;
4910 TopTools_SequenceOfShape Edges;
4911 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4912 while(itSM->more()) {
4913 SMESH_subMesh* SM = itSM->next();
4915 const TopoDS_Shape& aS = SM->GetSubShape();
4918 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4919 int startNid = theN1->GetID();
4920 TColStd_MapOfInteger UsedNums;
4922 int NbEdges = Edges.Length();
4924 for(; i<=NbEdges; i++) {
4926 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4927 for(; itLSM!=LSM.end(); itLSM++) {
4929 if(UsedNums.Contains(k)) continue;
4930 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4931 SMESH_subMesh* locTrack = *itLSM;
4932 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4933 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4934 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4935 const SMDS_MeshNode* aN1 = aItN->next();
4936 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4937 const SMDS_MeshNode* aN2 = aItN->next();
4938 // starting node must be aN1 or aN2
4939 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4940 // 2. Collect parameters on the track edge
4942 aItN = locMeshDS->GetNodes();
4943 while ( aItN->more() ) {
4944 const SMDS_MeshNode* pNode = aItN->next();
4945 const SMDS_EdgePosition* pEPos =
4946 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4947 double aT = pEPos->GetUParameter();
4948 aPrms.push_back( aT );
4950 list<SMESH_MeshEditor_PathPoint> LPP;
4951 //Extrusion_Error err =
4952 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4953 LLPPs.push_back(LPP);
4955 // update startN for search following egde
4956 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4957 else startNid = aN1->GetID();
4961 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4962 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4963 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4964 for(; itPP!=firstList.end(); itPP++) {
4965 fullList.push_back( *itPP );
4967 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4968 fullList.pop_back();
4970 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4971 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4972 itPP = currList.begin();
4973 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4974 gp_Dir D1 = PP1.Tangent();
4975 gp_Dir D2 = PP2.Tangent();
4976 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4977 (D1.Z()+D2.Z())/2 ) );
4978 PP1.SetTangent(Dnew);
4979 fullList.push_back(PP1);
4981 for(; itPP!=firstList.end(); itPP++) {
4982 fullList.push_back( *itPP );
4984 PP1 = fullList.back();
4985 fullList.pop_back();
4987 // if wire not closed
4988 fullList.push_back(PP1);
4992 return EXTR_BAD_PATH_SHAPE;
4995 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4996 theHasRefPoint, theRefPoint, theMakeGroups);
5000 //=======================================================================
5001 //function : ExtrusionAlongTrack
5003 //=======================================================================
5004 SMESH_MeshEditor::Extrusion_Error
5005 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5006 SMESH_Mesh* theTrack,
5007 const SMDS_MeshNode* theN1,
5008 const bool theHasAngles,
5009 list<double>& theAngles,
5010 const bool theLinearVariation,
5011 const bool theHasRefPoint,
5012 const gp_Pnt& theRefPoint,
5013 const bool theMakeGroups)
5015 myLastCreatedElems.Clear();
5016 myLastCreatedNodes.Clear();
5019 std::list<double> aPrms;
5020 TIDSortedElemSet::iterator itElem;
5023 TopoDS_Edge aTrackEdge;
5024 TopoDS_Vertex aV1, aV2;
5026 SMDS_ElemIteratorPtr aItE;
5027 SMDS_NodeIteratorPtr aItN;
5028 SMDSAbs_ElementType aTypeE;
5030 TNodeOfNodeListMap mapNewNodes;
5033 aNbE = theElements.size();
5036 return EXTR_NO_ELEMENTS;
5038 // 1.1 Track Pattern
5041 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5043 aItE = pMeshDS->elementsIterator();
5044 while ( aItE->more() ) {
5045 const SMDS_MeshElement* pE = aItE->next();
5046 aTypeE = pE->GetType();
5047 // Pattern must contain links only
5048 if ( aTypeE != SMDSAbs_Edge )
5049 return EXTR_PATH_NOT_EDGE;
5052 list<SMESH_MeshEditor_PathPoint> fullList;
5054 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5056 if( aS == SMESH_Mesh::PseudoShape() ) {
5057 //Mesh without shape
5058 const SMDS_MeshNode* currentNode = NULL;
5059 const SMDS_MeshNode* prevNode = theN1;
5060 std::vector<const SMDS_MeshNode*> aNodesList;
5061 aNodesList.push_back(theN1);
5062 int nbEdges = 0, conn=0;
5063 const SMDS_MeshElement* prevElem = NULL;
5064 const SMDS_MeshElement* currentElem = NULL;
5065 int totalNbEdges = theTrack->NbEdges();
5066 SMDS_ElemIteratorPtr nIt;
5069 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5070 return EXTR_BAD_STARTING_NODE;
5073 conn = nbEdgeConnectivity(theN1);
5075 return EXTR_PATH_NOT_EDGE;
5077 aItE = theN1->GetInverseElementIterator();
5078 prevElem = aItE->next();
5079 currentElem = prevElem;
5081 if(totalNbEdges == 1 ) {
5082 nIt = currentElem->nodesIterator();
5083 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5084 if(currentNode == prevNode)
5085 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5086 aNodesList.push_back(currentNode);
5088 nIt = currentElem->nodesIterator();
5089 while( nIt->more() ) {
5090 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5091 if(currentNode == prevNode)
5092 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5093 aNodesList.push_back(currentNode);
5095 //case of the closed mesh
5096 if(currentNode == theN1) {
5101 conn = nbEdgeConnectivity(currentNode);
5103 return EXTR_PATH_NOT_EDGE;
5104 }else if( conn == 1 && nbEdges > 0 ) {
5109 prevNode = currentNode;
5110 aItE = currentNode->GetInverseElementIterator();
5111 currentElem = aItE->next();
5112 if( currentElem == prevElem)
5113 currentElem = aItE->next();
5114 nIt = currentElem->nodesIterator();
5115 prevElem = currentElem;
5121 if(nbEdges != totalNbEdges)
5122 return EXTR_PATH_NOT_EDGE;
5124 TopTools_SequenceOfShape Edges;
5125 double x1,x2,y1,y2,z1,z2;
5126 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5127 int startNid = theN1->GetID();
5128 for(int i = 1; i < aNodesList.size(); i++) {
5129 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5130 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5131 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5132 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5133 list<SMESH_MeshEditor_PathPoint> LPP;
5135 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5136 LLPPs.push_back(LPP);
5137 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5138 else startNid = aNodesList[i-1]->GetID();
5142 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5143 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5144 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5145 for(; itPP!=firstList.end(); itPP++) {
5146 fullList.push_back( *itPP );
5149 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5150 SMESH_MeshEditor_PathPoint PP2;
5151 fullList.pop_back();
5153 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5154 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5155 itPP = currList.begin();
5156 PP2 = currList.front();
5157 gp_Dir D1 = PP1.Tangent();
5158 gp_Dir D2 = PP2.Tangent();
5159 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5160 (D1.Z()+D2.Z())/2 ) );
5161 PP1.SetTangent(Dnew);
5162 fullList.push_back(PP1);
5164 for(; itPP!=currList.end(); itPP++) {
5165 fullList.push_back( *itPP );
5167 PP1 = fullList.back();
5168 fullList.pop_back();
5170 fullList.push_back(PP1);
5172 } // Sub-shape for the Pattern must be an Edge or Wire
5173 else if( aS.ShapeType() == TopAbs_EDGE ) {
5174 aTrackEdge = TopoDS::Edge( aS );
5175 // the Edge must not be degenerated
5176 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5177 return EXTR_BAD_PATH_SHAPE;
5178 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5179 const SMDS_MeshNode* aN1 = 0;
5180 const SMDS_MeshNode* aN2 = 0;
5181 if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5182 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5185 if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5186 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5189 // starting node must be aN1 or aN2
5190 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5191 return EXTR_BAD_STARTING_NODE;
5192 aItN = pMeshDS->nodesIterator();
5193 while ( aItN->more() ) {
5194 const SMDS_MeshNode* pNode = aItN->next();
5195 if( pNode==aN1 || pNode==aN2 ) continue;
5196 const SMDS_EdgePosition* pEPos =
5197 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5198 double aT = pEPos->GetUParameter();
5199 aPrms.push_back( aT );
5201 //Extrusion_Error err =
5202 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5204 else if( aS.ShapeType() == TopAbs_WIRE ) {
5205 list< SMESH_subMesh* > LSM;
5206 TopTools_SequenceOfShape Edges;
5207 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5208 for(; eExp.More(); eExp.Next()) {
5209 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5210 if( BRep_Tool::Degenerated(E) ) continue;
5211 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5217 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5218 TopoDS_Vertex aVprev;
5219 TColStd_MapOfInteger UsedNums;
5220 int NbEdges = Edges.Length();
5222 for(; i<=NbEdges; i++) {
5224 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5225 for(; itLSM!=LSM.end(); itLSM++) {
5227 if(UsedNums.Contains(k)) continue;
5228 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5229 SMESH_subMesh* locTrack = *itLSM;
5230 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5231 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5232 bool aN1isOK = false, aN2isOK = false;
5233 if ( aVprev.IsNull() ) {
5234 // if previous vertex is not yet defined, it means that we in the beginning of wire
5235 // and we have to find initial vertex corresponding to starting node theN1
5236 const SMDS_MeshNode* aN1 = 0;
5237 const SMDS_MeshNode* aN2 = 0;
5239 if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5240 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5243 if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5244 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5247 // starting node must be aN1 or aN2
5248 aN1isOK = ( aN1 && aN1 == theN1 );
5249 aN2isOK = ( aN2 && aN2 == theN1 );
5252 // we have specified ending vertex of the previous edge on the previous iteration
5253 // and we have just to check that it corresponds to any vertex in current segment
5254 aN1isOK = aVprev.IsSame( aV1 );
5255 aN2isOK = aVprev.IsSame( aV2 );
5257 if ( !aN1isOK && !aN2isOK ) continue;
5258 // 2. Collect parameters on the track edge
5260 aItN = locMeshDS->GetNodes();
5261 while ( aItN->more() ) {
5262 const SMDS_MeshNode* pNode = aItN->next();
5263 const SMDS_EdgePosition* pEPos =
5264 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5265 double aT = pEPos->GetUParameter();
5266 aPrms.push_back( aT );
5268 list<SMESH_MeshEditor_PathPoint> LPP;
5269 //Extrusion_Error err =
5270 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5271 LLPPs.push_back(LPP);
5273 // update startN for search following egde
5274 if ( aN1isOK ) aVprev = aV2;
5279 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5280 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5281 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5282 for(; itPP!=firstList.end(); itPP++) {
5283 fullList.push_back( *itPP );
5285 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5286 fullList.pop_back();
5288 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5289 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5290 itPP = currList.begin();
5291 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5292 gp_Dir D1 = PP1.Tangent();
5293 gp_Dir D2 = PP2.Tangent();
5294 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5295 PP1.SetTangent(Dnew);
5296 fullList.push_back(PP1);
5298 for(; itPP!=currList.end(); itPP++) {
5299 fullList.push_back( *itPP );
5301 PP1 = fullList.back();
5302 fullList.pop_back();
5304 // if wire not closed
5305 fullList.push_back(PP1);
5309 return EXTR_BAD_PATH_SHAPE;
5312 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5313 theHasRefPoint, theRefPoint, theMakeGroups);
5317 //=======================================================================
5318 //function : MakeEdgePathPoints
5319 //purpose : auxilary for ExtrusionAlongTrack
5320 //=======================================================================
5321 SMESH_MeshEditor::Extrusion_Error
5322 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5323 const TopoDS_Edge& aTrackEdge,
5325 list<SMESH_MeshEditor_PathPoint>& LPP)
5327 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5329 aTolVec2=aTolVec*aTolVec;
5331 TopoDS_Vertex aV1, aV2;
5332 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5333 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5334 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5335 // 2. Collect parameters on the track edge
5336 aPrms.push_front( aT1 );
5337 aPrms.push_back( aT2 );
5340 if( FirstIsStart ) {
5351 SMESH_MeshEditor_PathPoint aPP;
5352 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5353 std::list<double>::iterator aItD = aPrms.begin();
5354 for(; aItD != aPrms.end(); ++aItD) {
5358 aC3D->D1( aT, aP3D, aVec );
5359 aL2 = aVec.SquareMagnitude();
5360 if ( aL2 < aTolVec2 )
5361 return EXTR_CANT_GET_TANGENT;
5362 gp_Dir aTgt( aVec );
5364 aPP.SetTangent( aTgt );
5365 aPP.SetParameter( aT );
5372 //=======================================================================
5373 //function : MakeExtrElements
5374 //purpose : auxilary for ExtrusionAlongTrack
5375 //=======================================================================
5376 SMESH_MeshEditor::Extrusion_Error
5377 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5378 list<SMESH_MeshEditor_PathPoint>& fullList,
5379 const bool theHasAngles,
5380 list<double>& theAngles,
5381 const bool theLinearVariation,
5382 const bool theHasRefPoint,
5383 const gp_Pnt& theRefPoint,
5384 const bool theMakeGroups)
5386 MESSAGE("MakeExtrElements");
5387 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5388 int aNbTP = fullList.size();
5389 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5391 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5392 LinearAngleVariation(aNbTP-1, theAngles);
5394 vector<double> aAngles( aNbTP );
5396 for(; j<aNbTP; ++j) {
5399 if ( theHasAngles ) {
5401 std::list<double>::iterator aItD = theAngles.begin();
5402 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5404 aAngles[j] = anAngle;
5407 // fill vector of path points with angles
5408 //aPPs.resize(fullList.size());
5410 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5411 for(; itPP!=fullList.end(); itPP++) {
5413 SMESH_MeshEditor_PathPoint PP = *itPP;
5414 PP.SetAngle(aAngles[j]);
5418 TNodeOfNodeListMap mapNewNodes;
5419 TElemOfVecOfNnlmiMap mapElemNewNodes;
5420 TElemOfElemListMap newElemsMap;
5421 TIDSortedElemSet::iterator itElem;
5424 SMDSAbs_ElementType aTypeE;
5425 // source elements for each generated one
5426 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5428 // 3. Center of rotation aV0
5429 gp_Pnt aV0 = theRefPoint;
5431 if ( !theHasRefPoint ) {
5433 aGC.SetCoord( 0.,0.,0. );
5435 itElem = theElements.begin();
5436 for ( ; itElem != theElements.end(); itElem++ ) {
5437 const SMDS_MeshElement* elem = *itElem;
5439 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5440 while ( itN->more() ) {
5441 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5446 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5447 list<const SMDS_MeshNode*> aLNx;
5448 mapNewNodes[node] = aLNx;
5450 gp_XYZ aXYZ( aX, aY, aZ );
5458 } // if (!theHasRefPoint) {
5459 mapNewNodes.clear();
5461 // 4. Processing the elements
5462 SMESHDS_Mesh* aMesh = GetMeshDS();
5464 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5465 // check element type
5466 const SMDS_MeshElement* elem = *itElem;
5467 aTypeE = elem->GetType();
5468 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5471 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5472 newNodesItVec.reserve( elem->NbNodes() );
5474 // loop on elem nodes
5476 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5477 while ( itN->more() )
5480 // check if a node has been already processed
5481 const SMDS_MeshNode* node =
5482 static_cast<const SMDS_MeshNode*>( itN->next() );
5483 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5484 if ( nIt == mapNewNodes.end() ) {
5485 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5486 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5489 aX = node->X(); aY = node->Y(); aZ = node->Z();
5491 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5492 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5493 gp_Ax1 anAx1, anAxT1T0;
5494 gp_Dir aDT1x, aDT0x, aDT1T0;
5499 aPN0.SetCoord(aX, aY, aZ);
5501 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5503 aDT0x= aPP0.Tangent();
5504 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5506 for ( j = 1; j < aNbTP; ++j ) {
5507 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5509 aDT1x = aPP1.Tangent();
5510 aAngle1x = aPP1.Angle();
5512 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5514 gp_Vec aV01x( aP0x, aP1x );
5515 aTrsf.SetTranslation( aV01x );
5518 aV1x = aV0x.Transformed( aTrsf );
5519 aPN1 = aPN0.Transformed( aTrsf );
5521 // rotation 1 [ T1,T0 ]
5522 aAngleT1T0=-aDT1x.Angle( aDT0x );
5523 if (fabs(aAngleT1T0) > aTolAng) {
5525 anAxT1T0.SetLocation( aV1x );
5526 anAxT1T0.SetDirection( aDT1T0 );
5527 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5529 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5533 if ( theHasAngles ) {
5534 anAx1.SetLocation( aV1x );
5535 anAx1.SetDirection( aDT1x );
5536 aTrsfRot.SetRotation( anAx1, aAngle1x );
5538 aPN1 = aPN1.Transformed( aTrsfRot );
5542 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5543 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5544 // create additional node
5545 double x = ( aPN1.X() + aPN0.X() )/2.;
5546 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5547 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5548 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5549 myLastCreatedNodes.Append(newNode);
5550 srcNodes.Append( node );
5551 listNewNodes.push_back( newNode );
5556 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5557 myLastCreatedNodes.Append(newNode);
5558 srcNodes.Append( node );
5559 listNewNodes.push_back( newNode );
5569 // if current elem is quadratic and current node is not medium
5570 // we have to check - may be it is needed to insert additional nodes
5571 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5572 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5573 if(listNewNodes.size()==aNbTP-1) {
5574 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5575 gp_XYZ P(node->X(), node->Y(), node->Z());
5576 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5578 for(i=0; i<aNbTP-1; i++) {
5579 const SMDS_MeshNode* N = *it;
5580 double x = ( N->X() + P.X() )/2.;
5581 double y = ( N->Y() + P.Y() )/2.;
5582 double z = ( N->Z() + P.Z() )/2.;
5583 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5584 srcNodes.Append( node );
5585 myLastCreatedNodes.Append(newN);
5588 P = gp_XYZ(N->X(),N->Y(),N->Z());
5590 listNewNodes.clear();
5591 for(i=0; i<2*(aNbTP-1); i++) {
5592 listNewNodes.push_back(aNodes[i]);
5598 newNodesItVec.push_back( nIt );
5600 // make new elements
5601 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5602 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5603 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5606 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5608 if ( theMakeGroups )
5609 generateGroups( srcNodes, srcElems, "extruded");
5615 //=======================================================================
5616 //function : LinearAngleVariation
5617 //purpose : auxilary for ExtrusionAlongTrack
5618 //=======================================================================
5619 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5620 list<double>& Angles)
5622 int nbAngles = Angles.size();
5623 if( nbSteps > nbAngles ) {
5624 vector<double> theAngles(nbAngles);
5625 list<double>::iterator it = Angles.begin();
5627 for(; it!=Angles.end(); it++) {
5629 theAngles[i] = (*it);
5632 double rAn2St = double( nbAngles ) / double( nbSteps );
5633 double angPrev = 0, angle;
5634 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5635 double angCur = rAn2St * ( iSt+1 );
5636 double angCurFloor = floor( angCur );
5637 double angPrevFloor = floor( angPrev );
5638 if ( angPrevFloor == angCurFloor )
5639 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5641 int iP = int( angPrevFloor );
5642 double angPrevCeil = ceil(angPrev);
5643 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5645 int iC = int( angCurFloor );
5646 if ( iC < nbAngles )
5647 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5649 iP = int( angPrevCeil );
5651 angle += theAngles[ iC ];
5653 res.push_back(angle);
5658 for(; it!=res.end(); it++)
5659 Angles.push_back( *it );
5664 //================================================================================
5666 * \brief Move or copy theElements applying theTrsf to their nodes
5667 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5668 * \param theTrsf - transformation to apply
5669 * \param theCopy - if true, create translated copies of theElems
5670 * \param theMakeGroups - if true and theCopy, create translated groups
5671 * \param theTargetMesh - mesh to copy translated elements into
5672 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5674 //================================================================================
5676 SMESH_MeshEditor::PGroupIDs
5677 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5678 const gp_Trsf& theTrsf,
5680 const bool theMakeGroups,
5681 SMESH_Mesh* theTargetMesh)
5683 myLastCreatedElems.Clear();
5684 myLastCreatedNodes.Clear();
5686 bool needReverse = false;
5687 string groupPostfix;
5688 switch ( theTrsf.Form() ) {
5690 MESSAGE("gp_PntMirror");
5692 groupPostfix = "mirrored";
5695 MESSAGE("gp_Ax1Mirror");
5696 groupPostfix = "mirrored";
5699 MESSAGE("gp_Ax2Mirror");
5701 groupPostfix = "mirrored";
5704 MESSAGE("gp_Rotation");
5705 groupPostfix = "rotated";
5707 case gp_Translation:
5708 MESSAGE("gp_Translation");
5709 groupPostfix = "translated";
5712 MESSAGE("gp_Scale");
5713 groupPostfix = "scaled";
5715 case gp_CompoundTrsf: // different scale by axis
5716 MESSAGE("gp_CompoundTrsf");
5717 groupPostfix = "scaled";
5721 needReverse = false;
5722 groupPostfix = "transformed";
5725 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5726 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5727 SMESHDS_Mesh* aMesh = GetMeshDS();
5730 // map old node to new one
5731 TNodeNodeMap nodeMap;
5733 // elements sharing moved nodes; those of them which have all
5734 // nodes mirrored but are not in theElems are to be reversed
5735 TIDSortedElemSet inverseElemSet;
5737 // source elements for each generated one
5738 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5740 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5741 TIDSortedElemSet orphanNode;
5743 if ( theElems.empty() ) // transform the whole mesh
5746 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5747 while ( eIt->more() ) theElems.insert( eIt->next() );
5749 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5750 while ( nIt->more() )
5752 const SMDS_MeshNode* node = nIt->next();
5753 if ( node->NbInverseElements() == 0)
5754 orphanNode.insert( node );
5758 // loop on elements to transform nodes : first orphan nodes then elems
5759 TIDSortedElemSet::iterator itElem;
5760 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5761 for (int i=0; i<2; i++)
5762 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5763 const SMDS_MeshElement* elem = *itElem;
5767 // loop on elem nodes
5768 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5769 while ( itN->more() ) {
5771 const SMDS_MeshNode* node = cast2Node( itN->next() );
5772 // check if a node has been already transformed
5773 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5774 nodeMap.insert( make_pair ( node, node ));
5775 if ( !n2n_isnew.second )
5779 coord[0] = node->X();
5780 coord[1] = node->Y();
5781 coord[2] = node->Z();
5782 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5783 if ( theTargetMesh ) {
5784 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5785 n2n_isnew.first->second = newNode;
5786 myLastCreatedNodes.Append(newNode);
5787 srcNodes.Append( node );
5789 else if ( theCopy ) {
5790 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5791 n2n_isnew.first->second = newNode;
5792 myLastCreatedNodes.Append(newNode);
5793 srcNodes.Append( node );
5796 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5797 // node position on shape becomes invalid
5798 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5799 ( SMDS_SpacePosition::originSpacePosition() );
5802 // keep inverse elements
5803 if ( !theCopy && !theTargetMesh && needReverse ) {
5804 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5805 while ( invElemIt->more() ) {
5806 const SMDS_MeshElement* iel = invElemIt->next();
5807 inverseElemSet.insert( iel );
5813 // either create new elements or reverse mirrored ones
5814 if ( !theCopy && !needReverse && !theTargetMesh )
5817 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5818 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5819 theElems.insert( *invElemIt );
5821 // Replicate or reverse elements
5823 std::vector<int> iForw;
5824 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5826 const SMDS_MeshElement* elem = *itElem;
5827 if ( !elem ) continue;
5829 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5830 int nbNodes = elem->NbNodes();
5831 if ( geomType == SMDSGeom_NONE ) continue; // node
5833 switch ( geomType ) {
5835 case SMDSGeom_POLYGON: // ---------------------- polygon
5837 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5839 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5840 while (itN->more()) {
5841 const SMDS_MeshNode* node =
5842 static_cast<const SMDS_MeshNode*>(itN->next());
5843 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5844 if (nodeMapIt == nodeMap.end())
5845 break; // not all nodes transformed
5847 // reverse mirrored faces and volumes
5848 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5850 poly_nodes[iNode] = (*nodeMapIt).second;
5854 if ( iNode != nbNodes )
5855 continue; // not all nodes transformed
5857 if ( theTargetMesh ) {
5858 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5859 srcElems.Append( elem );
5861 else if ( theCopy ) {
5862 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5863 srcElems.Append( elem );
5866 aMesh->ChangePolygonNodes(elem, poly_nodes);
5871 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5873 const SMDS_VtkVolume* aPolyedre =
5874 dynamic_cast<const SMDS_VtkVolume*>( elem );
5876 MESSAGE("Warning: bad volumic element");
5880 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5881 vector<int> quantities; quantities.reserve( nbNodes );
5883 bool allTransformed = true;
5884 int nbFaces = aPolyedre->NbFaces();
5885 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5886 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5887 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5888 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5889 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5890 if (nodeMapIt == nodeMap.end()) {
5891 allTransformed = false; // not all nodes transformed
5893 poly_nodes.push_back((*nodeMapIt).second);
5895 if ( needReverse && allTransformed )
5896 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5898 quantities.push_back(nbFaceNodes);
5900 if ( !allTransformed )
5901 continue; // not all nodes transformed
5903 if ( theTargetMesh ) {
5904 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5905 srcElems.Append( elem );
5907 else if ( theCopy ) {
5908 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5909 srcElems.Append( elem );
5912 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5917 case SMDSGeom_BALL: // -------------------- Ball
5919 if ( !theCopy && !theTargetMesh ) continue;
5921 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5922 if (nodeMapIt == nodeMap.end())
5923 continue; // not all nodes transformed
5925 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5926 if ( theTargetMesh ) {
5927 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5928 srcElems.Append( elem );
5931 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5932 srcElems.Append( elem );
5937 default: // ----------------------- Regular elements
5939 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5940 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5941 const std::vector<int>& i = needReverse ? iRev : iForw;
5943 // find transformed nodes
5944 vector<const SMDS_MeshNode*> nodes(nbNodes);
5946 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5947 while ( itN->more() ) {
5948 const SMDS_MeshNode* node =
5949 static_cast<const SMDS_MeshNode*>( itN->next() );
5950 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5951 if ( nodeMapIt == nodeMap.end() )
5952 break; // not all nodes transformed
5953 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5955 if ( iNode != nbNodes )
5956 continue; // not all nodes transformed
5958 if ( theTargetMesh ) {
5959 if ( SMDS_MeshElement* copy =
5960 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5961 myLastCreatedElems.Append( copy );
5962 srcElems.Append( elem );
5965 else if ( theCopy ) {
5966 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5967 srcElems.Append( elem );
5970 // reverse element as it was reversed by transformation
5972 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5974 } // switch ( geomType )
5976 } // loop on elements
5978 PGroupIDs newGroupIDs;
5980 if ( ( theMakeGroups && theCopy ) ||
5981 ( theMakeGroups && theTargetMesh ) )
5982 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5987 //=======================================================================
5989 * \brief Create groups of elements made during transformation
5990 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5991 * \param elemGens - elements making corresponding myLastCreatedElems
5992 * \param postfix - to append to names of new groups
5994 //=======================================================================
5996 SMESH_MeshEditor::PGroupIDs
5997 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5998 const SMESH_SequenceOfElemPtr& elemGens,
5999 const std::string& postfix,
6000 SMESH_Mesh* targetMesh)
6002 PGroupIDs newGroupIDs( new list<int> );
6003 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6005 // Sort existing groups by types and collect their names
6007 // to store an old group and a generated new ones
6009 using boost::make_tuple;
6010 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6011 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6012 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6014 set< string > groupNames;
6016 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6017 if ( !groupIt->more() ) return newGroupIDs;
6019 int newGroupID = mesh->GetGroupIds().back()+1;
6020 while ( groupIt->more() )
6022 SMESH_Group * group = groupIt->next();
6023 if ( !group ) continue;
6024 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6025 if ( !groupDS || groupDS->IsEmpty() ) continue;
6026 groupNames.insert ( group->GetName() );
6027 groupDS->SetStoreName( group->GetName() );
6028 const SMDSAbs_ElementType type = groupDS->GetType();
6029 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6030 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6031 groupsByType[ groupDS->GetType() ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6032 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6035 // Loop on nodes and elements to add them in new groups
6037 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6039 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6040 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6041 if ( gens.Length() != elems.Length() )
6042 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6044 // loop on created elements
6045 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6047 const SMDS_MeshElement* sourceElem = gens( iElem );
6048 if ( !sourceElem ) {
6049 MESSAGE("generateGroups(): NULL source element");
6052 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6053 if ( groupsOldNew.empty() ) { // no groups of this type at all
6054 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6055 ++iElem; // skip all elements made by sourceElem
6058 // collect all elements made by the iElem-th sourceElem
6059 list< const SMDS_MeshElement* > resultElems;
6060 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6061 if ( resElem != sourceElem )
6062 resultElems.push_back( resElem );
6063 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6064 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6065 if ( resElem != sourceElem )
6066 resultElems.push_back( resElem );
6068 // add resultElems to groups made by ones the sourceElem belongs to
6069 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6070 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6072 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6073 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6075 // fill in a new group
6076 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6077 list< const SMDS_MeshElement* > rejectedElems; // elements of other type
6078 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6079 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6080 if ( !newGroup.Add( *resElemIt ))
6081 rejectedElems.push_back( *resElemIt );
6084 if ( !rejectedElems.empty() )
6086 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6087 resLast = rejectedElems.end();
6088 for ( resElemIt = rejectedElems.begin(); resElemIt != resLast; ++resElemIt )
6089 !newTopGroup.Add( *resElemIt );
6093 } // loop on created elements
6094 }// loop on nodes and elements
6096 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6098 list<int> topGrouIds;
6099 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6101 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6102 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6103 orderedOldNewGroups[i]->get<2>() };
6104 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6105 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6107 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6108 if ( newGroupDS->IsEmpty() )
6110 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6115 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6118 const bool isTop = ( nbNewGroups == 2 &&
6119 newGroupDS->GetType() == oldGroupDS->GetType() );
6120 string name = oldGroupDS->GetStoreName();
6121 if ( !targetMesh ) {
6122 string suffix = ( isTop ? "top": postfix.c_str() );
6126 while ( !groupNames.insert( name ).second ) // name exists
6127 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6132 newGroupDS->SetStoreName( name.c_str() );
6134 // make a SMESH_Groups
6135 mesh->AddGroup( newGroupDS );
6137 topGrouIds.push_back( newGroupDS->GetID() );
6139 newGroupIDs->push_back( newGroupDS->GetID() );
6143 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6148 //================================================================================
6150 * \brief Return list of group of nodes close to each other within theTolerance
6151 * Search among theNodes or in the whole mesh if theNodes is empty using
6152 * an Octree algorithm
6154 //================================================================================
6156 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6157 const double theTolerance,
6158 TListOfListOfNodes & theGroupsOfNodes)
6160 myLastCreatedElems.Clear();
6161 myLastCreatedNodes.Clear();
6163 if ( theNodes.empty() )
6164 { // get all nodes in the mesh
6165 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6166 while ( nIt->more() )
6167 theNodes.insert( theNodes.end(),nIt->next());
6170 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6174 //=======================================================================
6176 * \brief Implementation of search for the node closest to point
6178 //=======================================================================
6180 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6182 //---------------------------------------------------------------------
6184 * \brief Constructor
6186 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6188 myMesh = ( SMESHDS_Mesh* ) theMesh;
6190 TIDSortedNodeSet nodes;
6192 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6193 while ( nIt->more() )
6194 nodes.insert( nodes.end(), nIt->next() );
6196 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6198 // get max size of a leaf box
6199 SMESH_OctreeNode* tree = myOctreeNode;
6200 while ( !tree->isLeaf() )
6202 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6206 myHalfLeafSize = tree->maxSize() / 2.;
6209 //---------------------------------------------------------------------
6211 * \brief Move node and update myOctreeNode accordingly
6213 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6215 myOctreeNode->UpdateByMoveNode( node, toPnt );
6216 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6219 //---------------------------------------------------------------------
6221 * \brief Do it's job
6223 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6225 map<double, const SMDS_MeshNode*> dist2Nodes;
6226 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6227 if ( !dist2Nodes.empty() )
6228 return dist2Nodes.begin()->second;
6229 list<const SMDS_MeshNode*> nodes;
6230 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6232 double minSqDist = DBL_MAX;
6233 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6235 // sort leafs by their distance from thePnt
6236 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6237 TDistTreeMap treeMap;
6238 list< SMESH_OctreeNode* > treeList;
6239 list< SMESH_OctreeNode* >::iterator trIt;
6240 treeList.push_back( myOctreeNode );
6242 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6243 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6244 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6246 SMESH_OctreeNode* tree = *trIt;
6247 if ( !tree->isLeaf() ) // put children to the queue
6249 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6250 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6251 while ( cIt->more() )
6252 treeList.push_back( cIt->next() );
6254 else if ( tree->NbNodes() ) // put a tree to the treeMap
6256 const Bnd_B3d& box = *tree->getBox();
6257 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6258 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6259 if ( !it_in.second ) // not unique distance to box center
6260 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6263 // find distance after which there is no sense to check tree's
6264 double sqLimit = DBL_MAX;
6265 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6266 if ( treeMap.size() > 5 ) {
6267 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6268 const Bnd_B3d& box = *closestTree->getBox();
6269 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6270 sqLimit = limit * limit;
6272 // get all nodes from trees
6273 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6274 if ( sqDist_tree->first > sqLimit )
6276 SMESH_OctreeNode* tree = sqDist_tree->second;
6277 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6280 // find closest among nodes
6281 minSqDist = DBL_MAX;
6282 const SMDS_MeshNode* closestNode = 0;
6283 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6284 for ( ; nIt != nodes.end(); ++nIt ) {
6285 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6286 if ( minSqDist > sqDist ) {
6294 //---------------------------------------------------------------------
6298 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6300 //---------------------------------------------------------------------
6302 * \brief Return the node tree
6304 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6307 SMESH_OctreeNode* myOctreeNode;
6308 SMESHDS_Mesh* myMesh;
6309 double myHalfLeafSize; // max size of a leaf box
6312 //=======================================================================
6314 * \brief Return SMESH_NodeSearcher
6316 //=======================================================================
6318 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6320 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6323 // ========================================================================
6324 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6326 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6327 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6328 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6330 //=======================================================================
6332 * \brief Octal tree of bounding boxes of elements
6334 //=======================================================================
6336 class ElementBndBoxTree : public SMESH_Octree
6340 ElementBndBoxTree(const SMDS_Mesh& mesh,
6341 SMDSAbs_ElementType elemType,
6342 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6343 double tolerance = NodeRadius );
6344 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6345 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6346 void getElementsInSphere ( const gp_XYZ& center,
6347 const double radius, TIDSortedElemSet& foundElems);
6348 size_t getSize() { return std::max( _size, _elements.size() ); }
6349 ~ElementBndBoxTree();
6352 ElementBndBoxTree():_size(0) {}
6353 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6354 void buildChildrenData();
6355 Bnd_B3d* buildRootBox();
6357 //!< Bounding box of element
6358 struct ElementBox : public Bnd_B3d
6360 const SMDS_MeshElement* _element;
6361 int _refCount; // an ElementBox can be included in several tree branches
6362 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6364 vector< ElementBox* > _elements;
6368 //================================================================================
6370 * \brief ElementBndBoxTree creation
6372 //================================================================================
6374 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6375 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6377 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6378 _elements.reserve( nbElems );
6380 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6381 while ( elemIt->more() )
6382 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6387 //================================================================================
6391 //================================================================================
6393 ElementBndBoxTree::~ElementBndBoxTree()
6395 for ( int i = 0; i < _elements.size(); ++i )
6396 if ( --_elements[i]->_refCount <= 0 )
6397 delete _elements[i];
6400 //================================================================================
6402 * \brief Return the maximal box
6404 //================================================================================
6406 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6408 Bnd_B3d* box = new Bnd_B3d;
6409 for ( int i = 0; i < _elements.size(); ++i )
6410 box->Add( *_elements[i] );
6414 //================================================================================
6416 * \brief Redistrubute element boxes among children
6418 //================================================================================
6420 void ElementBndBoxTree::buildChildrenData()
6422 for ( int i = 0; i < _elements.size(); ++i )
6424 for (int j = 0; j < 8; j++)
6426 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6428 _elements[i]->_refCount++;
6429 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6432 _elements[i]->_refCount--;
6434 _size = _elements.size();
6435 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6437 for (int j = 0; j < 8; j++)
6439 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6440 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6441 child->myIsLeaf = true;
6443 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6444 SMESHUtils::CompactVector( child->_elements );
6448 //================================================================================
6450 * \brief Return elements which can include the point
6452 //================================================================================
6454 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6455 TIDSortedElemSet& foundElems)
6457 if ( getBox()->IsOut( point.XYZ() ))
6462 for ( int i = 0; i < _elements.size(); ++i )
6463 if ( !_elements[i]->IsOut( point.XYZ() ))
6464 foundElems.insert( _elements[i]->_element );
6468 for (int i = 0; i < 8; i++)
6469 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6473 //================================================================================
6475 * \brief Return elements which can be intersected by the line
6477 //================================================================================
6479 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6480 TIDSortedElemSet& foundElems)
6482 if ( getBox()->IsOut( line ))
6487 for ( int i = 0; i < _elements.size(); ++i )
6488 if ( !_elements[i]->IsOut( line ))
6489 foundElems.insert( _elements[i]->_element );
6493 for (int i = 0; i < 8; i++)
6494 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6498 //================================================================================
6500 * \brief Return elements from leaves intersecting the sphere
6502 //================================================================================
6504 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6505 const double radius,
6506 TIDSortedElemSet& foundElems)
6508 if ( getBox()->IsOut( center, radius ))
6513 for ( int i = 0; i < _elements.size(); ++i )
6514 if ( !_elements[i]->IsOut( center, radius ))
6515 foundElems.insert( _elements[i]->_element );
6519 for (int i = 0; i < 8; i++)
6520 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6524 //================================================================================
6526 * \brief Construct the element box
6528 //================================================================================
6530 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6534 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6535 while ( nIt->more() )
6536 Add( SMESH_TNodeXYZ( nIt->next() ));
6537 Enlarge( tolerance );
6542 //=======================================================================
6544 * \brief Implementation of search for the elements by point and
6545 * of classification of point in 2D mesh
6547 //=======================================================================
6549 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6551 SMESHDS_Mesh* _mesh;
6552 SMDS_ElemIteratorPtr _meshPartIt;
6553 ElementBndBoxTree* _ebbTree;
6554 SMESH_NodeSearcherImpl* _nodeSearcher;
6555 SMDSAbs_ElementType _elementType;
6557 bool _outerFacesFound;
6558 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6560 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6561 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6562 ~SMESH_ElementSearcherImpl()
6564 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6565 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6567 virtual int FindElementsByPoint(const gp_Pnt& point,
6568 SMDSAbs_ElementType type,
6569 vector< const SMDS_MeshElement* >& foundElements);
6570 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6571 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6572 SMDSAbs_ElementType type );
6574 void GetElementsNearLine( const gp_Ax1& line,
6575 SMDSAbs_ElementType type,
6576 vector< const SMDS_MeshElement* >& foundElems);
6577 double getTolerance();
6578 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6579 const double tolerance, double & param);
6580 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6581 bool isOuterBoundary(const SMDS_MeshElement* face) const
6583 return _outerFaces.empty() || _outerFaces.count(face);
6585 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6587 const SMDS_MeshElement* _face;
6589 bool _coincides; //!< the line lays in face plane
6590 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6591 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6593 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6596 TIDSortedElemSet _faces;
6597 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6598 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6602 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6604 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6605 << ", _coincides="<<i._coincides << ")";
6608 //=======================================================================
6610 * \brief define tolerance for search
6612 //=======================================================================
6614 double SMESH_ElementSearcherImpl::getTolerance()
6616 if ( _tolerance < 0 )
6618 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6621 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6623 double boxSize = _nodeSearcher->getTree()->maxSize();
6624 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6626 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6628 double boxSize = _ebbTree->maxSize();
6629 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6631 if ( _tolerance == 0 )
6633 // define tolerance by size of a most complex element
6634 int complexType = SMDSAbs_Volume;
6635 while ( complexType > SMDSAbs_All &&
6636 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6638 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6640 if ( complexType == int( SMDSAbs_Node ))
6642 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6644 if ( meshInfo.NbNodes() > 2 )
6645 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6649 SMDS_ElemIteratorPtr elemIt =
6650 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6651 const SMDS_MeshElement* elem = elemIt->next();
6652 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6653 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6655 while ( nodeIt->more() )
6657 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6658 elemSize = max( dist, elemSize );
6661 _tolerance = 1e-4 * elemSize;
6667 //================================================================================
6669 * \brief Find intersection of the line and an edge of face and return parameter on line
6671 //================================================================================
6673 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6674 const SMDS_MeshElement* face,
6681 GeomAPI_ExtremaCurveCurve anExtCC;
6682 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6684 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6685 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6687 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6688 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6689 anExtCC.Init( lineCurve, edge);
6690 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6692 Quantity_Parameter pl, pe;
6693 anExtCC.LowerDistanceParameters( pl, pe );
6695 if ( ++nbInts == 2 )
6699 if ( nbInts > 0 ) param /= nbInts;
6702 //================================================================================
6704 * \brief Find all faces belonging to the outer boundary of mesh
6706 //================================================================================
6708 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6710 if ( _outerFacesFound ) return;
6712 // Collect all outer faces by passing from one outer face to another via their links
6713 // and BTW find out if there are internal faces at all.
6715 // checked links and links where outer boundary meets internal one
6716 set< SMESH_TLink > visitedLinks, seamLinks;
6718 // links to treat with already visited faces sharing them
6719 list < TFaceLink > startLinks;
6721 // load startLinks with the first outerFace
6722 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6723 _outerFaces.insert( outerFace );
6725 TIDSortedElemSet emptySet;
6726 while ( !startLinks.empty() )
6728 const SMESH_TLink& link = startLinks.front()._link;
6729 TIDSortedElemSet& faces = startLinks.front()._faces;
6731 outerFace = *faces.begin();
6732 // find other faces sharing the link
6733 const SMDS_MeshElement* f;
6734 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6737 // select another outer face among the found
6738 const SMDS_MeshElement* outerFace2 = 0;
6739 if ( faces.size() == 2 )
6741 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6743 else if ( faces.size() > 2 )
6745 seamLinks.insert( link );
6747 // link direction within the outerFace
6748 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6749 SMESH_TNodeXYZ( link.node2()));
6750 int i1 = outerFace->GetNodeIndex( link.node1() );
6751 int i2 = outerFace->GetNodeIndex( link.node2() );
6752 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6753 if ( rev ) n1n2.Reverse();
6755 gp_XYZ ofNorm, fNorm;
6756 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6758 // direction from the link inside outerFace
6759 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6760 // sort all other faces by angle with the dirInOF
6761 map< double, const SMDS_MeshElement* > angle2Face;
6762 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6763 for ( ; face != faces.end(); ++face )
6765 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6767 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6768 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6769 if ( angle < 0 ) angle += 2. * M_PI;
6770 angle2Face.insert( make_pair( angle, *face ));
6772 if ( !angle2Face.empty() )
6773 outerFace2 = angle2Face.begin()->second;
6776 // store the found outer face and add its links to continue seaching from
6779 _outerFaces.insert( outerFace );
6780 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6781 for ( int i = 0; i < nbNodes; ++i )
6783 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6784 if ( visitedLinks.insert( link2 ).second )
6785 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6788 startLinks.pop_front();
6790 _outerFacesFound = true;
6792 if ( !seamLinks.empty() )
6794 // There are internal boundaries touching the outher one,
6795 // find all faces of internal boundaries in order to find
6796 // faces of boundaries of holes, if any.
6801 _outerFaces.clear();
6805 //=======================================================================
6807 * \brief Find elements of given type where the given point is IN or ON.
6808 * Returns nb of found elements and elements them-selves.
6810 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6812 //=======================================================================
6814 int SMESH_ElementSearcherImpl::
6815 FindElementsByPoint(const gp_Pnt& point,
6816 SMDSAbs_ElementType type,
6817 vector< const SMDS_MeshElement* >& foundElements)
6819 foundElements.clear();
6821 double tolerance = getTolerance();
6823 // =================================================================================
6824 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6826 if ( !_nodeSearcher )
6827 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6829 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6830 if ( !closeNode ) return foundElements.size();
6832 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6833 return foundElements.size(); // to far from any node
6835 if ( type == SMDSAbs_Node )
6837 foundElements.push_back( closeNode );
6841 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6842 while ( elemIt->more() )
6843 foundElements.push_back( elemIt->next() );
6846 // =================================================================================
6847 else // elements more complex than 0D
6849 if ( !_ebbTree || _elementType != type )
6851 if ( _ebbTree ) delete _ebbTree;
6852 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6854 TIDSortedElemSet suspectElems;
6855 _ebbTree->getElementsNearPoint( point, suspectElems );
6856 TIDSortedElemSet::iterator elem = suspectElems.begin();
6857 for ( ; elem != suspectElems.end(); ++elem )
6858 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6859 foundElements.push_back( *elem );
6861 return foundElements.size();
6864 //=======================================================================
6866 * \brief Find an element of given type most close to the given point
6868 * WARNING: Only face search is implemeneted so far
6870 //=======================================================================
6872 const SMDS_MeshElement*
6873 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6874 SMDSAbs_ElementType type )
6876 const SMDS_MeshElement* closestElem = 0;
6878 if ( type == SMDSAbs_Face )
6880 if ( !_ebbTree || _elementType != type )
6882 if ( _ebbTree ) delete _ebbTree;
6883 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6885 TIDSortedElemSet suspectElems;
6886 _ebbTree->getElementsNearPoint( point, suspectElems );
6888 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6890 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6891 _ebbTree->getBox()->CornerMax() );
6893 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6894 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6896 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6897 while ( suspectElems.empty() )
6899 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6903 double minDist = std::numeric_limits<double>::max();
6904 multimap< double, const SMDS_MeshElement* > dist2face;
6905 TIDSortedElemSet::iterator elem = suspectElems.begin();
6906 for ( ; elem != suspectElems.end(); ++elem )
6908 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6910 if ( dist < minDist + 1e-10)
6913 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6916 if ( !dist2face.empty() )
6918 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6919 closestElem = d2f->second;
6920 // if there are several elements at the same distance, select one
6921 // with GC closest to the point
6922 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6923 double minDistToGC = 0;
6924 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6926 if ( minDistToGC == 0 )
6929 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6930 TXyzIterator(), gc ) / closestElem->NbNodes();
6931 minDistToGC = point.SquareDistance( gc );
6934 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6935 TXyzIterator(), gc ) / d2f->second->NbNodes();
6936 double d = point.SquareDistance( gc );
6937 if ( d < minDistToGC )
6940 closestElem = d2f->second;
6943 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6944 // <<closestElem->GetID() << " DIST " << minDist << endl;
6949 // NOT IMPLEMENTED SO FAR
6955 //================================================================================
6957 * \brief Classify the given point in the closed 2D mesh
6959 //================================================================================
6961 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6963 double tolerance = getTolerance();
6964 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6966 if ( _ebbTree ) delete _ebbTree;
6967 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6969 // Algo: analyse transition of a line starting at the point through mesh boundary;
6970 // try three lines parallel to axis of the coordinate system and perform rough
6971 // analysis. If solution is not clear perform thorough analysis.
6973 const int nbAxes = 3;
6974 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6975 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6976 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6977 multimap< int, int > nbInt2Axis; // to find the simplest case
6978 for ( int axis = 0; axis < nbAxes; ++axis )
6980 gp_Ax1 lineAxis( point, axisDir[axis]);
6981 gp_Lin line ( lineAxis );
6983 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6984 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6986 // Intersect faces with the line
6988 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6989 TIDSortedElemSet::iterator face = suspectFaces.begin();
6990 for ( ; face != suspectFaces.end(); ++face )
6994 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6995 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6997 // perform intersection
6998 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6999 if ( !intersection.IsDone() )
7001 if ( intersection.IsInQuadric() )
7003 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7005 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7007 gp_Pnt intersectionPoint = intersection.Point(1);
7008 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7009 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7012 // Analyse intersections roughly
7014 int nbInter = u2inters.size();
7018 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7019 if ( nbInter == 1 ) // not closed mesh
7020 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7022 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7025 if ( (f<0) == (l<0) )
7028 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7029 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
7030 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7033 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7035 if ( _outerFacesFound ) break; // pass to thorough analysis
7037 } // three attempts - loop on CS axes
7039 // Analyse intersections thoroughly.
7040 // We make two loops maximum, on the first one we only exclude touching intersections,
7041 // on the second, if situation is still unclear, we gather and use information on
7042 // position of faces (internal or outer). If faces position is already gathered,
7043 // we make the second loop right away.
7045 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7047 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7048 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7050 int axis = nb_axis->second;
7051 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7053 gp_Ax1 lineAxis( point, axisDir[axis]);
7054 gp_Lin line ( lineAxis );
7056 // add tangent intersections to u2inters
7058 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7059 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7060 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7061 u2inters.insert(make_pair( param, *tgtInt ));
7062 tangentInters[ axis ].clear();
7064 // Count intersections before and after the point excluding touching ones.
7065 // If hasPositionInfo we count intersections of outer boundary only
7067 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7068 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7069 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7070 bool ok = ! u_int1->second._coincides;
7071 while ( ok && u_int1 != u2inters.end() )
7073 double u = u_int1->first;
7074 bool touchingInt = false;
7075 if ( ++u_int2 != u2inters.end() )
7077 // skip intersections at the same point (if the line passes through edge or node)
7079 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7085 // skip tangent intersections
7087 const SMDS_MeshElement* prevFace = u_int1->second._face;
7088 while ( ok && u_int2->second._coincides )
7090 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7096 ok = ( u_int2 != u2inters.end() );
7101 // skip intersections at the same point after tangent intersections
7104 double u2 = u_int2->first;
7106 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7112 // decide if we skipped a touching intersection
7113 if ( nbSamePnt + nbTgt > 0 )
7115 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7116 map< double, TInters >::iterator u_int = u_int1;
7117 for ( ; u_int != u_int2; ++u_int )
7119 if ( u_int->second._coincides ) continue;
7120 double dot = u_int->second._faceNorm * line.Direction();
7121 if ( dot > maxDot ) maxDot = dot;
7122 if ( dot < minDot ) minDot = dot;
7124 touchingInt = ( minDot*maxDot < 0 );
7129 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7140 u_int1 = u_int2; // to next intersection
7142 } // loop on intersections with one line
7146 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7149 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7152 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7153 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7155 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7158 if ( (f<0) == (l<0) )
7161 if ( hasPositionInfo )
7162 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7164 } // loop on intersections of the tree lines - thorough analysis
7166 if ( !hasPositionInfo )
7168 // gather info on faces position - is face in the outer boundary or not
7169 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7170 findOuterBoundary( u2inters.begin()->second._face );
7173 } // two attempts - with and w/o faces position info in the mesh
7175 return TopAbs_UNKNOWN;
7178 //=======================================================================
7180 * \brief Return elements possibly intersecting the line
7182 //=======================================================================
7184 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7185 SMDSAbs_ElementType type,
7186 vector< const SMDS_MeshElement* >& foundElems)
7188 if ( !_ebbTree || _elementType != type )
7190 if ( _ebbTree ) delete _ebbTree;
7191 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7193 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7194 _ebbTree->getElementsNearLine( line, suspectFaces );
7195 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7198 //=======================================================================
7200 * \brief Return SMESH_ElementSearcher
7202 //=======================================================================
7204 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7206 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7209 //=======================================================================
7211 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7213 //=======================================================================
7215 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7217 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7220 //=======================================================================
7222 * \brief Return true if the point is IN or ON of the element
7224 //=======================================================================
7226 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7228 if ( element->GetType() == SMDSAbs_Volume)
7230 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7233 // get ordered nodes
7235 vector< gp_XYZ > xyz;
7236 vector<const SMDS_MeshNode*> nodeList;
7238 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7239 if ( element->IsQuadratic() ) {
7240 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7241 nodeIt = f->interlacedNodesElemIterator();
7242 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7243 nodeIt = e->interlacedNodesElemIterator();
7245 while ( nodeIt->more() )
7247 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7248 xyz.push_back( SMESH_TNodeXYZ(node) );
7249 nodeList.push_back(node);
7252 int i, nbNodes = element->NbNodes();
7254 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7256 // compute face normal
7257 gp_Vec faceNorm(0,0,0);
7258 xyz.push_back( xyz.front() );
7259 nodeList.push_back( nodeList.front() );
7260 for ( i = 0; i < nbNodes; ++i )
7262 gp_Vec edge1( xyz[i+1], xyz[i]);
7263 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7264 faceNorm += edge1 ^ edge2;
7266 double normSize = faceNorm.Magnitude();
7267 if ( normSize <= tol )
7269 // degenerated face: point is out if it is out of all face edges
7270 for ( i = 0; i < nbNodes; ++i )
7272 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7273 if ( !IsOut( &edge, point, tol ))
7278 faceNorm /= normSize;
7280 // check if the point lays on face plane
7281 gp_Vec n2p( xyz[0], point );
7282 if ( fabs( n2p * faceNorm ) > tol )
7283 return true; // not on face plane
7285 // check if point is out of face boundary:
7286 // define it by closest transition of a ray point->infinity through face boundary
7287 // on the face plane.
7288 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7289 // to find intersections of the ray with the boundary.
7291 gp_Vec plnNorm = ray ^ faceNorm;
7292 normSize = plnNorm.Magnitude();
7293 if ( normSize <= tol ) return false; // point coincides with the first node
7294 plnNorm /= normSize;
7295 // for each node of the face, compute its signed distance to the plane
7296 vector<double> dist( nbNodes + 1);
7297 for ( i = 0; i < nbNodes; ++i )
7299 gp_Vec n2p( xyz[i], point );
7300 dist[i] = n2p * plnNorm;
7302 dist.back() = dist.front();
7303 // find the closest intersection
7305 double rClosest, distClosest = 1e100;;
7307 for ( i = 0; i < nbNodes; ++i )
7310 if ( fabs( dist[i]) < tol )
7312 else if ( fabs( dist[i+1]) < tol )
7314 else if ( dist[i] * dist[i+1] < 0 )
7315 r = dist[i] / ( dist[i] - dist[i+1] );
7317 continue; // no intersection
7318 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7319 gp_Vec p2int ( point, pInt);
7320 if ( p2int * ray > -tol ) // right half-space
7322 double intDist = p2int.SquareMagnitude();
7323 if ( intDist < distClosest )
7328 distClosest = intDist;
7333 return true; // no intesections - out
7335 // analyse transition
7336 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7337 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7338 gp_Vec p2int ( point, pClosest );
7339 bool out = (edgeNorm * p2int) < -tol;
7340 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7343 // ray pass through a face node; analyze transition through an adjacent edge
7344 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7345 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7346 gp_Vec edgeAdjacent( p1, p2 );
7347 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7348 bool out2 = (edgeNorm2 * p2int) < -tol;
7350 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7351 return covexCorner ? (out || out2) : (out && out2);
7353 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7355 // point is out of edge if it is NOT ON any straight part of edge
7356 // (we consider quadratic edge as being composed of two straight parts)
7357 for ( i = 1; i < nbNodes; ++i )
7359 gp_Vec edge( xyz[i-1], xyz[i]);
7360 gp_Vec n1p ( xyz[i-1], point);
7361 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7364 gp_Vec n2p( xyz[i], point );
7365 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7367 return false; // point is ON this part
7371 // Node or 0D element -------------------------------------------------------------------------
7373 gp_Vec n2p ( xyz[0], point );
7374 return n2p.Magnitude() <= tol;
7379 //=======================================================================
7383 // Position of a point relative to a segment
7387 // VERTEX 1 o----ON-----> VERTEX 2
7391 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7392 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7396 int _index; // index of vertex or segment
7398 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7399 bool operator < (const PointPos& other ) const
7401 if ( _name == other._name )
7402 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7403 return _name < other._name;
7407 //================================================================================
7409 * \brief Return of a point relative to a segment
7410 * \param point2D - the point to analyze position of
7411 * \param xyVec - end points of segments
7412 * \param index0 - 0-based index of the first point of segment
7413 * \param posToFindOut - flags of positions to detect
7414 * \retval PointPos - point position
7416 //================================================================================
7418 PointPos getPointPosition( const gp_XY& point2D,
7419 const gp_XY* segEnds,
7420 const int index0 = 0,
7421 const int posToFindOut = POS_ALL)
7423 const gp_XY& p1 = segEnds[ index0 ];
7424 const gp_XY& p2 = segEnds[ index0+1 ];
7425 const gp_XY grad = p2 - p1;
7427 if ( posToFindOut & POS_VERTEX )
7429 // check if the point2D is at "vertex 1" zone
7430 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7431 p1.Y() + grad.X() ) };
7432 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7433 return PointPos( POS_VERTEX, index0 );
7435 // check if the point2D is at "vertex 2" zone
7436 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7437 p2.Y() + grad.X() ) };
7438 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7439 return PointPos( POS_VERTEX, index0 + 1);
7441 double edgeEquation =
7442 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7443 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7447 //=======================================================================
7449 * \brief Return minimal distance from a point to a face
7451 * Currently we ignore non-planarity and 2nd order of face
7453 //=======================================================================
7455 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7456 const gp_Pnt& point )
7458 double badDistance = -1;
7459 if ( !face ) return badDistance;
7461 // coordinates of nodes (medium nodes, if any, ignored)
7462 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7463 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7464 xyz.resize( face->NbCornerNodes()+1 );
7466 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7467 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7469 gp_Vec OZ ( xyz[0], xyz[1] );
7470 gp_Vec OX ( xyz[0], xyz[2] );
7471 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7473 if ( xyz.size() < 4 ) return badDistance;
7474 OZ = gp_Vec ( xyz[0], xyz[2] );
7475 OX = gp_Vec ( xyz[0], xyz[3] );
7479 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7481 catch ( Standard_Failure ) {
7484 trsf.SetTransformation( tgtCS );
7486 // move all the nodes to 2D
7487 vector<gp_XY> xy( xyz.size() );
7488 for ( size_t i = 0;i < xyz.size()-1; ++i )
7490 gp_XYZ p3d = xyz[i];
7491 trsf.Transforms( p3d );
7492 xy[i].SetCoord( p3d.X(), p3d.Z() );
7494 xyz.back() = xyz.front();
7495 xy.back() = xy.front();
7497 // // move the point in 2D
7498 gp_XYZ tmpPnt = point.XYZ();
7499 trsf.Transforms( tmpPnt );
7500 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7502 // loop on segments of the face to analyze point position ralative to the face
7503 set< PointPos > pntPosSet;
7504 for ( size_t i = 1; i < xy.size(); ++i )
7506 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7507 pntPosSet.insert( pos );
7511 PointPos pos = *pntPosSet.begin();
7512 // cout << "Face " << face->GetID() << " DIST: ";
7513 switch ( pos._name )
7516 // point is most close to a segment
7517 gp_Vec p0p1( point, xyz[ pos._index ] );
7518 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7520 double projDist = p0p1 * p1p2; // distance projected to the segment
7521 gp_Vec projVec = p1p2 * projDist;
7522 gp_Vec distVec = p0p1 - projVec;
7523 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7524 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7525 return distVec.Magnitude();
7528 // point is inside the face
7529 double distToFacePlane = tmpPnt.Y();
7530 // cout << distToFacePlane << ", INSIDE " << endl;
7531 return Abs( distToFacePlane );
7534 // point is most close to a node
7535 gp_Vec distVec( point, xyz[ pos._index ]);
7536 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7537 return distVec.Magnitude();
7543 //=======================================================================
7544 //function : SimplifyFace
7546 //=======================================================================
7547 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7548 vector<const SMDS_MeshNode *>& poly_nodes,
7549 vector<int>& quantities) const
7551 int nbNodes = faceNodes.size();
7556 set<const SMDS_MeshNode*> nodeSet;
7558 // get simple seq of nodes
7559 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7560 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7561 int iSimple = 0, nbUnique = 0;
7563 simpleNodes[iSimple++] = faceNodes[0];
7565 for (int iCur = 1; iCur < nbNodes; iCur++) {
7566 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7567 simpleNodes[iSimple++] = faceNodes[iCur];
7568 if (nodeSet.insert( faceNodes[iCur] ).second)
7572 int nbSimple = iSimple;
7573 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7583 bool foundLoop = (nbSimple > nbUnique);
7586 set<const SMDS_MeshNode*> loopSet;
7587 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7588 const SMDS_MeshNode* n = simpleNodes[iSimple];
7589 if (!loopSet.insert( n ).second) {
7593 int iC = 0, curLast = iSimple;
7594 for (; iC < curLast; iC++) {
7595 if (simpleNodes[iC] == n) break;
7597 int loopLen = curLast - iC;
7599 // create sub-element
7601 quantities.push_back(loopLen);
7602 for (; iC < curLast; iC++) {
7603 poly_nodes.push_back(simpleNodes[iC]);
7606 // shift the rest nodes (place from the first loop position)
7607 for (iC = curLast + 1; iC < nbSimple; iC++) {
7608 simpleNodes[iC - loopLen] = simpleNodes[iC];
7610 nbSimple -= loopLen;
7613 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7614 } // while (foundLoop)
7618 quantities.push_back(iSimple);
7619 for (int i = 0; i < iSimple; i++)
7620 poly_nodes.push_back(simpleNodes[i]);
7626 //=======================================================================
7627 //function : MergeNodes
7628 //purpose : In each group, the cdr of nodes are substituted by the first one
7630 //=======================================================================
7632 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7634 MESSAGE("MergeNodes");
7635 myLastCreatedElems.Clear();
7636 myLastCreatedNodes.Clear();
7638 SMESHDS_Mesh* aMesh = GetMeshDS();
7640 TNodeNodeMap nodeNodeMap; // node to replace - new node
7641 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7642 list< int > rmElemIds, rmNodeIds;
7644 // Fill nodeNodeMap and elems
7646 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7647 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7648 list<const SMDS_MeshNode*>& nodes = *grIt;
7649 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7650 const SMDS_MeshNode* nToKeep = *nIt;
7651 //MESSAGE("node to keep " << nToKeep->GetID());
7652 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7653 const SMDS_MeshNode* nToRemove = *nIt;
7654 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7655 if ( nToRemove != nToKeep ) {
7656 //MESSAGE(" node to remove " << nToRemove->GetID());
7657 rmNodeIds.push_back( nToRemove->GetID() );
7658 AddToSameGroups( nToKeep, nToRemove, aMesh );
7661 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7662 while ( invElemIt->more() ) {
7663 const SMDS_MeshElement* elem = invElemIt->next();
7668 // Change element nodes or remove an element
7670 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7671 for ( ; eIt != elems.end(); eIt++ ) {
7672 const SMDS_MeshElement* elem = *eIt;
7673 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7674 int nbNodes = elem->NbNodes();
7675 int aShapeId = FindShape( elem );
7677 set<const SMDS_MeshNode*> nodeSet;
7678 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7679 int iUnique = 0, iCur = 0, nbRepl = 0;
7680 vector<int> iRepl( nbNodes );
7682 // get new seq of nodes
7683 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7684 while ( itN->more() ) {
7685 const SMDS_MeshNode* n =
7686 static_cast<const SMDS_MeshNode*>( itN->next() );
7688 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7689 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7691 // BUG 0020185: begin
7693 bool stopRecur = false;
7694 set<const SMDS_MeshNode*> nodesRecur;
7695 nodesRecur.insert(n);
7696 while (!stopRecur) {
7697 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7698 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7699 n = (*nnIt_i).second;
7700 if (!nodesRecur.insert(n).second) {
7701 // error: recursive dependancy
7711 curNodes[ iCur ] = n;
7712 bool isUnique = nodeSet.insert( n ).second;
7714 uniqueNodes[ iUnique++ ] = n;
7716 iRepl[ nbRepl++ ] = iCur;
7720 // Analyse element topology after replacement
7723 int nbUniqueNodes = nodeSet.size();
7724 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7725 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7726 // Polygons and Polyhedral volumes
7727 if (elem->IsPoly()) {
7729 if (elem->GetType() == SMDSAbs_Face) {
7731 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7733 for (; inode < nbNodes; inode++) {
7734 face_nodes[inode] = curNodes[inode];
7737 vector<const SMDS_MeshNode *> polygons_nodes;
7738 vector<int> quantities;
7739 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7742 for (int iface = 0; iface < nbNew; iface++) {
7743 int nbNodes = quantities[iface];
7744 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7745 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7746 poly_nodes[ii] = polygons_nodes[inode];
7748 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7749 myLastCreatedElems.Append(newElem);
7751 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7754 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7755 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7756 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7758 if (nbNew > 0) quid = nbNew - 1;
7759 vector<int> newquant(quantities.begin()+quid, quantities.end());
7760 const SMDS_MeshElement* newElem = 0;
7761 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7762 myLastCreatedElems.Append(newElem);
7763 if ( aShapeId && newElem )
7764 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7765 rmElemIds.push_back(elem->GetID());
7768 rmElemIds.push_back(elem->GetID());
7772 else if (elem->GetType() == SMDSAbs_Volume) {
7773 // Polyhedral volume
7774 if (nbUniqueNodes < 4) {
7775 rmElemIds.push_back(elem->GetID());
7778 // each face has to be analyzed in order to check volume validity
7779 const SMDS_VtkVolume* aPolyedre =
7780 dynamic_cast<const SMDS_VtkVolume*>( elem );
7782 int nbFaces = aPolyedre->NbFaces();
7784 vector<const SMDS_MeshNode *> poly_nodes;
7785 vector<int> quantities;
7787 for (int iface = 1; iface <= nbFaces; iface++) {
7788 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7789 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7791 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7792 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7793 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7794 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7795 faceNode = (*nnIt).second;
7797 faceNodes[inode - 1] = faceNode;
7800 SimplifyFace(faceNodes, poly_nodes, quantities);
7803 if (quantities.size() > 3) {
7804 // to be done: remove coincident faces
7807 if (quantities.size() > 3)
7809 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7810 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7811 const SMDS_MeshElement* newElem = 0;
7812 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7813 myLastCreatedElems.Append(newElem);
7814 if ( aShapeId && newElem )
7815 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7816 rmElemIds.push_back(elem->GetID());
7820 rmElemIds.push_back(elem->GetID());
7831 // TODO not all the possible cases are solved. Find something more generic?
7832 switch ( nbNodes ) {
7833 case 2: ///////////////////////////////////// EDGE
7834 isOk = false; break;
7835 case 3: ///////////////////////////////////// TRIANGLE
7836 isOk = false; break;
7838 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7840 else { //////////////////////////////////// QUADRANGLE
7841 if ( nbUniqueNodes < 3 )
7843 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7844 isOk = false; // opposite nodes stick
7845 //MESSAGE("isOk " << isOk);
7848 case 6: ///////////////////////////////////// PENTAHEDRON
7849 if ( nbUniqueNodes == 4 ) {
7850 // ---------------------------------> tetrahedron
7852 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7853 // all top nodes stick: reverse a bottom
7854 uniqueNodes[ 0 ] = curNodes [ 1 ];
7855 uniqueNodes[ 1 ] = curNodes [ 0 ];
7857 else if (nbRepl == 3 &&
7858 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7859 // all bottom nodes stick: set a top before
7860 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7861 uniqueNodes[ 0 ] = curNodes [ 3 ];
7862 uniqueNodes[ 1 ] = curNodes [ 4 ];
7863 uniqueNodes[ 2 ] = curNodes [ 5 ];
7865 else if (nbRepl == 4 &&
7866 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7867 // a lateral face turns into a line: reverse a bottom
7868 uniqueNodes[ 0 ] = curNodes [ 1 ];
7869 uniqueNodes[ 1 ] = curNodes [ 0 ];
7874 else if ( nbUniqueNodes == 5 ) {
7875 // PENTAHEDRON --------------------> 2 tetrahedrons
7876 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7877 // a bottom node sticks with a linked top one
7879 SMDS_MeshElement* newElem =
7880 aMesh->AddVolume(curNodes[ 3 ],
7883 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7884 myLastCreatedElems.Append(newElem);
7886 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7887 // 2. : reverse a bottom
7888 uniqueNodes[ 0 ] = curNodes [ 1 ];
7889 uniqueNodes[ 1 ] = curNodes [ 0 ];
7899 if(elem->IsQuadratic()) { // Quadratic quadrangle
7911 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7914 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7916 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7917 uniqueNodes[0] = curNodes[0];
7918 uniqueNodes[1] = curNodes[2];
7919 uniqueNodes[2] = curNodes[3];
7920 uniqueNodes[3] = curNodes[5];
7921 uniqueNodes[4] = curNodes[6];
7922 uniqueNodes[5] = curNodes[7];
7925 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7926 uniqueNodes[0] = curNodes[0];
7927 uniqueNodes[1] = curNodes[1];
7928 uniqueNodes[2] = curNodes[2];
7929 uniqueNodes[3] = curNodes[4];
7930 uniqueNodes[4] = curNodes[5];
7931 uniqueNodes[5] = curNodes[6];
7934 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7935 uniqueNodes[0] = curNodes[1];
7936 uniqueNodes[1] = curNodes[2];
7937 uniqueNodes[2] = curNodes[3];
7938 uniqueNodes[3] = curNodes[5];
7939 uniqueNodes[4] = curNodes[6];
7940 uniqueNodes[5] = curNodes[0];
7943 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7944 uniqueNodes[0] = curNodes[0];
7945 uniqueNodes[1] = curNodes[1];
7946 uniqueNodes[2] = curNodes[3];
7947 uniqueNodes[3] = curNodes[4];
7948 uniqueNodes[4] = curNodes[6];
7949 uniqueNodes[5] = curNodes[7];
7952 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7953 uniqueNodes[0] = curNodes[0];
7954 uniqueNodes[1] = curNodes[2];
7955 uniqueNodes[2] = curNodes[3];
7956 uniqueNodes[3] = curNodes[1];
7957 uniqueNodes[4] = curNodes[6];
7958 uniqueNodes[5] = curNodes[7];
7961 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7962 uniqueNodes[0] = curNodes[0];
7963 uniqueNodes[1] = curNodes[1];
7964 uniqueNodes[2] = curNodes[2];
7965 uniqueNodes[3] = curNodes[4];
7966 uniqueNodes[4] = curNodes[5];
7967 uniqueNodes[5] = curNodes[7];
7970 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7971 uniqueNodes[0] = curNodes[0];
7972 uniqueNodes[1] = curNodes[1];
7973 uniqueNodes[2] = curNodes[3];
7974 uniqueNodes[3] = curNodes[4];
7975 uniqueNodes[4] = curNodes[2];
7976 uniqueNodes[5] = curNodes[7];
7979 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7980 uniqueNodes[0] = curNodes[0];
7981 uniqueNodes[1] = curNodes[1];
7982 uniqueNodes[2] = curNodes[2];
7983 uniqueNodes[3] = curNodes[4];
7984 uniqueNodes[4] = curNodes[5];
7985 uniqueNodes[5] = curNodes[3];
7990 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7993 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7997 //////////////////////////////////// HEXAHEDRON
7999 SMDS_VolumeTool hexa (elem);
8000 hexa.SetExternalNormal();
8001 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8002 //////////////////////// HEX ---> 1 tetrahedron
8003 for ( int iFace = 0; iFace < 6; iFace++ ) {
8004 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8005 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8006 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8007 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8008 // one face turns into a point ...
8009 int iOppFace = hexa.GetOppFaceIndex( iFace );
8010 ind = hexa.GetFaceNodesIndices( iOppFace );
8012 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8013 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8016 if ( nbStick == 1 ) {
8017 // ... and the opposite one - into a triangle.
8019 ind = hexa.GetFaceNodesIndices( iFace );
8020 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8027 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8028 //////////////////////// HEX ---> 1 prism
8029 int nbTria = 0, iTria[3];
8030 const int *ind; // indices of face nodes
8031 // look for triangular faces
8032 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8033 ind = hexa.GetFaceNodesIndices( iFace );
8034 TIDSortedNodeSet faceNodes;
8035 for ( iCur = 0; iCur < 4; iCur++ )
8036 faceNodes.insert( curNodes[ind[iCur]] );
8037 if ( faceNodes.size() == 3 )
8038 iTria[ nbTria++ ] = iFace;
8040 // check if triangles are opposite
8041 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8044 // set nodes of the bottom triangle
8045 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8047 for ( iCur = 0; iCur < 4; iCur++ )
8048 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8049 indB.push_back( ind[iCur] );
8050 if ( !hexa.IsForward() )
8051 std::swap( indB[0], indB[2] );
8052 for ( iCur = 0; iCur < 3; iCur++ )
8053 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8054 // set nodes of the top triangle
8055 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8056 for ( iCur = 0; iCur < 3; ++iCur )
8057 for ( int j = 0; j < 4; ++j )
8058 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8060 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8066 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8067 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8068 for ( int iFace = 0; iFace < 6; iFace++ ) {
8069 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8070 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8071 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8072 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8073 // one face turns into a point ...
8074 int iOppFace = hexa.GetOppFaceIndex( iFace );
8075 ind = hexa.GetFaceNodesIndices( iOppFace );
8077 iUnique = 2; // reverse a tetrahedron 1 bottom
8078 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8079 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8081 else if ( iUnique >= 0 )
8082 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8084 if ( nbStick == 0 ) {
8085 // ... and the opposite one is a quadrangle
8087 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8088 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8091 SMDS_MeshElement* newElem =
8092 aMesh->AddVolume(curNodes[ind[ 0 ]],
8095 curNodes[indTop[ 0 ]]);
8096 myLastCreatedElems.Append(newElem);
8098 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8105 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8106 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8107 // find indices of quad and tri faces
8108 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8109 for ( iFace = 0; iFace < 6; iFace++ ) {
8110 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8112 for ( iCur = 0; iCur < 4; iCur++ )
8113 nodeSet.insert( curNodes[ind[ iCur ]] );
8114 nbUniqueNodes = nodeSet.size();
8115 if ( nbUniqueNodes == 3 )
8116 iTriFace[ nbTri++ ] = iFace;
8117 else if ( nbUniqueNodes == 4 )
8118 iQuadFace[ nbQuad++ ] = iFace;
8120 if (nbQuad == 2 && nbTri == 4 &&
8121 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8122 // 2 opposite quadrangles stuck with a diagonal;
8123 // sample groups of merged indices: (0-4)(2-6)
8124 // --------------------------------------------> 2 tetrahedrons
8125 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8126 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8127 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8128 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8129 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8130 // stuck with 0-2 diagonal
8138 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8139 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8140 // stuck with 1-3 diagonal
8152 uniqueNodes[ 0 ] = curNodes [ i0 ];
8153 uniqueNodes[ 1 ] = curNodes [ i1d ];
8154 uniqueNodes[ 2 ] = curNodes [ i3d ];
8155 uniqueNodes[ 3 ] = curNodes [ i0t ];
8158 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8162 myLastCreatedElems.Append(newElem);
8164 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8167 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8168 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8169 // --------------------------------------------> prism
8170 // find 2 opposite triangles
8172 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8173 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8174 // find indices of kept and replaced nodes
8175 // and fill unique nodes of 2 opposite triangles
8176 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8177 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8178 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8179 // fill unique nodes
8182 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8183 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8184 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8186 // iCur of a linked node of the opposite face (make normals co-directed):
8187 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8188 // check that correspondent corners of triangles are linked
8189 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8192 uniqueNodes[ iUnique ] = n;
8193 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8202 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8205 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8212 } // switch ( nbNodes )
8214 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8216 if ( isOk ) { // the elem remains valid after sticking nodes
8217 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8219 // Change nodes of polyedre
8220 const SMDS_VtkVolume* aPolyedre =
8221 dynamic_cast<const SMDS_VtkVolume*>( elem );
8223 int nbFaces = aPolyedre->NbFaces();
8225 vector<const SMDS_MeshNode *> poly_nodes;
8226 vector<int> quantities (nbFaces);
8228 for (int iface = 1; iface <= nbFaces; iface++) {
8229 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8230 quantities[iface - 1] = nbFaceNodes;
8232 for (inode = 1; inode <= nbFaceNodes; inode++) {
8233 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8235 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8236 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8237 curNode = (*nnIt).second;
8239 poly_nodes.push_back(curNode);
8242 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8245 else // replace non-polyhedron elements
8247 const SMDSAbs_ElementType etyp = elem->GetType();
8248 const int elemId = elem->GetID();
8249 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8250 uniqueNodes.resize(nbUniqueNodes);
8252 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8254 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8255 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8256 if ( sm && newElem )
8257 sm->AddElement( newElem );
8258 if ( elem != newElem )
8259 ReplaceElemInGroups( elem, newElem, aMesh );
8263 // Remove invalid regular element or invalid polygon
8264 rmElemIds.push_back( elem->GetID() );
8267 } // loop on elements
8269 // Remove bad elements, then equal nodes (order important)
8271 Remove( rmElemIds, false );
8272 Remove( rmNodeIds, true );
8277 // ========================================================
8278 // class : SortableElement
8279 // purpose : allow sorting elements basing on their nodes
8280 // ========================================================
8281 class SortableElement : public set <const SMDS_MeshElement*>
8285 SortableElement( const SMDS_MeshElement* theElem )
8288 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8289 while ( nodeIt->more() )
8290 this->insert( nodeIt->next() );
8293 const SMDS_MeshElement* Get() const
8296 void Set(const SMDS_MeshElement* e) const
8301 mutable const SMDS_MeshElement* myElem;
8304 //=======================================================================
8305 //function : FindEqualElements
8306 //purpose : Return list of group of elements built on the same nodes.
8307 // Search among theElements or in the whole mesh if theElements is empty
8308 //=======================================================================
8310 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8311 TListOfListOfElementsID & theGroupsOfElementsID)
8313 myLastCreatedElems.Clear();
8314 myLastCreatedNodes.Clear();
8316 typedef map< SortableElement, int > TMapOfNodeSet;
8317 typedef list<int> TGroupOfElems;
8319 if ( theElements.empty() )
8320 { // get all elements in the mesh
8321 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8322 while ( eIt->more() )
8323 theElements.insert( theElements.end(), eIt->next());
8326 vector< TGroupOfElems > arrayOfGroups;
8327 TGroupOfElems groupOfElems;
8328 TMapOfNodeSet mapOfNodeSet;
8330 TIDSortedElemSet::iterator elemIt = theElements.begin();
8331 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8332 const SMDS_MeshElement* curElem = *elemIt;
8333 SortableElement SE(curElem);
8336 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8337 if( !(pp.second) ) {
8338 TMapOfNodeSet::iterator& itSE = pp.first;
8339 ind = (*itSE).second;
8340 arrayOfGroups[ind].push_back(curElem->GetID());
8343 groupOfElems.clear();
8344 groupOfElems.push_back(curElem->GetID());
8345 arrayOfGroups.push_back(groupOfElems);
8350 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8351 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8352 groupOfElems = *groupIt;
8353 if ( groupOfElems.size() > 1 ) {
8354 groupOfElems.sort();
8355 theGroupsOfElementsID.push_back(groupOfElems);
8360 //=======================================================================
8361 //function : MergeElements
8362 //purpose : In each given group, substitute all elements by the first one.
8363 //=======================================================================
8365 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8367 myLastCreatedElems.Clear();
8368 myLastCreatedNodes.Clear();
8370 typedef list<int> TListOfIDs;
8371 TListOfIDs rmElemIds; // IDs of elems to remove
8373 SMESHDS_Mesh* aMesh = GetMeshDS();
8375 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8376 while ( groupsIt != theGroupsOfElementsID.end() ) {
8377 TListOfIDs& aGroupOfElemID = *groupsIt;
8378 aGroupOfElemID.sort();
8379 int elemIDToKeep = aGroupOfElemID.front();
8380 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8381 aGroupOfElemID.pop_front();
8382 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8383 while ( idIt != aGroupOfElemID.end() ) {
8384 int elemIDToRemove = *idIt;
8385 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8386 // add the kept element in groups of removed one (PAL15188)
8387 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8388 rmElemIds.push_back( elemIDToRemove );
8394 Remove( rmElemIds, false );
8397 //=======================================================================
8398 //function : MergeEqualElements
8399 //purpose : Remove all but one of elements built on the same nodes.
8400 //=======================================================================
8402 void SMESH_MeshEditor::MergeEqualElements()
8404 TIDSortedElemSet aMeshElements; /* empty input ==
8405 to merge equal elements in the whole mesh */
8406 TListOfListOfElementsID aGroupsOfElementsID;
8407 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8408 MergeElements(aGroupsOfElementsID);
8411 //=======================================================================
8412 //function : FindFaceInSet
8413 //purpose : Return a face having linked nodes n1 and n2 and which is
8414 // - not in avoidSet,
8415 // - in elemSet provided that !elemSet.empty()
8416 // i1 and i2 optionally returns indices of n1 and n2
8417 //=======================================================================
8419 const SMDS_MeshElement*
8420 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8421 const SMDS_MeshNode* n2,
8422 const TIDSortedElemSet& elemSet,
8423 const TIDSortedElemSet& avoidSet,
8429 const SMDS_MeshElement* face = 0;
8431 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8432 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8433 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8435 //MESSAGE("in while ( invElemIt->more() && !face )");
8436 const SMDS_MeshElement* elem = invElemIt->next();
8437 if (avoidSet.count( elem ))
8439 if ( !elemSet.empty() && !elemSet.count( elem ))
8442 i1 = elem->GetNodeIndex( n1 );
8443 // find a n2 linked to n1
8444 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8445 for ( int di = -1; di < 2 && !face; di += 2 )
8447 i2 = (i1+di+nbN) % nbN;
8448 if ( elem->GetNode( i2 ) == n2 )
8451 if ( !face && elem->IsQuadratic())
8453 // analysis for quadratic elements using all nodes
8454 const SMDS_VtkFace* F =
8455 dynamic_cast<const SMDS_VtkFace*>(elem);
8456 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8457 // use special nodes iterator
8458 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8459 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8460 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8462 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8463 if ( n1 == prevN && n2 == n )
8467 else if ( n2 == prevN && n1 == n )
8469 face = elem; swap( i1, i2 );
8475 if ( n1ind ) *n1ind = i1;
8476 if ( n2ind ) *n2ind = i2;
8480 //=======================================================================
8481 //function : findAdjacentFace
8483 //=======================================================================
8485 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8486 const SMDS_MeshNode* n2,
8487 const SMDS_MeshElement* elem)
8489 TIDSortedElemSet elemSet, avoidSet;
8491 avoidSet.insert ( elem );
8492 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8495 //=======================================================================
8496 //function : FindFreeBorder
8498 //=======================================================================
8500 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8502 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8503 const SMDS_MeshNode* theSecondNode,
8504 const SMDS_MeshNode* theLastNode,
8505 list< const SMDS_MeshNode* > & theNodes,
8506 list< const SMDS_MeshElement* >& theFaces)
8508 if ( !theFirstNode || !theSecondNode )
8510 // find border face between theFirstNode and theSecondNode
8511 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8515 theFaces.push_back( curElem );
8516 theNodes.push_back( theFirstNode );
8517 theNodes.push_back( theSecondNode );
8519 //vector<const SMDS_MeshNode*> nodes;
8520 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8521 TIDSortedElemSet foundElems;
8522 bool needTheLast = ( theLastNode != 0 );
8524 while ( nStart != theLastNode ) {
8525 if ( nStart == theFirstNode )
8526 return !needTheLast;
8528 // find all free border faces sharing form nStart
8530 list< const SMDS_MeshElement* > curElemList;
8531 list< const SMDS_MeshNode* > nStartList;
8532 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8533 while ( invElemIt->more() ) {
8534 const SMDS_MeshElement* e = invElemIt->next();
8535 if ( e == curElem || foundElems.insert( e ).second ) {
8537 int iNode = 0, nbNodes = e->NbNodes();
8538 //const SMDS_MeshNode* nodes[nbNodes+1];
8539 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8541 if(e->IsQuadratic()) {
8542 const SMDS_VtkFace* F =
8543 dynamic_cast<const SMDS_VtkFace*>(e);
8544 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8545 // use special nodes iterator
8546 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8547 while( anIter->more() ) {
8548 nodes[ iNode++ ] = cast2Node(anIter->next());
8552 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8553 while ( nIt->more() )
8554 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8556 nodes[ iNode ] = nodes[ 0 ];
8558 for ( iNode = 0; iNode < nbNodes; iNode++ )
8559 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8560 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8561 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8563 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8564 curElemList.push_back( e );
8568 // analyse the found
8570 int nbNewBorders = curElemList.size();
8571 if ( nbNewBorders == 0 ) {
8572 // no free border furthermore
8573 return !needTheLast;
8575 else if ( nbNewBorders == 1 ) {
8576 // one more element found
8578 nStart = nStartList.front();
8579 curElem = curElemList.front();
8580 theFaces.push_back( curElem );
8581 theNodes.push_back( nStart );
8584 // several continuations found
8585 list< const SMDS_MeshElement* >::iterator curElemIt;
8586 list< const SMDS_MeshNode* >::iterator nStartIt;
8587 // check if one of them reached the last node
8588 if ( needTheLast ) {
8589 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8590 curElemIt!= curElemList.end();
8591 curElemIt++, nStartIt++ )
8592 if ( *nStartIt == theLastNode ) {
8593 theFaces.push_back( *curElemIt );
8594 theNodes.push_back( *nStartIt );
8598 // find the best free border by the continuations
8599 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8600 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8601 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8602 curElemIt!= curElemList.end();
8603 curElemIt++, nStartIt++ )
8605 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8606 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8607 // find one more free border
8608 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8612 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8613 // choice: clear a worse one
8614 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8615 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8616 contNodes[ iWorse ].clear();
8617 contFaces[ iWorse ].clear();
8620 if ( contNodes[0].empty() && contNodes[1].empty() )
8623 // append the best free border
8624 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8625 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8626 theNodes.pop_back(); // remove nIgnore
8627 theNodes.pop_back(); // remove nStart
8628 theFaces.pop_back(); // remove curElem
8629 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8630 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8631 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8632 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8635 } // several continuations found
8636 } // while ( nStart != theLastNode )
8641 //=======================================================================
8642 //function : CheckFreeBorderNodes
8643 //purpose : Return true if the tree nodes are on a free border
8644 //=======================================================================
8646 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8647 const SMDS_MeshNode* theNode2,
8648 const SMDS_MeshNode* theNode3)
8650 list< const SMDS_MeshNode* > nodes;
8651 list< const SMDS_MeshElement* > faces;
8652 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8655 //=======================================================================
8656 //function : SewFreeBorder
8658 //=======================================================================
8660 SMESH_MeshEditor::Sew_Error
8661 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8662 const SMDS_MeshNode* theBordSecondNode,
8663 const SMDS_MeshNode* theBordLastNode,
8664 const SMDS_MeshNode* theSideFirstNode,
8665 const SMDS_MeshNode* theSideSecondNode,
8666 const SMDS_MeshNode* theSideThirdNode,
8667 const bool theSideIsFreeBorder,
8668 const bool toCreatePolygons,
8669 const bool toCreatePolyedrs)
8671 myLastCreatedElems.Clear();
8672 myLastCreatedNodes.Clear();
8674 MESSAGE("::SewFreeBorder()");
8675 Sew_Error aResult = SEW_OK;
8677 // ====================================
8678 // find side nodes and elements
8679 // ====================================
8681 list< const SMDS_MeshNode* > nSide[ 2 ];
8682 list< const SMDS_MeshElement* > eSide[ 2 ];
8683 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8684 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8688 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8689 nSide[0], eSide[0])) {
8690 MESSAGE(" Free Border 1 not found " );
8691 aResult = SEW_BORDER1_NOT_FOUND;
8693 if (theSideIsFreeBorder) {
8696 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8697 nSide[1], eSide[1])) {
8698 MESSAGE(" Free Border 2 not found " );
8699 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8702 if ( aResult != SEW_OK )
8705 if (!theSideIsFreeBorder) {
8709 // -------------------------------------------------------------------------
8711 // 1. If nodes to merge are not coincident, move nodes of the free border
8712 // from the coord sys defined by the direction from the first to last
8713 // nodes of the border to the correspondent sys of the side 2
8714 // 2. On the side 2, find the links most co-directed with the correspondent
8715 // links of the free border
8716 // -------------------------------------------------------------------------
8718 // 1. Since sewing may break if there are volumes to split on the side 2,
8719 // we wont move nodes but just compute new coordinates for them
8720 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8721 TNodeXYZMap nBordXYZ;
8722 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8723 list< const SMDS_MeshNode* >::iterator nBordIt;
8725 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8726 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8727 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8728 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8729 double tol2 = 1.e-8;
8730 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8731 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8732 // Need node movement.
8734 // find X and Z axes to create trsf
8735 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8737 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8739 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8742 gp_Ax3 toBordAx( Pb1, Zb, X );
8743 gp_Ax3 fromSideAx( Ps1, Zs, X );
8744 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8746 gp_Trsf toBordSys, fromSide2Sys;
8747 toBordSys.SetTransformation( toBordAx );
8748 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8749 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8752 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8753 const SMDS_MeshNode* n = *nBordIt;
8754 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8755 toBordSys.Transforms( xyz );
8756 fromSide2Sys.Transforms( xyz );
8757 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8761 // just insert nodes XYZ in the nBordXYZ map
8762 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8763 const SMDS_MeshNode* n = *nBordIt;
8764 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8768 // 2. On the side 2, find the links most co-directed with the correspondent
8769 // links of the free border
8771 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8772 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8773 sideNodes.push_back( theSideFirstNode );
8775 bool hasVolumes = false;
8776 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8777 set<long> foundSideLinkIDs, checkedLinkIDs;
8778 SMDS_VolumeTool volume;
8779 //const SMDS_MeshNode* faceNodes[ 4 ];
8781 const SMDS_MeshNode* sideNode;
8782 const SMDS_MeshElement* sideElem;
8783 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8784 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8785 nBordIt = bordNodes.begin();
8787 // border node position and border link direction to compare with
8788 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8789 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8790 // choose next side node by link direction or by closeness to
8791 // the current border node:
8792 bool searchByDir = ( *nBordIt != theBordLastNode );
8794 // find the next node on the Side 2
8796 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8798 checkedLinkIDs.clear();
8799 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8801 // loop on inverse elements of current node (prevSideNode) on the Side 2
8802 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8803 while ( invElemIt->more() )
8805 const SMDS_MeshElement* elem = invElemIt->next();
8806 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8807 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8808 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8809 bool isVolume = volume.Set( elem );
8810 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8811 if ( isVolume ) // --volume
8813 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8814 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8815 if(elem->IsQuadratic()) {
8816 const SMDS_VtkFace* F =
8817 dynamic_cast<const SMDS_VtkFace*>(elem);
8818 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8819 // use special nodes iterator
8820 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8821 while( anIter->more() ) {
8822 nodes[ iNode ] = cast2Node(anIter->next());
8823 if ( nodes[ iNode++ ] == prevSideNode )
8824 iPrevNode = iNode - 1;
8828 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8829 while ( nIt->more() ) {
8830 nodes[ iNode ] = cast2Node( nIt->next() );
8831 if ( nodes[ iNode++ ] == prevSideNode )
8832 iPrevNode = iNode - 1;
8835 // there are 2 links to check
8840 // loop on links, to be precise, on the second node of links
8841 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8842 const SMDS_MeshNode* n = nodes[ iNode ];
8844 if ( !volume.IsLinked( n, prevSideNode ))
8848 if ( iNode ) // a node before prevSideNode
8849 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8850 else // a node after prevSideNode
8851 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8853 // check if this link was already used
8854 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8855 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8856 if (!isJustChecked &&
8857 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8859 // test a link geometrically
8860 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8861 bool linkIsBetter = false;
8862 double dot = 0.0, dist = 0.0;
8863 if ( searchByDir ) { // choose most co-directed link
8864 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8865 linkIsBetter = ( dot > maxDot );
8867 else { // choose link with the node closest to bordPos
8868 dist = ( nextXYZ - bordPos ).SquareModulus();
8869 linkIsBetter = ( dist < minDist );
8871 if ( linkIsBetter ) {
8880 } // loop on inverse elements of prevSideNode
8883 MESSAGE(" Cant find path by links of the Side 2 ");
8884 return SEW_BAD_SIDE_NODES;
8886 sideNodes.push_back( sideNode );
8887 sideElems.push_back( sideElem );
8888 foundSideLinkIDs.insert ( linkID );
8889 prevSideNode = sideNode;
8891 if ( *nBordIt == theBordLastNode )
8892 searchByDir = false;
8894 // find the next border link to compare with
8895 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8896 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8897 // move to next border node if sideNode is before forward border node (bordPos)
8898 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8899 prevBordNode = *nBordIt;
8901 bordPos = nBordXYZ[ *nBordIt ];
8902 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8903 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8907 while ( sideNode != theSideSecondNode );
8909 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8910 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8911 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8913 } // end nodes search on the side 2
8915 // ============================
8916 // sew the border to the side 2
8917 // ============================
8919 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8920 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8922 TListOfListOfNodes nodeGroupsToMerge;
8923 if ( nbNodes[0] == nbNodes[1] ||
8924 ( theSideIsFreeBorder && !theSideThirdNode)) {
8926 // all nodes are to be merged
8928 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8929 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8930 nIt[0]++, nIt[1]++ )
8932 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8933 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8934 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8939 // insert new nodes into the border and the side to get equal nb of segments
8941 // get normalized parameters of nodes on the borders
8942 //double param[ 2 ][ maxNbNodes ];
8944 param[0] = new double [ maxNbNodes ];
8945 param[1] = new double [ maxNbNodes ];
8947 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8948 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8949 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8950 const SMDS_MeshNode* nPrev = *nIt;
8951 double bordLength = 0;
8952 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8953 const SMDS_MeshNode* nCur = *nIt;
8954 gp_XYZ segment (nCur->X() - nPrev->X(),
8955 nCur->Y() - nPrev->Y(),
8956 nCur->Z() - nPrev->Z());
8957 double segmentLen = segment.Modulus();
8958 bordLength += segmentLen;
8959 param[ iBord ][ iNode ] = bordLength;
8962 // normalize within [0,1]
8963 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8964 param[ iBord ][ iNode ] /= bordLength;
8968 // loop on border segments
8969 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8970 int i[ 2 ] = { 0, 0 };
8971 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8972 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8974 TElemOfNodeListMap insertMap;
8975 TElemOfNodeListMap::iterator insertMapIt;
8977 // key: elem to insert nodes into
8978 // value: 2 nodes to insert between + nodes to be inserted
8980 bool next[ 2 ] = { false, false };
8982 // find min adjacent segment length after sewing
8983 double nextParam = 10., prevParam = 0;
8984 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8985 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8986 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8987 if ( i[ iBord ] > 0 )
8988 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8990 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8991 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8992 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8994 // choose to insert or to merge nodes
8995 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8996 if ( Abs( du ) <= minSegLen * 0.2 ) {
8999 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9000 const SMDS_MeshNode* n0 = *nIt[0];
9001 const SMDS_MeshNode* n1 = *nIt[1];
9002 nodeGroupsToMerge.back().push_back( n1 );
9003 nodeGroupsToMerge.back().push_back( n0 );
9004 // position of node of the border changes due to merge
9005 param[ 0 ][ i[0] ] += du;
9006 // move n1 for the sake of elem shape evaluation during insertion.
9007 // n1 will be removed by MergeNodes() anyway
9008 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9009 next[0] = next[1] = true;
9014 int intoBord = ( du < 0 ) ? 0 : 1;
9015 const SMDS_MeshElement* elem = *eIt[ intoBord ];
9016 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
9017 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
9018 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
9019 if ( intoBord == 1 ) {
9020 // move node of the border to be on a link of elem of the side
9021 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9022 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9023 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9024 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9025 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9027 insertMapIt = insertMap.find( elem );
9028 bool notFound = ( insertMapIt == insertMap.end() );
9029 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9031 // insert into another link of the same element:
9032 // 1. perform insertion into the other link of the elem
9033 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9034 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9035 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9036 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9037 // 2. perform insertion into the link of adjacent faces
9039 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9041 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9045 if (toCreatePolyedrs) {
9046 // perform insertion into the links of adjacent volumes
9047 UpdateVolumes(n12, n22, nodeList);
9049 // 3. find an element appeared on n1 and n2 after the insertion
9050 insertMap.erase( elem );
9051 elem = findAdjacentFace( n1, n2, 0 );
9053 if ( notFound || otherLink ) {
9054 // add element and nodes of the side into the insertMap
9055 insertMapIt = insertMap.insert
9056 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9057 (*insertMapIt).second.push_back( n1 );
9058 (*insertMapIt).second.push_back( n2 );
9060 // add node to be inserted into elem
9061 (*insertMapIt).second.push_back( nIns );
9062 next[ 1 - intoBord ] = true;
9065 // go to the next segment
9066 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9067 if ( next[ iBord ] ) {
9068 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9070 nPrev[ iBord ] = *nIt[ iBord ];
9071 nIt[ iBord ]++; i[ iBord ]++;
9075 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9077 // perform insertion of nodes into elements
9079 for (insertMapIt = insertMap.begin();
9080 insertMapIt != insertMap.end();
9083 const SMDS_MeshElement* elem = (*insertMapIt).first;
9084 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9085 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9086 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9088 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9090 if ( !theSideIsFreeBorder ) {
9091 // look for and insert nodes into the faces adjacent to elem
9093 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9095 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9100 if (toCreatePolyedrs) {
9101 // perform insertion into the links of adjacent volumes
9102 UpdateVolumes(n1, n2, nodeList);
9108 } // end: insert new nodes
9110 MergeNodes ( nodeGroupsToMerge );
9115 //=======================================================================
9116 //function : InsertNodesIntoLink
9117 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9118 // and theBetweenNode2 and split theElement
9119 //=======================================================================
9121 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9122 const SMDS_MeshNode* theBetweenNode1,
9123 const SMDS_MeshNode* theBetweenNode2,
9124 list<const SMDS_MeshNode*>& theNodesToInsert,
9125 const bool toCreatePoly)
9127 if ( theFace->GetType() != SMDSAbs_Face ) return;
9129 // find indices of 2 link nodes and of the rest nodes
9130 int iNode = 0, il1, il2, i3, i4;
9131 il1 = il2 = i3 = i4 = -1;
9132 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9133 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9135 if(theFace->IsQuadratic()) {
9136 const SMDS_VtkFace* F =
9137 dynamic_cast<const SMDS_VtkFace*>(theFace);
9138 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9139 // use special nodes iterator
9140 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9141 while( anIter->more() ) {
9142 const SMDS_MeshNode* n = cast2Node(anIter->next());
9143 if ( n == theBetweenNode1 )
9145 else if ( n == theBetweenNode2 )
9151 nodes[ iNode++ ] = n;
9155 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9156 while ( nodeIt->more() ) {
9157 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9158 if ( n == theBetweenNode1 )
9160 else if ( n == theBetweenNode2 )
9166 nodes[ iNode++ ] = n;
9169 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9172 // arrange link nodes to go one after another regarding the face orientation
9173 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9174 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9179 aNodesToInsert.reverse();
9181 // check that not link nodes of a quadrangles are in good order
9182 int nbFaceNodes = theFace->NbNodes();
9183 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9189 if (toCreatePoly || theFace->IsPoly()) {
9192 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9194 // add nodes of face up to first node of link
9197 if(theFace->IsQuadratic()) {
9198 const SMDS_VtkFace* F =
9199 dynamic_cast<const SMDS_VtkFace*>(theFace);
9200 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9201 // use special nodes iterator
9202 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9203 while( anIter->more() && !isFLN ) {
9204 const SMDS_MeshNode* n = cast2Node(anIter->next());
9205 poly_nodes[iNode++] = n;
9206 if (n == nodes[il1]) {
9210 // add nodes to insert
9211 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9212 for (; nIt != aNodesToInsert.end(); nIt++) {
9213 poly_nodes[iNode++] = *nIt;
9215 // add nodes of face starting from last node of link
9216 while ( anIter->more() ) {
9217 poly_nodes[iNode++] = cast2Node(anIter->next());
9221 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9222 while ( nodeIt->more() && !isFLN ) {
9223 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9224 poly_nodes[iNode++] = n;
9225 if (n == nodes[il1]) {
9229 // add nodes to insert
9230 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9231 for (; nIt != aNodesToInsert.end(); nIt++) {
9232 poly_nodes[iNode++] = *nIt;
9234 // add nodes of face starting from last node of link
9235 while ( nodeIt->more() ) {
9236 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9237 poly_nodes[iNode++] = n;
9241 // edit or replace the face
9242 SMESHDS_Mesh *aMesh = GetMeshDS();
9244 if (theFace->IsPoly()) {
9245 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9248 int aShapeId = FindShape( theFace );
9250 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9251 myLastCreatedElems.Append(newElem);
9252 if ( aShapeId && newElem )
9253 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9255 aMesh->RemoveElement(theFace);
9260 SMESHDS_Mesh *aMesh = GetMeshDS();
9261 if( !theFace->IsQuadratic() ) {
9263 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9264 int nbLinkNodes = 2 + aNodesToInsert.size();
9265 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9266 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9267 linkNodes[ 0 ] = nodes[ il1 ];
9268 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9269 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9270 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9271 linkNodes[ iNode++ ] = *nIt;
9273 // decide how to split a quadrangle: compare possible variants
9274 // and choose which of splits to be a quadrangle
9275 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9276 if ( nbFaceNodes == 3 ) {
9277 iBestQuad = nbSplits;
9280 else if ( nbFaceNodes == 4 ) {
9281 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9282 double aBestRate = DBL_MAX;
9283 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9285 double aBadRate = 0;
9286 // evaluate elements quality
9287 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9288 if ( iSplit == iQuad ) {
9289 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9293 aBadRate += getBadRate( &quad, aCrit );
9296 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9298 nodes[ iSplit < iQuad ? i4 : i3 ]);
9299 aBadRate += getBadRate( &tria, aCrit );
9303 if ( aBadRate < aBestRate ) {
9305 aBestRate = aBadRate;
9310 // create new elements
9311 int aShapeId = FindShape( theFace );
9314 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9315 SMDS_MeshElement* newElem = 0;
9316 if ( iSplit == iBestQuad )
9317 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9322 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9324 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9325 myLastCreatedElems.Append(newElem);
9326 if ( aShapeId && newElem )
9327 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9330 // change nodes of theFace
9331 const SMDS_MeshNode* newNodes[ 4 ];
9332 newNodes[ 0 ] = linkNodes[ i1 ];
9333 newNodes[ 1 ] = linkNodes[ i2 ];
9334 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9335 newNodes[ 3 ] = nodes[ i4 ];
9336 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9337 const SMDS_MeshElement* newElem = 0;
9338 if (iSplit == iBestQuad)
9339 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9341 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9342 myLastCreatedElems.Append(newElem);
9343 if ( aShapeId && newElem )
9344 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9345 } // end if(!theFace->IsQuadratic())
9346 else { // theFace is quadratic
9347 // we have to split theFace on simple triangles and one simple quadrangle
9349 int nbshift = tmp*2;
9350 // shift nodes in nodes[] by nbshift
9352 for(i=0; i<nbshift; i++) {
9353 const SMDS_MeshNode* n = nodes[0];
9354 for(j=0; j<nbFaceNodes-1; j++) {
9355 nodes[j] = nodes[j+1];
9357 nodes[nbFaceNodes-1] = n;
9359 il1 = il1 - nbshift;
9360 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9361 // n0 n1 n2 n0 n1 n2
9362 // +-----+-----+ +-----+-----+
9371 // create new elements
9372 int aShapeId = FindShape( theFace );
9375 if(nbFaceNodes==6) { // quadratic triangle
9376 SMDS_MeshElement* newElem =
9377 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9378 myLastCreatedElems.Append(newElem);
9379 if ( aShapeId && newElem )
9380 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9381 if(theFace->IsMediumNode(nodes[il1])) {
9382 // create quadrangle
9383 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9384 myLastCreatedElems.Append(newElem);
9385 if ( aShapeId && newElem )
9386 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9392 // create quadrangle
9393 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9394 myLastCreatedElems.Append(newElem);
9395 if ( aShapeId && newElem )
9396 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9402 else { // nbFaceNodes==8 - quadratic quadrangle
9403 SMDS_MeshElement* newElem =
9404 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9405 myLastCreatedElems.Append(newElem);
9406 if ( aShapeId && newElem )
9407 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9408 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9409 myLastCreatedElems.Append(newElem);
9410 if ( aShapeId && newElem )
9411 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9412 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9413 myLastCreatedElems.Append(newElem);
9414 if ( aShapeId && newElem )
9415 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9416 if(theFace->IsMediumNode(nodes[il1])) {
9417 // create quadrangle
9418 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9419 myLastCreatedElems.Append(newElem);
9420 if ( aShapeId && newElem )
9421 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9427 // create quadrangle
9428 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9429 myLastCreatedElems.Append(newElem);
9430 if ( aShapeId && newElem )
9431 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9437 // create needed triangles using n1,n2,n3 and inserted nodes
9438 int nbn = 2 + aNodesToInsert.size();
9439 //const SMDS_MeshNode* aNodes[nbn];
9440 vector<const SMDS_MeshNode*> aNodes(nbn);
9441 aNodes[0] = nodes[n1];
9442 aNodes[nbn-1] = nodes[n2];
9443 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9444 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9445 aNodes[iNode++] = *nIt;
9447 for(i=1; i<nbn; i++) {
9448 SMDS_MeshElement* newElem =
9449 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9450 myLastCreatedElems.Append(newElem);
9451 if ( aShapeId && newElem )
9452 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9456 aMesh->RemoveElement(theFace);
9459 //=======================================================================
9460 //function : UpdateVolumes
9462 //=======================================================================
9463 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9464 const SMDS_MeshNode* theBetweenNode2,
9465 list<const SMDS_MeshNode*>& theNodesToInsert)
9467 myLastCreatedElems.Clear();
9468 myLastCreatedNodes.Clear();
9470 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9471 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9472 const SMDS_MeshElement* elem = invElemIt->next();
9474 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9475 SMDS_VolumeTool aVolume (elem);
9476 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9479 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9480 int iface, nbFaces = aVolume.NbFaces();
9481 vector<const SMDS_MeshNode *> poly_nodes;
9482 vector<int> quantities (nbFaces);
9484 for (iface = 0; iface < nbFaces; iface++) {
9485 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9486 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9487 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9489 for (int inode = 0; inode < nbFaceNodes; inode++) {
9490 poly_nodes.push_back(faceNodes[inode]);
9492 if (nbInserted == 0) {
9493 if (faceNodes[inode] == theBetweenNode1) {
9494 if (faceNodes[inode + 1] == theBetweenNode2) {
9495 nbInserted = theNodesToInsert.size();
9497 // add nodes to insert
9498 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9499 for (; nIt != theNodesToInsert.end(); nIt++) {
9500 poly_nodes.push_back(*nIt);
9504 else if (faceNodes[inode] == theBetweenNode2) {
9505 if (faceNodes[inode + 1] == theBetweenNode1) {
9506 nbInserted = theNodesToInsert.size();
9508 // add nodes to insert in reversed order
9509 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9511 for (; nIt != theNodesToInsert.begin(); nIt--) {
9512 poly_nodes.push_back(*nIt);
9514 poly_nodes.push_back(*nIt);
9521 quantities[iface] = nbFaceNodes + nbInserted;
9524 // Replace or update the volume
9525 SMESHDS_Mesh *aMesh = GetMeshDS();
9527 if (elem->IsPoly()) {
9528 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9532 int aShapeId = FindShape( elem );
9534 SMDS_MeshElement* newElem =
9535 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9536 myLastCreatedElems.Append(newElem);
9537 if (aShapeId && newElem)
9538 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9540 aMesh->RemoveElement(elem);
9547 //================================================================================
9549 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9551 //================================================================================
9553 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9554 vector<const SMDS_MeshNode *> & nodes,
9555 vector<int> & nbNodeInFaces )
9558 nbNodeInFaces.clear();
9559 SMDS_VolumeTool vTool ( elem );
9560 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9562 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9563 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9564 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9569 //=======================================================================
9571 * \brief Convert elements contained in a submesh to quadratic
9572 * \return int - nb of checked elements
9574 //=======================================================================
9576 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9577 SMESH_MesherHelper& theHelper,
9578 const bool theForce3d)
9581 if( !theSm ) return nbElem;
9583 vector<int> nbNodeInFaces;
9584 vector<const SMDS_MeshNode *> nodes;
9585 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9586 while(ElemItr->more())
9589 const SMDS_MeshElement* elem = ElemItr->next();
9590 if( !elem || elem->IsQuadratic() ) continue;
9592 // get elem data needed to re-create it
9594 const int id = elem->GetID();
9595 const int nbNodes = elem->NbNodes();
9596 const SMDSAbs_ElementType aType = elem->GetType();
9597 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9598 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9599 if ( aGeomType == SMDSEntity_Polyhedra )
9600 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9601 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9602 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9604 // remove a linear element
9605 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9607 const SMDS_MeshElement* NewElem = 0;
9613 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9621 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9624 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9627 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9632 case SMDSAbs_Volume :
9636 case SMDSEntity_Tetra:
9637 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9639 case SMDSEntity_Pyramid:
9640 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9642 case SMDSEntity_Penta:
9643 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9645 case SMDSEntity_Hexa:
9646 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9647 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9649 case SMDSEntity_Hexagonal_Prism:
9651 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9658 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9660 theSm->AddElement( NewElem );
9665 //=======================================================================
9666 //function : ConvertToQuadratic
9668 //=======================================================================
9670 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9672 SMESHDS_Mesh* meshDS = GetMeshDS();
9674 SMESH_MesherHelper aHelper(*myMesh);
9675 aHelper.SetIsQuadratic( true );
9677 int nbCheckedElems = 0;
9678 if ( myMesh->HasShapeToMesh() )
9680 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9682 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9683 while ( smIt->more() ) {
9684 SMESH_subMesh* sm = smIt->next();
9685 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9686 aHelper.SetSubShape( sm->GetSubShape() );
9687 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9692 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9693 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9695 SMESHDS_SubMesh *smDS = 0;
9696 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9697 while(aEdgeItr->more())
9699 const SMDS_MeshEdge* edge = aEdgeItr->next();
9700 if(edge && !edge->IsQuadratic())
9702 int id = edge->GetID();
9703 //MESSAGE("edge->GetID() " << id);
9704 const SMDS_MeshNode* n1 = edge->GetNode(0);
9705 const SMDS_MeshNode* n2 = edge->GetNode(1);
9707 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9709 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9710 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9713 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9714 while(aFaceItr->more())
9716 const SMDS_MeshFace* face = aFaceItr->next();
9717 if(!face || face->IsQuadratic() ) continue;
9719 const int id = face->GetID();
9720 const SMDSAbs_EntityType type = face->GetEntityType();
9721 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9723 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9725 SMDS_MeshFace * NewFace = 0;
9728 case SMDSEntity_Triangle:
9729 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9731 case SMDSEntity_Quadrangle:
9732 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9735 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9737 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9739 vector<int> nbNodeInFaces;
9740 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9741 while(aVolumeItr->more())
9743 const SMDS_MeshVolume* volume = aVolumeItr->next();
9744 if(!volume || volume->IsQuadratic() ) continue;
9746 const int id = volume->GetID();
9747 const SMDSAbs_EntityType type = volume->GetEntityType();
9748 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9749 if ( type == SMDSEntity_Polyhedra )
9750 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9751 else if ( type == SMDSEntity_Hexagonal_Prism )
9752 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9754 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9756 SMDS_MeshVolume * NewVolume = 0;
9759 case SMDSEntity_Tetra:
9760 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9762 case SMDSEntity_Hexa:
9763 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9764 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9766 case SMDSEntity_Pyramid:
9767 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9768 nodes[3], nodes[4], id, theForce3d);
9770 case SMDSEntity_Penta:
9771 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9772 nodes[3], nodes[4], nodes[5], id, theForce3d);
9774 case SMDSEntity_Hexagonal_Prism:
9776 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9778 ReplaceElemInGroups(volume, NewVolume, meshDS);
9783 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9784 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9785 aHelper.FixQuadraticElements(myError);
9789 //================================================================================
9791 * \brief Makes given elements quadratic
9792 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9793 * \param theElements - elements to make quadratic
9795 //================================================================================
9797 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9798 TIDSortedElemSet& theElements)
9800 if ( theElements.empty() ) return;
9802 // we believe that all theElements are of the same type
9803 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9805 // get all nodes shared by theElements
9806 TIDSortedNodeSet allNodes;
9807 TIDSortedElemSet::iterator eIt = theElements.begin();
9808 for ( ; eIt != theElements.end(); ++eIt )
9809 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9811 // complete theElements with elements of lower dim whose all nodes are in allNodes
9813 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9814 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9815 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9816 for ( ; nIt != allNodes.end(); ++nIt )
9818 const SMDS_MeshNode* n = *nIt;
9819 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9820 while ( invIt->more() )
9822 const SMDS_MeshElement* e = invIt->next();
9823 if ( e->IsQuadratic() )
9825 quadAdjacentElems[ e->GetType() ].insert( e );
9828 if ( e->GetType() >= elemType )
9830 continue; // same type of more complex linear element
9833 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9834 continue; // e is already checked
9838 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9839 while ( nodeIt->more() && allIn )
9840 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9842 theElements.insert(e );
9846 SMESH_MesherHelper helper(*myMesh);
9847 helper.SetIsQuadratic( true );
9849 // add links of quadratic adjacent elements to the helper
9851 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9852 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9853 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9855 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9857 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9858 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9859 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9861 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9863 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9864 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9865 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9867 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9870 // make quadratic elements instead of linear ones
9872 SMESHDS_Mesh* meshDS = GetMeshDS();
9873 SMESHDS_SubMesh* smDS = 0;
9874 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9876 const SMDS_MeshElement* elem = *eIt;
9877 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9880 const int id = elem->GetID();
9881 const SMDSAbs_ElementType type = elem->GetType();
9882 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9884 if ( !smDS || !smDS->Contains( elem ))
9885 smDS = meshDS->MeshElements( elem->getshapeId() );
9886 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9888 SMDS_MeshElement * newElem = 0;
9889 switch( nodes.size() )
9891 case 4: // cases for most frequently used element types go first (for optimization)
9892 if ( type == SMDSAbs_Volume )
9893 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9895 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9898 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9899 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9902 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9905 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9908 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9909 nodes[4], id, theForce3d);
9912 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9913 nodes[4], nodes[5], id, theForce3d);
9917 ReplaceElemInGroups( elem, newElem, meshDS);
9918 if( newElem && smDS )
9919 smDS->AddElement( newElem );
9922 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9923 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9924 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9925 helper.FixQuadraticElements( myError );
9929 //=======================================================================
9931 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9932 * \return int - nb of checked elements
9934 //=======================================================================
9936 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9937 SMDS_ElemIteratorPtr theItr,
9938 const int theShapeID)
9941 SMESHDS_Mesh* meshDS = GetMeshDS();
9943 while( theItr->more() )
9945 const SMDS_MeshElement* elem = theItr->next();
9947 if( elem && elem->IsQuadratic())
9949 int id = elem->GetID();
9950 int nbCornerNodes = elem->NbCornerNodes();
9951 SMDSAbs_ElementType aType = elem->GetType();
9953 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9955 //remove a quadratic element
9956 if ( !theSm || !theSm->Contains( elem ))
9957 theSm = meshDS->MeshElements( elem->getshapeId() );
9958 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9960 // remove medium nodes
9961 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9962 if ( nodes[i]->NbInverseElements() == 0 )
9963 meshDS->RemoveFreeNode( nodes[i], theSm );
9965 // add a linear element
9966 nodes.resize( nbCornerNodes );
9967 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9968 ReplaceElemInGroups(elem, newElem, meshDS);
9969 if( theSm && newElem )
9970 theSm->AddElement( newElem );
9976 //=======================================================================
9977 //function : ConvertFromQuadratic
9979 //=======================================================================
9981 bool SMESH_MeshEditor::ConvertFromQuadratic()
9983 int nbCheckedElems = 0;
9984 if ( myMesh->HasShapeToMesh() )
9986 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9988 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9989 while ( smIt->more() ) {
9990 SMESH_subMesh* sm = smIt->next();
9991 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9992 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9998 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9999 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10001 SMESHDS_SubMesh *aSM = 0;
10002 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10010 //================================================================================
10012 * \brief Return true if all medium nodes of the element are in the node set
10014 //================================================================================
10016 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10018 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10019 if ( !nodeSet.count( elem->GetNode(i) ))
10025 //================================================================================
10027 * \brief Makes given elements linear
10029 //================================================================================
10031 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10033 if ( theElements.empty() ) return;
10035 // collect IDs of medium nodes of theElements; some of these nodes will be removed
10036 set<int> mediumNodeIDs;
10037 TIDSortedElemSet::iterator eIt = theElements.begin();
10038 for ( ; eIt != theElements.end(); ++eIt )
10040 const SMDS_MeshElement* e = *eIt;
10041 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10042 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10045 // replace given elements by linear ones
10046 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10047 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10048 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10050 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10051 // except those elements sharing medium nodes of quadratic element whose medium nodes
10052 // are not all in mediumNodeIDs
10054 // get remaining medium nodes
10055 TIDSortedNodeSet mediumNodes;
10056 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10057 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10058 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10059 mediumNodes.insert( mediumNodes.end(), n );
10061 // find more quadratic elements to convert
10062 TIDSortedElemSet moreElemsToConvert;
10063 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10064 for ( ; nIt != mediumNodes.end(); ++nIt )
10066 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10067 while ( invIt->more() )
10069 const SMDS_MeshElement* e = invIt->next();
10070 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10072 // find a more complex element including e and
10073 // whose medium nodes are not in mediumNodes
10074 bool complexFound = false;
10075 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10077 SMDS_ElemIteratorPtr invIt2 =
10078 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10079 while ( invIt2->more() )
10081 const SMDS_MeshElement* eComplex = invIt2->next();
10082 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10084 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10085 if ( nbCommonNodes == e->NbNodes())
10087 complexFound = true;
10088 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10094 if ( !complexFound )
10095 moreElemsToConvert.insert( e );
10099 elemIt = SMDS_ElemIteratorPtr
10100 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10101 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10104 //=======================================================================
10105 //function : SewSideElements
10107 //=======================================================================
10109 SMESH_MeshEditor::Sew_Error
10110 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10111 TIDSortedElemSet& theSide2,
10112 const SMDS_MeshNode* theFirstNode1,
10113 const SMDS_MeshNode* theFirstNode2,
10114 const SMDS_MeshNode* theSecondNode1,
10115 const SMDS_MeshNode* theSecondNode2)
10117 myLastCreatedElems.Clear();
10118 myLastCreatedNodes.Clear();
10120 MESSAGE ("::::SewSideElements()");
10121 if ( theSide1.size() != theSide2.size() )
10122 return SEW_DIFF_NB_OF_ELEMENTS;
10124 Sew_Error aResult = SEW_OK;
10126 // 1. Build set of faces representing each side
10127 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10128 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10130 // =======================================================================
10131 // 1. Build set of faces representing each side:
10132 // =======================================================================
10133 // a. build set of nodes belonging to faces
10134 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10135 // c. create temporary faces representing side of volumes if correspondent
10136 // face does not exist
10138 SMESHDS_Mesh* aMesh = GetMeshDS();
10139 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10140 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10141 TIDSortedElemSet faceSet1, faceSet2;
10142 set<const SMDS_MeshElement*> volSet1, volSet2;
10143 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10144 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10145 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10146 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10147 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10148 int iSide, iFace, iNode;
10150 list<const SMDS_MeshElement* > tempFaceList;
10151 for ( iSide = 0; iSide < 2; iSide++ ) {
10152 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10153 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10154 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10155 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10156 set<const SMDS_MeshElement*>::iterator vIt;
10157 TIDSortedElemSet::iterator eIt;
10158 set<const SMDS_MeshNode*>::iterator nIt;
10160 // check that given nodes belong to given elements
10161 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10162 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10163 int firstIndex = -1, secondIndex = -1;
10164 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10165 const SMDS_MeshElement* elem = *eIt;
10166 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10167 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10168 if ( firstIndex > -1 && secondIndex > -1 ) break;
10170 if ( firstIndex < 0 || secondIndex < 0 ) {
10171 // we can simply return until temporary faces created
10172 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10175 // -----------------------------------------------------------
10176 // 1a. Collect nodes of existing faces
10177 // and build set of face nodes in order to detect missing
10178 // faces corresponding to sides of volumes
10179 // -----------------------------------------------------------
10181 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10183 // loop on the given element of a side
10184 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10185 //const SMDS_MeshElement* elem = *eIt;
10186 const SMDS_MeshElement* elem = *eIt;
10187 if ( elem->GetType() == SMDSAbs_Face ) {
10188 faceSet->insert( elem );
10189 set <const SMDS_MeshNode*> faceNodeSet;
10190 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10191 while ( nodeIt->more() ) {
10192 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10193 nodeSet->insert( n );
10194 faceNodeSet.insert( n );
10196 setOfFaceNodeSet.insert( faceNodeSet );
10198 else if ( elem->GetType() == SMDSAbs_Volume )
10199 volSet->insert( elem );
10201 // ------------------------------------------------------------------------------
10202 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10203 // ------------------------------------------------------------------------------
10205 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10206 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10207 while ( fIt->more() ) { // loop on faces sharing a node
10208 const SMDS_MeshElement* f = fIt->next();
10209 if ( faceSet->find( f ) == faceSet->end() ) {
10210 // check if all nodes are in nodeSet and
10211 // complete setOfFaceNodeSet if they are
10212 set <const SMDS_MeshNode*> faceNodeSet;
10213 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10214 bool allInSet = true;
10215 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10216 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10217 if ( nodeSet->find( n ) == nodeSet->end() )
10220 faceNodeSet.insert( n );
10223 faceSet->insert( f );
10224 setOfFaceNodeSet.insert( faceNodeSet );
10230 // -------------------------------------------------------------------------
10231 // 1c. Create temporary faces representing sides of volumes if correspondent
10232 // face does not exist
10233 // -------------------------------------------------------------------------
10235 if ( !volSet->empty() ) {
10236 //int nodeSetSize = nodeSet->size();
10238 // loop on given volumes
10239 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10240 SMDS_VolumeTool vol (*vIt);
10241 // loop on volume faces: find free faces
10242 // --------------------------------------
10243 list<const SMDS_MeshElement* > freeFaceList;
10244 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10245 if ( !vol.IsFreeFace( iFace ))
10247 // check if there is already a face with same nodes in a face set
10248 const SMDS_MeshElement* aFreeFace = 0;
10249 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10250 int nbNodes = vol.NbFaceNodes( iFace );
10251 set <const SMDS_MeshNode*> faceNodeSet;
10252 vol.GetFaceNodes( iFace, faceNodeSet );
10253 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10255 // no such a face is given but it still can exist, check it
10256 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10257 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10259 if ( !aFreeFace ) {
10260 // create a temporary face
10261 if ( nbNodes == 3 ) {
10262 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10263 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10265 else if ( nbNodes == 4 ) {
10266 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10267 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10270 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10271 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10272 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10275 tempFaceList.push_back( aFreeFace );
10279 freeFaceList.push_back( aFreeFace );
10281 } // loop on faces of a volume
10283 // choose one of several free faces of a volume
10284 // --------------------------------------------
10285 if ( freeFaceList.size() > 1 ) {
10286 // choose a face having max nb of nodes shared by other elems of a side
10287 int maxNbNodes = -1;
10288 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10289 while ( fIt != freeFaceList.end() ) { // loop on free faces
10290 int nbSharedNodes = 0;
10291 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10292 while ( nodeIt->more() ) { // loop on free face nodes
10293 const SMDS_MeshNode* n =
10294 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10295 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10296 while ( invElemIt->more() ) {
10297 const SMDS_MeshElement* e = invElemIt->next();
10298 nbSharedNodes += faceSet->count( e );
10299 nbSharedNodes += elemSet->count( e );
10302 if ( nbSharedNodes > maxNbNodes ) {
10303 maxNbNodes = nbSharedNodes;
10304 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10306 else if ( nbSharedNodes == maxNbNodes ) {
10310 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10313 if ( freeFaceList.size() > 1 )
10315 // could not choose one face, use another way
10316 // choose a face most close to the bary center of the opposite side
10317 gp_XYZ aBC( 0., 0., 0. );
10318 set <const SMDS_MeshNode*> addedNodes;
10319 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10320 eIt = elemSet2->begin();
10321 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10322 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10323 while ( nodeIt->more() ) { // loop on free face nodes
10324 const SMDS_MeshNode* n =
10325 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10326 if ( addedNodes.insert( n ).second )
10327 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10330 aBC /= addedNodes.size();
10331 double minDist = DBL_MAX;
10332 fIt = freeFaceList.begin();
10333 while ( fIt != freeFaceList.end() ) { // loop on free faces
10335 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10336 while ( nodeIt->more() ) { // loop on free face nodes
10337 const SMDS_MeshNode* n =
10338 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10339 gp_XYZ p( n->X(),n->Y(),n->Z() );
10340 dist += ( aBC - p ).SquareModulus();
10342 if ( dist < minDist ) {
10344 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10347 fIt = freeFaceList.erase( fIt++ );
10350 } // choose one of several free faces of a volume
10352 if ( freeFaceList.size() == 1 ) {
10353 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10354 faceSet->insert( aFreeFace );
10355 // complete a node set with nodes of a found free face
10356 // for ( iNode = 0; iNode < ; iNode++ )
10357 // nodeSet->insert( fNodes[ iNode ] );
10360 } // loop on volumes of a side
10362 // // complete a set of faces if new nodes in a nodeSet appeared
10363 // // ----------------------------------------------------------
10364 // if ( nodeSetSize != nodeSet->size() ) {
10365 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10366 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10367 // while ( fIt->more() ) { // loop on faces sharing a node
10368 // const SMDS_MeshElement* f = fIt->next();
10369 // if ( faceSet->find( f ) == faceSet->end() ) {
10370 // // check if all nodes are in nodeSet and
10371 // // complete setOfFaceNodeSet if they are
10372 // set <const SMDS_MeshNode*> faceNodeSet;
10373 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10374 // bool allInSet = true;
10375 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10376 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10377 // if ( nodeSet->find( n ) == nodeSet->end() )
10378 // allInSet = false;
10380 // faceNodeSet.insert( n );
10382 // if ( allInSet ) {
10383 // faceSet->insert( f );
10384 // setOfFaceNodeSet.insert( faceNodeSet );
10390 } // Create temporary faces, if there are volumes given
10393 if ( faceSet1.size() != faceSet2.size() ) {
10394 // delete temporary faces: they are in reverseElements of actual nodes
10395 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10396 // while ( tmpFaceIt->more() )
10397 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10398 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10399 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10400 // aMesh->RemoveElement(*tmpFaceIt);
10401 MESSAGE("Diff nb of faces");
10402 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10405 // ============================================================
10406 // 2. Find nodes to merge:
10407 // bind a node to remove to a node to put instead
10408 // ============================================================
10410 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10411 if ( theFirstNode1 != theFirstNode2 )
10412 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10413 if ( theSecondNode1 != theSecondNode2 )
10414 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10416 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10417 set< long > linkIdSet; // links to process
10418 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10420 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10421 list< NLink > linkList[2];
10422 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10423 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10424 // loop on links in linkList; find faces by links and append links
10425 // of the found faces to linkList
10426 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10427 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10429 NLink link[] = { *linkIt[0], *linkIt[1] };
10430 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10431 if ( !linkIdSet.count( linkID ) )
10434 // by links, find faces in the face sets,
10435 // and find indices of link nodes in the found faces;
10436 // in a face set, there is only one or no face sharing a link
10437 // ---------------------------------------------------------------
10439 const SMDS_MeshElement* face[] = { 0, 0 };
10440 vector<const SMDS_MeshNode*> fnodes[2];
10441 int iLinkNode[2][2];
10442 TIDSortedElemSet avoidSet;
10443 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10444 const SMDS_MeshNode* n1 = link[iSide].first;
10445 const SMDS_MeshNode* n2 = link[iSide].second;
10446 //cout << "Side " << iSide << " ";
10447 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10448 // find a face by two link nodes
10449 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10450 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10451 if ( face[ iSide ])
10453 //cout << " F " << face[ iSide]->GetID() <<endl;
10454 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10455 // put face nodes to fnodes
10456 if ( face[ iSide ]->IsQuadratic() )
10458 // use interlaced nodes iterator
10459 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10460 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10461 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10462 while ( nIter->more() )
10463 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10467 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10468 face[ iSide ]->end_nodes() );
10470 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10474 // check similarity of elements of the sides
10475 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10476 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10477 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10478 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10481 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10483 break; // do not return because it's necessary to remove tmp faces
10486 // set nodes to merge
10487 // -------------------
10489 if ( face[0] && face[1] ) {
10490 const int nbNodes = face[0]->NbNodes();
10491 if ( nbNodes != face[1]->NbNodes() ) {
10492 MESSAGE("Diff nb of face nodes");
10493 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10494 break; // do not return because it s necessary to remove tmp faces
10496 bool reverse[] = { false, false }; // order of nodes in the link
10497 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10498 // analyse link orientation in faces
10499 int i1 = iLinkNode[ iSide ][ 0 ];
10500 int i2 = iLinkNode[ iSide ][ 1 ];
10501 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10503 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10504 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10505 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10507 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10508 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10511 // add other links of the faces to linkList
10512 // -----------------------------------------
10514 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10515 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10516 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10517 if ( !iter_isnew.second ) { // already in a set: no need to process
10518 linkIdSet.erase( iter_isnew.first );
10520 else // new in set == encountered for the first time: add
10522 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10523 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10524 linkList[0].push_back ( NLink( n1, n2 ));
10525 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10530 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10533 } // loop on link lists
10535 if ( aResult == SEW_OK &&
10536 ( //linkIt[0] != linkList[0].end() ||
10537 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10538 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10539 " " << (faceSetPtr[1]->empty()));
10540 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10543 // ====================================================================
10544 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10545 // ====================================================================
10547 // delete temporary faces
10548 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10549 // while ( tmpFaceIt->more() )
10550 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10551 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10552 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10553 aMesh->RemoveElement(*tmpFaceIt);
10555 if ( aResult != SEW_OK)
10558 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10559 // loop on nodes replacement map
10560 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10561 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10562 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10563 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10564 nodeIDsToRemove.push_back( nToRemove->GetID() );
10565 // loop on elements sharing nToRemove
10566 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10567 while ( invElemIt->more() ) {
10568 const SMDS_MeshElement* e = invElemIt->next();
10569 // get a new suite of nodes: make replacement
10570 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10571 vector< const SMDS_MeshNode*> nodes( nbNodes );
10572 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10573 while ( nIt->more() ) {
10574 const SMDS_MeshNode* n =
10575 static_cast<const SMDS_MeshNode*>( nIt->next() );
10576 nnIt = nReplaceMap.find( n );
10577 if ( nnIt != nReplaceMap.end() ) {
10579 n = (*nnIt).second;
10583 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10584 // elemIDsToRemove.push_back( e->GetID() );
10588 SMDSAbs_ElementType etyp = e->GetType();
10589 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10592 myLastCreatedElems.Append(newElem);
10593 AddToSameGroups(newElem, e, aMesh);
10594 int aShapeId = e->getshapeId();
10597 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10600 aMesh->RemoveElement(e);
10605 Remove( nodeIDsToRemove, true );
10610 //================================================================================
10612 * \brief Find corresponding nodes in two sets of faces
10613 * \param theSide1 - first face set
10614 * \param theSide2 - second first face
10615 * \param theFirstNode1 - a boundary node of set 1
10616 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10617 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10618 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10619 * \param nReplaceMap - output map of corresponding nodes
10620 * \return bool - is a success or not
10622 //================================================================================
10625 //#define DEBUG_MATCHING_NODES
10628 SMESH_MeshEditor::Sew_Error
10629 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10630 set<const SMDS_MeshElement*>& theSide2,
10631 const SMDS_MeshNode* theFirstNode1,
10632 const SMDS_MeshNode* theFirstNode2,
10633 const SMDS_MeshNode* theSecondNode1,
10634 const SMDS_MeshNode* theSecondNode2,
10635 TNodeNodeMap & nReplaceMap)
10637 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10639 nReplaceMap.clear();
10640 if ( theFirstNode1 != theFirstNode2 )
10641 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10642 if ( theSecondNode1 != theSecondNode2 )
10643 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10645 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10646 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10648 list< NLink > linkList[2];
10649 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10650 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10652 // loop on links in linkList; find faces by links and append links
10653 // of the found faces to linkList
10654 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10655 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10656 NLink link[] = { *linkIt[0], *linkIt[1] };
10657 if ( linkSet.find( link[0] ) == linkSet.end() )
10660 // by links, find faces in the face sets,
10661 // and find indices of link nodes in the found faces;
10662 // in a face set, there is only one or no face sharing a link
10663 // ---------------------------------------------------------------
10665 const SMDS_MeshElement* face[] = { 0, 0 };
10666 list<const SMDS_MeshNode*> notLinkNodes[2];
10667 //bool reverse[] = { false, false }; // order of notLinkNodes
10669 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10671 const SMDS_MeshNode* n1 = link[iSide].first;
10672 const SMDS_MeshNode* n2 = link[iSide].second;
10673 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10674 set< const SMDS_MeshElement* > facesOfNode1;
10675 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10677 // during a loop of the first node, we find all faces around n1,
10678 // during a loop of the second node, we find one face sharing both n1 and n2
10679 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10680 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10681 while ( fIt->more() ) { // loop on faces sharing a node
10682 const SMDS_MeshElement* f = fIt->next();
10683 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10684 ! facesOfNode1.insert( f ).second ) // f encounters twice
10686 if ( face[ iSide ] ) {
10687 MESSAGE( "2 faces per link " );
10688 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10691 faceSet->erase( f );
10693 // get not link nodes
10694 int nbN = f->NbNodes();
10695 if ( f->IsQuadratic() )
10697 nbNodes[ iSide ] = nbN;
10698 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10699 int i1 = f->GetNodeIndex( n1 );
10700 int i2 = f->GetNodeIndex( n2 );
10701 int iEnd = nbN, iBeg = -1, iDelta = 1;
10702 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10704 std::swap( iEnd, iBeg ); iDelta = -1;
10709 if ( i == iEnd ) i = iBeg + iDelta;
10710 if ( i == i1 ) break;
10711 nodes.push_back ( f->GetNode( i ) );
10717 // check similarity of elements of the sides
10718 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10719 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10720 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10721 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10724 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10728 // set nodes to merge
10729 // -------------------
10731 if ( face[0] && face[1] ) {
10732 if ( nbNodes[0] != nbNodes[1] ) {
10733 MESSAGE("Diff nb of face nodes");
10734 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10736 #ifdef DEBUG_MATCHING_NODES
10737 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10738 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10739 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10741 int nbN = nbNodes[0];
10743 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10744 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10745 for ( int i = 0 ; i < nbN - 2; ++i ) {
10746 #ifdef DEBUG_MATCHING_NODES
10747 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10749 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10753 // add other links of the face 1 to linkList
10754 // -----------------------------------------
10756 const SMDS_MeshElement* f0 = face[0];
10757 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10758 for ( int i = 0; i < nbN; i++ )
10760 const SMDS_MeshNode* n2 = f0->GetNode( i );
10761 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10762 linkSet.insert( SMESH_TLink( n1, n2 ));
10763 if ( !iter_isnew.second ) { // already in a set: no need to process
10764 linkSet.erase( iter_isnew.first );
10766 else // new in set == encountered for the first time: add
10768 #ifdef DEBUG_MATCHING_NODES
10769 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10770 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10772 linkList[0].push_back ( NLink( n1, n2 ));
10773 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10778 } // loop on link lists
10783 //================================================================================
10785 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10786 \param theElems - the list of elements (edges or faces) to be replicated
10787 The nodes for duplication could be found from these elements
10788 \param theNodesNot - list of nodes to NOT replicate
10789 \param theAffectedElems - the list of elements (cells and edges) to which the
10790 replicated nodes should be associated to.
10791 \return TRUE if operation has been completed successfully, FALSE otherwise
10793 //================================================================================
10795 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10796 const TIDSortedElemSet& theNodesNot,
10797 const TIDSortedElemSet& theAffectedElems )
10799 myLastCreatedElems.Clear();
10800 myLastCreatedNodes.Clear();
10802 if ( theElems.size() == 0 )
10805 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10810 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10811 // duplicate elements and nodes
10812 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10813 // replce nodes by duplications
10814 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10818 //================================================================================
10820 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10821 \param theMeshDS - mesh instance
10822 \param theElems - the elements replicated or modified (nodes should be changed)
10823 \param theNodesNot - nodes to NOT replicate
10824 \param theNodeNodeMap - relation of old node to new created node
10825 \param theIsDoubleElem - flag os to replicate element or modify
10826 \return TRUE if operation has been completed successfully, FALSE otherwise
10828 //================================================================================
10830 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10831 const TIDSortedElemSet& theElems,
10832 const TIDSortedElemSet& theNodesNot,
10833 std::map< const SMDS_MeshNode*,
10834 const SMDS_MeshNode* >& theNodeNodeMap,
10835 const bool theIsDoubleElem )
10837 MESSAGE("doubleNodes");
10838 // iterate on through element and duplicate them (by nodes duplication)
10840 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10841 for ( ; elemItr != theElems.end(); ++elemItr )
10843 const SMDS_MeshElement* anElem = *elemItr;
10847 bool isDuplicate = false;
10848 // duplicate nodes to duplicate element
10849 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10850 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10852 while ( anIter->more() )
10855 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10856 SMDS_MeshNode* aNewNode = aCurrNode;
10857 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10858 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10859 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10862 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10863 theNodeNodeMap[ aCurrNode ] = aNewNode;
10864 myLastCreatedNodes.Append( aNewNode );
10866 isDuplicate |= (aCurrNode != aNewNode);
10867 newNodes[ ind++ ] = aNewNode;
10869 if ( !isDuplicate )
10872 if ( theIsDoubleElem )
10873 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10876 MESSAGE("ChangeElementNodes");
10877 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10884 //================================================================================
10886 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10887 \param theNodes - identifiers of nodes to be doubled
10888 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10889 nodes. If list of element identifiers is empty then nodes are doubled but
10890 they not assigned to elements
10891 \return TRUE if operation has been completed successfully, FALSE otherwise
10893 //================================================================================
10895 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10896 const std::list< int >& theListOfModifiedElems )
10898 MESSAGE("DoubleNodes");
10899 myLastCreatedElems.Clear();
10900 myLastCreatedNodes.Clear();
10902 if ( theListOfNodes.size() == 0 )
10905 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10909 // iterate through nodes and duplicate them
10911 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10913 std::list< int >::const_iterator aNodeIter;
10914 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10916 int aCurr = *aNodeIter;
10917 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10923 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10926 anOldNodeToNewNode[ aNode ] = aNewNode;
10927 myLastCreatedNodes.Append( aNewNode );
10931 // Create map of new nodes for modified elements
10933 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10935 std::list< int >::const_iterator anElemIter;
10936 for ( anElemIter = theListOfModifiedElems.begin();
10937 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10939 int aCurr = *anElemIter;
10940 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10944 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10946 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10948 while ( anIter->more() )
10950 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10951 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10953 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10954 aNodeArr[ ind++ ] = aNewNode;
10957 aNodeArr[ ind++ ] = aCurrNode;
10959 anElemToNodes[ anElem ] = aNodeArr;
10962 // Change nodes of elements
10964 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10965 anElemToNodesIter = anElemToNodes.begin();
10966 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10968 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10969 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10972 MESSAGE("ChangeElementNodes");
10973 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10982 //================================================================================
10984 \brief Check if element located inside shape
10985 \return TRUE if IN or ON shape, FALSE otherwise
10987 //================================================================================
10989 template<class Classifier>
10990 bool isInside(const SMDS_MeshElement* theElem,
10991 Classifier& theClassifier,
10992 const double theTol)
10994 gp_XYZ centerXYZ (0, 0, 0);
10995 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10996 while (aNodeItr->more())
10997 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10999 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11000 theClassifier.Perform(aPnt, theTol);
11001 TopAbs_State aState = theClassifier.State();
11002 return (aState == TopAbs_IN || aState == TopAbs_ON );
11005 //================================================================================
11007 * \brief Classifier of the 3D point on the TopoDS_Face
11008 * with interaface suitable for isInside()
11010 //================================================================================
11012 struct _FaceClassifier
11014 Extrema_ExtPS _extremum;
11015 BRepAdaptor_Surface _surface;
11016 TopAbs_State _state;
11018 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11020 _extremum.Initialize( _surface,
11021 _surface.FirstUParameter(), _surface.LastUParameter(),
11022 _surface.FirstVParameter(), _surface.LastVParameter(),
11023 _surface.Tolerance(), _surface.Tolerance() );
11025 void Perform(const gp_Pnt& aPnt, double theTol)
11027 _state = TopAbs_OUT;
11028 _extremum.Perform(aPnt);
11029 if ( _extremum.IsDone() )
11030 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11031 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11032 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11034 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11037 TopAbs_State State() const
11044 //================================================================================
11046 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11047 This method is the first step of DoubleNodeElemGroupsInRegion.
11048 \param theElems - list of groups of elements (edges or faces) to be replicated
11049 \param theNodesNot - list of groups of nodes not to replicated
11050 \param theShape - shape to detect affected elements (element which geometric center
11051 located on or inside shape).
11052 The replicated nodes should be associated to affected elements.
11053 \return groups of affected elements
11054 \sa DoubleNodeElemGroupsInRegion()
11056 //================================================================================
11058 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11059 const TIDSortedElemSet& theNodesNot,
11060 const TopoDS_Shape& theShape,
11061 TIDSortedElemSet& theAffectedElems)
11063 if ( theShape.IsNull() )
11066 const double aTol = Precision::Confusion();
11067 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11068 auto_ptr<_FaceClassifier> aFaceClassifier;
11069 if ( theShape.ShapeType() == TopAbs_SOLID )
11071 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11072 bsc3d->PerformInfinitePoint(aTol);
11074 else if (theShape.ShapeType() == TopAbs_FACE )
11076 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11079 // iterates on indicated elements and get elements by back references from their nodes
11080 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11081 for ( ; elemItr != theElems.end(); ++elemItr )
11083 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11087 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11088 while ( nodeItr->more() )
11090 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11091 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11093 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11094 while ( backElemItr->more() )
11096 const SMDS_MeshElement* curElem = backElemItr->next();
11097 if ( curElem && theElems.find(curElem) == theElems.end() &&
11099 isInside( curElem, *bsc3d, aTol ) :
11100 isInside( curElem, *aFaceClassifier, aTol )))
11101 theAffectedElems.insert( curElem );
11108 //================================================================================
11110 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11111 \param theElems - group of of elements (edges or faces) to be replicated
11112 \param theNodesNot - group of nodes not to replicate
11113 \param theShape - shape to detect affected elements (element which geometric center
11114 located on or inside shape).
11115 The replicated nodes should be associated to affected elements.
11116 \return TRUE if operation has been completed successfully, FALSE otherwise
11118 //================================================================================
11120 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11121 const TIDSortedElemSet& theNodesNot,
11122 const TopoDS_Shape& theShape )
11124 if ( theShape.IsNull() )
11127 const double aTol = Precision::Confusion();
11128 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11129 auto_ptr<_FaceClassifier> aFaceClassifier;
11130 if ( theShape.ShapeType() == TopAbs_SOLID )
11132 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11133 bsc3d->PerformInfinitePoint(aTol);
11135 else if (theShape.ShapeType() == TopAbs_FACE )
11137 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11140 // iterates on indicated elements and get elements by back references from their nodes
11141 TIDSortedElemSet anAffected;
11142 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11143 for ( ; elemItr != theElems.end(); ++elemItr )
11145 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11149 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11150 while ( nodeItr->more() )
11152 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11153 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11155 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11156 while ( backElemItr->more() )
11158 const SMDS_MeshElement* curElem = backElemItr->next();
11159 if ( curElem && theElems.find(curElem) == theElems.end() &&
11161 isInside( curElem, *bsc3d, aTol ) :
11162 isInside( curElem, *aFaceClassifier, aTol )))
11163 anAffected.insert( curElem );
11167 return DoubleNodes( theElems, theNodesNot, anAffected );
11171 * \brief compute an oriented angle between two planes defined by four points.
11172 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11173 * @param p0 base of the rotation axe
11174 * @param p1 extremity of the rotation axe
11175 * @param g1 belongs to the first plane
11176 * @param g2 belongs to the second plane
11178 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11180 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11181 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11182 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11183 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11184 gp_Vec vref(p0, p1);
11187 gp_Vec n1 = vref.Crossed(v1);
11188 gp_Vec n2 = vref.Crossed(v2);
11189 return n2.AngleWithRef(n1, vref);
11193 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11194 * The list of groups must describe a partition of the mesh volumes.
11195 * The nodes of the internal faces at the boundaries of the groups are doubled.
11196 * In option, the internal faces are replaced by flat elements.
11197 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11198 * The flat elements are stored in groups of volumes.
11199 * @param theElems - list of groups of volumes, where a group of volume is a set of
11200 * SMDS_MeshElements sorted by Id.
11201 * @param createJointElems - if TRUE, create the elements
11202 * @return TRUE if operation has been completed successfully, FALSE otherwise
11204 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11205 bool createJointElems)
11207 MESSAGE("----------------------------------------------");
11208 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11209 MESSAGE("----------------------------------------------");
11211 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11212 meshDS->BuildDownWardConnectivity(true);
11214 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11216 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11217 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11218 // build the list of nodes shared by 2 or more domains, with their domain indexes
11220 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11221 std::map<int,int>celldom; // cell vtkId --> domain
11222 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11223 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11224 faceDomains.clear();
11226 cellDomains.clear();
11227 nodeDomains.clear();
11228 std::map<int,int> emptyMap;
11229 std::set<int> emptySet;
11232 for (int idom = 0; idom < theElems.size(); idom++)
11235 // --- build a map (face to duplicate --> volume to modify)
11236 // with all the faces shared by 2 domains (group of elements)
11237 // and corresponding volume of this domain, for each shared face.
11238 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11240 //MESSAGE("Domain " << idom);
11241 const TIDSortedElemSet& domain = theElems[idom];
11242 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11243 for (; elemItr != domain.end(); ++elemItr)
11245 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11248 int vtkId = anElem->getVtkId();
11249 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11250 int neighborsVtkIds[NBMAXNEIGHBORS];
11251 int downIds[NBMAXNEIGHBORS];
11252 unsigned char downTypes[NBMAXNEIGHBORS];
11253 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11254 for (int n = 0; n < nbNeighbors; n++)
11256 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11257 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11258 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11260 DownIdType face(downIds[n], downTypes[n]);
11261 if (!faceDomains.count(face))
11262 faceDomains[face] = emptyMap; // create an empty entry for face
11263 if (!faceDomains[face].count(idom))
11265 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11266 celldom[vtkId] = idom;
11267 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11274 //MESSAGE("Number of shared faces " << faceDomains.size());
11275 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11277 // --- explore the shared faces domain by domain,
11278 // explore the nodes of the face and see if they belong to a cell in the domain,
11279 // which has only a node or an edge on the border (not a shared face)
11281 for (int idomain = 0; idomain < theElems.size(); idomain++)
11283 //MESSAGE("Domain " << idomain);
11284 const TIDSortedElemSet& domain = theElems[idomain];
11285 itface = faceDomains.begin();
11286 for (; itface != faceDomains.end(); ++itface)
11288 std::map<int, int> domvol = itface->second;
11289 if (!domvol.count(idomain))
11291 DownIdType face = itface->first;
11292 //MESSAGE(" --- face " << face.cellId);
11293 std::set<int> oldNodes;
11295 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11296 std::set<int>::iterator itn = oldNodes.begin();
11297 for (; itn != oldNodes.end(); ++itn)
11300 //MESSAGE(" node " << oldId);
11301 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11302 for (int i=0; i<l.ncells; i++)
11304 int vtkId = l.cells[i];
11305 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11306 if (!domain.count(anElem))
11308 int vtkType = grid->GetCellType(vtkId);
11309 int downId = grid->CellIdToDownId(vtkId);
11312 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11313 continue; // not OK at this stage of the algorithm:
11314 //no cells created after BuildDownWardConnectivity
11316 DownIdType aCell(downId, vtkType);
11317 if (!cellDomains.count(aCell))
11318 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11319 cellDomains[aCell][idomain] = vtkId;
11320 celldom[vtkId] = idomain;
11321 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11327 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11328 // for each shared face, get the nodes
11329 // for each node, for each domain of the face, create a clone of the node
11331 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11332 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11333 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11335 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11336 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11337 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11339 for (int idomain = 0; idomain < theElems.size(); idomain++)
11341 itface = faceDomains.begin();
11342 for (; itface != faceDomains.end(); ++itface)
11344 std::map<int, int> domvol = itface->second;
11345 if (!domvol.count(idomain))
11347 DownIdType face = itface->first;
11348 //MESSAGE(" --- face " << face.cellId);
11349 std::set<int> oldNodes;
11351 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11352 std::set<int>::iterator itn = oldNodes.begin();
11353 for (; itn != oldNodes.end(); ++itn)
11356 //MESSAGE("-+-+-a node " << oldId);
11357 if (!nodeDomains.count(oldId))
11358 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11359 if (nodeDomains[oldId].empty())
11361 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11362 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11364 std::map<int, int>::iterator itdom = domvol.begin();
11365 for (; itdom != domvol.end(); ++itdom)
11367 int idom = itdom->first;
11368 //MESSAGE(" domain " << idom);
11369 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11371 if (nodeDomains[oldId].size() >= 2) // a multiple node
11373 vector<int> orderedDoms;
11374 //MESSAGE("multiple node " << oldId);
11375 if (mutipleNodes.count(oldId))
11376 orderedDoms = mutipleNodes[oldId];
11379 map<int,int>::iterator it = nodeDomains[oldId].begin();
11380 for (; it != nodeDomains[oldId].end(); ++it)
11381 orderedDoms.push_back(it->first);
11383 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11384 //stringstream txt;
11385 //for (int i=0; i<orderedDoms.size(); i++)
11386 // txt << orderedDoms[i] << " ";
11387 //MESSAGE("orderedDoms " << txt.str());
11388 mutipleNodes[oldId] = orderedDoms;
11390 double *coords = grid->GetPoint(oldId);
11391 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11392 int newId = newNode->getVtkId();
11393 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11394 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11401 for (int idomain = 0; idomain < theElems.size(); idomain++)
11403 itface = faceDomains.begin();
11404 for (; itface != faceDomains.end(); ++itface)
11406 std::map<int, int> domvol = itface->second;
11407 if (!domvol.count(idomain))
11409 DownIdType face = itface->first;
11410 //MESSAGE(" --- face " << face.cellId);
11411 std::set<int> oldNodes;
11413 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11414 int nbMultipleNodes = 0;
11415 std::set<int>::iterator itn = oldNodes.begin();
11416 for (; itn != oldNodes.end(); ++itn)
11419 if (mutipleNodes.count(oldId))
11422 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11424 //MESSAGE("multiple Nodes detected on a shared face");
11425 int downId = itface->first.cellId;
11426 unsigned char cellType = itface->first.cellType;
11427 // --- shared edge or shared face ?
11428 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11431 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11432 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11433 if (mutipleNodes.count(nodes[i]))
11434 if (!mutipleNodesToFace.count(nodes[i]))
11435 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11437 else // shared face (between two volumes)
11439 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11440 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11441 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11442 for (int ie =0; ie < nbEdges; ie++)
11445 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11446 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11448 vector<int> vn0 = mutipleNodes[nodes[0]];
11449 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11451 for (int i0 = 0; i0 < vn0.size(); i0++)
11452 for (int i1 = 0; i1 < vn1.size(); i1++)
11453 if (vn0[i0] == vn1[i1])
11454 doms.push_back(vn0[i0]);
11455 if (doms.size() >2)
11457 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11458 double *coords = grid->GetPoint(nodes[0]);
11459 gp_Pnt p0(coords[0], coords[1], coords[2]);
11460 coords = grid->GetPoint(nodes[nbNodes - 1]);
11461 gp_Pnt p1(coords[0], coords[1], coords[2]);
11463 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11464 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11465 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11466 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11467 for (int id=0; id < doms.size(); id++)
11469 int idom = doms[id];
11470 for (int ivol=0; ivol<nbvol; ivol++)
11472 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11473 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11474 if (theElems[idom].count(elem))
11476 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11477 domvol[idom] = svol;
11478 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11480 vtkIdType npts = 0;
11481 vtkIdType* pts = 0;
11482 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11483 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11486 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11487 angleDom[idom] = 0;
11491 gp_Pnt g(values[0], values[1], values[2]);
11492 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11493 //MESSAGE(" angle=" << angleDom[idom]);
11499 map<double, int> sortedDom; // sort domains by angle
11500 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11501 sortedDom[ia->second] = ia->first;
11502 vector<int> vnodes;
11504 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11506 vdom.push_back(ib->second);
11507 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11509 for (int ino = 0; ino < nbNodes; ino++)
11510 vnodes.push_back(nodes[ino]);
11511 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11520 // --- iterate on shared faces (volumes to modify, face to extrude)
11521 // get node id's of the face (id SMDS = id VTK)
11522 // create flat element with old and new nodes if requested
11524 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11525 // (domain1 X domain2) = domain1 + MAXINT*domain2
11527 std::map<int, std::map<long,int> > nodeQuadDomains;
11528 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11530 if (createJointElems)
11533 string joints2DName = "joints2D";
11534 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11535 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11536 string joints3DName = "joints3D";
11537 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11538 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11540 itface = faceDomains.begin();
11541 for (; itface != faceDomains.end(); ++itface)
11543 DownIdType face = itface->first;
11544 std::set<int> oldNodes;
11545 std::set<int>::iterator itn;
11547 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11549 std::map<int, int> domvol = itface->second;
11550 std::map<int, int>::iterator itdom = domvol.begin();
11551 int dom1 = itdom->first;
11552 int vtkVolId = itdom->second;
11554 int dom2 = itdom->first;
11555 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11557 stringstream grpname;
11560 grpname << dom1 << "_" << dom2;
11562 grpname << dom2 << "_" << dom1;
11563 string namegrp = grpname.str();
11564 if (!mapOfJunctionGroups.count(namegrp))
11565 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11566 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11568 sgrp->Add(vol->GetID());
11569 if (vol->GetType() == SMDSAbs_Volume)
11570 joints3DGrp->Add(vol->GetID());
11571 else if (vol->GetType() == SMDSAbs_Face)
11572 joints2DGrp->Add(vol->GetID());
11576 // --- create volumes on multiple domain intersection if requested
11577 // iterate on mutipleNodesToFace
11578 // iterate on edgesMultiDomains
11580 if (createJointElems)
11582 // --- iterate on mutipleNodesToFace
11584 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11585 for (; itn != mutipleNodesToFace.end(); ++itn)
11587 int node = itn->first;
11588 vector<int> orderDom = itn->second;
11589 vector<vtkIdType> orderedNodes;
11590 for (int idom = 0; idom <orderDom.size(); idom++)
11591 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11592 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11594 stringstream grpname;
11596 grpname << 0 << "_" << 0;
11598 string namegrp = grpname.str();
11599 if (!mapOfJunctionGroups.count(namegrp))
11600 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11601 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11603 sgrp->Add(face->GetID());
11606 // --- iterate on edgesMultiDomains
11608 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11609 for (; ite != edgesMultiDomains.end(); ++ite)
11611 vector<int> nodes = ite->first;
11612 vector<int> orderDom = ite->second;
11613 vector<vtkIdType> orderedNodes;
11614 if (nodes.size() == 2)
11616 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11617 for (int ino=0; ino < nodes.size(); ino++)
11618 if (orderDom.size() == 3)
11619 for (int idom = 0; idom <orderDom.size(); idom++)
11620 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11622 for (int idom = orderDom.size()-1; idom >=0; idom--)
11623 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11624 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11627 string namegrp = "jointsMultiples";
11628 if (!mapOfJunctionGroups.count(namegrp))
11629 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11630 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11632 sgrp->Add(vol->GetID());
11636 INFOS("Quadratic multiple joints not implemented");
11637 // TODO quadratic nodes
11642 // --- list the explicit faces and edges of the mesh that need to be modified,
11643 // i.e. faces and edges built with one or more duplicated nodes.
11644 // associate these faces or edges to their corresponding domain.
11645 // only the first domain found is kept when a face or edge is shared
11647 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11648 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11649 faceOrEdgeDom.clear();
11652 for (int idomain = 0; idomain < theElems.size(); idomain++)
11654 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11655 for (; itnod != nodeDomains.end(); ++itnod)
11657 int oldId = itnod->first;
11658 //MESSAGE(" node " << oldId);
11659 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11660 for (int i = 0; i < l.ncells; i++)
11662 int vtkId = l.cells[i];
11663 int vtkType = grid->GetCellType(vtkId);
11664 int downId = grid->CellIdToDownId(vtkId);
11666 continue; // new cells: not to be modified
11667 DownIdType aCell(downId, vtkType);
11668 int volParents[1000];
11669 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11670 for (int j = 0; j < nbvol; j++)
11671 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11672 if (!feDom.count(vtkId))
11674 feDom[vtkId] = idomain;
11675 faceOrEdgeDom[aCell] = emptyMap;
11676 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11677 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11678 // << " type " << vtkType << " downId " << downId);
11684 // --- iterate on shared faces (volumes to modify, face to extrude)
11685 // get node id's of the face
11686 // replace old nodes by new nodes in volumes, and update inverse connectivity
11688 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11689 for (int m=0; m<3; m++)
11691 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11692 itface = (*amap).begin();
11693 for (; itface != (*amap).end(); ++itface)
11695 DownIdType face = itface->first;
11696 std::set<int> oldNodes;
11697 std::set<int>::iterator itn;
11699 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11700 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11701 std::map<int, int> localClonedNodeIds;
11703 std::map<int, int> domvol = itface->second;
11704 std::map<int, int>::iterator itdom = domvol.begin();
11705 for (; itdom != domvol.end(); ++itdom)
11707 int idom = itdom->first;
11708 int vtkVolId = itdom->second;
11709 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11710 localClonedNodeIds.clear();
11711 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11714 if (nodeDomains[oldId].count(idom))
11716 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11717 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11720 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11725 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11726 grid->BuildLinks();
11734 * \brief Double nodes on some external faces and create flat elements.
11735 * Flat elements are mainly used by some types of mechanic calculations.
11737 * Each group of the list must be constituted of faces.
11738 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11739 * @param theElems - list of groups of faces, where a group of faces is a set of
11740 * SMDS_MeshElements sorted by Id.
11741 * @return TRUE if operation has been completed successfully, FALSE otherwise
11743 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11745 MESSAGE("-------------------------------------------------");
11746 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11747 MESSAGE("-------------------------------------------------");
11749 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11751 // --- For each group of faces
11752 // duplicate the nodes, create a flat element based on the face
11753 // replace the nodes of the faces by their clones
11755 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11756 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11757 clonedNodes.clear();
11758 intermediateNodes.clear();
11759 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11760 mapOfJunctionGroups.clear();
11762 for (int idom = 0; idom < theElems.size(); idom++)
11764 const TIDSortedElemSet& domain = theElems[idom];
11765 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11766 for (; elemItr != domain.end(); ++elemItr)
11768 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11769 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11772 // MESSAGE("aFace=" << aFace->GetID());
11773 bool isQuad = aFace->IsQuadratic();
11774 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11776 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11778 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11779 while (nodeIt->more())
11781 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11782 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11784 ln2.push_back(node);
11786 ln0.push_back(node);
11788 const SMDS_MeshNode* clone = 0;
11789 if (!clonedNodes.count(node))
11791 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11792 clonedNodes[node] = clone;
11795 clone = clonedNodes[node];
11798 ln3.push_back(clone);
11800 ln1.push_back(clone);
11802 const SMDS_MeshNode* inter = 0;
11803 if (isQuad && (!isMedium))
11805 if (!intermediateNodes.count(node))
11807 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11808 intermediateNodes[node] = inter;
11811 inter = intermediateNodes[node];
11812 ln4.push_back(inter);
11816 // --- extrude the face
11818 vector<const SMDS_MeshNode*> ln;
11819 SMDS_MeshVolume* vol = 0;
11820 vtkIdType aType = aFace->GetVtkType();
11824 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11825 // MESSAGE("vol prism " << vol->GetID());
11826 ln.push_back(ln1[0]);
11827 ln.push_back(ln1[1]);
11828 ln.push_back(ln1[2]);
11831 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11832 // MESSAGE("vol hexa " << vol->GetID());
11833 ln.push_back(ln1[0]);
11834 ln.push_back(ln1[1]);
11835 ln.push_back(ln1[2]);
11836 ln.push_back(ln1[3]);
11838 case VTK_QUADRATIC_TRIANGLE:
11839 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11840 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11841 // MESSAGE("vol quad prism " << vol->GetID());
11842 ln.push_back(ln1[0]);
11843 ln.push_back(ln1[1]);
11844 ln.push_back(ln1[2]);
11845 ln.push_back(ln3[0]);
11846 ln.push_back(ln3[1]);
11847 ln.push_back(ln3[2]);
11849 case VTK_QUADRATIC_QUAD:
11850 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11851 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11852 // ln4[0], ln4[1], ln4[2], ln4[3]);
11853 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11854 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11855 ln4[0], ln4[1], ln4[2], ln4[3]);
11856 // MESSAGE("vol quad hexa " << vol->GetID());
11857 ln.push_back(ln1[0]);
11858 ln.push_back(ln1[1]);
11859 ln.push_back(ln1[2]);
11860 ln.push_back(ln1[3]);
11861 ln.push_back(ln3[0]);
11862 ln.push_back(ln3[1]);
11863 ln.push_back(ln3[2]);
11864 ln.push_back(ln3[3]);
11874 stringstream grpname;
11878 string namegrp = grpname.str();
11879 if (!mapOfJunctionGroups.count(namegrp))
11880 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11881 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11883 sgrp->Add(vol->GetID());
11886 // --- modify the face
11888 aFace->ChangeNodes(&ln[0], ln.size());
11895 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11896 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11897 * groups of faces to remove inside the object, (idem edges).
11898 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11900 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11901 const TopoDS_Shape& theShape,
11902 SMESH_NodeSearcher* theNodeSearcher,
11903 const char* groupName,
11904 std::vector<double>& nodesCoords,
11905 std::vector<std::vector<int> >& listOfListOfNodes)
11907 MESSAGE("--------------------------------");
11908 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11909 MESSAGE("--------------------------------");
11911 // --- zone of volumes to remove is given :
11912 // 1 either by a geom shape (one or more vertices) and a radius,
11913 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11914 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11915 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11916 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11917 // defined by it's name.
11919 SMESHDS_GroupBase* groupDS = 0;
11920 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11921 while ( groupIt->more() )
11924 SMESH_Group * group = groupIt->next();
11925 if ( !group ) continue;
11926 groupDS = group->GetGroupDS();
11927 if ( !groupDS || groupDS->IsEmpty() ) continue;
11928 std::string grpName = group->GetName();
11929 //MESSAGE("grpName=" << grpName);
11930 if (grpName == groupName)
11936 bool isNodeGroup = false;
11937 bool isNodeCoords = false;
11940 if (groupDS->GetType() != SMDSAbs_Node)
11942 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11945 if (nodesCoords.size() > 0)
11946 isNodeCoords = true; // a list o nodes given by their coordinates
11947 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11949 // --- define groups to build
11951 int idg; // --- group of SMDS volumes
11952 string grpvName = groupName;
11953 grpvName += "_vol";
11954 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11957 MESSAGE("group not created " << grpvName);
11960 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11962 int idgs; // --- group of SMDS faces on the skin
11963 string grpsName = groupName;
11964 grpsName += "_skin";
11965 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11968 MESSAGE("group not created " << grpsName);
11971 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11973 int idgi; // --- group of SMDS faces internal (several shapes)
11974 string grpiName = groupName;
11975 grpiName += "_internalFaces";
11976 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11979 MESSAGE("group not created " << grpiName);
11982 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11984 int idgei; // --- group of SMDS faces internal (several shapes)
11985 string grpeiName = groupName;
11986 grpeiName += "_internalEdges";
11987 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11990 MESSAGE("group not created " << grpeiName);
11993 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11995 // --- build downward connectivity
11997 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11998 meshDS->BuildDownWardConnectivity(true);
11999 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12001 // --- set of volumes detected inside
12003 std::set<int> setOfInsideVol;
12004 std::set<int> setOfVolToCheck;
12006 std::vector<gp_Pnt> gpnts;
12009 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12011 MESSAGE("group of nodes provided");
12012 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12013 while ( elemIt->more() )
12015 const SMDS_MeshElement* elem = elemIt->next();
12018 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12021 SMDS_MeshElement* vol = 0;
12022 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12023 while (volItr->more())
12025 vol = (SMDS_MeshElement*)volItr->next();
12026 setOfInsideVol.insert(vol->getVtkId());
12027 sgrp->Add(vol->GetID());
12031 else if (isNodeCoords)
12033 MESSAGE("list of nodes coordinates provided");
12036 while (i < nodesCoords.size()-2)
12038 double x = nodesCoords[i++];
12039 double y = nodesCoords[i++];
12040 double z = nodesCoords[i++];
12041 gp_Pnt p = gp_Pnt(x, y ,z);
12042 gpnts.push_back(p);
12043 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12046 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12048 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12049 TopTools_IndexedMapOfShape vertexMap;
12050 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12051 gp_Pnt p = gp_Pnt(0,0,0);
12052 if (vertexMap.Extent() < 1)
12055 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12057 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12058 p = BRep_Tool::Pnt(vertex);
12059 gpnts.push_back(p);
12060 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12064 if (gpnts.size() > 0)
12067 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12069 nodeId = startNode->GetID();
12070 MESSAGE("nodeId " << nodeId);
12072 double radius2 = radius*radius;
12073 MESSAGE("radius2 " << radius2);
12075 // --- volumes on start node
12077 setOfVolToCheck.clear();
12078 SMDS_MeshElement* startVol = 0;
12079 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12080 while (volItr->more())
12082 startVol = (SMDS_MeshElement*)volItr->next();
12083 setOfVolToCheck.insert(startVol->getVtkId());
12085 if (setOfVolToCheck.empty())
12087 MESSAGE("No volumes found");
12091 // --- starting with central volumes then their neighbors, check if they are inside
12092 // or outside the domain, until no more new neighbor volume is inside.
12093 // Fill the group of inside volumes
12095 std::map<int, double> mapOfNodeDistance2;
12096 mapOfNodeDistance2.clear();
12097 std::set<int> setOfOutsideVol;
12098 while (!setOfVolToCheck.empty())
12100 std::set<int>::iterator it = setOfVolToCheck.begin();
12102 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12103 bool volInside = false;
12104 vtkIdType npts = 0;
12105 vtkIdType* pts = 0;
12106 grid->GetCellPoints(vtkId, npts, pts);
12107 for (int i=0; i<npts; i++)
12109 double distance2 = 0;
12110 if (mapOfNodeDistance2.count(pts[i]))
12112 distance2 = mapOfNodeDistance2[pts[i]];
12113 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12117 double *coords = grid->GetPoint(pts[i]);
12118 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12120 for (int j=0; j<gpnts.size(); j++)
12122 double d2 = aPoint.SquareDistance(gpnts[j]);
12123 if (d2 < distance2)
12126 if (distance2 < radius2)
12130 mapOfNodeDistance2[pts[i]] = distance2;
12131 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12133 if (distance2 < radius2)
12135 volInside = true; // one or more nodes inside the domain
12136 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12142 setOfInsideVol.insert(vtkId);
12143 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12144 int neighborsVtkIds[NBMAXNEIGHBORS];
12145 int downIds[NBMAXNEIGHBORS];
12146 unsigned char downTypes[NBMAXNEIGHBORS];
12147 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12148 for (int n = 0; n < nbNeighbors; n++)
12149 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12150 setOfVolToCheck.insert(neighborsVtkIds[n]);
12154 setOfOutsideVol.insert(vtkId);
12155 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12157 setOfVolToCheck.erase(vtkId);
12161 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12162 // If yes, add the volume to the inside set
12164 bool addedInside = true;
12165 std::set<int> setOfVolToReCheck;
12166 while (addedInside)
12168 MESSAGE(" --------------------------- re check");
12169 addedInside = false;
12170 std::set<int>::iterator itv = setOfInsideVol.begin();
12171 for (; itv != setOfInsideVol.end(); ++itv)
12174 int neighborsVtkIds[NBMAXNEIGHBORS];
12175 int downIds[NBMAXNEIGHBORS];
12176 unsigned char downTypes[NBMAXNEIGHBORS];
12177 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12178 for (int n = 0; n < nbNeighbors; n++)
12179 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12180 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12182 setOfVolToCheck = setOfVolToReCheck;
12183 setOfVolToReCheck.clear();
12184 while (!setOfVolToCheck.empty())
12186 std::set<int>::iterator it = setOfVolToCheck.begin();
12188 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12190 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12191 int countInside = 0;
12192 int neighborsVtkIds[NBMAXNEIGHBORS];
12193 int downIds[NBMAXNEIGHBORS];
12194 unsigned char downTypes[NBMAXNEIGHBORS];
12195 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12196 for (int n = 0; n < nbNeighbors; n++)
12197 if (setOfInsideVol.count(neighborsVtkIds[n]))
12199 MESSAGE("countInside " << countInside);
12200 if (countInside > 1)
12202 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12203 setOfInsideVol.insert(vtkId);
12204 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12205 addedInside = true;
12208 setOfVolToReCheck.insert(vtkId);
12210 setOfVolToCheck.erase(vtkId);
12214 // --- map of Downward faces at the boundary, inside the global volume
12215 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12216 // fill group of SMDS faces inside the volume (when several volume shapes)
12217 // fill group of SMDS faces on the skin of the global volume (if skin)
12219 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12220 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12221 std::set<int>::iterator it = setOfInsideVol.begin();
12222 for (; it != setOfInsideVol.end(); ++it)
12225 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12226 int neighborsVtkIds[NBMAXNEIGHBORS];
12227 int downIds[NBMAXNEIGHBORS];
12228 unsigned char downTypes[NBMAXNEIGHBORS];
12229 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12230 for (int n = 0; n < nbNeighbors; n++)
12232 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12233 if (neighborDim == 3)
12235 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12237 DownIdType face(downIds[n], downTypes[n]);
12238 boundaryFaces[face] = vtkId;
12240 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12241 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12242 if (vtkFaceId >= 0)
12244 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12245 // find also the smds edges on this face
12246 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12247 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12248 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12249 for (int i = 0; i < nbEdges; i++)
12251 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12252 if (vtkEdgeId >= 0)
12253 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12257 else if (neighborDim == 2) // skin of the volume
12259 DownIdType face(downIds[n], downTypes[n]);
12260 skinFaces[face] = vtkId;
12261 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12262 if (vtkFaceId >= 0)
12263 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12268 // --- identify the edges constituting the wire of each subshape on the skin
12269 // define polylines with the nodes of edges, equivalent to wires
12270 // project polylines on subshapes, and partition, to get geom faces
12272 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12273 std::set<int> emptySet;
12275 std::set<int> shapeIds;
12277 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12278 while (itelem->more())
12280 const SMDS_MeshElement *elem = itelem->next();
12281 int shapeId = elem->getshapeId();
12282 int vtkId = elem->getVtkId();
12283 if (!shapeIdToVtkIdSet.count(shapeId))
12285 shapeIdToVtkIdSet[shapeId] = emptySet;
12286 shapeIds.insert(shapeId);
12288 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12291 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12292 std::set<DownIdType, DownIdCompare> emptyEdges;
12293 emptyEdges.clear();
12295 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12296 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12298 int shapeId = itShape->first;
12299 MESSAGE(" --- Shape ID --- "<< shapeId);
12300 shapeIdToEdges[shapeId] = emptyEdges;
12302 std::vector<int> nodesEdges;
12304 std::set<int>::iterator its = itShape->second.begin();
12305 for (; its != itShape->second.end(); ++its)
12308 MESSAGE(" " << vtkId);
12309 int neighborsVtkIds[NBMAXNEIGHBORS];
12310 int downIds[NBMAXNEIGHBORS];
12311 unsigned char downTypes[NBMAXNEIGHBORS];
12312 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12313 for (int n = 0; n < nbNeighbors; n++)
12315 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12317 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12318 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12319 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12321 DownIdType edge(downIds[n], downTypes[n]);
12322 if (!shapeIdToEdges[shapeId].count(edge))
12324 shapeIdToEdges[shapeId].insert(edge);
12326 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12327 nodesEdges.push_back(vtkNodeId[0]);
12328 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12329 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12335 std::list<int> order;
12337 if (nodesEdges.size() > 0)
12339 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12340 nodesEdges[0] = -1;
12341 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12342 nodesEdges[1] = -1; // do not reuse this edge
12346 int nodeTofind = order.back(); // try first to push back
12348 for (i = 0; i<nodesEdges.size(); i++)
12349 if (nodesEdges[i] == nodeTofind)
12351 if (i == nodesEdges.size())
12352 found = false; // no follower found on back
12355 if (i%2) // odd ==> use the previous one
12356 if (nodesEdges[i-1] < 0)
12360 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12361 nodesEdges[i-1] = -1;
12363 else // even ==> use the next one
12364 if (nodesEdges[i+1] < 0)
12368 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12369 nodesEdges[i+1] = -1;
12374 // try to push front
12376 nodeTofind = order.front(); // try to push front
12377 for (i = 0; i<nodesEdges.size(); i++)
12378 if (nodesEdges[i] == nodeTofind)
12380 if (i == nodesEdges.size())
12382 found = false; // no predecessor found on front
12385 if (i%2) // odd ==> use the previous one
12386 if (nodesEdges[i-1] < 0)
12390 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12391 nodesEdges[i-1] = -1;
12393 else // even ==> use the next one
12394 if (nodesEdges[i+1] < 0)
12398 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12399 nodesEdges[i+1] = -1;
12405 std::vector<int> nodes;
12406 nodes.push_back(shapeId);
12407 std::list<int>::iterator itl = order.begin();
12408 for (; itl != order.end(); itl++)
12410 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12411 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12413 listOfListOfNodes.push_back(nodes);
12416 // partition geom faces with blocFissure
12417 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12418 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12424 //================================================================================
12426 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12427 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12428 * \return TRUE if operation has been completed successfully, FALSE otherwise
12430 //================================================================================
12432 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12434 // iterates on volume elements and detect all free faces on them
12435 SMESHDS_Mesh* aMesh = GetMeshDS();
12438 //bool res = false;
12439 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12440 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12443 const SMDS_MeshVolume* volume = vIt->next();
12444 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12445 vTool.SetExternalNormal();
12446 //const bool isPoly = volume->IsPoly();
12447 const int iQuad = volume->IsQuadratic();
12448 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12450 if (!vTool.IsFreeFace(iface))
12453 vector<const SMDS_MeshNode *> nodes;
12454 int nbFaceNodes = vTool.NbFaceNodes(iface);
12455 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12457 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12458 nodes.push_back(faceNodes[inode]);
12459 if (iQuad) { // add medium nodes
12460 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12461 nodes.push_back(faceNodes[inode]);
12462 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12463 nodes.push_back(faceNodes[8]);
12465 // add new face based on volume nodes
12466 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12468 continue; // face already exsist
12470 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12474 return ( nbFree==(nbExisted+nbCreated) );
12479 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12481 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12483 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12486 //================================================================================
12488 * \brief Creates missing boundary elements
12489 * \param elements - elements whose boundary is to be checked
12490 * \param dimension - defines type of boundary elements to create
12491 * \param group - a group to store created boundary elements in
12492 * \param targetMesh - a mesh to store created boundary elements in
12493 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12494 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12495 * boundary elements will be copied into the targetMesh
12496 * \param toAddExistingBondary - if true, not only new but also pre-existing
12497 * boundary elements will be added into the new group
12498 * \param aroundElements - if true, elements will be created on boundary of given
12499 * elements else, on boundary of the whole mesh.
12500 * \return nb of added boundary elements
12502 //================================================================================
12504 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12505 Bnd_Dimension dimension,
12506 SMESH_Group* group/*=0*/,
12507 SMESH_Mesh* targetMesh/*=0*/,
12508 bool toCopyElements/*=false*/,
12509 bool toCopyExistingBoundary/*=false*/,
12510 bool toAddExistingBondary/*= false*/,
12511 bool aroundElements/*= false*/)
12513 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12514 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12515 // hope that all elements are of the same type, do not check them all
12516 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12517 throw SALOME_Exception(LOCALIZED("wrong element type"));
12520 toCopyElements = toCopyExistingBoundary = false;
12522 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12523 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12524 int nbAddedBnd = 0;
12526 // editor adding present bnd elements and optionally holding elements to add to the group
12527 SMESH_MeshEditor* presentEditor;
12528 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12529 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12531 SMESH_MesherHelper helper( *myMesh );
12532 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12533 SMDS_VolumeTool vTool;
12534 TIDSortedElemSet avoidSet;
12535 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12538 typedef vector<const SMDS_MeshNode*> TConnectivity;
12540 SMDS_ElemIteratorPtr eIt;
12541 if (elements.empty())
12542 eIt = aMesh->elementsIterator(elemType);
12544 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12546 while (eIt->more())
12548 const SMDS_MeshElement* elem = eIt->next();
12549 const int iQuad = elem->IsQuadratic();
12551 // ------------------------------------------------------------------------------------
12552 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12553 // ------------------------------------------------------------------------------------
12554 vector<const SMDS_MeshElement*> presentBndElems;
12555 vector<TConnectivity> missingBndElems;
12556 TConnectivity nodes;
12557 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12559 vTool.SetExternalNormal();
12560 const SMDS_MeshElement* otherVol = 0;
12561 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12563 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12564 ( !aroundElements || elements.count( otherVol )))
12566 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12567 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12568 if ( missType == SMDSAbs_Edge ) // boundary edges
12570 nodes.resize( 2+iQuad );
12571 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12573 for ( int j = 0; j < nodes.size(); ++j )
12575 if ( const SMDS_MeshElement* edge =
12576 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12577 presentBndElems.push_back( edge );
12579 missingBndElems.push_back( nodes );
12582 else // boundary face
12585 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12586 nodes.push_back( nn[inode] );
12587 if (iQuad) // add medium nodes
12588 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12589 nodes.push_back( nn[inode] );
12590 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12592 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12594 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12595 SMDSAbs_Face, /*noMedium=*/false ))
12596 presentBndElems.push_back( f );
12598 missingBndElems.push_back( nodes );
12600 if ( targetMesh != myMesh )
12602 // add 1D elements on face boundary to be added to a new mesh
12603 const SMDS_MeshElement* edge;
12604 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12607 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12609 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12610 if ( edge && avoidSet.insert( edge ).second )
12611 presentBndElems.push_back( edge );
12617 else // elem is a face ------------------------------------------
12619 avoidSet.clear(), avoidSet.insert( elem );
12620 int nbNodes = elem->NbCornerNodes();
12621 nodes.resize( 2 /*+ iQuad*/);
12622 for ( int i = 0; i < nbNodes; i++ )
12624 nodes[0] = elem->GetNode(i);
12625 nodes[1] = elem->GetNode((i+1)%nbNodes);
12626 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12627 continue; // not free link
12630 //nodes[2] = elem->GetNode( i + nbNodes );
12631 if ( const SMDS_MeshElement* edge =
12632 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12633 presentBndElems.push_back( edge );
12635 missingBndElems.push_back( nodes );
12639 // ---------------------------------
12640 // 2. Add missing boundary elements
12641 // ---------------------------------
12642 if ( targetMesh != myMesh )
12643 // instead of making a map of nodes in this mesh and targetMesh,
12644 // we create nodes with same IDs.
12645 for ( int i = 0; i < missingBndElems.size(); ++i )
12647 TConnectivity& srcNodes = missingBndElems[i];
12648 TConnectivity nodes( srcNodes.size() );
12649 for ( inode = 0; inode < nodes.size(); ++inode )
12650 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12651 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12653 /*noMedium=*/false))
12655 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12659 for ( int i = 0; i < missingBndElems.size(); ++i )
12661 TConnectivity& nodes = missingBndElems[i];
12662 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12664 /*noMedium=*/false))
12666 SMDS_MeshElement* elem =
12667 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12670 // try to set a new element to a shape
12671 if ( myMesh->HasShapeToMesh() )
12674 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12675 const int nbN = nodes.size() / (iQuad+1 );
12676 for ( inode = 0; inode < nbN && ok; ++inode )
12678 pair<int, TopAbs_ShapeEnum> i_stype =
12679 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12680 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12681 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12683 if ( ok && mediumShapes.size() > 1 )
12685 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12686 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12687 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12689 if (( ok = ( stype_i->first != stype_i_0.first )))
12690 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12691 aMesh->IndexToShape( stype_i_0.second ));
12694 if ( ok && mediumShapes.begin()->first == missShapeType )
12695 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12699 // ----------------------------------
12700 // 3. Copy present boundary elements
12701 // ----------------------------------
12702 if ( toCopyExistingBoundary )
12703 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12705 const SMDS_MeshElement* e = presentBndElems[i];
12706 TConnectivity nodes( e->NbNodes() );
12707 for ( inode = 0; inode < nodes.size(); ++inode )
12708 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12709 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12711 else // store present elements to add them to a group
12712 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12714 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12717 } // loop on given elements
12719 // ---------------------------------------------
12720 // 4. Fill group with boundary elements
12721 // ---------------------------------------------
12724 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12725 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12726 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12728 tgtEditor.myLastCreatedElems.Clear();
12729 tgtEditor2.myLastCreatedElems.Clear();
12731 // -----------------------
12732 // 5. Copy given elements
12733 // -----------------------
12734 if ( toCopyElements && targetMesh != myMesh )
12736 if (elements.empty())
12737 eIt = aMesh->elementsIterator(elemType);
12739 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12740 while (eIt->more())
12742 const SMDS_MeshElement* elem = eIt->next();
12743 TConnectivity nodes( elem->NbNodes() );
12744 for ( inode = 0; inode < nodes.size(); ++inode )
12745 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12746 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12748 tgtEditor.myLastCreatedElems.Clear();