1 // Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
86 #include <gp_Trsf.hxx>
100 #include <boost/tuple/tuple.hpp>
102 #include <Standard_Failure.hxx>
103 #include <Standard_ErrorHandler.hxx>
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
110 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
112 //=======================================================================
113 //function : SMESH_MeshEditor
115 //=======================================================================
117 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
118 :myMesh( theMesh ) // theMesh may be NULL
122 //================================================================================
124 * \brief Clears myLastCreatedNodes and myLastCreatedElems
126 //================================================================================
128 void SMESH_MeshEditor::CrearLastCreated()
130 myLastCreatedNodes.Clear();
131 myLastCreatedElems.Clear();
135 //=======================================================================
139 //=======================================================================
142 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
143 const SMDSAbs_ElementType type,
146 const double ballDiameter)
148 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
149 SMDS_MeshElement* e = 0;
150 int nbnode = node.size();
151 SMESHDS_Mesh* mesh = GetMeshDS();
156 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
157 else e = mesh->AddFace (node[0], node[1], node[2] );
159 else if (nbnode == 4) {
160 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
161 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
163 else if (nbnode == 6) {
164 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
165 node[4], node[5], ID);
166 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
169 else if (nbnode == 8) {
170 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171 node[4], node[5], node[6], node[7], ID);
172 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
173 node[4], node[5], node[6], node[7] );
175 else if (nbnode == 9) {
176 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177 node[4], node[5], node[6], node[7], node[8], ID);
178 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
179 node[4], node[5], node[6], node[7], node[8] );
182 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
183 else e = mesh->AddPolygonalFace (node );
190 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
191 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
193 else if (nbnode == 5) {
194 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
196 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
199 else if (nbnode == 6) {
200 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
201 node[4], node[5], ID);
202 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
205 else if (nbnode == 8) {
206 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207 node[4], node[5], node[6], node[7], ID);
208 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], node[7] );
211 else if (nbnode == 10) {
212 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213 node[4], node[5], node[6], node[7],
214 node[8], node[9], ID);
215 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7],
219 else if (nbnode == 12) {
220 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7],
222 node[8], node[9], node[10], node[11], ID);
223 else e = mesh->AddVolume (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] );
227 else if (nbnode == 13) {
228 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
229 node[4], node[5], node[6], node[7],
230 node[8], node[9], node[10],node[11],
232 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7],
234 node[8], node[9], node[10],node[11],
237 else if (nbnode == 15) {
238 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
239 node[4], node[5], node[6], node[7],
240 node[8], node[9], node[10],node[11],
241 node[12],node[13],node[14],ID);
242 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
243 node[4], node[5], node[6], node[7],
244 node[8], node[9], node[10],node[11],
245 node[12],node[13],node[14] );
247 else if (nbnode == 20) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249 node[4], node[5], node[6], node[7],
250 node[8], node[9], node[10],node[11],
251 node[12],node[13],node[14],node[15],
252 node[16],node[17],node[18],node[19],ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
254 node[4], node[5], node[6], node[7],
255 node[8], node[9], node[10],node[11],
256 node[12],node[13],node[14],node[15],
257 node[16],node[17],node[18],node[19] );
259 else if (nbnode == 27) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7],
262 node[8], node[9], node[10],node[11],
263 node[12],node[13],node[14],node[15],
264 node[16],node[17],node[18],node[19],
265 node[20],node[21],node[22],node[23],
266 node[24],node[25],node[26], ID);
267 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
268 node[4], node[5], node[6], node[7],
269 node[8], node[9], node[10],node[11],
270 node[12],node[13],node[14],node[15],
271 node[16],node[17],node[18],node[19],
272 node[20],node[21],node[22],node[23],
273 node[24],node[25],node[26] );
280 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
281 else e = mesh->AddEdge (node[0], node[1] );
283 else if ( nbnode == 3 ) {
284 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
285 else e = mesh->AddEdge (node[0], node[1], node[2] );
289 case SMDSAbs_0DElement:
291 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
292 else e = mesh->Add0DElement (node[0] );
297 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
298 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
302 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
303 else e = mesh->AddBall (node[0], ballDiameter);
308 if ( e ) myLastCreatedElems.Append( e );
312 //=======================================================================
316 //=======================================================================
318 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
319 const SMDSAbs_ElementType type,
323 vector<const SMDS_MeshNode*> nodes;
324 nodes.reserve( nodeIDs.size() );
325 vector<int>::const_iterator id = nodeIDs.begin();
326 while ( id != nodeIDs.end() ) {
327 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
328 nodes.push_back( node );
332 return AddElement( nodes, type, isPoly, ID );
335 //=======================================================================
337 //purpose : Remove a node or an element.
338 // Modify a compute state of sub-meshes which become empty
339 //=======================================================================
341 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
344 myLastCreatedElems.Clear();
345 myLastCreatedNodes.Clear();
347 SMESHDS_Mesh* aMesh = GetMeshDS();
348 set< SMESH_subMesh *> smmap;
351 list<int>::const_iterator it = theIDs.begin();
352 for ( ; it != theIDs.end(); it++ ) {
353 const SMDS_MeshElement * elem;
355 elem = aMesh->FindNode( *it );
357 elem = aMesh->FindElement( *it );
361 // Notify VERTEX sub-meshes about modification
363 const SMDS_MeshNode* node = cast2Node( elem );
364 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
365 if ( int aShapeID = node->getshapeId() )
366 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
369 // Find sub-meshes to notify about modification
370 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
371 // while ( nodeIt->more() ) {
372 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
373 // const SMDS_PositionPtr& aPosition = node->GetPosition();
374 // if ( aPosition.get() ) {
375 // if ( int aShapeID = aPosition->GetShapeId() ) {
376 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
377 // smmap.insert( sm );
384 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
386 aMesh->RemoveElement( elem );
390 // Notify sub-meshes about modification
391 if ( !smmap.empty() ) {
392 set< SMESH_subMesh *>::iterator smIt;
393 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
394 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
397 // // Check if the whole mesh becomes empty
398 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
399 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
404 //================================================================================
406 * \brief Create 0D elements on all nodes of the given object except those
407 * nodes on which a 0D element already exists.
408 * \param elements - Elements on whose nodes to create 0D elements; if empty,
409 * the all mesh is treated
410 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
412 //================================================================================
414 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
415 TIDSortedElemSet& all0DElems )
417 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
418 SMDS_ElemIteratorPtr elemIt;
419 if ( elements.empty() )
420 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
422 elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
424 while ( elemIt->more() )
426 const SMDS_MeshElement* e = elemIt->next();
427 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
428 while ( nodeIt->more() )
430 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
431 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
433 all0DElems.insert( it0D->next() );
435 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
436 all0DElems.insert( myLastCreatedElems.Last() );
442 //=======================================================================
443 //function : FindShape
444 //purpose : Return an index of the shape theElem is on
445 // or zero if a shape not found
446 //=======================================================================
448 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
450 myLastCreatedElems.Clear();
451 myLastCreatedNodes.Clear();
453 SMESHDS_Mesh * aMesh = GetMeshDS();
454 if ( aMesh->ShapeToMesh().IsNull() )
457 int aShapeID = theElem->getshapeId();
461 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
462 if ( sm->Contains( theElem ))
465 if ( theElem->GetType() == SMDSAbs_Node ) {
466 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
469 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
472 TopoDS_Shape aShape; // the shape a node of theElem is on
473 if ( theElem->GetType() != SMDSAbs_Node )
475 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
476 while ( nodeIt->more() ) {
477 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
478 if ((aShapeID = node->getshapeId()) > 0) {
479 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
480 if ( sm->Contains( theElem ))
482 if ( aShape.IsNull() )
483 aShape = aMesh->IndexToShape( aShapeID );
489 // None of nodes is on a proper shape,
490 // find the shape among ancestors of aShape on which a node is
491 if ( !aShape.IsNull() ) {
492 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
493 for ( ; ancIt.More(); ancIt.Next() ) {
494 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
495 if ( sm && sm->Contains( theElem ))
496 return aMesh->ShapeToIndex( ancIt.Value() );
501 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
502 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
503 for ( ; id_sm != id2sm.end(); ++id_sm )
504 if ( id_sm->second->Contains( theElem ))
508 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
512 //=======================================================================
513 //function : IsMedium
515 //=======================================================================
517 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
518 const SMDSAbs_ElementType typeToCheck)
520 bool isMedium = false;
521 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
522 while (it->more() && !isMedium ) {
523 const SMDS_MeshElement* elem = it->next();
524 isMedium = elem->IsMediumNode(node);
529 //=======================================================================
530 //function : ShiftNodesQuadTria
532 // Shift nodes in the array corresponded to quadratic triangle
533 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
534 //=======================================================================
535 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
537 const SMDS_MeshNode* nd1 = aNodes[0];
538 aNodes[0] = aNodes[1];
539 aNodes[1] = aNodes[2];
541 const SMDS_MeshNode* nd2 = aNodes[3];
542 aNodes[3] = aNodes[4];
543 aNodes[4] = aNodes[5];
547 //=======================================================================
548 //function : edgeConnectivity
550 // return number of the edges connected with the theNode.
551 // if theEdges has connections with the other type of the
552 // elements, return -1
553 //=======================================================================
554 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
556 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
558 while(elemIt->more()) {
566 //=======================================================================
567 //function : GetNodesFromTwoTria
569 // Shift nodes in the array corresponded to quadratic triangle
570 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
571 //=======================================================================
572 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
573 const SMDS_MeshElement * theTria2,
574 const SMDS_MeshNode* N1[],
575 const SMDS_MeshNode* N2[])
577 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
580 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
583 if(it->more()) return false;
584 it = theTria2->nodesIterator();
587 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
590 if(it->more()) return false;
592 int sames[3] = {-1,-1,-1};
604 if(nbsames!=2) return false;
606 ShiftNodesQuadTria(N1);
608 ShiftNodesQuadTria(N1);
611 i = sames[0] + sames[1] + sames[2];
613 ShiftNodesQuadTria(N2);
615 // now we receive following N1 and N2 (using numeration as above image)
616 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
617 // i.e. first nodes from both arrays determ new diagonal
621 //=======================================================================
622 //function : InverseDiag
623 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
624 // but having other common link.
625 // Return False if args are improper
626 //=======================================================================
628 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
629 const SMDS_MeshElement * theTria2 )
631 MESSAGE("InverseDiag");
632 myLastCreatedElems.Clear();
633 myLastCreatedNodes.Clear();
635 if (!theTria1 || !theTria2)
638 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
639 if (!F1) return false;
640 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
641 if (!F2) return false;
642 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
643 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
645 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
646 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
650 // put nodes in array and find out indices of the same ones
651 const SMDS_MeshNode* aNodes [6];
652 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
654 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
655 while ( it->more() ) {
656 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
658 if ( i > 2 ) // theTria2
659 // find same node of theTria1
660 for ( int j = 0; j < 3; j++ )
661 if ( aNodes[ i ] == aNodes[ j ]) {
670 return false; // theTria1 is not a triangle
671 it = theTria2->nodesIterator();
673 if ( i == 6 && it->more() )
674 return false; // theTria2 is not a triangle
677 // find indices of 1,2 and of A,B in theTria1
678 int iA = 0, iB = 0, i1 = 0, i2 = 0;
679 for ( i = 0; i < 6; i++ ) {
680 if ( sameInd [ i ] == 0 ) {
689 // nodes 1 and 2 should not be the same
690 if ( aNodes[ i1 ] == aNodes[ i2 ] )
694 aNodes[ iA ] = aNodes[ i2 ];
696 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
698 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
699 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
703 } // end if(F1 && F2)
705 // check case of quadratic faces
706 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
708 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
712 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
713 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
721 const SMDS_MeshNode* N1 [6];
722 const SMDS_MeshNode* N2 [6];
723 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
725 // now we receive following N1 and N2 (using numeration as above image)
726 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
727 // i.e. first nodes from both arrays determ new diagonal
729 const SMDS_MeshNode* N1new [6];
730 const SMDS_MeshNode* N2new [6];
743 // replaces nodes in faces
744 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
745 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
750 //=======================================================================
751 //function : findTriangles
752 //purpose : find triangles sharing theNode1-theNode2 link
753 //=======================================================================
755 static bool findTriangles(const SMDS_MeshNode * theNode1,
756 const SMDS_MeshNode * theNode2,
757 const SMDS_MeshElement*& theTria1,
758 const SMDS_MeshElement*& theTria2)
760 if ( !theNode1 || !theNode2 ) return false;
762 theTria1 = theTria2 = 0;
764 set< const SMDS_MeshElement* > emap;
765 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
767 const SMDS_MeshElement* elem = it->next();
768 if ( elem->NbNodes() == 3 )
771 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
773 const SMDS_MeshElement* elem = it->next();
774 if ( emap.find( elem ) != emap.end() ) {
776 // theTria1 must be element with minimum ID
777 if( theTria1->GetID() < elem->GetID() ) {
791 return ( theTria1 && theTria2 );
794 //=======================================================================
795 //function : InverseDiag
796 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
797 // with ones built on the same 4 nodes but having other common link.
798 // Return false if proper faces not found
799 //=======================================================================
801 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
802 const SMDS_MeshNode * theNode2)
804 myLastCreatedElems.Clear();
805 myLastCreatedNodes.Clear();
807 MESSAGE( "::InverseDiag()" );
809 const SMDS_MeshElement *tr1, *tr2;
810 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
813 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
814 if (!F1) return false;
815 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
816 if (!F2) return false;
817 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
818 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
820 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
821 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
825 // put nodes in array
826 // and find indices of 1,2 and of A in tr1 and of B in tr2
827 int i, iA1 = 0, i1 = 0;
828 const SMDS_MeshNode* aNodes1 [3];
829 SMDS_ElemIteratorPtr it;
830 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
831 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
832 if ( aNodes1[ i ] == theNode1 )
833 iA1 = i; // node A in tr1
834 else if ( aNodes1[ i ] != theNode2 )
838 const SMDS_MeshNode* aNodes2 [3];
839 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
840 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
841 if ( aNodes2[ i ] == theNode2 )
842 iB2 = i; // node B in tr2
843 else if ( aNodes2[ i ] != theNode1 )
847 // nodes 1 and 2 should not be the same
848 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
852 aNodes1[ iA1 ] = aNodes2[ i2 ];
854 aNodes2[ iB2 ] = aNodes1[ i1 ];
856 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
857 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
862 // check case of quadratic faces
863 return InverseDiag(tr1,tr2);
866 //=======================================================================
867 //function : getQuadrangleNodes
868 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
869 // fusion of triangles tr1 and tr2 having shared link on
870 // theNode1 and theNode2
871 //=======================================================================
873 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
874 const SMDS_MeshNode * theNode1,
875 const SMDS_MeshNode * theNode2,
876 const SMDS_MeshElement * tr1,
877 const SMDS_MeshElement * tr2 )
879 if( tr1->NbNodes() != tr2->NbNodes() )
881 // find the 4-th node to insert into tr1
882 const SMDS_MeshNode* n4 = 0;
883 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
885 while ( !n4 && i<3 ) {
886 const SMDS_MeshNode * n = cast2Node( it->next() );
888 bool isDiag = ( n == theNode1 || n == theNode2 );
892 // Make an array of nodes to be in a quadrangle
893 int iNode = 0, iFirstDiag = -1;
894 it = tr1->nodesIterator();
897 const SMDS_MeshNode * n = cast2Node( it->next() );
899 bool isDiag = ( n == theNode1 || n == theNode2 );
901 if ( iFirstDiag < 0 )
903 else if ( iNode - iFirstDiag == 1 )
904 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
906 else if ( n == n4 ) {
907 return false; // tr1 and tr2 should not have all the same nodes
909 theQuadNodes[ iNode++ ] = n;
911 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
912 theQuadNodes[ iNode ] = n4;
917 //=======================================================================
918 //function : DeleteDiag
919 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
920 // with a quadrangle built on the same 4 nodes.
921 // Return false if proper faces not found
922 //=======================================================================
924 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
925 const SMDS_MeshNode * theNode2)
927 myLastCreatedElems.Clear();
928 myLastCreatedNodes.Clear();
930 MESSAGE( "::DeleteDiag()" );
932 const SMDS_MeshElement *tr1, *tr2;
933 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
936 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
937 if (!F1) return false;
938 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
939 if (!F2) return false;
940 SMESHDS_Mesh * aMesh = GetMeshDS();
942 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
943 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
945 const SMDS_MeshNode* aNodes [ 4 ];
946 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
949 const SMDS_MeshElement* newElem = 0;
950 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
951 myLastCreatedElems.Append(newElem);
952 AddToSameGroups( newElem, tr1, aMesh );
953 int aShapeId = tr1->getshapeId();
956 aMesh->SetMeshElementOnShape( newElem, aShapeId );
958 aMesh->RemoveElement( tr1 );
959 aMesh->RemoveElement( tr2 );
964 // check case of quadratic faces
965 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
967 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
971 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
972 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
980 const SMDS_MeshNode* N1 [6];
981 const SMDS_MeshNode* N2 [6];
982 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
984 // now we receive following N1 and N2 (using numeration as above image)
985 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
986 // i.e. first nodes from both arrays determ new diagonal
988 const SMDS_MeshNode* aNodes[8];
998 const SMDS_MeshElement* newElem = 0;
999 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1000 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1001 myLastCreatedElems.Append(newElem);
1002 AddToSameGroups( newElem, tr1, aMesh );
1003 int aShapeId = tr1->getshapeId();
1006 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1008 aMesh->RemoveElement( tr1 );
1009 aMesh->RemoveElement( tr2 );
1011 // remove middle node (9)
1012 GetMeshDS()->RemoveNode( N1[4] );
1017 //=======================================================================
1018 //function : Reorient
1019 //purpose : Reverse theElement orientation
1020 //=======================================================================
1022 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1024 MESSAGE("Reorient");
1025 myLastCreatedElems.Clear();
1026 myLastCreatedNodes.Clear();
1030 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1031 if ( !it || !it->more() )
1034 switch ( theElem->GetType() ) {
1037 case SMDSAbs_Face: {
1038 if(!theElem->IsQuadratic()) {
1039 int i = theElem->NbNodes();
1040 vector<const SMDS_MeshNode*> aNodes( i );
1041 while ( it->more() )
1042 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1043 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1046 // quadratic elements
1047 if(theElem->GetType()==SMDSAbs_Edge) {
1048 vector<const SMDS_MeshNode*> aNodes(3);
1049 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1050 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1051 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1052 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1055 int nbn = theElem->NbNodes();
1056 vector<const SMDS_MeshNode*> aNodes(nbn);
1057 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1059 for(; i<nbn/2; i++) {
1060 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1062 for(i=0; i<nbn/2; i++) {
1063 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1065 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1069 case SMDSAbs_Volume: {
1070 if (theElem->IsPoly()) {
1071 // TODO reorient vtk polyhedron
1072 MESSAGE("reorient vtk polyhedron ?");
1073 const SMDS_VtkVolume* aPolyedre =
1074 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1076 MESSAGE("Warning: bad volumic element");
1080 int nbFaces = aPolyedre->NbFaces();
1081 vector<const SMDS_MeshNode *> poly_nodes;
1082 vector<int> quantities (nbFaces);
1084 // reverse each face of the polyedre
1085 for (int iface = 1; iface <= nbFaces; iface++) {
1086 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1087 quantities[iface - 1] = nbFaceNodes;
1089 for (inode = nbFaceNodes; inode >= 1; inode--) {
1090 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1091 poly_nodes.push_back(curNode);
1095 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099 SMDS_VolumeTool vTool;
1100 if ( !vTool.Set( theElem ))
1103 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1104 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1113 //================================================================================
1115 * \brief Reorient faces.
1116 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1117 * \param theDirection - desired direction of normal of \a theFace
1118 * \param theFace - one of \a theFaces that sould be oriented according to
1119 * \a theDirection and whose orientation defines orientation of other faces
1120 * \return number of reoriented faces.
1122 //================================================================================
1124 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1125 const gp_Dir& theDirection,
1126 const SMDS_MeshElement * theFace)
1129 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1131 if ( theFaces.empty() )
1133 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1134 while ( fIt->more() )
1135 theFaces.insert( theFaces.end(), fIt->next() );
1138 // orient theFace according to theDirection
1140 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1141 if ( normal * theDirection.XYZ() < 0 )
1142 nbReori += Reorient( theFace );
1144 // Orient other faces
1146 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1147 TIDSortedElemSet avoidSet;
1148 set< SMESH_TLink > checkedLinks;
1149 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1151 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1152 theFaces.erase( theFace );
1153 startFaces.insert( theFace );
1155 int nodeInd1, nodeInd2;
1156 const SMDS_MeshElement* otherFace;
1157 vector< const SMDS_MeshElement* > facesNearLink;
1158 vector< std::pair< int, int > > nodeIndsOfFace;
1160 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1161 while ( !startFaces.empty() )
1163 startFace = startFaces.begin();
1164 theFace = *startFace;
1165 startFaces.erase( startFace );
1166 if ( !visitedFaces.insert( theFace ).second )
1170 avoidSet.insert(theFace);
1172 NLink link( theFace->GetNode( 0 ), 0 );
1174 const int nbNodes = theFace->NbCornerNodes();
1175 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1177 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1178 linkIt_isNew = checkedLinks.insert( link );
1179 if ( !linkIt_isNew.second )
1181 // link has already been checked and won't be encountered more
1182 // if the group (theFaces) is manifold
1183 //checkedLinks.erase( linkIt_isNew.first );
1187 facesNearLink.clear();
1188 nodeIndsOfFace.clear();
1189 while (( otherFace = FindFaceInSet( link.first, link.second,
1190 theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1191 if ( otherFace != theFace)
1193 facesNearLink.push_back( otherFace );
1194 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1195 avoidSet.insert( otherFace );
1197 if ( facesNearLink.size() > 1 )
1199 // NON-MANIFOLD mesh shell !
1200 // select a face most co-directed with theFace,
1201 // other faces won't be visited this time
1203 SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1204 double proj, maxProj = -1;
1205 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1206 SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1207 if (( proj = Abs( NF * NOF )) > maxProj ) {
1209 otherFace = facesNearLink[i];
1210 nodeInd1 = nodeIndsOfFace[i].first;
1211 nodeInd2 = nodeIndsOfFace[i].second;
1214 // not to visit rejected faces
1215 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1216 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1217 visitedFaces.insert( facesNearLink[i] );
1219 else if ( facesNearLink.size() == 1 )
1221 otherFace = facesNearLink[0];
1222 nodeInd1 = nodeIndsOfFace.back().first;
1223 nodeInd2 = nodeIndsOfFace.back().second;
1225 if ( otherFace && otherFace != theFace)
1227 // link must be reverse in otherFace if orientation ot otherFace
1228 // is same as that of theFace
1229 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1231 nbReori += Reorient( otherFace );
1233 startFaces.insert( otherFace );
1236 std::swap( link.first, link.second ); // reverse the link
1242 //=======================================================================
1243 //function : getBadRate
1245 //=======================================================================
1247 static double getBadRate (const SMDS_MeshElement* theElem,
1248 SMESH::Controls::NumericalFunctorPtr& theCrit)
1250 SMESH::Controls::TSequenceOfXYZ P;
1251 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1253 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1254 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1257 //=======================================================================
1258 //function : QuadToTri
1259 //purpose : Cut quadrangles into triangles.
1260 // theCrit is used to select a diagonal to cut
1261 //=======================================================================
1263 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1264 SMESH::Controls::NumericalFunctorPtr theCrit)
1266 myLastCreatedElems.Clear();
1267 myLastCreatedNodes.Clear();
1269 MESSAGE( "::QuadToTri()" );
1271 if ( !theCrit.get() )
1274 SMESHDS_Mesh * aMesh = GetMeshDS();
1276 Handle(Geom_Surface) surface;
1277 SMESH_MesherHelper helper( *GetMesh() );
1279 TIDSortedElemSet::iterator itElem;
1280 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1281 const SMDS_MeshElement* elem = *itElem;
1282 if ( !elem || elem->GetType() != SMDSAbs_Face )
1284 if ( elem->NbCornerNodes() != 4 )
1287 // retrieve element nodes
1288 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1290 // compare two sets of possible triangles
1291 double aBadRate1, aBadRate2; // to what extent a set is bad
1292 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1293 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1294 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1296 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1297 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1298 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1300 int aShapeId = FindShape( elem );
1301 const SMDS_MeshElement* newElem1 = 0;
1302 const SMDS_MeshElement* newElem2 = 0;
1304 if( !elem->IsQuadratic() ) {
1306 // split liner quadrangle
1307 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1308 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1309 if ( aBadRate1 <= aBadRate2 ) {
1310 // tr1 + tr2 is better
1311 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1312 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1315 // tr3 + tr4 is better
1316 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1317 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1322 // split quadratic quadrangle
1324 // get surface elem is on
1325 if ( aShapeId != helper.GetSubShapeID() ) {
1329 shape = aMesh->IndexToShape( aShapeId );
1330 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1331 TopoDS_Face face = TopoDS::Face( shape );
1332 surface = BRep_Tool::Surface( face );
1333 if ( !surface.IsNull() )
1334 helper.SetSubShape( shape );
1337 // find middle point for (0,1,2,3)
1338 // and create a node in this point;
1339 const SMDS_MeshNode* newN = 0;
1340 if ( aNodes.size() == 9 )
1342 // SMDSEntity_BiQuad_Quadrangle
1343 newN = aNodes.back();
1348 if ( surface.IsNull() )
1350 for ( int i = 0; i < 4; i++ )
1351 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1356 const SMDS_MeshNode* inFaceNode = 0;
1357 if ( helper.GetNodeUVneedInFaceNode() )
1358 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1359 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1360 inFaceNode = aNodes[ i ];
1362 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1364 for ( int i = 0; i < 4; i++ )
1365 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1367 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1369 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1370 myLastCreatedNodes.Append(newN);
1372 // create a new element
1373 if ( aBadRate1 <= aBadRate2 ) {
1374 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1375 aNodes[6], aNodes[7], newN );
1376 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1377 newN, aNodes[4], aNodes[5] );
1380 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1381 aNodes[7], aNodes[4], newN );
1382 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1383 newN, aNodes[5], aNodes[6] );
1387 // care of a new element
1389 myLastCreatedElems.Append(newElem1);
1390 myLastCreatedElems.Append(newElem2);
1391 AddToSameGroups( newElem1, elem, aMesh );
1392 AddToSameGroups( newElem2, elem, aMesh );
1394 // put a new triangle on the same shape
1397 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1398 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1400 aMesh->RemoveElement( elem );
1405 //=======================================================================
1406 //function : BestSplit
1407 //purpose : Find better diagonal for cutting.
1408 //=======================================================================
1410 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1411 SMESH::Controls::NumericalFunctorPtr theCrit)
1413 myLastCreatedElems.Clear();
1414 myLastCreatedNodes.Clear();
1419 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1422 if( theQuad->NbNodes()==4 ||
1423 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1425 // retrieve element nodes
1426 const SMDS_MeshNode* aNodes [4];
1427 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1429 //while (itN->more())
1431 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1433 // compare two sets of possible triangles
1434 double aBadRate1, aBadRate2; // to what extent a set is bad
1435 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1436 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1437 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1439 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1440 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1441 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1442 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1443 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1444 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1445 return 1; // diagonal 1-3
1447 return 2; // diagonal 2-4
1454 // Methods of splitting volumes into tetra
1456 const int theHexTo5_1[5*4+1] =
1458 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1460 const int theHexTo5_2[5*4+1] =
1462 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1464 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1466 const int theHexTo6_1[6*4+1] =
1468 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
1470 const int theHexTo6_2[6*4+1] =
1472 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
1474 const int theHexTo6_3[6*4+1] =
1476 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
1478 const int theHexTo6_4[6*4+1] =
1480 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
1482 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1484 const int thePyraTo2_1[2*4+1] =
1486 0, 1, 2, 4, 0, 2, 3, 4, -1
1488 const int thePyraTo2_2[2*4+1] =
1490 1, 2, 3, 4, 1, 3, 0, 4, -1
1492 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1494 const int thePentaTo3_1[3*4+1] =
1496 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1498 const int thePentaTo3_2[3*4+1] =
1500 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1502 const int thePentaTo3_3[3*4+1] =
1504 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1506 const int thePentaTo3_4[3*4+1] =
1508 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1510 const int thePentaTo3_5[3*4+1] =
1512 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1514 const int thePentaTo3_6[3*4+1] =
1516 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1518 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1519 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1521 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1524 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1525 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1526 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1531 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1532 bool _baryNode; //!< additional node is to be created at cell barycenter
1533 bool _ownConn; //!< to delete _connectivity in destructor
1534 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1536 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1537 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1538 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1539 bool hasFacet( const TTriangleFacet& facet ) const
1541 const int* tetConn = _connectivity;
1542 for ( ; tetConn[0] >= 0; tetConn += 4 )
1543 if (( facet.contains( tetConn[0] ) +
1544 facet.contains( tetConn[1] ) +
1545 facet.contains( tetConn[2] ) +
1546 facet.contains( tetConn[3] )) == 3 )
1552 //=======================================================================
1554 * \brief return TSplitMethod for the given element
1556 //=======================================================================
1558 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1560 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1562 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1563 // an edge and a face barycenter; tertaherdons are based on triangles and
1564 // a volume barycenter
1565 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1567 // Find out how adjacent volumes are split
1569 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1570 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1571 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1573 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1574 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1575 if ( nbNodes < 4 ) continue;
1577 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1578 const int* nInd = vol.GetFaceNodesIndices( iF );
1581 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1582 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1583 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1584 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1588 int iCom = 0; // common node of triangle faces to split into
1589 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1591 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1592 nInd[ iQ * ( (iCom+1)%nbNodes )],
1593 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1594 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1595 nInd[ iQ * ( (iCom+2)%nbNodes )],
1596 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1597 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1599 triaSplits.push_back( t012 );
1600 triaSplits.push_back( t023 );
1605 if ( !triaSplits.empty() )
1606 hasAdjacentSplits = true;
1609 // Among variants of split method select one compliant with adjacent volumes
1611 TSplitMethod method;
1612 if ( !vol.Element()->IsPoly() && !is24TetMode )
1614 int nbVariants = 2, nbTet = 0;
1615 const int** connVariants = 0;
1616 switch ( vol.Element()->GetEntityType() )
1618 case SMDSEntity_Hexa:
1619 case SMDSEntity_Quad_Hexa:
1620 case SMDSEntity_TriQuad_Hexa:
1621 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1622 connVariants = theHexTo5, nbTet = 5;
1624 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1626 case SMDSEntity_Pyramid:
1627 case SMDSEntity_Quad_Pyramid:
1628 connVariants = thePyraTo2; nbTet = 2;
1630 case SMDSEntity_Penta:
1631 case SMDSEntity_Quad_Penta:
1632 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1637 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1639 // check method compliancy with adjacent tetras,
1640 // all found splits must be among facets of tetras described by this method
1641 method = TSplitMethod( nbTet, connVariants[variant] );
1642 if ( hasAdjacentSplits && method._nbTetra > 0 )
1644 bool facetCreated = true;
1645 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1647 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1648 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1649 facetCreated = method.hasFacet( *facet );
1651 if ( !facetCreated )
1652 method = TSplitMethod(0); // incompatible method
1656 if ( method._nbTetra < 1 )
1658 // No standard method is applicable, use a generic solution:
1659 // each facet of a volume is split into triangles and
1660 // each of triangles and a volume barycenter form a tetrahedron.
1662 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1664 int* connectivity = new int[ maxTetConnSize + 1 ];
1665 method._connectivity = connectivity;
1666 method._ownConn = true;
1667 method._baryNode = !isHex27; // to create central node or not
1670 int baryCenInd = vol.NbNodes() - int( isHex27 );
1671 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1673 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1674 const int* nInd = vol.GetFaceNodesIndices( iF );
1675 // find common node of triangle facets of tetra to create
1676 int iCommon = 0; // index in linear numeration
1677 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1678 if ( !triaSplits.empty() )
1681 const TTriangleFacet* facet = &triaSplits.front();
1682 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1683 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1684 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1687 else if ( nbNodes > 3 && !is24TetMode )
1689 // find the best method of splitting into triangles by aspect ratio
1690 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1691 map< double, int > badness2iCommon;
1692 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1693 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1694 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1697 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1699 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1700 nodes[ iQ*((iLast-1)%nbNodes)],
1701 nodes[ iQ*((iLast )%nbNodes)]);
1702 badness += getBadRate( &tria, aspectRatio );
1704 badness2iCommon.insert( make_pair( badness, iCommon ));
1706 // use iCommon with lowest badness
1707 iCommon = badness2iCommon.begin()->second;
1709 if ( iCommon >= nbNodes )
1710 iCommon = 0; // something wrong
1712 // fill connectivity of tetrahedra based on a current face
1713 int nbTet = nbNodes - 2;
1714 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1719 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1720 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1724 method._faceBaryNode[ iF ] = 0;
1725 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1728 for ( int i = 0; i < nbTet; ++i )
1730 int i1 = i, i2 = (i+1) % nbNodes;
1731 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1732 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1733 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1734 connectivity[ connSize++ ] = faceBaryCenInd;
1735 connectivity[ connSize++ ] = baryCenInd;
1740 for ( int i = 0; i < nbTet; ++i )
1742 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1743 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1744 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1745 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1746 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1747 connectivity[ connSize++ ] = baryCenInd;
1750 method._nbTetra += nbTet;
1752 } // loop on volume faces
1754 connectivity[ connSize++ ] = -1;
1756 } // end of generic solution
1760 //================================================================================
1762 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1764 //================================================================================
1766 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1768 // find the tetrahedron including the three nodes of facet
1769 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1770 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1771 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1772 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1773 while ( volIt1->more() )
1775 const SMDS_MeshElement* v = volIt1->next();
1776 SMDSAbs_EntityType type = v->GetEntityType();
1777 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1779 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1780 continue; // medium node not allowed
1781 const int ind2 = v->GetNodeIndex( n2 );
1782 if ( ind2 < 0 || 3 < ind2 )
1784 const int ind3 = v->GetNodeIndex( n3 );
1785 if ( ind3 < 0 || 3 < ind3 )
1792 //=======================================================================
1794 * \brief A key of a face of volume
1796 //=======================================================================
1798 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1800 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1802 TIDSortedNodeSet sortedNodes;
1803 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1804 int nbNodes = vol.NbFaceNodes( iF );
1805 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1806 for ( int i = 0; i < nbNodes; i += iQ )
1807 sortedNodes.insert( fNodes[i] );
1808 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1809 first.first = (*(n++))->GetID();
1810 first.second = (*(n++))->GetID();
1811 second.first = (*(n++))->GetID();
1812 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1817 //=======================================================================
1818 //function : SplitVolumesIntoTetra
1819 //purpose : Split volume elements into tetrahedra.
1820 //=======================================================================
1822 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1823 const int theMethodFlags)
1825 // std-like iterator on coordinates of nodes of mesh element
1826 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1827 NXyzIterator xyzEnd;
1829 SMDS_VolumeTool volTool;
1830 SMESH_MesherHelper helper( *GetMesh());
1832 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1833 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1835 SMESH_SequenceOfElemPtr newNodes, newElems;
1837 // map face of volume to it's baricenrtic node
1838 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1841 TIDSortedElemSet::const_iterator elem = theElems.begin();
1842 for ( ; elem != theElems.end(); ++elem )
1844 if ( (*elem)->GetType() != SMDSAbs_Volume )
1846 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1847 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1850 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1852 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1853 if ( splitMethod._nbTetra < 1 ) continue;
1855 // find submesh to add new tetras to
1856 if ( !subMesh || !subMesh->Contains( *elem ))
1858 int shapeID = FindShape( *elem );
1859 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1860 subMesh = GetMeshDS()->MeshElements( shapeID );
1863 if ( (*elem)->IsQuadratic() )
1866 // add quadratic links to the helper
1867 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1869 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1870 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1871 for ( int iN = 0; iN < nbN; iN += iQ )
1872 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1874 helper.SetIsQuadratic( true );
1879 helper.SetIsQuadratic( false );
1881 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1882 helper.SetElementsOnShape( true );
1883 if ( splitMethod._baryNode )
1885 // make a node at barycenter
1886 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1887 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1888 nodes.push_back( gcNode );
1889 newNodes.Append( gcNode );
1891 if ( !splitMethod._faceBaryNode.empty() )
1893 // make or find baricentric nodes of faces
1894 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1895 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1897 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1898 volFace2BaryNode.insert
1899 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1902 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1903 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1905 nodes.push_back( iF_n->second = f_n->second );
1910 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1911 const int* tetConn = splitMethod._connectivity;
1912 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1913 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1914 nodes[ tetConn[1] ],
1915 nodes[ tetConn[2] ],
1916 nodes[ tetConn[3] ]));
1918 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1920 // Split faces on sides of the split volume
1922 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1923 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1925 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1926 if ( nbNodes < 4 ) continue;
1928 // find an existing face
1929 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1930 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1931 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1932 /*noMedium=*/false))
1935 helper.SetElementsOnShape( false );
1936 vector< const SMDS_MeshElement* > triangles;
1938 // find submesh to add new triangles in
1939 if ( !fSubMesh || !fSubMesh->Contains( face ))
1941 int shapeID = FindShape( face );
1942 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1944 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1945 if ( iF_n != splitMethod._faceBaryNode.end() )
1947 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1949 const SMDS_MeshNode* n1 = fNodes[iN];
1950 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1951 const SMDS_MeshNode *n3 = iF_n->second;
1952 if ( !volTool.IsFaceExternal( iF ))
1954 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1956 if ( fSubMesh && n3->getshapeId() < 1 )
1957 fSubMesh->AddNode( n3 );
1962 // among possible triangles create ones discribed by split method
1963 const int* nInd = volTool.GetFaceNodesIndices( iF );
1964 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1965 int iCom = 0; // common node of triangle faces to split into
1966 list< TTriangleFacet > facets;
1967 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1969 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1970 nInd[ iQ * ( (iCom+1)%nbNodes )],
1971 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1972 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1973 nInd[ iQ * ( (iCom+2)%nbNodes )],
1974 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1975 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1977 facets.push_back( t012 );
1978 facets.push_back( t023 );
1979 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1980 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1981 nInd[ iQ * ((iLast-1)%nbNodes )],
1982 nInd[ iQ * ((iLast )%nbNodes )]));
1986 list< TTriangleFacet >::iterator facet = facets.begin();
1987 for ( ; facet != facets.end(); ++facet )
1989 if ( !volTool.IsFaceExternal( iF ))
1990 swap( facet->_n2, facet->_n3 );
1991 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1992 volNodes[ facet->_n2 ],
1993 volNodes[ facet->_n3 ]));
1996 for ( int i = 0; i < triangles.size(); ++i )
1998 if ( !triangles[i] ) continue;
2000 fSubMesh->AddElement( triangles[i]);
2001 newElems.Append( triangles[i] );
2003 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2004 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2007 } // loop on volume faces to split them into triangles
2009 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2011 if ( geomType == SMDSEntity_TriQuad_Hexa )
2013 // remove medium nodes that could become free
2014 for ( int i = 20; i < volTool.NbNodes(); ++i )
2015 if ( volNodes[i]->NbInverseElements() == 0 )
2016 GetMeshDS()->RemoveNode( volNodes[i] );
2018 } // loop on volumes to split
2020 myLastCreatedNodes = newNodes;
2021 myLastCreatedElems = newElems;
2024 //=======================================================================
2025 //function : AddToSameGroups
2026 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2027 //=======================================================================
2029 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2030 const SMDS_MeshElement* elemInGroups,
2031 SMESHDS_Mesh * aMesh)
2033 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2034 if (!groups.empty()) {
2035 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2036 for ( ; grIt != groups.end(); grIt++ ) {
2037 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2038 if ( group && group->Contains( elemInGroups ))
2039 group->SMDSGroup().Add( elemToAdd );
2045 //=======================================================================
2046 //function : RemoveElemFromGroups
2047 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2048 //=======================================================================
2049 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2050 SMESHDS_Mesh * aMesh)
2052 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2053 if (!groups.empty())
2055 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2056 for (; GrIt != groups.end(); GrIt++)
2058 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2059 if (!grp || grp->IsEmpty()) continue;
2060 grp->SMDSGroup().Remove(removeelem);
2065 //================================================================================
2067 * \brief Replace elemToRm by elemToAdd in the all groups
2069 //================================================================================
2071 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2072 const SMDS_MeshElement* elemToAdd,
2073 SMESHDS_Mesh * aMesh)
2075 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2076 if (!groups.empty()) {
2077 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2078 for ( ; grIt != groups.end(); grIt++ ) {
2079 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2080 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2081 group->SMDSGroup().Add( elemToAdd );
2086 //================================================================================
2088 * \brief Replace elemToRm by elemToAdd in the all groups
2090 //================================================================================
2092 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2093 const vector<const SMDS_MeshElement*>& elemToAdd,
2094 SMESHDS_Mesh * aMesh)
2096 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2097 if (!groups.empty())
2099 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2100 for ( ; grIt != groups.end(); grIt++ ) {
2101 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2102 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2103 for ( int i = 0; i < elemToAdd.size(); ++i )
2104 group->SMDSGroup().Add( elemToAdd[ i ] );
2109 //=======================================================================
2110 //function : QuadToTri
2111 //purpose : Cut quadrangles into triangles.
2112 // theCrit is used to select a diagonal to cut
2113 //=======================================================================
2115 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2116 const bool the13Diag)
2118 myLastCreatedElems.Clear();
2119 myLastCreatedNodes.Clear();
2121 MESSAGE( "::QuadToTri()" );
2123 SMESHDS_Mesh * aMesh = GetMeshDS();
2125 Handle(Geom_Surface) surface;
2126 SMESH_MesherHelper helper( *GetMesh() );
2128 TIDSortedElemSet::iterator itElem;
2129 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2130 const SMDS_MeshElement* elem = *itElem;
2131 if ( !elem || elem->GetType() != SMDSAbs_Face )
2133 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2134 if(!isquad) continue;
2136 if(elem->NbNodes()==4) {
2137 // retrieve element nodes
2138 const SMDS_MeshNode* aNodes [4];
2139 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2141 while ( itN->more() )
2142 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2144 int aShapeId = FindShape( elem );
2145 const SMDS_MeshElement* newElem1 = 0;
2146 const SMDS_MeshElement* newElem2 = 0;
2148 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2149 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2152 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2153 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2155 myLastCreatedElems.Append(newElem1);
2156 myLastCreatedElems.Append(newElem2);
2157 // put a new triangle on the same shape and add to the same groups
2160 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2161 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2163 AddToSameGroups( newElem1, elem, aMesh );
2164 AddToSameGroups( newElem2, elem, aMesh );
2165 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2166 aMesh->RemoveElement( elem );
2169 // Quadratic quadrangle
2171 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2173 // get surface elem is on
2174 int aShapeId = FindShape( elem );
2175 if ( aShapeId != helper.GetSubShapeID() ) {
2179 shape = aMesh->IndexToShape( aShapeId );
2180 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2181 TopoDS_Face face = TopoDS::Face( shape );
2182 surface = BRep_Tool::Surface( face );
2183 if ( !surface.IsNull() )
2184 helper.SetSubShape( shape );
2188 const SMDS_MeshNode* aNodes [8];
2189 const SMDS_MeshNode* inFaceNode = 0;
2190 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2192 while ( itN->more() ) {
2193 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2194 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2195 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2197 inFaceNode = aNodes[ i-1 ];
2201 // find middle point for (0,1,2,3)
2202 // and create a node in this point;
2204 if ( surface.IsNull() ) {
2206 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2210 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2213 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2215 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2217 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2218 myLastCreatedNodes.Append(newN);
2220 // create a new element
2221 const SMDS_MeshElement* newElem1 = 0;
2222 const SMDS_MeshElement* newElem2 = 0;
2224 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2225 aNodes[6], aNodes[7], newN );
2226 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2227 newN, aNodes[4], aNodes[5] );
2230 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2231 aNodes[7], aNodes[4], newN );
2232 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2233 newN, aNodes[5], aNodes[6] );
2235 myLastCreatedElems.Append(newElem1);
2236 myLastCreatedElems.Append(newElem2);
2237 // put a new triangle on the same shape and add to the same groups
2240 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2241 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2243 AddToSameGroups( newElem1, elem, aMesh );
2244 AddToSameGroups( newElem2, elem, aMesh );
2245 aMesh->RemoveElement( elem );
2252 //=======================================================================
2253 //function : getAngle
2255 //=======================================================================
2257 double getAngle(const SMDS_MeshElement * tr1,
2258 const SMDS_MeshElement * tr2,
2259 const SMDS_MeshNode * n1,
2260 const SMDS_MeshNode * n2)
2262 double angle = 2. * M_PI; // bad angle
2265 SMESH::Controls::TSequenceOfXYZ P1, P2;
2266 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2267 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2270 if(!tr1->IsQuadratic())
2271 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2273 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2274 if ( N1.SquareMagnitude() <= gp::Resolution() )
2276 if(!tr2->IsQuadratic())
2277 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2279 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2280 if ( N2.SquareMagnitude() <= gp::Resolution() )
2283 // find the first diagonal node n1 in the triangles:
2284 // take in account a diagonal link orientation
2285 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2286 for ( int t = 0; t < 2; t++ ) {
2287 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2288 int i = 0, iDiag = -1;
2289 while ( it->more()) {
2290 const SMDS_MeshElement *n = it->next();
2291 if ( n == n1 || n == n2 ) {
2295 if ( i - iDiag == 1 )
2296 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2305 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2308 angle = N1.Angle( N2 );
2313 // =================================================
2314 // class generating a unique ID for a pair of nodes
2315 // and able to return nodes by that ID
2316 // =================================================
2320 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2321 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2324 long GetLinkID (const SMDS_MeshNode * n1,
2325 const SMDS_MeshNode * n2) const
2327 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2330 bool GetNodes (const long theLinkID,
2331 const SMDS_MeshNode* & theNode1,
2332 const SMDS_MeshNode* & theNode2) const
2334 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2335 if ( !theNode1 ) return false;
2336 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2337 if ( !theNode2 ) return false;
2343 const SMESHDS_Mesh* myMesh;
2348 //=======================================================================
2349 //function : TriToQuad
2350 //purpose : Fuse neighbour triangles into quadrangles.
2351 // theCrit is used to select a neighbour to fuse with.
2352 // theMaxAngle is a max angle between element normals at which
2353 // fusion is still performed.
2354 //=======================================================================
2356 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2357 SMESH::Controls::NumericalFunctorPtr theCrit,
2358 const double theMaxAngle)
2360 myLastCreatedElems.Clear();
2361 myLastCreatedNodes.Clear();
2363 MESSAGE( "::TriToQuad()" );
2365 if ( !theCrit.get() )
2368 SMESHDS_Mesh * aMesh = GetMeshDS();
2370 // Prepare data for algo: build
2371 // 1. map of elements with their linkIDs
2372 // 2. map of linkIDs with their elements
2374 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2375 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2376 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2377 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2379 TIDSortedElemSet::iterator itElem;
2380 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2381 const SMDS_MeshElement* elem = *itElem;
2382 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2383 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2384 if(!IsTria) continue;
2386 // retrieve element nodes
2387 const SMDS_MeshNode* aNodes [4];
2388 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2391 aNodes[ i++ ] = cast2Node( itN->next() );
2392 aNodes[ 3 ] = aNodes[ 0 ];
2395 for ( i = 0; i < 3; i++ ) {
2396 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2397 // check if elements sharing a link can be fused
2398 itLE = mapLi_listEl.find( link );
2399 if ( itLE != mapLi_listEl.end() ) {
2400 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2402 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2403 //if ( FindShape( elem ) != FindShape( elem2 ))
2404 // continue; // do not fuse triangles laying on different shapes
2405 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2406 continue; // avoid making badly shaped quads
2407 (*itLE).second.push_back( elem );
2410 mapLi_listEl[ link ].push_back( elem );
2412 mapEl_setLi [ elem ].insert( link );
2415 // Clean the maps from the links shared by a sole element, ie
2416 // links to which only one element is bound in mapLi_listEl
2418 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2419 int nbElems = (*itLE).second.size();
2420 if ( nbElems < 2 ) {
2421 const SMDS_MeshElement* elem = (*itLE).second.front();
2422 SMESH_TLink link = (*itLE).first;
2423 mapEl_setLi[ elem ].erase( link );
2424 if ( mapEl_setLi[ elem ].empty() )
2425 mapEl_setLi.erase( elem );
2429 // Algo: fuse triangles into quadrangles
2431 while ( ! mapEl_setLi.empty() ) {
2432 // Look for the start element:
2433 // the element having the least nb of shared links
2434 const SMDS_MeshElement* startElem = 0;
2436 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2437 int nbLinks = (*itEL).second.size();
2438 if ( nbLinks < minNbLinks ) {
2439 startElem = (*itEL).first;
2440 minNbLinks = nbLinks;
2441 if ( minNbLinks == 1 )
2446 // search elements to fuse starting from startElem or links of elements
2447 // fused earlyer - startLinks
2448 list< SMESH_TLink > startLinks;
2449 while ( startElem || !startLinks.empty() ) {
2450 while ( !startElem && !startLinks.empty() ) {
2451 // Get an element to start, by a link
2452 SMESH_TLink linkId = startLinks.front();
2453 startLinks.pop_front();
2454 itLE = mapLi_listEl.find( linkId );
2455 if ( itLE != mapLi_listEl.end() ) {
2456 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2457 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2458 for ( ; itE != listElem.end() ; itE++ )
2459 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2461 mapLi_listEl.erase( itLE );
2466 // Get candidates to be fused
2467 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2468 const SMESH_TLink *link12, *link13;
2470 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2471 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2472 ASSERT( !setLi.empty() );
2473 set< SMESH_TLink >::iterator itLi;
2474 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2476 const SMESH_TLink & link = (*itLi);
2477 itLE = mapLi_listEl.find( link );
2478 if ( itLE == mapLi_listEl.end() )
2481 const SMDS_MeshElement* elem = (*itLE).second.front();
2483 elem = (*itLE).second.back();
2484 mapLi_listEl.erase( itLE );
2485 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2496 // add other links of elem to list of links to re-start from
2497 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2498 set< SMESH_TLink >::iterator it;
2499 for ( it = links.begin(); it != links.end(); it++ ) {
2500 const SMESH_TLink& link2 = (*it);
2501 if ( link2 != link )
2502 startLinks.push_back( link2 );
2506 // Get nodes of possible quadrangles
2507 const SMDS_MeshNode *n12 [4], *n13 [4];
2508 bool Ok12 = false, Ok13 = false;
2509 const SMDS_MeshNode *linkNode1, *linkNode2;
2511 linkNode1 = link12->first;
2512 linkNode2 = link12->second;
2513 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2517 linkNode1 = link13->first;
2518 linkNode2 = link13->second;
2519 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2523 // Choose a pair to fuse
2524 if ( Ok12 && Ok13 ) {
2525 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2526 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2527 double aBadRate12 = getBadRate( &quad12, theCrit );
2528 double aBadRate13 = getBadRate( &quad13, theCrit );
2529 if ( aBadRate13 < aBadRate12 )
2536 // and remove fused elems and removed links from the maps
2537 mapEl_setLi.erase( tr1 );
2539 mapEl_setLi.erase( tr2 );
2540 mapLi_listEl.erase( *link12 );
2541 if(tr1->NbNodes()==3) {
2542 const SMDS_MeshElement* newElem = 0;
2543 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2544 myLastCreatedElems.Append(newElem);
2545 AddToSameGroups( newElem, tr1, aMesh );
2546 int aShapeId = tr1->getshapeId();
2549 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2551 aMesh->RemoveElement( tr1 );
2552 aMesh->RemoveElement( tr2 );
2555 const SMDS_MeshNode* N1 [6];
2556 const SMDS_MeshNode* N2 [6];
2557 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2558 // now we receive following N1 and N2 (using numeration as above image)
2559 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2560 // i.e. first nodes from both arrays determ new diagonal
2561 const SMDS_MeshNode* aNodes[8];
2570 const SMDS_MeshElement* newElem = 0;
2571 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2572 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2573 myLastCreatedElems.Append(newElem);
2574 AddToSameGroups( newElem, tr1, aMesh );
2575 int aShapeId = tr1->getshapeId();
2578 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2580 aMesh->RemoveElement( tr1 );
2581 aMesh->RemoveElement( tr2 );
2582 // remove middle node (9)
2583 GetMeshDS()->RemoveNode( N1[4] );
2587 mapEl_setLi.erase( tr3 );
2588 mapLi_listEl.erase( *link13 );
2589 if(tr1->NbNodes()==3) {
2590 const SMDS_MeshElement* newElem = 0;
2591 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2592 myLastCreatedElems.Append(newElem);
2593 AddToSameGroups( newElem, tr1, aMesh );
2594 int aShapeId = tr1->getshapeId();
2597 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2599 aMesh->RemoveElement( tr1 );
2600 aMesh->RemoveElement( tr3 );
2603 const SMDS_MeshNode* N1 [6];
2604 const SMDS_MeshNode* N2 [6];
2605 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2606 // now we receive following N1 and N2 (using numeration as above image)
2607 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2608 // i.e. first nodes from both arrays determ new diagonal
2609 const SMDS_MeshNode* aNodes[8];
2618 const SMDS_MeshElement* newElem = 0;
2619 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2620 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2621 myLastCreatedElems.Append(newElem);
2622 AddToSameGroups( newElem, tr1, aMesh );
2623 int aShapeId = tr1->getshapeId();
2626 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2628 aMesh->RemoveElement( tr1 );
2629 aMesh->RemoveElement( tr3 );
2630 // remove middle node (9)
2631 GetMeshDS()->RemoveNode( N1[4] );
2635 // Next element to fuse: the rejected one
2637 startElem = Ok12 ? tr3 : tr2;
2639 } // if ( startElem )
2640 } // while ( startElem || !startLinks.empty() )
2641 } // while ( ! mapEl_setLi.empty() )
2647 /*#define DUMPSO(txt) \
2648 // cout << txt << endl;
2649 //=============================================================================
2653 //=============================================================================
2654 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2658 int tmp = idNodes[ i1 ];
2659 idNodes[ i1 ] = idNodes[ i2 ];
2660 idNodes[ i2 ] = tmp;
2661 gp_Pnt Ptmp = P[ i1 ];
2664 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2667 //=======================================================================
2668 //function : SortQuadNodes
2669 //purpose : Set 4 nodes of a quadrangle face in a good order.
2670 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2672 //=======================================================================
2674 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2679 for ( i = 0; i < 4; i++ ) {
2680 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2682 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2685 gp_Vec V1(P[0], P[1]);
2686 gp_Vec V2(P[0], P[2]);
2687 gp_Vec V3(P[0], P[3]);
2689 gp_Vec Cross1 = V1 ^ V2;
2690 gp_Vec Cross2 = V2 ^ V3;
2693 if (Cross1.Dot(Cross2) < 0)
2698 if (Cross1.Dot(Cross2) < 0)
2702 swap ( i, i + 1, idNodes, P );
2704 // for ( int ii = 0; ii < 4; ii++ ) {
2705 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2706 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2712 //=======================================================================
2713 //function : SortHexaNodes
2714 //purpose : Set 8 nodes of a hexahedron in a good order.
2715 // Return success status
2716 //=======================================================================
2718 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2723 DUMPSO( "INPUT: ========================================");
2724 for ( i = 0; i < 8; i++ ) {
2725 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2726 if ( !n ) return false;
2727 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2728 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2730 DUMPSO( "========================================");
2733 set<int> faceNodes; // ids of bottom face nodes, to be found
2734 set<int> checkedId1; // ids of tried 2-nd nodes
2735 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2736 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2737 int iMin, iLoop1 = 0;
2739 // Loop to try the 2-nd nodes
2741 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2743 // Find not checked 2-nd node
2744 for ( i = 1; i < 8; i++ )
2745 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2746 int id1 = idNodes[i];
2747 swap ( 1, i, idNodes, P );
2748 checkedId1.insert ( id1 );
2752 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2753 // ie that all but meybe one (id3 which is on the same face) nodes
2754 // lay on the same side from the triangle plane.
2756 bool manyInPlane = false; // more than 4 nodes lay in plane
2758 while ( ++iLoop2 < 6 ) {
2760 // get 1-2-3 plane coeffs
2761 Standard_Real A, B, C, D;
2762 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2763 if ( N.SquareMagnitude() > gp::Resolution() )
2765 gp_Pln pln ( P[0], N );
2766 pln.Coefficients( A, B, C, D );
2768 // find the node (iMin) closest to pln
2769 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2771 for ( i = 3; i < 8; i++ ) {
2772 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2773 if ( fabs( dist[i] ) < minDist ) {
2774 minDist = fabs( dist[i] );
2777 if ( fabs( dist[i] ) <= tol )
2778 idInPln.insert( idNodes[i] );
2781 // there should not be more than 4 nodes in bottom plane
2782 if ( idInPln.size() > 1 )
2784 DUMPSO( "### idInPln.size() = " << idInPln.size());
2785 // idInPlane does not contain the first 3 nodes
2786 if ( manyInPlane || idInPln.size() == 5)
2787 return false; // all nodes in one plane
2790 // set the 1-st node to be not in plane
2791 for ( i = 3; i < 8; i++ ) {
2792 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2793 DUMPSO( "### Reset 0-th node");
2794 swap( 0, i, idNodes, P );
2799 // reset to re-check second nodes
2800 leastDist = DBL_MAX;
2804 break; // from iLoop2;
2807 // check that the other 4 nodes are on the same side
2808 bool sameSide = true;
2809 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2810 for ( i = 3; sameSide && i < 8; i++ ) {
2812 sameSide = ( isNeg == dist[i] <= 0.);
2815 // keep best solution
2816 if ( sameSide && minDist < leastDist ) {
2817 leastDist = minDist;
2819 faceNodes.insert( idNodes[ 1 ] );
2820 faceNodes.insert( idNodes[ 2 ] );
2821 faceNodes.insert( idNodes[ iMin ] );
2822 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2823 << " leastDist = " << leastDist);
2824 if ( leastDist <= DBL_MIN )
2829 // set next 3-d node to check
2830 int iNext = 2 + iLoop2;
2832 DUMPSO( "Try 2-nd");
2833 swap ( 2, iNext, idNodes, P );
2835 } // while ( iLoop2 < 6 )
2838 if ( faceNodes.empty() ) return false;
2840 // Put the faceNodes in proper places
2841 for ( i = 4; i < 8; i++ ) {
2842 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2843 // find a place to put
2845 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2847 DUMPSO( "Set faceNodes");
2848 swap ( iTo, i, idNodes, P );
2853 // Set nodes of the found bottom face in good order
2854 DUMPSO( " Found bottom face: ");
2855 i = SortQuadNodes( theMesh, idNodes );
2857 gp_Pnt Ptmp = P[ i ];
2862 // for ( int ii = 0; ii < 4; ii++ ) {
2863 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2864 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2867 // Gravity center of the top and bottom faces
2868 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2869 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2871 // Get direction from the bottom to the top face
2872 gp_Vec upDir ( aGCb, aGCt );
2873 Standard_Real upDirSize = upDir.Magnitude();
2874 if ( upDirSize <= gp::Resolution() ) return false;
2877 // Assure that the bottom face normal points up
2878 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2879 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2880 if ( Nb.Dot( upDir ) < 0 ) {
2881 DUMPSO( "Reverse bottom face");
2882 swap( 1, 3, idNodes, P );
2885 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2886 Standard_Real minDist = DBL_MAX;
2887 for ( i = 4; i < 8; i++ ) {
2888 // projection of P[i] to the plane defined by P[0] and upDir
2889 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2890 Standard_Real sqDist = P[0].SquareDistance( Pp );
2891 if ( sqDist < minDist ) {
2896 DUMPSO( "Set 4-th");
2897 swap ( 4, iMin, idNodes, P );
2899 // Set nodes of the top face in good order
2900 DUMPSO( "Sort top face");
2901 i = SortQuadNodes( theMesh, &idNodes[4] );
2904 gp_Pnt Ptmp = P[ i ];
2909 // Assure that direction of the top face normal is from the bottom face
2910 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2911 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2912 if ( Nt.Dot( upDir ) < 0 ) {
2913 DUMPSO( "Reverse top face");
2914 swap( 5, 7, idNodes, P );
2917 // DUMPSO( "OUTPUT: ========================================");
2918 // for ( i = 0; i < 8; i++ ) {
2919 // float *p = ugrid->GetPoint(idNodes[i]);
2920 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2926 //================================================================================
2928 * \brief Return nodes linked to the given one
2929 * \param theNode - the node
2930 * \param linkedNodes - the found nodes
2931 * \param type - the type of elements to check
2933 * Medium nodes are ignored
2935 //================================================================================
2937 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2938 TIDSortedElemSet & linkedNodes,
2939 SMDSAbs_ElementType type )
2941 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2942 while ( elemIt->more() )
2944 const SMDS_MeshElement* elem = elemIt->next();
2945 if(elem->GetType() == SMDSAbs_0DElement)
2948 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2949 if ( elem->GetType() == SMDSAbs_Volume )
2951 SMDS_VolumeTool vol( elem );
2952 while ( nodeIt->more() ) {
2953 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2954 if ( theNode != n && vol.IsLinked( theNode, n ))
2955 linkedNodes.insert( n );
2960 for ( int i = 0; nodeIt->more(); ++i ) {
2961 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2962 if ( n == theNode ) {
2963 int iBefore = i - 1;
2965 if ( elem->IsQuadratic() ) {
2966 int nb = elem->NbNodes() / 2;
2967 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2968 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2970 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2971 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2978 //=======================================================================
2979 //function : laplacianSmooth
2980 //purpose : pulls theNode toward the center of surrounding nodes directly
2981 // connected to that node along an element edge
2982 //=======================================================================
2984 void laplacianSmooth(const SMDS_MeshNode* theNode,
2985 const Handle(Geom_Surface)& theSurface,
2986 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2988 // find surrounding nodes
2990 TIDSortedElemSet nodeSet;
2991 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2993 // compute new coodrs
2995 double coord[] = { 0., 0., 0. };
2996 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2997 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2998 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2999 if ( theSurface.IsNull() ) { // smooth in 3D
3000 coord[0] += node->X();
3001 coord[1] += node->Y();
3002 coord[2] += node->Z();
3004 else { // smooth in 2D
3005 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3006 gp_XY* uv = theUVMap[ node ];
3007 coord[0] += uv->X();
3008 coord[1] += uv->Y();
3011 int nbNodes = nodeSet.size();
3014 coord[0] /= nbNodes;
3015 coord[1] /= nbNodes;
3017 if ( !theSurface.IsNull() ) {
3018 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3019 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3020 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3026 coord[2] /= nbNodes;
3030 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3033 //=======================================================================
3034 //function : centroidalSmooth
3035 //purpose : pulls theNode toward the element-area-weighted centroid of the
3036 // surrounding elements
3037 //=======================================================================
3039 void centroidalSmooth(const SMDS_MeshNode* theNode,
3040 const Handle(Geom_Surface)& theSurface,
3041 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3043 gp_XYZ aNewXYZ(0.,0.,0.);
3044 SMESH::Controls::Area anAreaFunc;
3045 double totalArea = 0.;
3050 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3051 while ( elemIt->more() )
3053 const SMDS_MeshElement* elem = elemIt->next();
3056 gp_XYZ elemCenter(0.,0.,0.);
3057 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3058 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3059 int nn = elem->NbNodes();
3060 if(elem->IsQuadratic()) nn = nn/2;
3062 //while ( itN->more() ) {
3064 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3066 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3067 aNodePoints.push_back( aP );
3068 if ( !theSurface.IsNull() ) { // smooth in 2D
3069 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3070 gp_XY* uv = theUVMap[ aNode ];
3071 aP.SetCoord( uv->X(), uv->Y(), 0. );
3075 double elemArea = anAreaFunc.GetValue( aNodePoints );
3076 totalArea += elemArea;
3078 aNewXYZ += elemCenter * elemArea;
3080 aNewXYZ /= totalArea;
3081 if ( !theSurface.IsNull() ) {
3082 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3083 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3088 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3091 //=======================================================================
3092 //function : getClosestUV
3093 //purpose : return UV of closest projection
3094 //=======================================================================
3096 static bool getClosestUV (Extrema_GenExtPS& projector,
3097 const gp_Pnt& point,
3100 projector.Perform( point );
3101 if ( projector.IsDone() ) {
3102 double u, v, minVal = DBL_MAX;
3103 for ( int i = projector.NbExt(); i > 0; i-- )
3104 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3105 if ( projector.SquareDistance( i ) < minVal ) {
3106 minVal = projector.SquareDistance( i );
3108 if ( projector.Value( i ) < minVal ) {
3109 minVal = projector.Value( i );
3111 projector.Point( i ).Parameter( u, v );
3113 result.SetCoord( u, v );
3119 //=======================================================================
3121 //purpose : Smooth theElements during theNbIterations or until a worst
3122 // element has aspect ratio <= theTgtAspectRatio.
3123 // Aspect Ratio varies in range [1.0, inf].
3124 // If theElements is empty, the whole mesh is smoothed.
3125 // theFixedNodes contains additionally fixed nodes. Nodes built
3126 // on edges and boundary nodes are always fixed.
3127 //=======================================================================
3129 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3130 set<const SMDS_MeshNode*> & theFixedNodes,
3131 const SmoothMethod theSmoothMethod,
3132 const int theNbIterations,
3133 double theTgtAspectRatio,
3136 myLastCreatedElems.Clear();
3137 myLastCreatedNodes.Clear();
3139 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3141 if ( theTgtAspectRatio < 1.0 )
3142 theTgtAspectRatio = 1.0;
3144 const double disttol = 1.e-16;
3146 SMESH::Controls::AspectRatio aQualityFunc;
3148 SMESHDS_Mesh* aMesh = GetMeshDS();
3150 if ( theElems.empty() ) {
3151 // add all faces to theElems
3152 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3153 while ( fIt->more() ) {
3154 const SMDS_MeshElement* face = fIt->next();
3155 theElems.insert( theElems.end(), face );
3158 // get all face ids theElems are on
3159 set< int > faceIdSet;
3160 TIDSortedElemSet::iterator itElem;
3162 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3163 int fId = FindShape( *itElem );
3164 // check that corresponding submesh exists and a shape is face
3166 faceIdSet.find( fId ) == faceIdSet.end() &&
3167 aMesh->MeshElements( fId )) {
3168 TopoDS_Shape F = aMesh->IndexToShape( fId );
3169 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3170 faceIdSet.insert( fId );
3173 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3175 // ===============================================
3176 // smooth elements on each TopoDS_Face separately
3177 // ===============================================
3179 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3180 for ( ; fId != faceIdSet.rend(); ++fId ) {
3181 // get face surface and submesh
3182 Handle(Geom_Surface) surface;
3183 SMESHDS_SubMesh* faceSubMesh = 0;
3185 double fToler2 = 0, f,l;
3186 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3187 bool isUPeriodic = false, isVPeriodic = false;
3189 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3190 surface = BRep_Tool::Surface( face );
3191 faceSubMesh = aMesh->MeshElements( *fId );
3192 fToler2 = BRep_Tool::Tolerance( face );
3193 fToler2 *= fToler2 * 10.;
3194 isUPeriodic = surface->IsUPeriodic();
3197 isVPeriodic = surface->IsVPeriodic();
3200 surface->Bounds( u1, u2, v1, v2 );
3202 // ---------------------------------------------------------
3203 // for elements on a face, find movable and fixed nodes and
3204 // compute UV for them
3205 // ---------------------------------------------------------
3206 bool checkBoundaryNodes = false;
3207 bool isQuadratic = false;
3208 set<const SMDS_MeshNode*> setMovableNodes;
3209 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3210 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3211 list< const SMDS_MeshElement* > elemsOnFace;
3213 Extrema_GenExtPS projector;
3214 GeomAdaptor_Surface surfAdaptor;
3215 if ( !surface.IsNull() ) {
3216 surfAdaptor.Load( surface );
3217 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3219 int nbElemOnFace = 0;
3220 itElem = theElems.begin();
3221 // loop on not yet smoothed elements: look for elems on a face
3222 while ( itElem != theElems.end() ) {
3223 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3224 break; // all elements found
3226 const SMDS_MeshElement* elem = *itElem;
3227 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3228 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3232 elemsOnFace.push_back( elem );
3233 theElems.erase( itElem++ );
3237 isQuadratic = elem->IsQuadratic();
3239 // get movable nodes of elem
3240 const SMDS_MeshNode* node;
3241 SMDS_TypeOfPosition posType;
3242 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3243 int nn = 0, nbn = elem->NbNodes();
3244 if(elem->IsQuadratic())
3246 while ( nn++ < nbn ) {
3247 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3248 const SMDS_PositionPtr& pos = node->GetPosition();
3249 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3250 if (posType != SMDS_TOP_EDGE &&
3251 posType != SMDS_TOP_VERTEX &&
3252 theFixedNodes.find( node ) == theFixedNodes.end())
3254 // check if all faces around the node are on faceSubMesh
3255 // because a node on edge may be bound to face
3256 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3258 if ( faceSubMesh ) {
3259 while ( eIt->more() && all ) {
3260 const SMDS_MeshElement* e = eIt->next();
3261 all = faceSubMesh->Contains( e );
3265 setMovableNodes.insert( node );
3267 checkBoundaryNodes = true;
3269 if ( posType == SMDS_TOP_3DSPACE )
3270 checkBoundaryNodes = true;
3273 if ( surface.IsNull() )
3276 // get nodes to check UV
3277 list< const SMDS_MeshNode* > uvCheckNodes;
3278 itN = elem->nodesIterator();
3279 nn = 0; nbn = elem->NbNodes();
3280 if(elem->IsQuadratic())
3282 while ( nn++ < nbn ) {
3283 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3284 if ( uvMap.find( node ) == uvMap.end() )
3285 uvCheckNodes.push_back( node );
3286 // add nodes of elems sharing node
3287 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3288 // while ( eIt->more() ) {
3289 // const SMDS_MeshElement* e = eIt->next();
3290 // if ( e != elem ) {
3291 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3292 // while ( nIt->more() ) {
3293 // const SMDS_MeshNode* n =
3294 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3295 // if ( uvMap.find( n ) == uvMap.end() )
3296 // uvCheckNodes.push_back( n );
3302 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3303 for ( ; n != uvCheckNodes.end(); ++n ) {
3306 const SMDS_PositionPtr& pos = node->GetPosition();
3307 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3309 switch ( posType ) {
3310 case SMDS_TOP_FACE: {
3311 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3312 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3315 case SMDS_TOP_EDGE: {
3316 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3317 Handle(Geom2d_Curve) pcurve;
3318 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3319 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3320 if ( !pcurve.IsNull() ) {
3321 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3322 uv = pcurve->Value( u ).XY();
3326 case SMDS_TOP_VERTEX: {
3327 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3328 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3329 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3334 // check existing UV
3335 bool project = true;
3336 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3337 double dist1 = DBL_MAX, dist2 = 0;
3338 if ( posType != SMDS_TOP_3DSPACE ) {
3339 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3340 project = dist1 > fToler2;
3342 if ( project ) { // compute new UV
3344 if ( !getClosestUV( projector, pNode, newUV )) {
3345 MESSAGE("Node Projection Failed " << node);
3349 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3351 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3353 if ( posType != SMDS_TOP_3DSPACE )
3354 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3355 if ( dist2 < dist1 )
3359 // store UV in the map
3360 listUV.push_back( uv );
3361 uvMap.insert( make_pair( node, &listUV.back() ));
3363 } // loop on not yet smoothed elements
3365 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3366 checkBoundaryNodes = true;
3368 // fix nodes on mesh boundary
3370 if ( checkBoundaryNodes ) {
3371 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3372 map< SMESH_TLink, int >::iterator link_nb;
3373 // put all elements links to linkNbMap
3374 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3375 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3376 const SMDS_MeshElement* elem = (*elemIt);
3377 int nbn = elem->NbCornerNodes();
3378 // loop on elem links: insert them in linkNbMap
3379 for ( int iN = 0; iN < nbn; ++iN ) {
3380 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3381 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3382 SMESH_TLink link( n1, n2 );
3383 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3387 // remove nodes that are in links encountered only once from setMovableNodes
3388 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3389 if ( link_nb->second == 1 ) {
3390 setMovableNodes.erase( link_nb->first.node1() );
3391 setMovableNodes.erase( link_nb->first.node2() );
3396 // -----------------------------------------------------
3397 // for nodes on seam edge, compute one more UV ( uvMap2 );
3398 // find movable nodes linked to nodes on seam and which
3399 // are to be smoothed using the second UV ( uvMap2 )
3400 // -----------------------------------------------------
3402 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3403 if ( !surface.IsNull() ) {
3404 TopExp_Explorer eExp( face, TopAbs_EDGE );
3405 for ( ; eExp.More(); eExp.Next() ) {
3406 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3407 if ( !BRep_Tool::IsClosed( edge, face ))
3409 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3410 if ( !sm ) continue;
3411 // find out which parameter varies for a node on seam
3414 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3415 if ( pcurve.IsNull() ) continue;
3416 uv1 = pcurve->Value( f );
3418 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3419 if ( pcurve.IsNull() ) continue;
3420 uv2 = pcurve->Value( f );
3421 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3423 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3424 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3426 // get nodes on seam and its vertices
3427 list< const SMDS_MeshNode* > seamNodes;
3428 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3429 while ( nSeamIt->more() ) {
3430 const SMDS_MeshNode* node = nSeamIt->next();
3431 if ( !isQuadratic || !IsMedium( node ))
3432 seamNodes.push_back( node );
3434 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3435 for ( ; vExp.More(); vExp.Next() ) {
3436 sm = aMesh->MeshElements( vExp.Current() );
3438 nSeamIt = sm->GetNodes();
3439 while ( nSeamIt->more() )
3440 seamNodes.push_back( nSeamIt->next() );
3443 // loop on nodes on seam
3444 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3445 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3446 const SMDS_MeshNode* nSeam = *noSeIt;
3447 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3448 if ( n_uv == uvMap.end() )
3451 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3452 // set the second UV
3453 listUV.push_back( *n_uv->second );
3454 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3455 if ( uvMap2.empty() )
3456 uvMap2 = uvMap; // copy the uvMap contents
3457 uvMap2[ nSeam ] = &listUV.back();
3459 // collect movable nodes linked to ones on seam in nodesNearSeam
3460 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3461 while ( eIt->more() ) {
3462 const SMDS_MeshElement* e = eIt->next();
3463 int nbUseMap1 = 0, nbUseMap2 = 0;
3464 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3465 int nn = 0, nbn = e->NbNodes();
3466 if(e->IsQuadratic()) nbn = nbn/2;
3467 while ( nn++ < nbn )
3469 const SMDS_MeshNode* n =
3470 static_cast<const SMDS_MeshNode*>( nIt->next() );
3472 setMovableNodes.find( n ) == setMovableNodes.end() )
3474 // add only nodes being closer to uv2 than to uv1
3475 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3476 0.5 * ( n->Y() + nSeam->Y() ),
3477 0.5 * ( n->Z() + nSeam->Z() ));
3479 getClosestUV( projector, pMid, uv );
3480 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3481 nodesNearSeam.insert( n );
3487 // for centroidalSmooth all element nodes must
3488 // be on one side of a seam
3489 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3490 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3492 while ( nn++ < nbn ) {
3493 const SMDS_MeshNode* n =
3494 static_cast<const SMDS_MeshNode*>( nIt->next() );
3495 setMovableNodes.erase( n );
3499 } // loop on nodes on seam
3500 } // loop on edge of a face
3501 } // if ( !face.IsNull() )
3503 if ( setMovableNodes.empty() ) {
3504 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3505 continue; // goto next face
3513 double maxRatio = -1., maxDisplacement = -1.;
3514 set<const SMDS_MeshNode*>::iterator nodeToMove;
3515 for ( it = 0; it < theNbIterations; it++ ) {
3516 maxDisplacement = 0.;
3517 nodeToMove = setMovableNodes.begin();
3518 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3519 const SMDS_MeshNode* node = (*nodeToMove);
3520 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3523 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3524 if ( theSmoothMethod == LAPLACIAN )
3525 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3527 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3529 // node displacement
3530 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3531 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3532 if ( aDispl > maxDisplacement )
3533 maxDisplacement = aDispl;
3535 // no node movement => exit
3536 //if ( maxDisplacement < 1.e-16 ) {
3537 if ( maxDisplacement < disttol ) {
3538 MESSAGE("-- no node movement --");
3542 // check elements quality
3544 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3545 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3546 const SMDS_MeshElement* elem = (*elemIt);
3547 if ( !elem || elem->GetType() != SMDSAbs_Face )
3549 SMESH::Controls::TSequenceOfXYZ aPoints;
3550 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3551 double aValue = aQualityFunc.GetValue( aPoints );
3552 if ( aValue > maxRatio )
3556 if ( maxRatio <= theTgtAspectRatio ) {
3557 MESSAGE("-- quality achived --");
3560 if (it+1 == theNbIterations) {
3561 MESSAGE("-- Iteration limit exceeded --");
3563 } // smoothing iterations
3565 MESSAGE(" Face id: " << *fId <<
3566 " Nb iterstions: " << it <<
3567 " Displacement: " << maxDisplacement <<
3568 " Aspect Ratio " << maxRatio);
3570 // ---------------------------------------
3571 // new nodes positions are computed,
3572 // record movement in DS and set new UV
3573 // ---------------------------------------
3574 nodeToMove = setMovableNodes.begin();
3575 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3576 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3577 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3578 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3579 if ( node_uv != uvMap.end() ) {
3580 gp_XY* uv = node_uv->second;
3582 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3586 // move medium nodes of quadratic elements
3589 SMESH_MesherHelper helper( *GetMesh() );
3590 if ( !face.IsNull() )
3591 helper.SetSubShape( face );
3592 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3593 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3594 const SMDS_VtkFace* QF =
3595 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3596 if(QF && QF->IsQuadratic()) {
3597 vector<const SMDS_MeshNode*> Ns;
3598 Ns.reserve(QF->NbNodes()+1);
3599 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3600 while ( anIter->more() )
3601 Ns.push_back( cast2Node(anIter->next()) );
3602 Ns.push_back( Ns[0] );
3604 for(int i=0; i<QF->NbNodes(); i=i+2) {
3605 if ( !surface.IsNull() ) {
3606 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3607 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3608 gp_XY uv = ( uv1 + uv2 ) / 2.;
3609 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3610 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3613 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3614 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3615 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3617 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3618 fabs( Ns[i+1]->Y() - y ) > disttol ||
3619 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3620 // we have to move i+1 node
3621 aMesh->MoveNode( Ns[i+1], x, y, z );
3628 } // loop on face ids
3632 //=======================================================================
3633 //function : isReverse
3634 //purpose : Return true if normal of prevNodes is not co-directied with
3635 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3636 // iNotSame is where prevNodes and nextNodes are different.
3637 // If result is true then future volume orientation is OK
3638 //=======================================================================
3640 static bool isReverse(const SMDS_MeshElement* face,
3641 const vector<const SMDS_MeshNode*>& prevNodes,
3642 const vector<const SMDS_MeshNode*>& nextNodes,
3646 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3647 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3648 gp_XYZ extrDir( pN - pP ), faceNorm;
3649 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3651 return faceNorm * extrDir < 0.0;
3654 //=======================================================================
3656 * \brief Create elements by sweeping an element
3657 * \param elem - element to sweep
3658 * \param newNodesItVec - nodes generated from each node of the element
3659 * \param newElems - generated elements
3660 * \param nbSteps - number of sweeping steps
3661 * \param srcElements - to append elem for each generated element
3663 //=======================================================================
3665 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3666 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3667 list<const SMDS_MeshElement*>& newElems,
3669 SMESH_SequenceOfElemPtr& srcElements)
3671 //MESSAGE("sweepElement " << nbSteps);
3672 SMESHDS_Mesh* aMesh = GetMeshDS();
3674 const int nbNodes = elem->NbNodes();
3675 const int nbCorners = elem->NbCornerNodes();
3676 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3677 polyhedron creation !!! */
3678 // Loop on elem nodes:
3679 // find new nodes and detect same nodes indices
3680 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3681 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3682 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3683 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3685 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3686 vector<int> sames(nbNodes);
3687 vector<bool> isSingleNode(nbNodes);
3689 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3690 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3691 const SMDS_MeshNode* node = nnIt->first;
3692 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3693 if ( listNewNodes.empty() )
3696 itNN [ iNode ] = listNewNodes.begin();
3697 prevNod[ iNode ] = node;
3698 nextNod[ iNode ] = listNewNodes.front();
3700 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3701 corner node of linear */
3702 if ( prevNod[ iNode ] != nextNod [ iNode ])
3703 nbDouble += !isSingleNode[iNode];
3705 if( iNode < nbCorners ) { // check corners only
3706 if ( prevNod[ iNode ] == nextNod [ iNode ])
3707 sames[nbSame++] = iNode;
3709 iNotSameNode = iNode;
3713 if ( nbSame == nbNodes || nbSame > 2) {
3714 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3718 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3720 // fix nodes order to have bottom normal external
3721 if ( baseType == SMDSEntity_Polygon )
3723 std::reverse( itNN.begin(), itNN.end() );
3724 std::reverse( prevNod.begin(), prevNod.end() );
3725 std::reverse( midlNod.begin(), midlNod.end() );
3726 std::reverse( nextNod.begin(), nextNod.end() );
3727 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3731 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3732 SMDS_MeshCell::applyInterlace( ind, itNN );
3733 SMDS_MeshCell::applyInterlace( ind, prevNod );
3734 SMDS_MeshCell::applyInterlace( ind, nextNod );
3735 SMDS_MeshCell::applyInterlace( ind, midlNod );
3736 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3739 sames[nbSame] = iNotSameNode;
3740 for ( int j = 0; j <= nbSame; ++j )
3741 for ( size_t i = 0; i < ind.size(); ++i )
3742 if ( ind[i] == sames[j] )
3747 iNotSameNode = sames[nbSame];
3752 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3754 iSameNode = sames[ nbSame-1 ];
3755 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3756 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3757 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3760 // make new elements
3761 for (int iStep = 0; iStep < nbSteps; iStep++ )
3764 for ( iNode = 0; iNode < nbNodes; iNode++ )
3766 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3767 nextNod[ iNode ] = *itNN[ iNode ]++;
3770 SMDS_MeshElement* aNewElem = 0;
3771 /*if(!elem->IsPoly())*/ {
3772 switch ( baseType ) {
3774 case SMDSEntity_Node: { // sweep NODE
3775 if ( nbSame == 0 ) {
3776 if ( isSingleNode[0] )
3777 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3779 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3785 case SMDSEntity_Edge: { // sweep EDGE
3786 if ( nbDouble == 0 )
3788 if ( nbSame == 0 ) // ---> quadrangle
3789 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3790 nextNod[ 1 ], nextNod[ 0 ] );
3791 else // ---> triangle
3792 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3793 nextNod[ iNotSameNode ] );
3795 else // ---> polygon
3797 vector<const SMDS_MeshNode*> poly_nodes;
3798 poly_nodes.push_back( prevNod[0] );
3799 poly_nodes.push_back( prevNod[1] );
3800 if ( prevNod[1] != nextNod[1] )
3802 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3803 poly_nodes.push_back( nextNod[1] );
3805 if ( prevNod[0] != nextNod[0] )
3807 poly_nodes.push_back( nextNod[0] );
3808 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3810 switch ( poly_nodes.size() ) {
3812 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3815 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3816 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3819 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3824 case SMDSEntity_Triangle: // TRIANGLE --->
3826 if ( nbDouble > 0 ) break;
3827 if ( nbSame == 0 ) // ---> pentahedron
3828 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3829 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3831 else if ( nbSame == 1 ) // ---> pyramid
3832 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3833 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3834 nextNod[ iSameNode ]);
3836 else // 2 same nodes: ---> tetrahedron
3837 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3838 nextNod[ iNotSameNode ]);
3841 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3845 if ( nbDouble+nbSame == 2 )
3847 if(nbSame==0) { // ---> quadratic quadrangle
3848 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3849 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3851 else { //(nbSame==1) // ---> quadratic triangle
3853 return; // medium node on axis
3855 else if(sames[0]==0)
3856 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3857 nextNod[2], midlNod[1], prevNod[2]);
3859 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3860 midlNod[0], nextNod[2], prevNod[2]);
3863 else if ( nbDouble == 3 )
3865 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3866 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3867 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3874 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3875 if ( nbDouble > 0 ) break;
3877 if ( nbSame == 0 ) // ---> hexahedron
3878 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3879 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3881 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3882 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3883 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3884 nextNod[ iSameNode ]);
3885 newElems.push_back( aNewElem );
3886 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3887 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3888 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3890 else if ( nbSame == 2 ) { // ---> pentahedron
3891 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3892 // iBeforeSame is same too
3893 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3894 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3895 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3897 // iAfterSame is same too
3898 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3899 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3900 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3904 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3905 if ( nbDouble+nbSame != 3 ) break;
3907 // ---> pentahedron with 15 nodes
3908 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3909 nextNod[0], nextNod[1], nextNod[2],
3910 prevNod[3], prevNod[4], prevNod[5],
3911 nextNod[3], nextNod[4], nextNod[5],
3912 midlNod[0], midlNod[1], midlNod[2]);
3914 else if(nbSame==1) {
3915 // ---> 2d order pyramid of 13 nodes
3916 int apex = iSameNode;
3917 int i0 = ( apex + 1 ) % nbCorners;
3918 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3922 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3923 nextNod[i0], nextNod[i1], prevNod[apex],
3924 prevNod[i01], midlNod[i0],
3925 nextNod[i01], midlNod[i1],
3926 prevNod[i1a], prevNod[i0a],
3927 nextNod[i0a], nextNod[i1a]);
3929 else if(nbSame==2) {
3930 // ---> 2d order tetrahedron of 10 nodes
3931 int n1 = iNotSameNode;
3932 int n2 = ( n1 + 1 ) % nbCorners;
3933 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3937 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3938 prevNod[n12], prevNod[n23], prevNod[n31],
3939 midlNod[n1], nextNod[n12], nextNod[n31]);
3943 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3945 if ( nbDouble != 4 ) break;
3946 // ---> hexahedron with 20 nodes
3947 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3948 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3949 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3950 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3951 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3953 else if(nbSame==1) {
3954 // ---> pyramid + pentahedron - can not be created since it is needed
3955 // additional middle node at the center of face
3956 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3959 else if( nbSame == 2 ) {
3960 if ( nbDouble != 2 ) break;
3961 // ---> 2d order Pentahedron with 15 nodes
3963 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3964 // iBeforeSame is same too
3971 // iAfterSame is same too
3981 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3982 prevNod[n4], prevNod[n5], nextNod[n5],
3983 prevNod[n12], midlNod[n2], nextNod[n12],
3984 prevNod[n45], midlNod[n5], nextNod[n45],
3985 prevNod[n14], prevNod[n25], nextNod[n25]);
3989 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3991 if( nbSame == 0 && nbDouble == 9 ) {
3992 // ---> tri-quadratic hexahedron with 27 nodes
3993 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3994 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3995 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3996 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3997 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3998 prevNod[8], // bottom center
3999 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4000 nextNod[8], // top center
4001 midlNod[8]);// elem center
4009 case SMDSEntity_Polygon: { // sweep POLYGON
4011 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4012 // ---> hexagonal prism
4013 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4014 prevNod[3], prevNod[4], prevNod[5],
4015 nextNod[0], nextNod[1], nextNod[2],
4016 nextNod[3], nextNod[4], nextNod[5]);
4020 case SMDSEntity_Ball:
4028 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4030 if ( baseType != SMDSEntity_Polygon )
4032 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4033 SMDS_MeshCell::applyInterlace( ind, prevNod );
4034 SMDS_MeshCell::applyInterlace( ind, nextNod );
4035 SMDS_MeshCell::applyInterlace( ind, midlNod );
4036 SMDS_MeshCell::applyInterlace( ind, itNN );
4037 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4038 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4040 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4041 vector<int> quantities (nbNodes + 2);
4042 polyedre_nodes.clear();
4046 for (int inode = 0; inode < nbNodes; inode++)
4047 polyedre_nodes.push_back( prevNod[inode] );
4048 quantities.push_back( nbNodes );
4051 polyedre_nodes.push_back( nextNod[0] );
4052 for (int inode = nbNodes; inode-1; --inode )
4053 polyedre_nodes.push_back( nextNod[inode-1] );
4054 quantities.push_back( nbNodes );
4057 for (int iface = 0; iface < nbNodes; iface++)
4059 const int prevNbNodes = polyedre_nodes.size();
4060 int inextface = (iface+1) % nbNodes;
4061 polyedre_nodes.push_back( prevNod[inextface] );
4062 polyedre_nodes.push_back( prevNod[iface] );
4063 if ( prevNod[iface] != nextNod[iface] )
4065 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4066 polyedre_nodes.push_back( nextNod[iface] );
4068 if ( prevNod[inextface] != nextNod[inextface] )
4070 polyedre_nodes.push_back( nextNod[inextface] );
4071 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4073 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4074 if ( nbFaceNodes > 2 )
4075 quantities.push_back( nbFaceNodes );
4076 else // degenerated face
4077 polyedre_nodes.resize( prevNbNodes );
4079 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4083 newElems.push_back( aNewElem );
4084 myLastCreatedElems.Append(aNewElem);
4085 srcElements.Append( elem );
4088 // set new prev nodes
4089 for ( iNode = 0; iNode < nbNodes; iNode++ )
4090 prevNod[ iNode ] = nextNod[ iNode ];
4095 //=======================================================================
4097 * \brief Create 1D and 2D elements around swept elements
4098 * \param mapNewNodes - source nodes and ones generated from them
4099 * \param newElemsMap - source elements and ones generated from them
4100 * \param elemNewNodesMap - nodes generated from each node of each element
4101 * \param elemSet - all swept elements
4102 * \param nbSteps - number of sweeping steps
4103 * \param srcElements - to append elem for each generated element
4105 //=======================================================================
4107 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4108 TElemOfElemListMap & newElemsMap,
4109 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4110 TIDSortedElemSet& elemSet,
4112 SMESH_SequenceOfElemPtr& srcElements)
4114 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4115 SMESHDS_Mesh* aMesh = GetMeshDS();
4117 // Find nodes belonging to only one initial element - sweep them to get edges.
4119 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4120 for ( ; nList != mapNewNodes.end(); nList++ )
4122 const SMDS_MeshNode* node =
4123 static_cast<const SMDS_MeshNode*>( nList->first );
4124 if ( newElemsMap.count( node ))
4125 continue; // node was extruded into edge
4126 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4127 int nbInitElems = 0;
4128 const SMDS_MeshElement* el = 0;
4129 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4130 while ( eIt->more() && nbInitElems < 2 ) {
4132 SMDSAbs_ElementType type = el->GetType();
4133 if ( type == SMDSAbs_Volume || type < highType ) continue;
4134 if ( type > highType ) {
4138 nbInitElems += elemSet.count(el);
4140 if ( nbInitElems < 2 ) {
4141 bool NotCreateEdge = el && el->IsMediumNode(node);
4142 if(!NotCreateEdge) {
4143 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4144 list<const SMDS_MeshElement*> newEdges;
4145 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4150 // Make a ceiling for each element ie an equal element of last new nodes.
4151 // Find free links of faces - make edges and sweep them into faces.
4153 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4154 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4155 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4157 const SMDS_MeshElement* elem = itElem->first;
4158 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4160 if(itElem->second.size()==0) continue;
4162 const bool isQuadratic = elem->IsQuadratic();
4164 if ( elem->GetType() == SMDSAbs_Edge ) {
4165 // create a ceiling edge
4166 if ( !isQuadratic ) {
4167 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4168 vecNewNodes[ 1 ]->second.back())) {
4169 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4170 vecNewNodes[ 1 ]->second.back()));
4171 srcElements.Append( elem );
4175 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4176 vecNewNodes[ 1 ]->second.back(),
4177 vecNewNodes[ 2 ]->second.back())) {
4178 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4179 vecNewNodes[ 1 ]->second.back(),
4180 vecNewNodes[ 2 ]->second.back()));
4181 srcElements.Append( elem );
4185 if ( elem->GetType() != SMDSAbs_Face )
4188 bool hasFreeLinks = false;
4190 TIDSortedElemSet avoidSet;
4191 avoidSet.insert( elem );
4193 set<const SMDS_MeshNode*> aFaceLastNodes;
4194 int iNode, nbNodes = vecNewNodes.size();
4195 if ( !isQuadratic ) {
4196 // loop on the face nodes
4197 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4198 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4199 // look for free links of the face
4200 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4201 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4202 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4203 // check if a link n1-n2 is free
4204 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4205 hasFreeLinks = true;
4206 // make a new edge and a ceiling for a new edge
4207 const SMDS_MeshElement* edge;
4208 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4209 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4210 srcElements.Append( myLastCreatedElems.Last() );
4212 n1 = vecNewNodes[ iNode ]->second.back();
4213 n2 = vecNewNodes[ iNext ]->second.back();
4214 if ( !aMesh->FindEdge( n1, n2 )) {
4215 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4216 srcElements.Append( edge );
4221 else { // elem is quadratic face
4222 int nbn = nbNodes/2;
4223 for ( iNode = 0; iNode < nbn; iNode++ ) {
4224 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4225 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4226 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4227 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4228 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4229 // check if a link is free
4230 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4231 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4232 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4233 hasFreeLinks = true;
4234 // make an edge and a ceiling for a new edge
4236 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4237 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4238 srcElements.Append( elem );
4240 n1 = vecNewNodes[ iNode ]->second.back();
4241 n2 = vecNewNodes[ iNext ]->second.back();
4242 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4243 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4244 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4245 srcElements.Append( elem );
4249 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4250 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4254 // sweep free links into faces
4256 if ( hasFreeLinks ) {
4257 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4258 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4260 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4261 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4262 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4263 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4265 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4266 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4267 std::advance( v, volNb );
4268 // find indices of free faces of a volume and their source edges
4269 list< int > freeInd;
4270 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4271 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4272 int iF, nbF = vTool.NbFaces();
4273 for ( iF = 0; iF < nbF; iF ++ ) {
4274 if (vTool.IsFreeFace( iF ) &&
4275 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4276 initNodeSet != faceNodeSet) // except an initial face
4278 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4280 freeInd.push_back( iF );
4281 // find source edge of a free face iF
4282 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4283 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4284 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4285 initNodeSet.begin(), initNodeSet.end(),
4286 commonNodes.begin());
4287 if ( (*v)->IsQuadratic() )
4288 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4290 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4292 if ( !srcEdges.back() )
4294 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4295 << iF << " of volume #" << vTool.ID() << endl;
4300 if ( freeInd.empty() )
4303 // create faces for all steps;
4304 // if such a face has been already created by sweep of edge,
4305 // assure that its orientation is OK
4306 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4307 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4308 vTool.SetExternalNormal();
4309 const int nextShift = vTool.IsForward() ? +1 : -1;
4310 list< int >::iterator ind = freeInd.begin();
4311 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4312 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4314 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4315 int nbn = vTool.NbFaceNodes( *ind );
4316 const SMDS_MeshElement * f = 0;
4317 if ( nbn == 3 ) ///// triangle
4319 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4321 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4323 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4325 nodes[ 1 + nextShift ] };
4327 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4329 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4333 else if ( nbn == 4 ) ///// quadrangle
4335 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4337 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4339 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4340 nodes[ 2 ], nodes[ 2+nextShift ] };
4342 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4344 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4345 newOrder[ 2 ], newOrder[ 3 ]));
4348 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4350 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4352 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4354 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4356 nodes[2 + 2*nextShift],
4357 nodes[3 - 2*nextShift],
4359 nodes[3 + 2*nextShift]};
4361 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4363 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4371 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4373 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4374 nodes[1], nodes[3], nodes[5], nodes[7] );
4376 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4378 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4379 nodes[4 - 2*nextShift],
4381 nodes[4 + 2*nextShift],
4383 nodes[5 - 2*nextShift],
4385 nodes[5 + 2*nextShift] };
4387 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4389 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4390 newOrder[ 2 ], newOrder[ 3 ],
4391 newOrder[ 4 ], newOrder[ 5 ],
4392 newOrder[ 6 ], newOrder[ 7 ]));
4395 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4397 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4398 SMDSAbs_Face, /*noMedium=*/false);
4400 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4402 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4403 nodes[4 - 2*nextShift],
4405 nodes[4 + 2*nextShift],
4407 nodes[5 - 2*nextShift],
4409 nodes[5 + 2*nextShift],
4412 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4414 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4415 newOrder[ 2 ], newOrder[ 3 ],
4416 newOrder[ 4 ], newOrder[ 5 ],
4417 newOrder[ 6 ], newOrder[ 7 ],
4421 else //////// polygon
4423 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4424 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4426 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4428 if ( !vTool.IsForward() )
4429 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4431 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4433 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4437 while ( srcElements.Length() < myLastCreatedElems.Length() )
4438 srcElements.Append( *srcEdge );
4440 } // loop on free faces
4442 // go to the next volume
4444 while ( iVol++ < nbVolumesByStep ) v++;
4447 } // loop on volumes of one step
4448 } // sweep free links into faces
4450 // Make a ceiling face with a normal external to a volume
4452 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4454 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4456 lastVol.SetExternalNormal();
4457 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4458 int nbn = lastVol.NbFaceNodes( iF );
4460 if (!hasFreeLinks ||
4461 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4462 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4464 else if ( nbn == 4 )
4466 if (!hasFreeLinks ||
4467 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4468 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4470 else if ( nbn == 6 && isQuadratic )
4472 if (!hasFreeLinks ||
4473 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4474 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4475 nodes[1], nodes[3], nodes[5]));
4477 else if ( nbn == 8 && isQuadratic )
4479 if (!hasFreeLinks ||
4480 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4481 nodes[1], nodes[3], nodes[5], nodes[7]) )
4482 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4483 nodes[1], nodes[3], nodes[5], nodes[7]));
4485 else if ( nbn == 9 && isQuadratic )
4487 if (!hasFreeLinks ||
4488 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4489 SMDSAbs_Face, /*noMedium=*/false) )
4490 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4491 nodes[1], nodes[3], nodes[5], nodes[7],
4495 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4496 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4497 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4500 while ( srcElements.Length() < myLastCreatedElems.Length() )
4501 srcElements.Append( elem );
4503 } // loop on swept elements
4506 //=======================================================================
4507 //function : RotationSweep
4509 //=======================================================================
4511 SMESH_MeshEditor::PGroupIDs
4512 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4513 const gp_Ax1& theAxis,
4514 const double theAngle,
4515 const int theNbSteps,
4516 const double theTol,
4517 const bool theMakeGroups,
4518 const bool theMakeWalls)
4520 myLastCreatedElems.Clear();
4521 myLastCreatedNodes.Clear();
4523 // source elements for each generated one
4524 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4526 MESSAGE( "RotationSweep()");
4528 aTrsf.SetRotation( theAxis, theAngle );
4530 aTrsf2.SetRotation( theAxis, theAngle/2. );
4532 gp_Lin aLine( theAxis );
4533 double aSqTol = theTol * theTol;
4535 SMESHDS_Mesh* aMesh = GetMeshDS();
4537 TNodeOfNodeListMap mapNewNodes;
4538 TElemOfVecOfNnlmiMap mapElemNewNodes;
4539 TElemOfElemListMap newElemsMap;
4541 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4542 myMesh->NbFaces(ORDER_QUADRATIC) +
4543 myMesh->NbVolumes(ORDER_QUADRATIC) );
4545 TIDSortedElemSet::iterator itElem;
4546 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4547 const SMDS_MeshElement* elem = *itElem;
4548 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4550 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4551 newNodesItVec.reserve( elem->NbNodes() );
4553 // loop on elem nodes
4554 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4555 while ( itN->more() )
4557 // check if a node has been already sweeped
4558 const SMDS_MeshNode* node = cast2Node( itN->next() );
4560 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4562 aXYZ.Coord( coord[0], coord[1], coord[2] );
4563 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4565 TNodeOfNodeListMapItr nIt =
4566 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4567 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4568 if ( listNewNodes.empty() )
4570 // check if we are to create medium nodes between corner ones
4571 bool needMediumNodes = false;
4572 if ( isQuadraticMesh )
4574 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4575 while (it->more() && !needMediumNodes )
4577 const SMDS_MeshElement* invElem = it->next();
4578 if ( invElem != elem && !theElems.count( invElem )) continue;
4579 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4580 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4581 needMediumNodes = true;
4586 const SMDS_MeshNode * newNode = node;
4587 for ( int i = 0; i < theNbSteps; i++ ) {
4589 if ( needMediumNodes ) // create a medium node
4591 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4592 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4593 myLastCreatedNodes.Append(newNode);
4594 srcNodes.Append( node );
4595 listNewNodes.push_back( newNode );
4596 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4599 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4601 // create a corner node
4602 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4603 myLastCreatedNodes.Append(newNode);
4604 srcNodes.Append( node );
4605 listNewNodes.push_back( newNode );
4608 listNewNodes.push_back( newNode );
4609 // if ( needMediumNodes )
4610 // listNewNodes.push_back( newNode );
4614 newNodesItVec.push_back( nIt );
4616 // make new elements
4617 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4621 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4623 PGroupIDs newGroupIDs;
4624 if ( theMakeGroups )
4625 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4631 //=======================================================================
4632 //function : CreateNode
4634 //=======================================================================
4635 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4638 const double tolnode,
4639 SMESH_SequenceOfNode& aNodes)
4641 // myLastCreatedElems.Clear();
4642 // myLastCreatedNodes.Clear();
4645 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4647 // try to search in sequence of existing nodes
4648 // if aNodes.Length()>0 we 'nave to use given sequence
4649 // else - use all nodes of mesh
4650 if(aNodes.Length()>0) {
4652 for(i=1; i<=aNodes.Length(); i++) {
4653 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4654 if(P1.Distance(P2)<tolnode)
4655 return aNodes.Value(i);
4659 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4660 while(itn->more()) {
4661 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4662 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4663 if(P1.Distance(P2)<tolnode)
4668 // create new node and return it
4669 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4670 //myLastCreatedNodes.Append(NewNode);
4675 //=======================================================================
4676 //function : ExtrusionSweep
4678 //=======================================================================
4680 SMESH_MeshEditor::PGroupIDs
4681 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4682 const gp_Vec& theStep,
4683 const int theNbSteps,
4684 TElemOfElemListMap& newElemsMap,
4685 const bool theMakeGroups,
4687 const double theTolerance)
4689 ExtrusParam aParams;
4690 aParams.myDir = gp_Dir(theStep);
4691 aParams.myNodes.Clear();
4692 aParams.mySteps = new TColStd_HSequenceOfReal;
4694 for(i=1; i<=theNbSteps; i++)
4695 aParams.mySteps->Append(theStep.Magnitude());
4698 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4702 //=======================================================================
4703 //function : ExtrusionSweep
4705 //=======================================================================
4707 SMESH_MeshEditor::PGroupIDs
4708 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4709 ExtrusParam& theParams,
4710 TElemOfElemListMap& newElemsMap,
4711 const bool theMakeGroups,
4713 const double theTolerance)
4715 myLastCreatedElems.Clear();
4716 myLastCreatedNodes.Clear();
4718 // source elements for each generated one
4719 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4721 SMESHDS_Mesh* aMesh = GetMeshDS();
4723 int nbsteps = theParams.mySteps->Length();
4725 TNodeOfNodeListMap mapNewNodes;
4726 //TNodeOfNodeVecMap mapNewNodes;
4727 TElemOfVecOfNnlmiMap mapElemNewNodes;
4728 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4730 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4731 myMesh->NbFaces(ORDER_QUADRATIC) +
4732 myMesh->NbVolumes(ORDER_QUADRATIC) );
4734 TIDSortedElemSet::iterator itElem;
4735 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4736 // check element type
4737 const SMDS_MeshElement* elem = *itElem;
4738 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4741 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4742 newNodesItVec.reserve( elem->NbNodes() );
4744 // loop on elem nodes
4745 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4746 while ( itN->more() )
4748 // check if a node has been already sweeped
4749 const SMDS_MeshNode* node = cast2Node( itN->next() );
4750 TNodeOfNodeListMap::iterator nIt =
4751 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4752 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4753 if ( listNewNodes.empty() )
4757 // check if we are to create medium nodes between corner ones
4758 bool needMediumNodes = false;
4759 if ( isQuadraticMesh )
4761 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4762 while (it->more() && !needMediumNodes )
4764 const SMDS_MeshElement* invElem = it->next();
4765 if ( invElem != elem && !theElems.count( invElem )) continue;
4766 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4767 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4768 needMediumNodes = true;
4772 double coord[] = { node->X(), node->Y(), node->Z() };
4773 for ( int i = 0; i < nbsteps; i++ )
4775 if ( needMediumNodes ) // create a medium node
4777 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4778 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4779 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4780 if( theFlags & EXTRUSION_FLAG_SEW ) {
4781 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4782 theTolerance, theParams.myNodes);
4783 listNewNodes.push_back( newNode );
4786 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4787 myLastCreatedNodes.Append(newNode);
4788 srcNodes.Append( node );
4789 listNewNodes.push_back( newNode );
4792 // create a corner node
4793 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4794 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4795 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4796 if( theFlags & EXTRUSION_FLAG_SEW ) {
4797 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4798 theTolerance, theParams.myNodes);
4799 listNewNodes.push_back( newNode );
4802 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4803 myLastCreatedNodes.Append(newNode);
4804 srcNodes.Append( node );
4805 listNewNodes.push_back( newNode );
4809 newNodesItVec.push_back( nIt );
4811 // make new elements
4812 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4815 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4816 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4818 PGroupIDs newGroupIDs;
4819 if ( theMakeGroups )
4820 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4825 //=======================================================================
4826 //function : ExtrusionAlongTrack
4828 //=======================================================================
4829 SMESH_MeshEditor::Extrusion_Error
4830 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4831 SMESH_subMesh* theTrack,
4832 const SMDS_MeshNode* theN1,
4833 const bool theHasAngles,
4834 list<double>& theAngles,
4835 const bool theLinearVariation,
4836 const bool theHasRefPoint,
4837 const gp_Pnt& theRefPoint,
4838 const bool theMakeGroups)
4840 MESSAGE("ExtrusionAlongTrack");
4841 myLastCreatedElems.Clear();
4842 myLastCreatedNodes.Clear();
4845 std::list<double> aPrms;
4846 TIDSortedElemSet::iterator itElem;
4849 TopoDS_Edge aTrackEdge;
4850 TopoDS_Vertex aV1, aV2;
4852 SMDS_ElemIteratorPtr aItE;
4853 SMDS_NodeIteratorPtr aItN;
4854 SMDSAbs_ElementType aTypeE;
4856 TNodeOfNodeListMap mapNewNodes;
4859 aNbE = theElements.size();
4862 return EXTR_NO_ELEMENTS;
4864 // 1.1 Track Pattern
4867 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4869 aItE = pSubMeshDS->GetElements();
4870 while ( aItE->more() ) {
4871 const SMDS_MeshElement* pE = aItE->next();
4872 aTypeE = pE->GetType();
4873 // Pattern must contain links only
4874 if ( aTypeE != SMDSAbs_Edge )
4875 return EXTR_PATH_NOT_EDGE;
4878 list<SMESH_MeshEditor_PathPoint> fullList;
4880 const TopoDS_Shape& aS = theTrack->GetSubShape();
4881 // Sub-shape for the Pattern must be an Edge or Wire
4882 if( aS.ShapeType() == TopAbs_EDGE ) {
4883 aTrackEdge = TopoDS::Edge( aS );
4884 // the Edge must not be degenerated
4885 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4886 return EXTR_BAD_PATH_SHAPE;
4887 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4888 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4889 const SMDS_MeshNode* aN1 = aItN->next();
4890 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4891 const SMDS_MeshNode* aN2 = aItN->next();
4892 // starting node must be aN1 or aN2
4893 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4894 return EXTR_BAD_STARTING_NODE;
4895 aItN = pSubMeshDS->GetNodes();
4896 while ( aItN->more() ) {
4897 const SMDS_MeshNode* pNode = aItN->next();
4898 const SMDS_EdgePosition* pEPos =
4899 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4900 double aT = pEPos->GetUParameter();
4901 aPrms.push_back( aT );
4903 //Extrusion_Error err =
4904 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4905 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4906 list< SMESH_subMesh* > LSM;
4907 TopTools_SequenceOfShape Edges;
4908 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4909 while(itSM->more()) {
4910 SMESH_subMesh* SM = itSM->next();
4912 const TopoDS_Shape& aS = SM->GetSubShape();
4915 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4916 int startNid = theN1->GetID();
4917 TColStd_MapOfInteger UsedNums;
4919 int NbEdges = Edges.Length();
4921 for(; i<=NbEdges; i++) {
4923 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4924 for(; itLSM!=LSM.end(); itLSM++) {
4926 if(UsedNums.Contains(k)) continue;
4927 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4928 SMESH_subMesh* locTrack = *itLSM;
4929 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4930 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4931 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4932 const SMDS_MeshNode* aN1 = aItN->next();
4933 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4934 const SMDS_MeshNode* aN2 = aItN->next();
4935 // starting node must be aN1 or aN2
4936 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4937 // 2. Collect parameters on the track edge
4939 aItN = locMeshDS->GetNodes();
4940 while ( aItN->more() ) {
4941 const SMDS_MeshNode* pNode = aItN->next();
4942 const SMDS_EdgePosition* pEPos =
4943 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4944 double aT = pEPos->GetUParameter();
4945 aPrms.push_back( aT );
4947 list<SMESH_MeshEditor_PathPoint> LPP;
4948 //Extrusion_Error err =
4949 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4950 LLPPs.push_back(LPP);
4952 // update startN for search following egde
4953 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4954 else startNid = aN1->GetID();
4958 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4959 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4960 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4961 for(; itPP!=firstList.end(); itPP++) {
4962 fullList.push_back( *itPP );
4964 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4965 fullList.pop_back();
4967 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4968 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4969 itPP = currList.begin();
4970 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4971 gp_Dir D1 = PP1.Tangent();
4972 gp_Dir D2 = PP2.Tangent();
4973 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4974 (D1.Z()+D2.Z())/2 ) );
4975 PP1.SetTangent(Dnew);
4976 fullList.push_back(PP1);
4978 for(; itPP!=firstList.end(); itPP++) {
4979 fullList.push_back( *itPP );
4981 PP1 = fullList.back();
4982 fullList.pop_back();
4984 // if wire not closed
4985 fullList.push_back(PP1);
4989 return EXTR_BAD_PATH_SHAPE;
4992 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4993 theHasRefPoint, theRefPoint, theMakeGroups);
4997 //=======================================================================
4998 //function : ExtrusionAlongTrack
5000 //=======================================================================
5001 SMESH_MeshEditor::Extrusion_Error
5002 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5003 SMESH_Mesh* theTrack,
5004 const SMDS_MeshNode* theN1,
5005 const bool theHasAngles,
5006 list<double>& theAngles,
5007 const bool theLinearVariation,
5008 const bool theHasRefPoint,
5009 const gp_Pnt& theRefPoint,
5010 const bool theMakeGroups)
5012 myLastCreatedElems.Clear();
5013 myLastCreatedNodes.Clear();
5016 std::list<double> aPrms;
5017 TIDSortedElemSet::iterator itElem;
5020 TopoDS_Edge aTrackEdge;
5021 TopoDS_Vertex aV1, aV2;
5023 SMDS_ElemIteratorPtr aItE;
5024 SMDS_NodeIteratorPtr aItN;
5025 SMDSAbs_ElementType aTypeE;
5027 TNodeOfNodeListMap mapNewNodes;
5030 aNbE = theElements.size();
5033 return EXTR_NO_ELEMENTS;
5035 // 1.1 Track Pattern
5038 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5040 aItE = pMeshDS->elementsIterator();
5041 while ( aItE->more() ) {
5042 const SMDS_MeshElement* pE = aItE->next();
5043 aTypeE = pE->GetType();
5044 // Pattern must contain links only
5045 if ( aTypeE != SMDSAbs_Edge )
5046 return EXTR_PATH_NOT_EDGE;
5049 list<SMESH_MeshEditor_PathPoint> fullList;
5051 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5053 if( aS == SMESH_Mesh::PseudoShape() ) {
5054 //Mesh without shape
5055 const SMDS_MeshNode* currentNode = NULL;
5056 const SMDS_MeshNode* prevNode = theN1;
5057 std::vector<const SMDS_MeshNode*> aNodesList;
5058 aNodesList.push_back(theN1);
5059 int nbEdges = 0, conn=0;
5060 const SMDS_MeshElement* prevElem = NULL;
5061 const SMDS_MeshElement* currentElem = NULL;
5062 int totalNbEdges = theTrack->NbEdges();
5063 SMDS_ElemIteratorPtr nIt;
5066 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5067 return EXTR_BAD_STARTING_NODE;
5070 conn = nbEdgeConnectivity(theN1);
5072 return EXTR_PATH_NOT_EDGE;
5074 aItE = theN1->GetInverseElementIterator();
5075 prevElem = aItE->next();
5076 currentElem = prevElem;
5078 if(totalNbEdges == 1 ) {
5079 nIt = currentElem->nodesIterator();
5080 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5081 if(currentNode == prevNode)
5082 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5083 aNodesList.push_back(currentNode);
5085 nIt = currentElem->nodesIterator();
5086 while( nIt->more() ) {
5087 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5088 if(currentNode == prevNode)
5089 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5090 aNodesList.push_back(currentNode);
5092 //case of the closed mesh
5093 if(currentNode == theN1) {
5098 conn = nbEdgeConnectivity(currentNode);
5100 return EXTR_PATH_NOT_EDGE;
5101 }else if( conn == 1 && nbEdges > 0 ) {
5106 prevNode = currentNode;
5107 aItE = currentNode->GetInverseElementIterator();
5108 currentElem = aItE->next();
5109 if( currentElem == prevElem)
5110 currentElem = aItE->next();
5111 nIt = currentElem->nodesIterator();
5112 prevElem = currentElem;
5118 if(nbEdges != totalNbEdges)
5119 return EXTR_PATH_NOT_EDGE;
5121 TopTools_SequenceOfShape Edges;
5122 double x1,x2,y1,y2,z1,z2;
5123 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5124 int startNid = theN1->GetID();
5125 for(int i = 1; i < aNodesList.size(); i++) {
5126 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5127 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5128 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5129 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5130 list<SMESH_MeshEditor_PathPoint> LPP;
5132 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5133 LLPPs.push_back(LPP);
5134 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5135 else startNid = aNodesList[i-1]->GetID();
5139 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5140 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5141 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5142 for(; itPP!=firstList.end(); itPP++) {
5143 fullList.push_back( *itPP );
5146 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5147 SMESH_MeshEditor_PathPoint PP2;
5148 fullList.pop_back();
5150 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5151 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5152 itPP = currList.begin();
5153 PP2 = currList.front();
5154 gp_Dir D1 = PP1.Tangent();
5155 gp_Dir D2 = PP2.Tangent();
5156 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5157 (D1.Z()+D2.Z())/2 ) );
5158 PP1.SetTangent(Dnew);
5159 fullList.push_back(PP1);
5161 for(; itPP!=currList.end(); itPP++) {
5162 fullList.push_back( *itPP );
5164 PP1 = fullList.back();
5165 fullList.pop_back();
5167 fullList.push_back(PP1);
5169 } // Sub-shape for the Pattern must be an Edge or Wire
5170 else if( aS.ShapeType() == TopAbs_EDGE ) {
5171 aTrackEdge = TopoDS::Edge( aS );
5172 // the Edge must not be degenerated
5173 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5174 return EXTR_BAD_PATH_SHAPE;
5175 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5176 const SMDS_MeshNode* aN1 = 0;
5177 const SMDS_MeshNode* aN2 = 0;
5178 if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5179 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5182 if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5183 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5186 // starting node must be aN1 or aN2
5187 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5188 return EXTR_BAD_STARTING_NODE;
5189 aItN = pMeshDS->nodesIterator();
5190 while ( aItN->more() ) {
5191 const SMDS_MeshNode* pNode = aItN->next();
5192 if( pNode==aN1 || pNode==aN2 ) continue;
5193 const SMDS_EdgePosition* pEPos =
5194 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5195 double aT = pEPos->GetUParameter();
5196 aPrms.push_back( aT );
5198 //Extrusion_Error err =
5199 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5201 else if( aS.ShapeType() == TopAbs_WIRE ) {
5202 list< SMESH_subMesh* > LSM;
5203 TopTools_SequenceOfShape Edges;
5204 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5205 for(; eExp.More(); eExp.Next()) {
5206 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5207 if( BRep_Tool::Degenerated(E) ) continue;
5208 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5214 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5215 TopoDS_Vertex aVprev;
5216 TColStd_MapOfInteger UsedNums;
5217 int NbEdges = Edges.Length();
5219 for(; i<=NbEdges; i++) {
5221 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5222 for(; itLSM!=LSM.end(); itLSM++) {
5224 if(UsedNums.Contains(k)) continue;
5225 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5226 SMESH_subMesh* locTrack = *itLSM;
5227 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5228 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5229 bool aN1isOK = false, aN2isOK = false;
5230 if ( aVprev.IsNull() ) {
5231 // if previous vertex is not yet defined, it means that we in the beginning of wire
5232 // and we have to find initial vertex corresponding to starting node theN1
5233 const SMDS_MeshNode* aN1 = 0;
5234 const SMDS_MeshNode* aN2 = 0;
5236 if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5237 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5240 if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5241 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5244 // starting node must be aN1 or aN2
5245 aN1isOK = ( aN1 && aN1 == theN1 );
5246 aN2isOK = ( aN2 && aN2 == theN1 );
5249 // we have specified ending vertex of the previous edge on the previous iteration
5250 // and we have just to check that it corresponds to any vertex in current segment
5251 aN1isOK = aVprev.IsSame( aV1 );
5252 aN2isOK = aVprev.IsSame( aV2 );
5254 if ( !aN1isOK && !aN2isOK ) continue;
5255 // 2. Collect parameters on the track edge
5257 aItN = locMeshDS->GetNodes();
5258 while ( aItN->more() ) {
5259 const SMDS_MeshNode* pNode = aItN->next();
5260 const SMDS_EdgePosition* pEPos =
5261 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5262 double aT = pEPos->GetUParameter();
5263 aPrms.push_back( aT );
5265 list<SMESH_MeshEditor_PathPoint> LPP;
5266 //Extrusion_Error err =
5267 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5268 LLPPs.push_back(LPP);
5270 // update startN for search following egde
5271 if ( aN1isOK ) aVprev = aV2;
5276 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5277 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5278 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5279 for(; itPP!=firstList.end(); itPP++) {
5280 fullList.push_back( *itPP );
5282 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5283 fullList.pop_back();
5285 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5286 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5287 itPP = currList.begin();
5288 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5289 gp_Dir D1 = PP1.Tangent();
5290 gp_Dir D2 = PP2.Tangent();
5291 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5292 PP1.SetTangent(Dnew);
5293 fullList.push_back(PP1);
5295 for(; itPP!=currList.end(); itPP++) {
5296 fullList.push_back( *itPP );
5298 PP1 = fullList.back();
5299 fullList.pop_back();
5301 // if wire not closed
5302 fullList.push_back(PP1);
5306 return EXTR_BAD_PATH_SHAPE;
5309 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5310 theHasRefPoint, theRefPoint, theMakeGroups);
5314 //=======================================================================
5315 //function : MakeEdgePathPoints
5316 //purpose : auxilary for ExtrusionAlongTrack
5317 //=======================================================================
5318 SMESH_MeshEditor::Extrusion_Error
5319 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5320 const TopoDS_Edge& aTrackEdge,
5322 list<SMESH_MeshEditor_PathPoint>& LPP)
5324 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5326 aTolVec2=aTolVec*aTolVec;
5328 TopoDS_Vertex aV1, aV2;
5329 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5330 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5331 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5332 // 2. Collect parameters on the track edge
5333 aPrms.push_front( aT1 );
5334 aPrms.push_back( aT2 );
5337 if( FirstIsStart ) {
5348 SMESH_MeshEditor_PathPoint aPP;
5349 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5350 std::list<double>::iterator aItD = aPrms.begin();
5351 for(; aItD != aPrms.end(); ++aItD) {
5355 aC3D->D1( aT, aP3D, aVec );
5356 aL2 = aVec.SquareMagnitude();
5357 if ( aL2 < aTolVec2 )
5358 return EXTR_CANT_GET_TANGENT;
5359 gp_Dir aTgt( aVec );
5361 aPP.SetTangent( aTgt );
5362 aPP.SetParameter( aT );
5369 //=======================================================================
5370 //function : MakeExtrElements
5371 //purpose : auxilary for ExtrusionAlongTrack
5372 //=======================================================================
5373 SMESH_MeshEditor::Extrusion_Error
5374 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5375 list<SMESH_MeshEditor_PathPoint>& fullList,
5376 const bool theHasAngles,
5377 list<double>& theAngles,
5378 const bool theLinearVariation,
5379 const bool theHasRefPoint,
5380 const gp_Pnt& theRefPoint,
5381 const bool theMakeGroups)
5383 MESSAGE("MakeExtrElements");
5384 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5385 int aNbTP = fullList.size();
5386 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5388 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5389 LinearAngleVariation(aNbTP-1, theAngles);
5391 vector<double> aAngles( aNbTP );
5393 for(; j<aNbTP; ++j) {
5396 if ( theHasAngles ) {
5398 std::list<double>::iterator aItD = theAngles.begin();
5399 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5401 aAngles[j] = anAngle;
5404 // fill vector of path points with angles
5405 //aPPs.resize(fullList.size());
5407 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5408 for(; itPP!=fullList.end(); itPP++) {
5410 SMESH_MeshEditor_PathPoint PP = *itPP;
5411 PP.SetAngle(aAngles[j]);
5415 TNodeOfNodeListMap mapNewNodes;
5416 TElemOfVecOfNnlmiMap mapElemNewNodes;
5417 TElemOfElemListMap newElemsMap;
5418 TIDSortedElemSet::iterator itElem;
5421 SMDSAbs_ElementType aTypeE;
5422 // source elements for each generated one
5423 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5425 // 3. Center of rotation aV0
5426 gp_Pnt aV0 = theRefPoint;
5428 if ( !theHasRefPoint ) {
5430 aGC.SetCoord( 0.,0.,0. );
5432 itElem = theElements.begin();
5433 for ( ; itElem != theElements.end(); itElem++ ) {
5434 const SMDS_MeshElement* elem = *itElem;
5436 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5437 while ( itN->more() ) {
5438 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5443 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5444 list<const SMDS_MeshNode*> aLNx;
5445 mapNewNodes[node] = aLNx;
5447 gp_XYZ aXYZ( aX, aY, aZ );
5455 } // if (!theHasRefPoint) {
5456 mapNewNodes.clear();
5458 // 4. Processing the elements
5459 SMESHDS_Mesh* aMesh = GetMeshDS();
5461 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5462 // check element type
5463 const SMDS_MeshElement* elem = *itElem;
5464 aTypeE = elem->GetType();
5465 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5468 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5469 newNodesItVec.reserve( elem->NbNodes() );
5471 // loop on elem nodes
5473 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5474 while ( itN->more() )
5477 // check if a node has been already processed
5478 const SMDS_MeshNode* node =
5479 static_cast<const SMDS_MeshNode*>( itN->next() );
5480 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5481 if ( nIt == mapNewNodes.end() ) {
5482 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5483 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5486 aX = node->X(); aY = node->Y(); aZ = node->Z();
5488 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5489 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5490 gp_Ax1 anAx1, anAxT1T0;
5491 gp_Dir aDT1x, aDT0x, aDT1T0;
5496 aPN0.SetCoord(aX, aY, aZ);
5498 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5500 aDT0x= aPP0.Tangent();
5501 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5503 for ( j = 1; j < aNbTP; ++j ) {
5504 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5506 aDT1x = aPP1.Tangent();
5507 aAngle1x = aPP1.Angle();
5509 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5511 gp_Vec aV01x( aP0x, aP1x );
5512 aTrsf.SetTranslation( aV01x );
5515 aV1x = aV0x.Transformed( aTrsf );
5516 aPN1 = aPN0.Transformed( aTrsf );
5518 // rotation 1 [ T1,T0 ]
5519 aAngleT1T0=-aDT1x.Angle( aDT0x );
5520 if (fabs(aAngleT1T0) > aTolAng) {
5522 anAxT1T0.SetLocation( aV1x );
5523 anAxT1T0.SetDirection( aDT1T0 );
5524 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5526 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5530 if ( theHasAngles ) {
5531 anAx1.SetLocation( aV1x );
5532 anAx1.SetDirection( aDT1x );
5533 aTrsfRot.SetRotation( anAx1, aAngle1x );
5535 aPN1 = aPN1.Transformed( aTrsfRot );
5539 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5540 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5541 // create additional node
5542 double x = ( aPN1.X() + aPN0.X() )/2.;
5543 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5544 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5545 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5546 myLastCreatedNodes.Append(newNode);
5547 srcNodes.Append( node );
5548 listNewNodes.push_back( newNode );
5553 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5554 myLastCreatedNodes.Append(newNode);
5555 srcNodes.Append( node );
5556 listNewNodes.push_back( newNode );
5566 // if current elem is quadratic and current node is not medium
5567 // we have to check - may be it is needed to insert additional nodes
5568 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5569 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5570 if(listNewNodes.size()==aNbTP-1) {
5571 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5572 gp_XYZ P(node->X(), node->Y(), node->Z());
5573 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5575 for(i=0; i<aNbTP-1; i++) {
5576 const SMDS_MeshNode* N = *it;
5577 double x = ( N->X() + P.X() )/2.;
5578 double y = ( N->Y() + P.Y() )/2.;
5579 double z = ( N->Z() + P.Z() )/2.;
5580 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5581 srcNodes.Append( node );
5582 myLastCreatedNodes.Append(newN);
5585 P = gp_XYZ(N->X(),N->Y(),N->Z());
5587 listNewNodes.clear();
5588 for(i=0; i<2*(aNbTP-1); i++) {
5589 listNewNodes.push_back(aNodes[i]);
5595 newNodesItVec.push_back( nIt );
5597 // make new elements
5598 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5599 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5600 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5603 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5605 if ( theMakeGroups )
5606 generateGroups( srcNodes, srcElems, "extruded");
5612 //=======================================================================
5613 //function : LinearAngleVariation
5614 //purpose : auxilary for ExtrusionAlongTrack
5615 //=======================================================================
5616 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5617 list<double>& Angles)
5619 int nbAngles = Angles.size();
5620 if( nbSteps > nbAngles ) {
5621 vector<double> theAngles(nbAngles);
5622 list<double>::iterator it = Angles.begin();
5624 for(; it!=Angles.end(); it++) {
5626 theAngles[i] = (*it);
5629 double rAn2St = double( nbAngles ) / double( nbSteps );
5630 double angPrev = 0, angle;
5631 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5632 double angCur = rAn2St * ( iSt+1 );
5633 double angCurFloor = floor( angCur );
5634 double angPrevFloor = floor( angPrev );
5635 if ( angPrevFloor == angCurFloor )
5636 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5638 int iP = int( angPrevFloor );
5639 double angPrevCeil = ceil(angPrev);
5640 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5642 int iC = int( angCurFloor );
5643 if ( iC < nbAngles )
5644 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5646 iP = int( angPrevCeil );
5648 angle += theAngles[ iC ];
5650 res.push_back(angle);
5655 for(; it!=res.end(); it++)
5656 Angles.push_back( *it );
5661 //================================================================================
5663 * \brief Move or copy theElements applying theTrsf to their nodes
5664 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5665 * \param theTrsf - transformation to apply
5666 * \param theCopy - if true, create translated copies of theElems
5667 * \param theMakeGroups - if true and theCopy, create translated groups
5668 * \param theTargetMesh - mesh to copy translated elements into
5669 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5671 //================================================================================
5673 SMESH_MeshEditor::PGroupIDs
5674 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5675 const gp_Trsf& theTrsf,
5677 const bool theMakeGroups,
5678 SMESH_Mesh* theTargetMesh)
5680 myLastCreatedElems.Clear();
5681 myLastCreatedNodes.Clear();
5683 bool needReverse = false;
5684 string groupPostfix;
5685 switch ( theTrsf.Form() ) {
5687 MESSAGE("gp_PntMirror");
5689 groupPostfix = "mirrored";
5692 MESSAGE("gp_Ax1Mirror");
5693 groupPostfix = "mirrored";
5696 MESSAGE("gp_Ax2Mirror");
5698 groupPostfix = "mirrored";
5701 MESSAGE("gp_Rotation");
5702 groupPostfix = "rotated";
5704 case gp_Translation:
5705 MESSAGE("gp_Translation");
5706 groupPostfix = "translated";
5709 MESSAGE("gp_Scale");
5710 groupPostfix = "scaled";
5712 case gp_CompoundTrsf: // different scale by axis
5713 MESSAGE("gp_CompoundTrsf");
5714 groupPostfix = "scaled";
5718 needReverse = false;
5719 groupPostfix = "transformed";
5722 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5723 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5724 SMESHDS_Mesh* aMesh = GetMeshDS();
5727 // map old node to new one
5728 TNodeNodeMap nodeMap;
5730 // elements sharing moved nodes; those of them which have all
5731 // nodes mirrored but are not in theElems are to be reversed
5732 TIDSortedElemSet inverseElemSet;
5734 // source elements for each generated one
5735 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5737 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5738 TIDSortedElemSet orphanNode;
5740 if ( theElems.empty() ) // transform the whole mesh
5743 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5744 while ( eIt->more() ) theElems.insert( eIt->next() );
5746 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5747 while ( nIt->more() )
5749 const SMDS_MeshNode* node = nIt->next();
5750 if ( node->NbInverseElements() == 0)
5751 orphanNode.insert( node );
5755 // loop on elements to transform nodes : first orphan nodes then elems
5756 TIDSortedElemSet::iterator itElem;
5757 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5758 for (int i=0; i<2; i++)
5759 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5760 const SMDS_MeshElement* elem = *itElem;
5764 // loop on elem nodes
5765 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5766 while ( itN->more() ) {
5768 const SMDS_MeshNode* node = cast2Node( itN->next() );
5769 // check if a node has been already transformed
5770 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5771 nodeMap.insert( make_pair ( node, node ));
5772 if ( !n2n_isnew.second )
5776 coord[0] = node->X();
5777 coord[1] = node->Y();
5778 coord[2] = node->Z();
5779 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5780 if ( theTargetMesh ) {
5781 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5782 n2n_isnew.first->second = newNode;
5783 myLastCreatedNodes.Append(newNode);
5784 srcNodes.Append( node );
5786 else if ( theCopy ) {
5787 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5788 n2n_isnew.first->second = newNode;
5789 myLastCreatedNodes.Append(newNode);
5790 srcNodes.Append( node );
5793 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5794 // node position on shape becomes invalid
5795 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5796 ( SMDS_SpacePosition::originSpacePosition() );
5799 // keep inverse elements
5800 if ( !theCopy && !theTargetMesh && needReverse ) {
5801 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5802 while ( invElemIt->more() ) {
5803 const SMDS_MeshElement* iel = invElemIt->next();
5804 inverseElemSet.insert( iel );
5810 // either create new elements or reverse mirrored ones
5811 if ( !theCopy && !needReverse && !theTargetMesh )
5814 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5815 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5816 theElems.insert( *invElemIt );
5818 // Replicate or reverse elements
5820 std::vector<int> iForw;
5821 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5823 const SMDS_MeshElement* elem = *itElem;
5824 if ( !elem ) continue;
5826 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5827 int nbNodes = elem->NbNodes();
5828 if ( geomType == SMDSGeom_NONE ) continue; // node
5830 switch ( geomType ) {
5832 case SMDSGeom_POLYGON: // ---------------------- polygon
5834 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5836 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5837 while (itN->more()) {
5838 const SMDS_MeshNode* node =
5839 static_cast<const SMDS_MeshNode*>(itN->next());
5840 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5841 if (nodeMapIt == nodeMap.end())
5842 break; // not all nodes transformed
5844 // reverse mirrored faces and volumes
5845 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5847 poly_nodes[iNode] = (*nodeMapIt).second;
5851 if ( iNode != nbNodes )
5852 continue; // not all nodes transformed
5854 if ( theTargetMesh ) {
5855 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5856 srcElems.Append( elem );
5858 else if ( theCopy ) {
5859 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5860 srcElems.Append( elem );
5863 aMesh->ChangePolygonNodes(elem, poly_nodes);
5868 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5870 const SMDS_VtkVolume* aPolyedre =
5871 dynamic_cast<const SMDS_VtkVolume*>( elem );
5873 MESSAGE("Warning: bad volumic element");
5877 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5878 vector<int> quantities; quantities.reserve( nbNodes );
5880 bool allTransformed = true;
5881 int nbFaces = aPolyedre->NbFaces();
5882 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5883 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5884 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5885 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5886 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5887 if (nodeMapIt == nodeMap.end()) {
5888 allTransformed = false; // not all nodes transformed
5890 poly_nodes.push_back((*nodeMapIt).second);
5892 if ( needReverse && allTransformed )
5893 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5895 quantities.push_back(nbFaceNodes);
5897 if ( !allTransformed )
5898 continue; // not all nodes transformed
5900 if ( theTargetMesh ) {
5901 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5902 srcElems.Append( elem );
5904 else if ( theCopy ) {
5905 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5906 srcElems.Append( elem );
5909 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5914 case SMDSGeom_BALL: // -------------------- Ball
5916 if ( !theCopy && !theTargetMesh ) continue;
5918 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5919 if (nodeMapIt == nodeMap.end())
5920 continue; // not all nodes transformed
5922 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5923 if ( theTargetMesh ) {
5924 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5925 srcElems.Append( elem );
5928 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5929 srcElems.Append( elem );
5934 default: // ----------------------- Regular elements
5936 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5937 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5938 const std::vector<int>& i = needReverse ? iRev : iForw;
5940 // find transformed nodes
5941 vector<const SMDS_MeshNode*> nodes(nbNodes);
5943 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5944 while ( itN->more() ) {
5945 const SMDS_MeshNode* node =
5946 static_cast<const SMDS_MeshNode*>( itN->next() );
5947 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5948 if ( nodeMapIt == nodeMap.end() )
5949 break; // not all nodes transformed
5950 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5952 if ( iNode != nbNodes )
5953 continue; // not all nodes transformed
5955 if ( theTargetMesh ) {
5956 if ( SMDS_MeshElement* copy =
5957 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5958 myLastCreatedElems.Append( copy );
5959 srcElems.Append( elem );
5962 else if ( theCopy ) {
5963 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5964 srcElems.Append( elem );
5967 // reverse element as it was reversed by transformation
5969 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5971 } // switch ( geomType )
5973 } // loop on elements
5975 PGroupIDs newGroupIDs;
5977 if ( ( theMakeGroups && theCopy ) ||
5978 ( theMakeGroups && theTargetMesh ) )
5979 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5984 //=======================================================================
5986 * \brief Create groups of elements made during transformation
5987 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5988 * \param elemGens - elements making corresponding myLastCreatedElems
5989 * \param postfix - to append to names of new groups
5991 //=======================================================================
5993 SMESH_MeshEditor::PGroupIDs
5994 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5995 const SMESH_SequenceOfElemPtr& elemGens,
5996 const std::string& postfix,
5997 SMESH_Mesh* targetMesh)
5999 PGroupIDs newGroupIDs( new list<int> );
6000 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6002 // Sort existing groups by types and collect their names
6004 // to store an old group and a generated new ones
6006 using boost::make_tuple;
6007 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6008 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6009 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6011 set< string > groupNames;
6013 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6014 if ( !groupIt->more() ) return newGroupIDs;
6016 int newGroupID = mesh->GetGroupIds().back()+1;
6017 while ( groupIt->more() )
6019 SMESH_Group * group = groupIt->next();
6020 if ( !group ) continue;
6021 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6022 if ( !groupDS || groupDS->IsEmpty() ) continue;
6023 groupNames.insert ( group->GetName() );
6024 groupDS->SetStoreName( group->GetName() );
6025 const SMDSAbs_ElementType type = groupDS->GetType();
6026 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6027 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6028 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6029 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6032 // Loop on nodes and elements to add them in new groups
6034 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6036 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6037 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6038 if ( gens.Length() != elems.Length() )
6039 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6041 // loop on created elements
6042 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6044 const SMDS_MeshElement* sourceElem = gens( iElem );
6045 if ( !sourceElem ) {
6046 MESSAGE("generateGroups(): NULL source element");
6049 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6050 if ( groupsOldNew.empty() ) { // no groups of this type at all
6051 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6052 ++iElem; // skip all elements made by sourceElem
6055 // collect all elements made by the iElem-th sourceElem
6056 list< const SMDS_MeshElement* > resultElems;
6057 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6058 if ( resElem != sourceElem )
6059 resultElems.push_back( resElem );
6060 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6061 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6062 if ( resElem != sourceElem )
6063 resultElems.push_back( resElem );
6065 // there must be a top element
6066 const SMDS_MeshElement* topElem = 0;
6069 topElem = resultElems.back();
6070 resultElems.pop_back();
6074 list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6075 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6076 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6078 topElem = *resElemIt;
6079 resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6084 // add resultElems to groups originted from ones the sourceElem belongs to
6085 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6086 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6088 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6089 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6091 // fill in a new group
6092 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6093 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6094 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6095 newGroup.Add( *resElemIt );
6097 // fill a "top" group
6100 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6101 newTopGroup.Add( topElem );
6105 } // loop on created elements
6106 }// loop on nodes and elements
6108 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6110 list<int> topGrouIds;
6111 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6113 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6114 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6115 orderedOldNewGroups[i]->get<2>() };
6116 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6117 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6119 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6120 if ( newGroupDS->IsEmpty() )
6122 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6127 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6130 const bool isTop = ( nbNewGroups == 2 &&
6131 newGroupDS->GetType() == oldGroupDS->GetType() &&
6134 string name = oldGroupDS->GetStoreName();
6135 if ( !targetMesh ) {
6136 string suffix = ( isTop ? "top": postfix.c_str() );
6140 while ( !groupNames.insert( name ).second ) // name exists
6141 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6146 newGroupDS->SetStoreName( name.c_str() );
6148 // make a SMESH_Groups
6149 mesh->AddGroup( newGroupDS );
6151 topGrouIds.push_back( newGroupDS->GetID() );
6153 newGroupIDs->push_back( newGroupDS->GetID() );
6157 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6162 //================================================================================
6164 * \brief Return list of group of nodes close to each other within theTolerance
6165 * Search among theNodes or in the whole mesh if theNodes is empty using
6166 * an Octree algorithm
6168 //================================================================================
6170 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6171 const double theTolerance,
6172 TListOfListOfNodes & theGroupsOfNodes)
6174 myLastCreatedElems.Clear();
6175 myLastCreatedNodes.Clear();
6177 if ( theNodes.empty() )
6178 { // get all nodes in the mesh
6179 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6180 while ( nIt->more() )
6181 theNodes.insert( theNodes.end(),nIt->next());
6184 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6188 //=======================================================================
6190 * \brief Implementation of search for the node closest to point
6192 //=======================================================================
6194 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6196 //---------------------------------------------------------------------
6198 * \brief Constructor
6200 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6202 myMesh = ( SMESHDS_Mesh* ) theMesh;
6204 TIDSortedNodeSet nodes;
6206 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6207 while ( nIt->more() )
6208 nodes.insert( nodes.end(), nIt->next() );
6210 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6212 // get max size of a leaf box
6213 SMESH_OctreeNode* tree = myOctreeNode;
6214 while ( !tree->isLeaf() )
6216 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6220 myHalfLeafSize = tree->maxSize() / 2.;
6223 //---------------------------------------------------------------------
6225 * \brief Move node and update myOctreeNode accordingly
6227 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6229 myOctreeNode->UpdateByMoveNode( node, toPnt );
6230 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6233 //---------------------------------------------------------------------
6235 * \brief Do it's job
6237 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6239 map<double, const SMDS_MeshNode*> dist2Nodes;
6240 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6241 if ( !dist2Nodes.empty() )
6242 return dist2Nodes.begin()->second;
6243 list<const SMDS_MeshNode*> nodes;
6244 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6246 double minSqDist = DBL_MAX;
6247 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6249 // sort leafs by their distance from thePnt
6250 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6251 TDistTreeMap treeMap;
6252 list< SMESH_OctreeNode* > treeList;
6253 list< SMESH_OctreeNode* >::iterator trIt;
6254 treeList.push_back( myOctreeNode );
6256 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6257 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6258 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6260 SMESH_OctreeNode* tree = *trIt;
6261 if ( !tree->isLeaf() ) // put children to the queue
6263 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6264 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6265 while ( cIt->more() )
6266 treeList.push_back( cIt->next() );
6268 else if ( tree->NbNodes() ) // put a tree to the treeMap
6270 const Bnd_B3d& box = *tree->getBox();
6271 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6272 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6273 if ( !it_in.second ) // not unique distance to box center
6274 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6277 // find distance after which there is no sense to check tree's
6278 double sqLimit = DBL_MAX;
6279 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6280 if ( treeMap.size() > 5 ) {
6281 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6282 const Bnd_B3d& box = *closestTree->getBox();
6283 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6284 sqLimit = limit * limit;
6286 // get all nodes from trees
6287 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6288 if ( sqDist_tree->first > sqLimit )
6290 SMESH_OctreeNode* tree = sqDist_tree->second;
6291 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6294 // find closest among nodes
6295 minSqDist = DBL_MAX;
6296 const SMDS_MeshNode* closestNode = 0;
6297 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6298 for ( ; nIt != nodes.end(); ++nIt ) {
6299 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6300 if ( minSqDist > sqDist ) {
6308 //---------------------------------------------------------------------
6312 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6314 //---------------------------------------------------------------------
6316 * \brief Return the node tree
6318 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6321 SMESH_OctreeNode* myOctreeNode;
6322 SMESHDS_Mesh* myMesh;
6323 double myHalfLeafSize; // max size of a leaf box
6326 //=======================================================================
6328 * \brief Return SMESH_NodeSearcher
6330 //=======================================================================
6332 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6334 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6337 // ========================================================================
6338 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6340 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6341 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6342 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6344 //=======================================================================
6346 * \brief Octal tree of bounding boxes of elements
6348 //=======================================================================
6350 class ElementBndBoxTree : public SMESH_Octree
6354 ElementBndBoxTree(const SMDS_Mesh& mesh,
6355 SMDSAbs_ElementType elemType,
6356 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6357 double tolerance = NodeRadius );
6358 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6359 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6360 void getElementsInSphere ( const gp_XYZ& center,
6361 const double radius, TIDSortedElemSet& foundElems);
6362 size_t getSize() { return std::max( _size, _elements.size() ); }
6363 ~ElementBndBoxTree();
6366 ElementBndBoxTree():_size(0) {}
6367 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6368 void buildChildrenData();
6369 Bnd_B3d* buildRootBox();
6371 //!< Bounding box of element
6372 struct ElementBox : public Bnd_B3d
6374 const SMDS_MeshElement* _element;
6375 int _refCount; // an ElementBox can be included in several tree branches
6376 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6378 vector< ElementBox* > _elements;
6382 //================================================================================
6384 * \brief ElementBndBoxTree creation
6386 //================================================================================
6388 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6389 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6391 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6392 _elements.reserve( nbElems );
6394 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6395 while ( elemIt->more() )
6396 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6401 //================================================================================
6405 //================================================================================
6407 ElementBndBoxTree::~ElementBndBoxTree()
6409 for ( int i = 0; i < _elements.size(); ++i )
6410 if ( --_elements[i]->_refCount <= 0 )
6411 delete _elements[i];
6414 //================================================================================
6416 * \brief Return the maximal box
6418 //================================================================================
6420 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6422 Bnd_B3d* box = new Bnd_B3d;
6423 for ( int i = 0; i < _elements.size(); ++i )
6424 box->Add( *_elements[i] );
6428 //================================================================================
6430 * \brief Redistrubute element boxes among children
6432 //================================================================================
6434 void ElementBndBoxTree::buildChildrenData()
6436 for ( int i = 0; i < _elements.size(); ++i )
6438 for (int j = 0; j < 8; j++)
6440 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6442 _elements[i]->_refCount++;
6443 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6446 _elements[i]->_refCount--;
6448 _size = _elements.size();
6449 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6451 for (int j = 0; j < 8; j++)
6453 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6454 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6455 child->myIsLeaf = true;
6457 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6458 SMESHUtils::CompactVector( child->_elements );
6462 //================================================================================
6464 * \brief Return elements which can include the point
6466 //================================================================================
6468 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6469 TIDSortedElemSet& foundElems)
6471 if ( getBox()->IsOut( point.XYZ() ))
6476 for ( int i = 0; i < _elements.size(); ++i )
6477 if ( !_elements[i]->IsOut( point.XYZ() ))
6478 foundElems.insert( _elements[i]->_element );
6482 for (int i = 0; i < 8; i++)
6483 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6487 //================================================================================
6489 * \brief Return elements which can be intersected by the line
6491 //================================================================================
6493 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6494 TIDSortedElemSet& foundElems)
6496 if ( getBox()->IsOut( line ))
6501 for ( int i = 0; i < _elements.size(); ++i )
6502 if ( !_elements[i]->IsOut( line ))
6503 foundElems.insert( _elements[i]->_element );
6507 for (int i = 0; i < 8; i++)
6508 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6512 //================================================================================
6514 * \brief Return elements from leaves intersecting the sphere
6516 //================================================================================
6518 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6519 const double radius,
6520 TIDSortedElemSet& foundElems)
6522 if ( getBox()->IsOut( center, radius ))
6527 for ( int i = 0; i < _elements.size(); ++i )
6528 if ( !_elements[i]->IsOut( center, radius ))
6529 foundElems.insert( _elements[i]->_element );
6533 for (int i = 0; i < 8; i++)
6534 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6538 //================================================================================
6540 * \brief Construct the element box
6542 //================================================================================
6544 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6548 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6549 while ( nIt->more() )
6550 Add( SMESH_TNodeXYZ( nIt->next() ));
6551 Enlarge( tolerance );
6556 //=======================================================================
6558 * \brief Implementation of search for the elements by point and
6559 * of classification of point in 2D mesh
6561 //=======================================================================
6563 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6565 SMESHDS_Mesh* _mesh;
6566 SMDS_ElemIteratorPtr _meshPartIt;
6567 ElementBndBoxTree* _ebbTree;
6568 SMESH_NodeSearcherImpl* _nodeSearcher;
6569 SMDSAbs_ElementType _elementType;
6571 bool _outerFacesFound;
6572 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6574 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6575 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6576 ~SMESH_ElementSearcherImpl()
6578 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6579 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6581 virtual int FindElementsByPoint(const gp_Pnt& point,
6582 SMDSAbs_ElementType type,
6583 vector< const SMDS_MeshElement* >& foundElements);
6584 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6585 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6586 SMDSAbs_ElementType type );
6588 void GetElementsNearLine( const gp_Ax1& line,
6589 SMDSAbs_ElementType type,
6590 vector< const SMDS_MeshElement* >& foundElems);
6591 double getTolerance();
6592 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6593 const double tolerance, double & param);
6594 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6595 bool isOuterBoundary(const SMDS_MeshElement* face) const
6597 return _outerFaces.empty() || _outerFaces.count(face);
6599 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6601 const SMDS_MeshElement* _face;
6603 bool _coincides; //!< the line lays in face plane
6604 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6605 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6607 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6610 TIDSortedElemSet _faces;
6611 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6612 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6616 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6618 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6619 << ", _coincides="<<i._coincides << ")";
6622 //=======================================================================
6624 * \brief define tolerance for search
6626 //=======================================================================
6628 double SMESH_ElementSearcherImpl::getTolerance()
6630 if ( _tolerance < 0 )
6632 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6635 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6637 double boxSize = _nodeSearcher->getTree()->maxSize();
6638 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6640 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6642 double boxSize = _ebbTree->maxSize();
6643 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6645 if ( _tolerance == 0 )
6647 // define tolerance by size of a most complex element
6648 int complexType = SMDSAbs_Volume;
6649 while ( complexType > SMDSAbs_All &&
6650 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6652 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6654 if ( complexType == int( SMDSAbs_Node ))
6656 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6658 if ( meshInfo.NbNodes() > 2 )
6659 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6663 SMDS_ElemIteratorPtr elemIt =
6664 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6665 const SMDS_MeshElement* elem = elemIt->next();
6666 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6667 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6669 while ( nodeIt->more() )
6671 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6672 elemSize = max( dist, elemSize );
6675 _tolerance = 1e-4 * elemSize;
6681 //================================================================================
6683 * \brief Find intersection of the line and an edge of face and return parameter on line
6685 //================================================================================
6687 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6688 const SMDS_MeshElement* face,
6695 GeomAPI_ExtremaCurveCurve anExtCC;
6696 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6698 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6699 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6701 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6702 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6703 anExtCC.Init( lineCurve, edge);
6704 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6706 Quantity_Parameter pl, pe;
6707 anExtCC.LowerDistanceParameters( pl, pe );
6709 if ( ++nbInts == 2 )
6713 if ( nbInts > 0 ) param /= nbInts;
6716 //================================================================================
6718 * \brief Find all faces belonging to the outer boundary of mesh
6720 //================================================================================
6722 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6724 if ( _outerFacesFound ) return;
6726 // Collect all outer faces by passing from one outer face to another via their links
6727 // and BTW find out if there are internal faces at all.
6729 // checked links and links where outer boundary meets internal one
6730 set< SMESH_TLink > visitedLinks, seamLinks;
6732 // links to treat with already visited faces sharing them
6733 list < TFaceLink > startLinks;
6735 // load startLinks with the first outerFace
6736 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6737 _outerFaces.insert( outerFace );
6739 TIDSortedElemSet emptySet;
6740 while ( !startLinks.empty() )
6742 const SMESH_TLink& link = startLinks.front()._link;
6743 TIDSortedElemSet& faces = startLinks.front()._faces;
6745 outerFace = *faces.begin();
6746 // find other faces sharing the link
6747 const SMDS_MeshElement* f;
6748 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6751 // select another outer face among the found
6752 const SMDS_MeshElement* outerFace2 = 0;
6753 if ( faces.size() == 2 )
6755 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6757 else if ( faces.size() > 2 )
6759 seamLinks.insert( link );
6761 // link direction within the outerFace
6762 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6763 SMESH_TNodeXYZ( link.node2()));
6764 int i1 = outerFace->GetNodeIndex( link.node1() );
6765 int i2 = outerFace->GetNodeIndex( link.node2() );
6766 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6767 if ( rev ) n1n2.Reverse();
6769 gp_XYZ ofNorm, fNorm;
6770 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6772 // direction from the link inside outerFace
6773 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6774 // sort all other faces by angle with the dirInOF
6775 map< double, const SMDS_MeshElement* > angle2Face;
6776 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6777 for ( ; face != faces.end(); ++face )
6779 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6781 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6782 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6783 if ( angle < 0 ) angle += 2. * M_PI;
6784 angle2Face.insert( make_pair( angle, *face ));
6786 if ( !angle2Face.empty() )
6787 outerFace2 = angle2Face.begin()->second;
6790 // store the found outer face and add its links to continue seaching from
6793 _outerFaces.insert( outerFace );
6794 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6795 for ( int i = 0; i < nbNodes; ++i )
6797 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6798 if ( visitedLinks.insert( link2 ).second )
6799 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6802 startLinks.pop_front();
6804 _outerFacesFound = true;
6806 if ( !seamLinks.empty() )
6808 // There are internal boundaries touching the outher one,
6809 // find all faces of internal boundaries in order to find
6810 // faces of boundaries of holes, if any.
6815 _outerFaces.clear();
6819 //=======================================================================
6821 * \brief Find elements of given type where the given point is IN or ON.
6822 * Returns nb of found elements and elements them-selves.
6824 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6826 //=======================================================================
6828 int SMESH_ElementSearcherImpl::
6829 FindElementsByPoint(const gp_Pnt& point,
6830 SMDSAbs_ElementType type,
6831 vector< const SMDS_MeshElement* >& foundElements)
6833 foundElements.clear();
6835 double tolerance = getTolerance();
6837 // =================================================================================
6838 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6840 if ( !_nodeSearcher )
6841 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6843 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6844 if ( !closeNode ) return foundElements.size();
6846 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6847 return foundElements.size(); // to far from any node
6849 if ( type == SMDSAbs_Node )
6851 foundElements.push_back( closeNode );
6855 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6856 while ( elemIt->more() )
6857 foundElements.push_back( elemIt->next() );
6860 // =================================================================================
6861 else // elements more complex than 0D
6863 if ( !_ebbTree || _elementType != type )
6865 if ( _ebbTree ) delete _ebbTree;
6866 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6868 TIDSortedElemSet suspectElems;
6869 _ebbTree->getElementsNearPoint( point, suspectElems );
6870 TIDSortedElemSet::iterator elem = suspectElems.begin();
6871 for ( ; elem != suspectElems.end(); ++elem )
6872 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6873 foundElements.push_back( *elem );
6875 return foundElements.size();
6878 //=======================================================================
6880 * \brief Find an element of given type most close to the given point
6882 * WARNING: Only face search is implemeneted so far
6884 //=======================================================================
6886 const SMDS_MeshElement*
6887 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6888 SMDSAbs_ElementType type )
6890 const SMDS_MeshElement* closestElem = 0;
6892 if ( type == SMDSAbs_Face )
6894 if ( !_ebbTree || _elementType != type )
6896 if ( _ebbTree ) delete _ebbTree;
6897 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6899 TIDSortedElemSet suspectElems;
6900 _ebbTree->getElementsNearPoint( point, suspectElems );
6902 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6904 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6905 _ebbTree->getBox()->CornerMax() );
6907 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6908 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6910 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6911 while ( suspectElems.empty() )
6913 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6917 double minDist = std::numeric_limits<double>::max();
6918 multimap< double, const SMDS_MeshElement* > dist2face;
6919 TIDSortedElemSet::iterator elem = suspectElems.begin();
6920 for ( ; elem != suspectElems.end(); ++elem )
6922 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6924 if ( dist < minDist + 1e-10)
6927 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6930 if ( !dist2face.empty() )
6932 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6933 closestElem = d2f->second;
6934 // if there are several elements at the same distance, select one
6935 // with GC closest to the point
6936 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6937 double minDistToGC = 0;
6938 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6940 if ( minDistToGC == 0 )
6943 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6944 TXyzIterator(), gc ) / closestElem->NbNodes();
6945 minDistToGC = point.SquareDistance( gc );
6948 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6949 TXyzIterator(), gc ) / d2f->second->NbNodes();
6950 double d = point.SquareDistance( gc );
6951 if ( d < minDistToGC )
6954 closestElem = d2f->second;
6957 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6958 // <<closestElem->GetID() << " DIST " << minDist << endl;
6963 // NOT IMPLEMENTED SO FAR
6969 //================================================================================
6971 * \brief Classify the given point in the closed 2D mesh
6973 //================================================================================
6975 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6977 double tolerance = getTolerance();
6978 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6980 if ( _ebbTree ) delete _ebbTree;
6981 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6983 // Algo: analyse transition of a line starting at the point through mesh boundary;
6984 // try three lines parallel to axis of the coordinate system and perform rough
6985 // analysis. If solution is not clear perform thorough analysis.
6987 const int nbAxes = 3;
6988 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6989 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6990 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6991 multimap< int, int > nbInt2Axis; // to find the simplest case
6992 for ( int axis = 0; axis < nbAxes; ++axis )
6994 gp_Ax1 lineAxis( point, axisDir[axis]);
6995 gp_Lin line ( lineAxis );
6997 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6998 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
7000 // Intersect faces with the line
7002 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7003 TIDSortedElemSet::iterator face = suspectFaces.begin();
7004 for ( ; face != suspectFaces.end(); ++face )
7008 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
7009 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
7011 // perform intersection
7012 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
7013 if ( !intersection.IsDone() )
7015 if ( intersection.IsInQuadric() )
7017 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7019 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7021 gp_Pnt intersectionPoint = intersection.Point(1);
7022 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7023 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7026 // Analyse intersections roughly
7028 int nbInter = u2inters.size();
7032 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7033 if ( nbInter == 1 ) // not closed mesh
7034 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7036 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7039 if ( (f<0) == (l<0) )
7042 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7043 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
7044 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7047 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7049 if ( _outerFacesFound ) break; // pass to thorough analysis
7051 } // three attempts - loop on CS axes
7053 // Analyse intersections thoroughly.
7054 // We make two loops maximum, on the first one we only exclude touching intersections,
7055 // on the second, if situation is still unclear, we gather and use information on
7056 // position of faces (internal or outer). If faces position is already gathered,
7057 // we make the second loop right away.
7059 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7061 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7062 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7064 int axis = nb_axis->second;
7065 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7067 gp_Ax1 lineAxis( point, axisDir[axis]);
7068 gp_Lin line ( lineAxis );
7070 // add tangent intersections to u2inters
7072 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7073 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7074 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7075 u2inters.insert(make_pair( param, *tgtInt ));
7076 tangentInters[ axis ].clear();
7078 // Count intersections before and after the point excluding touching ones.
7079 // If hasPositionInfo we count intersections of outer boundary only
7081 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7082 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7083 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7084 bool ok = ! u_int1->second._coincides;
7085 while ( ok && u_int1 != u2inters.end() )
7087 double u = u_int1->first;
7088 bool touchingInt = false;
7089 if ( ++u_int2 != u2inters.end() )
7091 // skip intersections at the same point (if the line passes through edge or node)
7093 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7099 // skip tangent intersections
7101 const SMDS_MeshElement* prevFace = u_int1->second._face;
7102 while ( ok && u_int2->second._coincides )
7104 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7110 ok = ( u_int2 != u2inters.end() );
7115 // skip intersections at the same point after tangent intersections
7118 double u2 = u_int2->first;
7120 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7126 // decide if we skipped a touching intersection
7127 if ( nbSamePnt + nbTgt > 0 )
7129 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7130 map< double, TInters >::iterator u_int = u_int1;
7131 for ( ; u_int != u_int2; ++u_int )
7133 if ( u_int->second._coincides ) continue;
7134 double dot = u_int->second._faceNorm * line.Direction();
7135 if ( dot > maxDot ) maxDot = dot;
7136 if ( dot < minDot ) minDot = dot;
7138 touchingInt = ( minDot*maxDot < 0 );
7143 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7154 u_int1 = u_int2; // to next intersection
7156 } // loop on intersections with one line
7160 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7163 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7166 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7167 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7169 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7172 if ( (f<0) == (l<0) )
7175 if ( hasPositionInfo )
7176 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7178 } // loop on intersections of the tree lines - thorough analysis
7180 if ( !hasPositionInfo )
7182 // gather info on faces position - is face in the outer boundary or not
7183 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7184 findOuterBoundary( u2inters.begin()->second._face );
7187 } // two attempts - with and w/o faces position info in the mesh
7189 return TopAbs_UNKNOWN;
7192 //=======================================================================
7194 * \brief Return elements possibly intersecting the line
7196 //=======================================================================
7198 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7199 SMDSAbs_ElementType type,
7200 vector< const SMDS_MeshElement* >& foundElems)
7202 if ( !_ebbTree || _elementType != type )
7204 if ( _ebbTree ) delete _ebbTree;
7205 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7207 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7208 _ebbTree->getElementsNearLine( line, suspectFaces );
7209 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7212 //=======================================================================
7214 * \brief Return SMESH_ElementSearcher
7216 //=======================================================================
7218 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7220 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7223 //=======================================================================
7225 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7227 //=======================================================================
7229 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7231 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7234 //=======================================================================
7236 * \brief Return true if the point is IN or ON of the element
7238 //=======================================================================
7240 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7242 if ( element->GetType() == SMDSAbs_Volume)
7244 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7247 // get ordered nodes
7249 vector< gp_XYZ > xyz;
7250 vector<const SMDS_MeshNode*> nodeList;
7252 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7253 if ( element->IsQuadratic() ) {
7254 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7255 nodeIt = f->interlacedNodesElemIterator();
7256 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7257 nodeIt = e->interlacedNodesElemIterator();
7259 while ( nodeIt->more() )
7261 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7262 xyz.push_back( SMESH_TNodeXYZ(node) );
7263 nodeList.push_back(node);
7266 int i, nbNodes = element->NbNodes();
7268 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7270 // compute face normal
7271 gp_Vec faceNorm(0,0,0);
7272 xyz.push_back( xyz.front() );
7273 nodeList.push_back( nodeList.front() );
7274 for ( i = 0; i < nbNodes; ++i )
7276 gp_Vec edge1( xyz[i+1], xyz[i]);
7277 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7278 faceNorm += edge1 ^ edge2;
7280 double normSize = faceNorm.Magnitude();
7281 if ( normSize <= tol )
7283 // degenerated face: point is out if it is out of all face edges
7284 for ( i = 0; i < nbNodes; ++i )
7286 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7287 if ( !IsOut( &edge, point, tol ))
7292 faceNorm /= normSize;
7294 // check if the point lays on face plane
7295 gp_Vec n2p( xyz[0], point );
7296 if ( fabs( n2p * faceNorm ) > tol )
7297 return true; // not on face plane
7299 // check if point is out of face boundary:
7300 // define it by closest transition of a ray point->infinity through face boundary
7301 // on the face plane.
7302 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7303 // to find intersections of the ray with the boundary.
7305 gp_Vec plnNorm = ray ^ faceNorm;
7306 normSize = plnNorm.Magnitude();
7307 if ( normSize <= tol ) return false; // point coincides with the first node
7308 plnNorm /= normSize;
7309 // for each node of the face, compute its signed distance to the plane
7310 vector<double> dist( nbNodes + 1);
7311 for ( i = 0; i < nbNodes; ++i )
7313 gp_Vec n2p( xyz[i], point );
7314 dist[i] = n2p * plnNorm;
7316 dist.back() = dist.front();
7317 // find the closest intersection
7319 double rClosest, distClosest = 1e100;;
7321 for ( i = 0; i < nbNodes; ++i )
7324 if ( fabs( dist[i]) < tol )
7326 else if ( fabs( dist[i+1]) < tol )
7328 else if ( dist[i] * dist[i+1] < 0 )
7329 r = dist[i] / ( dist[i] - dist[i+1] );
7331 continue; // no intersection
7332 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7333 gp_Vec p2int ( point, pInt);
7334 if ( p2int * ray > -tol ) // right half-space
7336 double intDist = p2int.SquareMagnitude();
7337 if ( intDist < distClosest )
7342 distClosest = intDist;
7347 return true; // no intesections - out
7349 // analyse transition
7350 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7351 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7352 gp_Vec p2int ( point, pClosest );
7353 bool out = (edgeNorm * p2int) < -tol;
7354 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7357 // ray pass through a face node; analyze transition through an adjacent edge
7358 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7359 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7360 gp_Vec edgeAdjacent( p1, p2 );
7361 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7362 bool out2 = (edgeNorm2 * p2int) < -tol;
7364 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7365 return covexCorner ? (out || out2) : (out && out2);
7367 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7369 // point is out of edge if it is NOT ON any straight part of edge
7370 // (we consider quadratic edge as being composed of two straight parts)
7371 for ( i = 1; i < nbNodes; ++i )
7373 gp_Vec edge( xyz[i-1], xyz[i]);
7374 gp_Vec n1p ( xyz[i-1], point);
7375 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7378 gp_Vec n2p( xyz[i], point );
7379 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7381 return false; // point is ON this part
7385 // Node or 0D element -------------------------------------------------------------------------
7387 gp_Vec n2p ( xyz[0], point );
7388 return n2p.Magnitude() <= tol;
7393 //=======================================================================
7397 // Position of a point relative to a segment
7401 // VERTEX 1 o----ON-----> VERTEX 2
7405 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7406 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7410 int _index; // index of vertex or segment
7412 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7413 bool operator < (const PointPos& other ) const
7415 if ( _name == other._name )
7416 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7417 return _name < other._name;
7421 //================================================================================
7423 * \brief Return of a point relative to a segment
7424 * \param point2D - the point to analyze position of
7425 * \param xyVec - end points of segments
7426 * \param index0 - 0-based index of the first point of segment
7427 * \param posToFindOut - flags of positions to detect
7428 * \retval PointPos - point position
7430 //================================================================================
7432 PointPos getPointPosition( const gp_XY& point2D,
7433 const gp_XY* segEnds,
7434 const int index0 = 0,
7435 const int posToFindOut = POS_ALL)
7437 const gp_XY& p1 = segEnds[ index0 ];
7438 const gp_XY& p2 = segEnds[ index0+1 ];
7439 const gp_XY grad = p2 - p1;
7441 if ( posToFindOut & POS_VERTEX )
7443 // check if the point2D is at "vertex 1" zone
7444 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7445 p1.Y() + grad.X() ) };
7446 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7447 return PointPos( POS_VERTEX, index0 );
7449 // check if the point2D is at "vertex 2" zone
7450 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7451 p2.Y() + grad.X() ) };
7452 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7453 return PointPos( POS_VERTEX, index0 + 1);
7455 double edgeEquation =
7456 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7457 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7461 //=======================================================================
7463 * \brief Return minimal distance from a point to a face
7465 * Currently we ignore non-planarity and 2nd order of face
7467 //=======================================================================
7469 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7470 const gp_Pnt& point )
7472 double badDistance = -1;
7473 if ( !face ) return badDistance;
7475 // coordinates of nodes (medium nodes, if any, ignored)
7476 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7477 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7478 xyz.resize( face->NbCornerNodes()+1 );
7480 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7481 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7483 gp_Vec OZ ( xyz[0], xyz[1] );
7484 gp_Vec OX ( xyz[0], xyz[2] );
7485 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7487 if ( xyz.size() < 4 ) return badDistance;
7488 OZ = gp_Vec ( xyz[0], xyz[2] );
7489 OX = gp_Vec ( xyz[0], xyz[3] );
7493 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7495 catch ( Standard_Failure ) {
7498 trsf.SetTransformation( tgtCS );
7500 // move all the nodes to 2D
7501 vector<gp_XY> xy( xyz.size() );
7502 for ( size_t i = 0;i < xyz.size()-1; ++i )
7504 gp_XYZ p3d = xyz[i];
7505 trsf.Transforms( p3d );
7506 xy[i].SetCoord( p3d.X(), p3d.Z() );
7508 xyz.back() = xyz.front();
7509 xy.back() = xy.front();
7511 // // move the point in 2D
7512 gp_XYZ tmpPnt = point.XYZ();
7513 trsf.Transforms( tmpPnt );
7514 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7516 // loop on segments of the face to analyze point position ralative to the face
7517 set< PointPos > pntPosSet;
7518 for ( size_t i = 1; i < xy.size(); ++i )
7520 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7521 pntPosSet.insert( pos );
7525 PointPos pos = *pntPosSet.begin();
7526 // cout << "Face " << face->GetID() << " DIST: ";
7527 switch ( pos._name )
7530 // point is most close to a segment
7531 gp_Vec p0p1( point, xyz[ pos._index ] );
7532 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7534 double projDist = p0p1 * p1p2; // distance projected to the segment
7535 gp_Vec projVec = p1p2 * projDist;
7536 gp_Vec distVec = p0p1 - projVec;
7537 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7538 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7539 return distVec.Magnitude();
7542 // point is inside the face
7543 double distToFacePlane = tmpPnt.Y();
7544 // cout << distToFacePlane << ", INSIDE " << endl;
7545 return Abs( distToFacePlane );
7548 // point is most close to a node
7549 gp_Vec distVec( point, xyz[ pos._index ]);
7550 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7551 return distVec.Magnitude();
7557 //=======================================================================
7558 //function : SimplifyFace
7560 //=======================================================================
7561 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7562 vector<const SMDS_MeshNode *>& poly_nodes,
7563 vector<int>& quantities) const
7565 int nbNodes = faceNodes.size();
7570 set<const SMDS_MeshNode*> nodeSet;
7572 // get simple seq of nodes
7573 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7574 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7575 int iSimple = 0, nbUnique = 0;
7577 simpleNodes[iSimple++] = faceNodes[0];
7579 for (int iCur = 1; iCur < nbNodes; iCur++) {
7580 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7581 simpleNodes[iSimple++] = faceNodes[iCur];
7582 if (nodeSet.insert( faceNodes[iCur] ).second)
7586 int nbSimple = iSimple;
7587 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7597 bool foundLoop = (nbSimple > nbUnique);
7600 set<const SMDS_MeshNode*> loopSet;
7601 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7602 const SMDS_MeshNode* n = simpleNodes[iSimple];
7603 if (!loopSet.insert( n ).second) {
7607 int iC = 0, curLast = iSimple;
7608 for (; iC < curLast; iC++) {
7609 if (simpleNodes[iC] == n) break;
7611 int loopLen = curLast - iC;
7613 // create sub-element
7615 quantities.push_back(loopLen);
7616 for (; iC < curLast; iC++) {
7617 poly_nodes.push_back(simpleNodes[iC]);
7620 // shift the rest nodes (place from the first loop position)
7621 for (iC = curLast + 1; iC < nbSimple; iC++) {
7622 simpleNodes[iC - loopLen] = simpleNodes[iC];
7624 nbSimple -= loopLen;
7627 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7628 } // while (foundLoop)
7632 quantities.push_back(iSimple);
7633 for (int i = 0; i < iSimple; i++)
7634 poly_nodes.push_back(simpleNodes[i]);
7640 //=======================================================================
7641 //function : MergeNodes
7642 //purpose : In each group, the cdr of nodes are substituted by the first one
7644 //=======================================================================
7646 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7648 MESSAGE("MergeNodes");
7649 myLastCreatedElems.Clear();
7650 myLastCreatedNodes.Clear();
7652 SMESHDS_Mesh* aMesh = GetMeshDS();
7654 TNodeNodeMap nodeNodeMap; // node to replace - new node
7655 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7656 list< int > rmElemIds, rmNodeIds;
7658 // Fill nodeNodeMap and elems
7660 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7661 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7662 list<const SMDS_MeshNode*>& nodes = *grIt;
7663 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7664 const SMDS_MeshNode* nToKeep = *nIt;
7665 //MESSAGE("node to keep " << nToKeep->GetID());
7666 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7667 const SMDS_MeshNode* nToRemove = *nIt;
7668 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7669 if ( nToRemove != nToKeep ) {
7670 //MESSAGE(" node to remove " << nToRemove->GetID());
7671 rmNodeIds.push_back( nToRemove->GetID() );
7672 AddToSameGroups( nToKeep, nToRemove, aMesh );
7673 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7674 // after MergeNodes() w/o creating node in place of merged ones.
7675 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7676 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7677 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7678 sm->SetIsAlwaysComputed( true );
7681 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7682 while ( invElemIt->more() ) {
7683 const SMDS_MeshElement* elem = invElemIt->next();
7688 // Change element nodes or remove an element
7690 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7691 for ( ; eIt != elems.end(); eIt++ ) {
7692 const SMDS_MeshElement* elem = *eIt;
7693 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7694 int nbNodes = elem->NbNodes();
7695 int aShapeId = FindShape( elem );
7697 set<const SMDS_MeshNode*> nodeSet;
7698 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7699 int iUnique = 0, iCur = 0, nbRepl = 0;
7700 vector<int> iRepl( nbNodes );
7702 // get new seq of nodes
7703 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7704 while ( itN->more() ) {
7705 const SMDS_MeshNode* n =
7706 static_cast<const SMDS_MeshNode*>( itN->next() );
7708 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7709 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7711 // BUG 0020185: begin
7713 bool stopRecur = false;
7714 set<const SMDS_MeshNode*> nodesRecur;
7715 nodesRecur.insert(n);
7716 while (!stopRecur) {
7717 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7718 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7719 n = (*nnIt_i).second;
7720 if (!nodesRecur.insert(n).second) {
7721 // error: recursive dependancy
7731 curNodes[ iCur ] = n;
7732 bool isUnique = nodeSet.insert( n ).second;
7734 uniqueNodes[ iUnique++ ] = n;
7736 iRepl[ nbRepl++ ] = iCur;
7740 // Analyse element topology after replacement
7743 int nbUniqueNodes = nodeSet.size();
7744 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7745 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7746 // Polygons and Polyhedral volumes
7747 if (elem->IsPoly()) {
7749 if (elem->GetType() == SMDSAbs_Face) {
7751 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7753 for (; inode < nbNodes; inode++) {
7754 face_nodes[inode] = curNodes[inode];
7757 vector<const SMDS_MeshNode *> polygons_nodes;
7758 vector<int> quantities;
7759 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7762 for (int iface = 0; iface < nbNew; iface++) {
7763 int nbNodes = quantities[iface];
7764 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7765 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7766 poly_nodes[ii] = polygons_nodes[inode];
7768 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7769 myLastCreatedElems.Append(newElem);
7771 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7774 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7775 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7776 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7778 if (nbNew > 0) quid = nbNew - 1;
7779 vector<int> newquant(quantities.begin()+quid, quantities.end());
7780 const SMDS_MeshElement* newElem = 0;
7781 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7782 myLastCreatedElems.Append(newElem);
7783 if ( aShapeId && newElem )
7784 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7785 rmElemIds.push_back(elem->GetID());
7788 rmElemIds.push_back(elem->GetID());
7792 else if (elem->GetType() == SMDSAbs_Volume) {
7793 // Polyhedral volume
7794 if (nbUniqueNodes < 4) {
7795 rmElemIds.push_back(elem->GetID());
7798 // each face has to be analyzed in order to check volume validity
7799 const SMDS_VtkVolume* aPolyedre =
7800 dynamic_cast<const SMDS_VtkVolume*>( elem );
7802 int nbFaces = aPolyedre->NbFaces();
7804 vector<const SMDS_MeshNode *> poly_nodes;
7805 vector<int> quantities;
7807 for (int iface = 1; iface <= nbFaces; iface++) {
7808 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7809 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7811 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7812 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7813 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7814 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7815 faceNode = (*nnIt).second;
7817 faceNodes[inode - 1] = faceNode;
7820 SimplifyFace(faceNodes, poly_nodes, quantities);
7823 if (quantities.size() > 3) {
7824 // to be done: remove coincident faces
7827 if (quantities.size() > 3)
7829 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7830 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7831 const SMDS_MeshElement* newElem = 0;
7832 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7833 myLastCreatedElems.Append(newElem);
7834 if ( aShapeId && newElem )
7835 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7836 rmElemIds.push_back(elem->GetID());
7840 rmElemIds.push_back(elem->GetID());
7851 // TODO not all the possible cases are solved. Find something more generic?
7852 switch ( nbNodes ) {
7853 case 2: ///////////////////////////////////// EDGE
7854 isOk = false; break;
7855 case 3: ///////////////////////////////////// TRIANGLE
7856 isOk = false; break;
7858 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7860 else { //////////////////////////////////// QUADRANGLE
7861 if ( nbUniqueNodes < 3 )
7863 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7864 isOk = false; // opposite nodes stick
7865 //MESSAGE("isOk " << isOk);
7868 case 6: ///////////////////////////////////// PENTAHEDRON
7869 if ( nbUniqueNodes == 4 ) {
7870 // ---------------------------------> tetrahedron
7872 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7873 // all top nodes stick: reverse a bottom
7874 uniqueNodes[ 0 ] = curNodes [ 1 ];
7875 uniqueNodes[ 1 ] = curNodes [ 0 ];
7877 else if (nbRepl == 3 &&
7878 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7879 // all bottom nodes stick: set a top before
7880 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7881 uniqueNodes[ 0 ] = curNodes [ 3 ];
7882 uniqueNodes[ 1 ] = curNodes [ 4 ];
7883 uniqueNodes[ 2 ] = curNodes [ 5 ];
7885 else if (nbRepl == 4 &&
7886 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7887 // a lateral face turns into a line: reverse a bottom
7888 uniqueNodes[ 0 ] = curNodes [ 1 ];
7889 uniqueNodes[ 1 ] = curNodes [ 0 ];
7894 else if ( nbUniqueNodes == 5 ) {
7895 // PENTAHEDRON --------------------> 2 tetrahedrons
7896 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7897 // a bottom node sticks with a linked top one
7899 SMDS_MeshElement* newElem =
7900 aMesh->AddVolume(curNodes[ 3 ],
7903 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7904 myLastCreatedElems.Append(newElem);
7906 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7907 // 2. : reverse a bottom
7908 uniqueNodes[ 0 ] = curNodes [ 1 ];
7909 uniqueNodes[ 1 ] = curNodes [ 0 ];
7919 if(elem->IsQuadratic()) { // Quadratic quadrangle
7931 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7934 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7936 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7937 uniqueNodes[0] = curNodes[0];
7938 uniqueNodes[1] = curNodes[2];
7939 uniqueNodes[2] = curNodes[3];
7940 uniqueNodes[3] = curNodes[5];
7941 uniqueNodes[4] = curNodes[6];
7942 uniqueNodes[5] = curNodes[7];
7945 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7946 uniqueNodes[0] = curNodes[0];
7947 uniqueNodes[1] = curNodes[1];
7948 uniqueNodes[2] = curNodes[2];
7949 uniqueNodes[3] = curNodes[4];
7950 uniqueNodes[4] = curNodes[5];
7951 uniqueNodes[5] = curNodes[6];
7954 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7955 uniqueNodes[0] = curNodes[1];
7956 uniqueNodes[1] = curNodes[2];
7957 uniqueNodes[2] = curNodes[3];
7958 uniqueNodes[3] = curNodes[5];
7959 uniqueNodes[4] = curNodes[6];
7960 uniqueNodes[5] = curNodes[0];
7963 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7964 uniqueNodes[0] = curNodes[0];
7965 uniqueNodes[1] = curNodes[1];
7966 uniqueNodes[2] = curNodes[3];
7967 uniqueNodes[3] = curNodes[4];
7968 uniqueNodes[4] = curNodes[6];
7969 uniqueNodes[5] = curNodes[7];
7972 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7973 uniqueNodes[0] = curNodes[0];
7974 uniqueNodes[1] = curNodes[2];
7975 uniqueNodes[2] = curNodes[3];
7976 uniqueNodes[3] = curNodes[1];
7977 uniqueNodes[4] = curNodes[6];
7978 uniqueNodes[5] = curNodes[7];
7981 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7982 uniqueNodes[0] = curNodes[0];
7983 uniqueNodes[1] = curNodes[1];
7984 uniqueNodes[2] = curNodes[2];
7985 uniqueNodes[3] = curNodes[4];
7986 uniqueNodes[4] = curNodes[5];
7987 uniqueNodes[5] = curNodes[7];
7990 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7991 uniqueNodes[0] = curNodes[0];
7992 uniqueNodes[1] = curNodes[1];
7993 uniqueNodes[2] = curNodes[3];
7994 uniqueNodes[3] = curNodes[4];
7995 uniqueNodes[4] = curNodes[2];
7996 uniqueNodes[5] = curNodes[7];
7999 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
8000 uniqueNodes[0] = curNodes[0];
8001 uniqueNodes[1] = curNodes[1];
8002 uniqueNodes[2] = curNodes[2];
8003 uniqueNodes[3] = curNodes[4];
8004 uniqueNodes[4] = curNodes[5];
8005 uniqueNodes[5] = curNodes[3];
8010 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
8013 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
8017 //////////////////////////////////// HEXAHEDRON
8019 SMDS_VolumeTool hexa (elem);
8020 hexa.SetExternalNormal();
8021 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8022 //////////////////////// HEX ---> 1 tetrahedron
8023 for ( int iFace = 0; iFace < 6; iFace++ ) {
8024 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8025 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8026 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8027 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8028 // one face turns into a point ...
8029 int iOppFace = hexa.GetOppFaceIndex( iFace );
8030 ind = hexa.GetFaceNodesIndices( iOppFace );
8032 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8033 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8036 if ( nbStick == 1 ) {
8037 // ... and the opposite one - into a triangle.
8039 ind = hexa.GetFaceNodesIndices( iFace );
8040 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8047 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8048 //////////////////////// HEX ---> 1 prism
8049 int nbTria = 0, iTria[3];
8050 const int *ind; // indices of face nodes
8051 // look for triangular faces
8052 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8053 ind = hexa.GetFaceNodesIndices( iFace );
8054 TIDSortedNodeSet faceNodes;
8055 for ( iCur = 0; iCur < 4; iCur++ )
8056 faceNodes.insert( curNodes[ind[iCur]] );
8057 if ( faceNodes.size() == 3 )
8058 iTria[ nbTria++ ] = iFace;
8060 // check if triangles are opposite
8061 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8064 // set nodes of the bottom triangle
8065 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8067 for ( iCur = 0; iCur < 4; iCur++ )
8068 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8069 indB.push_back( ind[iCur] );
8070 if ( !hexa.IsForward() )
8071 std::swap( indB[0], indB[2] );
8072 for ( iCur = 0; iCur < 3; iCur++ )
8073 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8074 // set nodes of the top triangle
8075 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8076 for ( iCur = 0; iCur < 3; ++iCur )
8077 for ( int j = 0; j < 4; ++j )
8078 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8080 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8086 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8087 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8088 for ( int iFace = 0; iFace < 6; iFace++ ) {
8089 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8090 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8091 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8092 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8093 // one face turns into a point ...
8094 int iOppFace = hexa.GetOppFaceIndex( iFace );
8095 ind = hexa.GetFaceNodesIndices( iOppFace );
8097 iUnique = 2; // reverse a tetrahedron 1 bottom
8098 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8099 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8101 else if ( iUnique >= 0 )
8102 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8104 if ( nbStick == 0 ) {
8105 // ... and the opposite one is a quadrangle
8107 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8108 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8111 SMDS_MeshElement* newElem =
8112 aMesh->AddVolume(curNodes[ind[ 0 ]],
8115 curNodes[indTop[ 0 ]]);
8116 myLastCreatedElems.Append(newElem);
8118 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8125 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8126 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8127 // find indices of quad and tri faces
8128 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8129 for ( iFace = 0; iFace < 6; iFace++ ) {
8130 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8132 for ( iCur = 0; iCur < 4; iCur++ )
8133 nodeSet.insert( curNodes[ind[ iCur ]] );
8134 nbUniqueNodes = nodeSet.size();
8135 if ( nbUniqueNodes == 3 )
8136 iTriFace[ nbTri++ ] = iFace;
8137 else if ( nbUniqueNodes == 4 )
8138 iQuadFace[ nbQuad++ ] = iFace;
8140 if (nbQuad == 2 && nbTri == 4 &&
8141 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8142 // 2 opposite quadrangles stuck with a diagonal;
8143 // sample groups of merged indices: (0-4)(2-6)
8144 // --------------------------------------------> 2 tetrahedrons
8145 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8146 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8147 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8148 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8149 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8150 // stuck with 0-2 diagonal
8158 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8159 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8160 // stuck with 1-3 diagonal
8172 uniqueNodes[ 0 ] = curNodes [ i0 ];
8173 uniqueNodes[ 1 ] = curNodes [ i1d ];
8174 uniqueNodes[ 2 ] = curNodes [ i3d ];
8175 uniqueNodes[ 3 ] = curNodes [ i0t ];
8178 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8182 myLastCreatedElems.Append(newElem);
8184 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8187 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8188 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8189 // --------------------------------------------> prism
8190 // find 2 opposite triangles
8192 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8193 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8194 // find indices of kept and replaced nodes
8195 // and fill unique nodes of 2 opposite triangles
8196 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8197 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8198 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8199 // fill unique nodes
8202 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8203 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8204 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8206 // iCur of a linked node of the opposite face (make normals co-directed):
8207 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8208 // check that correspondent corners of triangles are linked
8209 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8212 uniqueNodes[ iUnique ] = n;
8213 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8222 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8225 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8232 } // switch ( nbNodes )
8234 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8236 if ( isOk ) { // the elem remains valid after sticking nodes
8237 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8239 // Change nodes of polyedre
8240 const SMDS_VtkVolume* aPolyedre =
8241 dynamic_cast<const SMDS_VtkVolume*>( elem );
8243 int nbFaces = aPolyedre->NbFaces();
8245 vector<const SMDS_MeshNode *> poly_nodes;
8246 vector<int> quantities (nbFaces);
8248 for (int iface = 1; iface <= nbFaces; iface++) {
8249 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8250 quantities[iface - 1] = nbFaceNodes;
8252 for (inode = 1; inode <= nbFaceNodes; inode++) {
8253 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8255 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8256 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8257 curNode = (*nnIt).second;
8259 poly_nodes.push_back(curNode);
8262 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8265 else // replace non-polyhedron elements
8267 const SMDSAbs_ElementType etyp = elem->GetType();
8268 const int elemId = elem->GetID();
8269 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8270 uniqueNodes.resize(nbUniqueNodes);
8272 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8274 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8275 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8276 if ( sm && newElem )
8277 sm->AddElement( newElem );
8278 if ( elem != newElem )
8279 ReplaceElemInGroups( elem, newElem, aMesh );
8283 // Remove invalid regular element or invalid polygon
8284 rmElemIds.push_back( elem->GetID() );
8287 } // loop on elements
8289 // Remove bad elements, then equal nodes (order important)
8291 Remove( rmElemIds, false );
8292 Remove( rmNodeIds, true );
8297 // ========================================================
8298 // class : SortableElement
8299 // purpose : allow sorting elements basing on their nodes
8300 // ========================================================
8301 class SortableElement : public set <const SMDS_MeshElement*>
8305 SortableElement( const SMDS_MeshElement* theElem )
8308 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8309 while ( nodeIt->more() )
8310 this->insert( nodeIt->next() );
8313 const SMDS_MeshElement* Get() const
8316 void Set(const SMDS_MeshElement* e) const
8321 mutable const SMDS_MeshElement* myElem;
8324 //=======================================================================
8325 //function : FindEqualElements
8326 //purpose : Return list of group of elements built on the same nodes.
8327 // Search among theElements or in the whole mesh if theElements is empty
8328 //=======================================================================
8330 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8331 TListOfListOfElementsID & theGroupsOfElementsID)
8333 myLastCreatedElems.Clear();
8334 myLastCreatedNodes.Clear();
8336 typedef map< SortableElement, int > TMapOfNodeSet;
8337 typedef list<int> TGroupOfElems;
8339 if ( theElements.empty() )
8340 { // get all elements in the mesh
8341 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8342 while ( eIt->more() )
8343 theElements.insert( theElements.end(), eIt->next());
8346 vector< TGroupOfElems > arrayOfGroups;
8347 TGroupOfElems groupOfElems;
8348 TMapOfNodeSet mapOfNodeSet;
8350 TIDSortedElemSet::iterator elemIt = theElements.begin();
8351 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8352 const SMDS_MeshElement* curElem = *elemIt;
8353 SortableElement SE(curElem);
8356 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8357 if( !(pp.second) ) {
8358 TMapOfNodeSet::iterator& itSE = pp.first;
8359 ind = (*itSE).second;
8360 arrayOfGroups[ind].push_back(curElem->GetID());
8363 groupOfElems.clear();
8364 groupOfElems.push_back(curElem->GetID());
8365 arrayOfGroups.push_back(groupOfElems);
8370 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8371 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8372 groupOfElems = *groupIt;
8373 if ( groupOfElems.size() > 1 ) {
8374 groupOfElems.sort();
8375 theGroupsOfElementsID.push_back(groupOfElems);
8380 //=======================================================================
8381 //function : MergeElements
8382 //purpose : In each given group, substitute all elements by the first one.
8383 //=======================================================================
8385 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8387 myLastCreatedElems.Clear();
8388 myLastCreatedNodes.Clear();
8390 typedef list<int> TListOfIDs;
8391 TListOfIDs rmElemIds; // IDs of elems to remove
8393 SMESHDS_Mesh* aMesh = GetMeshDS();
8395 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8396 while ( groupsIt != theGroupsOfElementsID.end() ) {
8397 TListOfIDs& aGroupOfElemID = *groupsIt;
8398 aGroupOfElemID.sort();
8399 int elemIDToKeep = aGroupOfElemID.front();
8400 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8401 aGroupOfElemID.pop_front();
8402 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8403 while ( idIt != aGroupOfElemID.end() ) {
8404 int elemIDToRemove = *idIt;
8405 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8406 // add the kept element in groups of removed one (PAL15188)
8407 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8408 rmElemIds.push_back( elemIDToRemove );
8414 Remove( rmElemIds, false );
8417 //=======================================================================
8418 //function : MergeEqualElements
8419 //purpose : Remove all but one of elements built on the same nodes.
8420 //=======================================================================
8422 void SMESH_MeshEditor::MergeEqualElements()
8424 TIDSortedElemSet aMeshElements; /* empty input ==
8425 to merge equal elements in the whole mesh */
8426 TListOfListOfElementsID aGroupsOfElementsID;
8427 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8428 MergeElements(aGroupsOfElementsID);
8431 //=======================================================================
8432 //function : FindFaceInSet
8433 //purpose : Return a face having linked nodes n1 and n2 and which is
8434 // - not in avoidSet,
8435 // - in elemSet provided that !elemSet.empty()
8436 // i1 and i2 optionally returns indices of n1 and n2
8437 //=======================================================================
8439 const SMDS_MeshElement*
8440 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8441 const SMDS_MeshNode* n2,
8442 const TIDSortedElemSet& elemSet,
8443 const TIDSortedElemSet& avoidSet,
8449 const SMDS_MeshElement* face = 0;
8451 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8452 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8453 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8455 //MESSAGE("in while ( invElemIt->more() && !face )");
8456 const SMDS_MeshElement* elem = invElemIt->next();
8457 if (avoidSet.count( elem ))
8459 if ( !elemSet.empty() && !elemSet.count( elem ))
8462 i1 = elem->GetNodeIndex( n1 );
8463 // find a n2 linked to n1
8464 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8465 for ( int di = -1; di < 2 && !face; di += 2 )
8467 i2 = (i1+di+nbN) % nbN;
8468 if ( elem->GetNode( i2 ) == n2 )
8471 if ( !face && elem->IsQuadratic())
8473 // analysis for quadratic elements using all nodes
8474 const SMDS_VtkFace* F =
8475 dynamic_cast<const SMDS_VtkFace*>(elem);
8476 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8477 // use special nodes iterator
8478 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8479 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8480 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8482 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8483 if ( n1 == prevN && n2 == n )
8487 else if ( n2 == prevN && n1 == n )
8489 face = elem; swap( i1, i2 );
8495 if ( n1ind ) *n1ind = i1;
8496 if ( n2ind ) *n2ind = i2;
8500 //=======================================================================
8501 //function : findAdjacentFace
8503 //=======================================================================
8505 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8506 const SMDS_MeshNode* n2,
8507 const SMDS_MeshElement* elem)
8509 TIDSortedElemSet elemSet, avoidSet;
8511 avoidSet.insert ( elem );
8512 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8515 //=======================================================================
8516 //function : FindFreeBorder
8518 //=======================================================================
8520 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8522 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8523 const SMDS_MeshNode* theSecondNode,
8524 const SMDS_MeshNode* theLastNode,
8525 list< const SMDS_MeshNode* > & theNodes,
8526 list< const SMDS_MeshElement* >& theFaces)
8528 if ( !theFirstNode || !theSecondNode )
8530 // find border face between theFirstNode and theSecondNode
8531 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8535 theFaces.push_back( curElem );
8536 theNodes.push_back( theFirstNode );
8537 theNodes.push_back( theSecondNode );
8539 //vector<const SMDS_MeshNode*> nodes;
8540 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8541 TIDSortedElemSet foundElems;
8542 bool needTheLast = ( theLastNode != 0 );
8544 while ( nStart != theLastNode ) {
8545 if ( nStart == theFirstNode )
8546 return !needTheLast;
8548 // find all free border faces sharing form nStart
8550 list< const SMDS_MeshElement* > curElemList;
8551 list< const SMDS_MeshNode* > nStartList;
8552 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8553 while ( invElemIt->more() ) {
8554 const SMDS_MeshElement* e = invElemIt->next();
8555 if ( e == curElem || foundElems.insert( e ).second ) {
8557 int iNode = 0, nbNodes = e->NbNodes();
8558 //const SMDS_MeshNode* nodes[nbNodes+1];
8559 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8561 if(e->IsQuadratic()) {
8562 const SMDS_VtkFace* F =
8563 dynamic_cast<const SMDS_VtkFace*>(e);
8564 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8565 // use special nodes iterator
8566 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8567 while( anIter->more() ) {
8568 nodes[ iNode++ ] = cast2Node(anIter->next());
8572 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8573 while ( nIt->more() )
8574 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8576 nodes[ iNode ] = nodes[ 0 ];
8578 for ( iNode = 0; iNode < nbNodes; iNode++ )
8579 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8580 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8581 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8583 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8584 curElemList.push_back( e );
8588 // analyse the found
8590 int nbNewBorders = curElemList.size();
8591 if ( nbNewBorders == 0 ) {
8592 // no free border furthermore
8593 return !needTheLast;
8595 else if ( nbNewBorders == 1 ) {
8596 // one more element found
8598 nStart = nStartList.front();
8599 curElem = curElemList.front();
8600 theFaces.push_back( curElem );
8601 theNodes.push_back( nStart );
8604 // several continuations found
8605 list< const SMDS_MeshElement* >::iterator curElemIt;
8606 list< const SMDS_MeshNode* >::iterator nStartIt;
8607 // check if one of them reached the last node
8608 if ( needTheLast ) {
8609 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8610 curElemIt!= curElemList.end();
8611 curElemIt++, nStartIt++ )
8612 if ( *nStartIt == theLastNode ) {
8613 theFaces.push_back( *curElemIt );
8614 theNodes.push_back( *nStartIt );
8618 // find the best free border by the continuations
8619 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8620 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8621 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8622 curElemIt!= curElemList.end();
8623 curElemIt++, nStartIt++ )
8625 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8626 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8627 // find one more free border
8628 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8632 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8633 // choice: clear a worse one
8634 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8635 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8636 contNodes[ iWorse ].clear();
8637 contFaces[ iWorse ].clear();
8640 if ( contNodes[0].empty() && contNodes[1].empty() )
8643 // append the best free border
8644 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8645 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8646 theNodes.pop_back(); // remove nIgnore
8647 theNodes.pop_back(); // remove nStart
8648 theFaces.pop_back(); // remove curElem
8649 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8650 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8651 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8652 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8655 } // several continuations found
8656 } // while ( nStart != theLastNode )
8661 //=======================================================================
8662 //function : CheckFreeBorderNodes
8663 //purpose : Return true if the tree nodes are on a free border
8664 //=======================================================================
8666 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8667 const SMDS_MeshNode* theNode2,
8668 const SMDS_MeshNode* theNode3)
8670 list< const SMDS_MeshNode* > nodes;
8671 list< const SMDS_MeshElement* > faces;
8672 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8675 //=======================================================================
8676 //function : SewFreeBorder
8678 //=======================================================================
8680 SMESH_MeshEditor::Sew_Error
8681 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8682 const SMDS_MeshNode* theBordSecondNode,
8683 const SMDS_MeshNode* theBordLastNode,
8684 const SMDS_MeshNode* theSideFirstNode,
8685 const SMDS_MeshNode* theSideSecondNode,
8686 const SMDS_MeshNode* theSideThirdNode,
8687 const bool theSideIsFreeBorder,
8688 const bool toCreatePolygons,
8689 const bool toCreatePolyedrs)
8691 myLastCreatedElems.Clear();
8692 myLastCreatedNodes.Clear();
8694 MESSAGE("::SewFreeBorder()");
8695 Sew_Error aResult = SEW_OK;
8697 // ====================================
8698 // find side nodes and elements
8699 // ====================================
8701 list< const SMDS_MeshNode* > nSide[ 2 ];
8702 list< const SMDS_MeshElement* > eSide[ 2 ];
8703 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8704 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8708 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8709 nSide[0], eSide[0])) {
8710 MESSAGE(" Free Border 1 not found " );
8711 aResult = SEW_BORDER1_NOT_FOUND;
8713 if (theSideIsFreeBorder) {
8716 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8717 nSide[1], eSide[1])) {
8718 MESSAGE(" Free Border 2 not found " );
8719 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8722 if ( aResult != SEW_OK )
8725 if (!theSideIsFreeBorder) {
8729 // -------------------------------------------------------------------------
8731 // 1. If nodes to merge are not coincident, move nodes of the free border
8732 // from the coord sys defined by the direction from the first to last
8733 // nodes of the border to the correspondent sys of the side 2
8734 // 2. On the side 2, find the links most co-directed with the correspondent
8735 // links of the free border
8736 // -------------------------------------------------------------------------
8738 // 1. Since sewing may break if there are volumes to split on the side 2,
8739 // we wont move nodes but just compute new coordinates for them
8740 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8741 TNodeXYZMap nBordXYZ;
8742 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8743 list< const SMDS_MeshNode* >::iterator nBordIt;
8745 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8746 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8747 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8748 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8749 double tol2 = 1.e-8;
8750 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8751 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8752 // Need node movement.
8754 // find X and Z axes to create trsf
8755 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8757 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8759 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8762 gp_Ax3 toBordAx( Pb1, Zb, X );
8763 gp_Ax3 fromSideAx( Ps1, Zs, X );
8764 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8766 gp_Trsf toBordSys, fromSide2Sys;
8767 toBordSys.SetTransformation( toBordAx );
8768 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8769 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8772 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8773 const SMDS_MeshNode* n = *nBordIt;
8774 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8775 toBordSys.Transforms( xyz );
8776 fromSide2Sys.Transforms( xyz );
8777 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8781 // just insert nodes XYZ in the nBordXYZ map
8782 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8783 const SMDS_MeshNode* n = *nBordIt;
8784 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8788 // 2. On the side 2, find the links most co-directed with the correspondent
8789 // links of the free border
8791 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8792 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8793 sideNodes.push_back( theSideFirstNode );
8795 bool hasVolumes = false;
8796 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8797 set<long> foundSideLinkIDs, checkedLinkIDs;
8798 SMDS_VolumeTool volume;
8799 //const SMDS_MeshNode* faceNodes[ 4 ];
8801 const SMDS_MeshNode* sideNode;
8802 const SMDS_MeshElement* sideElem;
8803 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8804 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8805 nBordIt = bordNodes.begin();
8807 // border node position and border link direction to compare with
8808 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8809 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8810 // choose next side node by link direction or by closeness to
8811 // the current border node:
8812 bool searchByDir = ( *nBordIt != theBordLastNode );
8814 // find the next node on the Side 2
8816 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8818 checkedLinkIDs.clear();
8819 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8821 // loop on inverse elements of current node (prevSideNode) on the Side 2
8822 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8823 while ( invElemIt->more() )
8825 const SMDS_MeshElement* elem = invElemIt->next();
8826 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8827 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8828 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8829 bool isVolume = volume.Set( elem );
8830 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8831 if ( isVolume ) // --volume
8833 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8834 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8835 if(elem->IsQuadratic()) {
8836 const SMDS_VtkFace* F =
8837 dynamic_cast<const SMDS_VtkFace*>(elem);
8838 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8839 // use special nodes iterator
8840 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8841 while( anIter->more() ) {
8842 nodes[ iNode ] = cast2Node(anIter->next());
8843 if ( nodes[ iNode++ ] == prevSideNode )
8844 iPrevNode = iNode - 1;
8848 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8849 while ( nIt->more() ) {
8850 nodes[ iNode ] = cast2Node( nIt->next() );
8851 if ( nodes[ iNode++ ] == prevSideNode )
8852 iPrevNode = iNode - 1;
8855 // there are 2 links to check
8860 // loop on links, to be precise, on the second node of links
8861 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8862 const SMDS_MeshNode* n = nodes[ iNode ];
8864 if ( !volume.IsLinked( n, prevSideNode ))
8868 if ( iNode ) // a node before prevSideNode
8869 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8870 else // a node after prevSideNode
8871 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8873 // check if this link was already used
8874 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8875 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8876 if (!isJustChecked &&
8877 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8879 // test a link geometrically
8880 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8881 bool linkIsBetter = false;
8882 double dot = 0.0, dist = 0.0;
8883 if ( searchByDir ) { // choose most co-directed link
8884 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8885 linkIsBetter = ( dot > maxDot );
8887 else { // choose link with the node closest to bordPos
8888 dist = ( nextXYZ - bordPos ).SquareModulus();
8889 linkIsBetter = ( dist < minDist );
8891 if ( linkIsBetter ) {
8900 } // loop on inverse elements of prevSideNode
8903 MESSAGE(" Cant find path by links of the Side 2 ");
8904 return SEW_BAD_SIDE_NODES;
8906 sideNodes.push_back( sideNode );
8907 sideElems.push_back( sideElem );
8908 foundSideLinkIDs.insert ( linkID );
8909 prevSideNode = sideNode;
8911 if ( *nBordIt == theBordLastNode )
8912 searchByDir = false;
8914 // find the next border link to compare with
8915 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8916 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8917 // move to next border node if sideNode is before forward border node (bordPos)
8918 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8919 prevBordNode = *nBordIt;
8921 bordPos = nBordXYZ[ *nBordIt ];
8922 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8923 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8927 while ( sideNode != theSideSecondNode );
8929 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8930 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8931 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8933 } // end nodes search on the side 2
8935 // ============================
8936 // sew the border to the side 2
8937 // ============================
8939 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8940 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8942 TListOfListOfNodes nodeGroupsToMerge;
8943 if ( nbNodes[0] == nbNodes[1] ||
8944 ( theSideIsFreeBorder && !theSideThirdNode)) {
8946 // all nodes are to be merged
8948 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8949 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8950 nIt[0]++, nIt[1]++ )
8952 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8953 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8954 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8959 // insert new nodes into the border and the side to get equal nb of segments
8961 // get normalized parameters of nodes on the borders
8962 //double param[ 2 ][ maxNbNodes ];
8964 param[0] = new double [ maxNbNodes ];
8965 param[1] = new double [ maxNbNodes ];
8967 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8968 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8969 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8970 const SMDS_MeshNode* nPrev = *nIt;
8971 double bordLength = 0;
8972 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8973 const SMDS_MeshNode* nCur = *nIt;
8974 gp_XYZ segment (nCur->X() - nPrev->X(),
8975 nCur->Y() - nPrev->Y(),
8976 nCur->Z() - nPrev->Z());
8977 double segmentLen = segment.Modulus();
8978 bordLength += segmentLen;
8979 param[ iBord ][ iNode ] = bordLength;
8982 // normalize within [0,1]
8983 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8984 param[ iBord ][ iNode ] /= bordLength;
8988 // loop on border segments
8989 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8990 int i[ 2 ] = { 0, 0 };
8991 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8992 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8994 TElemOfNodeListMap insertMap;
8995 TElemOfNodeListMap::iterator insertMapIt;
8997 // key: elem to insert nodes into
8998 // value: 2 nodes to insert between + nodes to be inserted
9000 bool next[ 2 ] = { false, false };
9002 // find min adjacent segment length after sewing
9003 double nextParam = 10., prevParam = 0;
9004 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9005 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
9006 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
9007 if ( i[ iBord ] > 0 )
9008 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
9010 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
9011 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
9012 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
9014 // choose to insert or to merge nodes
9015 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
9016 if ( Abs( du ) <= minSegLen * 0.2 ) {
9019 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9020 const SMDS_MeshNode* n0 = *nIt[0];
9021 const SMDS_MeshNode* n1 = *nIt[1];
9022 nodeGroupsToMerge.back().push_back( n1 );
9023 nodeGroupsToMerge.back().push_back( n0 );
9024 // position of node of the border changes due to merge
9025 param[ 0 ][ i[0] ] += du;
9026 // move n1 for the sake of elem shape evaluation during insertion.
9027 // n1 will be removed by MergeNodes() anyway
9028 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9029 next[0] = next[1] = true;
9034 int intoBord = ( du < 0 ) ? 0 : 1;
9035 const SMDS_MeshElement* elem = *eIt[ intoBord ];
9036 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
9037 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
9038 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
9039 if ( intoBord == 1 ) {
9040 // move node of the border to be on a link of elem of the side
9041 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9042 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9043 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9044 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9045 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9047 insertMapIt = insertMap.find( elem );
9048 bool notFound = ( insertMapIt == insertMap.end() );
9049 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9051 // insert into another link of the same element:
9052 // 1. perform insertion into the other link of the elem
9053 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9054 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9055 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9056 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9057 // 2. perform insertion into the link of adjacent faces
9059 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9061 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9065 if (toCreatePolyedrs) {
9066 // perform insertion into the links of adjacent volumes
9067 UpdateVolumes(n12, n22, nodeList);
9069 // 3. find an element appeared on n1 and n2 after the insertion
9070 insertMap.erase( elem );
9071 elem = findAdjacentFace( n1, n2, 0 );
9073 if ( notFound || otherLink ) {
9074 // add element and nodes of the side into the insertMap
9075 insertMapIt = insertMap.insert
9076 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9077 (*insertMapIt).second.push_back( n1 );
9078 (*insertMapIt).second.push_back( n2 );
9080 // add node to be inserted into elem
9081 (*insertMapIt).second.push_back( nIns );
9082 next[ 1 - intoBord ] = true;
9085 // go to the next segment
9086 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9087 if ( next[ iBord ] ) {
9088 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9090 nPrev[ iBord ] = *nIt[ iBord ];
9091 nIt[ iBord ]++; i[ iBord ]++;
9095 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9097 // perform insertion of nodes into elements
9099 for (insertMapIt = insertMap.begin();
9100 insertMapIt != insertMap.end();
9103 const SMDS_MeshElement* elem = (*insertMapIt).first;
9104 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9105 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9106 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9108 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9110 if ( !theSideIsFreeBorder ) {
9111 // look for and insert nodes into the faces adjacent to elem
9113 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9115 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9120 if (toCreatePolyedrs) {
9121 // perform insertion into the links of adjacent volumes
9122 UpdateVolumes(n1, n2, nodeList);
9128 } // end: insert new nodes
9130 MergeNodes ( nodeGroupsToMerge );
9135 //=======================================================================
9136 //function : InsertNodesIntoLink
9137 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9138 // and theBetweenNode2 and split theElement
9139 //=======================================================================
9141 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9142 const SMDS_MeshNode* theBetweenNode1,
9143 const SMDS_MeshNode* theBetweenNode2,
9144 list<const SMDS_MeshNode*>& theNodesToInsert,
9145 const bool toCreatePoly)
9147 if ( theFace->GetType() != SMDSAbs_Face ) return;
9149 // find indices of 2 link nodes and of the rest nodes
9150 int iNode = 0, il1, il2, i3, i4;
9151 il1 = il2 = i3 = i4 = -1;
9152 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9153 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9155 if(theFace->IsQuadratic()) {
9156 const SMDS_VtkFace* F =
9157 dynamic_cast<const SMDS_VtkFace*>(theFace);
9158 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9159 // use special nodes iterator
9160 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9161 while( anIter->more() ) {
9162 const SMDS_MeshNode* n = cast2Node(anIter->next());
9163 if ( n == theBetweenNode1 )
9165 else if ( n == theBetweenNode2 )
9171 nodes[ iNode++ ] = n;
9175 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9176 while ( nodeIt->more() ) {
9177 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9178 if ( n == theBetweenNode1 )
9180 else if ( n == theBetweenNode2 )
9186 nodes[ iNode++ ] = n;
9189 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9192 // arrange link nodes to go one after another regarding the face orientation
9193 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9194 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9199 aNodesToInsert.reverse();
9201 // check that not link nodes of a quadrangles are in good order
9202 int nbFaceNodes = theFace->NbNodes();
9203 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9209 if (toCreatePoly || theFace->IsPoly()) {
9212 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9214 // add nodes of face up to first node of link
9217 if(theFace->IsQuadratic()) {
9218 const SMDS_VtkFace* F =
9219 dynamic_cast<const SMDS_VtkFace*>(theFace);
9220 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9221 // use special nodes iterator
9222 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9223 while( anIter->more() && !isFLN ) {
9224 const SMDS_MeshNode* n = cast2Node(anIter->next());
9225 poly_nodes[iNode++] = n;
9226 if (n == nodes[il1]) {
9230 // add nodes to insert
9231 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9232 for (; nIt != aNodesToInsert.end(); nIt++) {
9233 poly_nodes[iNode++] = *nIt;
9235 // add nodes of face starting from last node of link
9236 while ( anIter->more() ) {
9237 poly_nodes[iNode++] = cast2Node(anIter->next());
9241 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9242 while ( nodeIt->more() && !isFLN ) {
9243 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9244 poly_nodes[iNode++] = n;
9245 if (n == nodes[il1]) {
9249 // add nodes to insert
9250 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9251 for (; nIt != aNodesToInsert.end(); nIt++) {
9252 poly_nodes[iNode++] = *nIt;
9254 // add nodes of face starting from last node of link
9255 while ( nodeIt->more() ) {
9256 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9257 poly_nodes[iNode++] = n;
9261 // edit or replace the face
9262 SMESHDS_Mesh *aMesh = GetMeshDS();
9264 if (theFace->IsPoly()) {
9265 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9268 int aShapeId = FindShape( theFace );
9270 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9271 myLastCreatedElems.Append(newElem);
9272 if ( aShapeId && newElem )
9273 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9275 aMesh->RemoveElement(theFace);
9280 SMESHDS_Mesh *aMesh = GetMeshDS();
9281 if( !theFace->IsQuadratic() ) {
9283 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9284 int nbLinkNodes = 2 + aNodesToInsert.size();
9285 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9286 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9287 linkNodes[ 0 ] = nodes[ il1 ];
9288 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9289 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9290 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9291 linkNodes[ iNode++ ] = *nIt;
9293 // decide how to split a quadrangle: compare possible variants
9294 // and choose which of splits to be a quadrangle
9295 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9296 if ( nbFaceNodes == 3 ) {
9297 iBestQuad = nbSplits;
9300 else if ( nbFaceNodes == 4 ) {
9301 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9302 double aBestRate = DBL_MAX;
9303 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9305 double aBadRate = 0;
9306 // evaluate elements quality
9307 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9308 if ( iSplit == iQuad ) {
9309 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9313 aBadRate += getBadRate( &quad, aCrit );
9316 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9318 nodes[ iSplit < iQuad ? i4 : i3 ]);
9319 aBadRate += getBadRate( &tria, aCrit );
9323 if ( aBadRate < aBestRate ) {
9325 aBestRate = aBadRate;
9330 // create new elements
9331 int aShapeId = FindShape( theFace );
9334 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9335 SMDS_MeshElement* newElem = 0;
9336 if ( iSplit == iBestQuad )
9337 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9342 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9344 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9345 myLastCreatedElems.Append(newElem);
9346 if ( aShapeId && newElem )
9347 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9350 // change nodes of theFace
9351 const SMDS_MeshNode* newNodes[ 4 ];
9352 newNodes[ 0 ] = linkNodes[ i1 ];
9353 newNodes[ 1 ] = linkNodes[ i2 ];
9354 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9355 newNodes[ 3 ] = nodes[ i4 ];
9356 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9357 const SMDS_MeshElement* newElem = 0;
9358 if (iSplit == iBestQuad)
9359 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9361 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9362 myLastCreatedElems.Append(newElem);
9363 if ( aShapeId && newElem )
9364 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9365 } // end if(!theFace->IsQuadratic())
9366 else { // theFace is quadratic
9367 // we have to split theFace on simple triangles and one simple quadrangle
9369 int nbshift = tmp*2;
9370 // shift nodes in nodes[] by nbshift
9372 for(i=0; i<nbshift; i++) {
9373 const SMDS_MeshNode* n = nodes[0];
9374 for(j=0; j<nbFaceNodes-1; j++) {
9375 nodes[j] = nodes[j+1];
9377 nodes[nbFaceNodes-1] = n;
9379 il1 = il1 - nbshift;
9380 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9381 // n0 n1 n2 n0 n1 n2
9382 // +-----+-----+ +-----+-----+
9391 // create new elements
9392 int aShapeId = FindShape( theFace );
9395 if(nbFaceNodes==6) { // quadratic triangle
9396 SMDS_MeshElement* newElem =
9397 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9398 myLastCreatedElems.Append(newElem);
9399 if ( aShapeId && newElem )
9400 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9401 if(theFace->IsMediumNode(nodes[il1])) {
9402 // create quadrangle
9403 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9404 myLastCreatedElems.Append(newElem);
9405 if ( aShapeId && newElem )
9406 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9412 // create quadrangle
9413 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9414 myLastCreatedElems.Append(newElem);
9415 if ( aShapeId && newElem )
9416 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9422 else { // nbFaceNodes==8 - quadratic quadrangle
9423 SMDS_MeshElement* newElem =
9424 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9425 myLastCreatedElems.Append(newElem);
9426 if ( aShapeId && newElem )
9427 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9428 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9429 myLastCreatedElems.Append(newElem);
9430 if ( aShapeId && newElem )
9431 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9432 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9433 myLastCreatedElems.Append(newElem);
9434 if ( aShapeId && newElem )
9435 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9436 if(theFace->IsMediumNode(nodes[il1])) {
9437 // create quadrangle
9438 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9439 myLastCreatedElems.Append(newElem);
9440 if ( aShapeId && newElem )
9441 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9447 // create quadrangle
9448 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9449 myLastCreatedElems.Append(newElem);
9450 if ( aShapeId && newElem )
9451 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9457 // create needed triangles using n1,n2,n3 and inserted nodes
9458 int nbn = 2 + aNodesToInsert.size();
9459 //const SMDS_MeshNode* aNodes[nbn];
9460 vector<const SMDS_MeshNode*> aNodes(nbn);
9461 aNodes[0] = nodes[n1];
9462 aNodes[nbn-1] = nodes[n2];
9463 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9464 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9465 aNodes[iNode++] = *nIt;
9467 for(i=1; i<nbn; i++) {
9468 SMDS_MeshElement* newElem =
9469 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9470 myLastCreatedElems.Append(newElem);
9471 if ( aShapeId && newElem )
9472 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9476 aMesh->RemoveElement(theFace);
9479 //=======================================================================
9480 //function : UpdateVolumes
9482 //=======================================================================
9483 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9484 const SMDS_MeshNode* theBetweenNode2,
9485 list<const SMDS_MeshNode*>& theNodesToInsert)
9487 myLastCreatedElems.Clear();
9488 myLastCreatedNodes.Clear();
9490 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9491 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9492 const SMDS_MeshElement* elem = invElemIt->next();
9494 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9495 SMDS_VolumeTool aVolume (elem);
9496 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9499 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9500 int iface, nbFaces = aVolume.NbFaces();
9501 vector<const SMDS_MeshNode *> poly_nodes;
9502 vector<int> quantities (nbFaces);
9504 for (iface = 0; iface < nbFaces; iface++) {
9505 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9506 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9507 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9509 for (int inode = 0; inode < nbFaceNodes; inode++) {
9510 poly_nodes.push_back(faceNodes[inode]);
9512 if (nbInserted == 0) {
9513 if (faceNodes[inode] == theBetweenNode1) {
9514 if (faceNodes[inode + 1] == theBetweenNode2) {
9515 nbInserted = theNodesToInsert.size();
9517 // add nodes to insert
9518 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9519 for (; nIt != theNodesToInsert.end(); nIt++) {
9520 poly_nodes.push_back(*nIt);
9524 else if (faceNodes[inode] == theBetweenNode2) {
9525 if (faceNodes[inode + 1] == theBetweenNode1) {
9526 nbInserted = theNodesToInsert.size();
9528 // add nodes to insert in reversed order
9529 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9531 for (; nIt != theNodesToInsert.begin(); nIt--) {
9532 poly_nodes.push_back(*nIt);
9534 poly_nodes.push_back(*nIt);
9541 quantities[iface] = nbFaceNodes + nbInserted;
9544 // Replace or update the volume
9545 SMESHDS_Mesh *aMesh = GetMeshDS();
9547 if (elem->IsPoly()) {
9548 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9552 int aShapeId = FindShape( elem );
9554 SMDS_MeshElement* newElem =
9555 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9556 myLastCreatedElems.Append(newElem);
9557 if (aShapeId && newElem)
9558 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9560 aMesh->RemoveElement(elem);
9567 //================================================================================
9569 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9571 //================================================================================
9573 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9574 vector<const SMDS_MeshNode *> & nodes,
9575 vector<int> & nbNodeInFaces )
9578 nbNodeInFaces.clear();
9579 SMDS_VolumeTool vTool ( elem );
9580 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9582 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9583 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9584 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9589 //=======================================================================
9591 * \brief Convert elements contained in a submesh to quadratic
9592 * \return int - nb of checked elements
9594 //=======================================================================
9596 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9597 SMESH_MesherHelper& theHelper,
9598 const bool theForce3d)
9601 if( !theSm ) return nbElem;
9603 vector<int> nbNodeInFaces;
9604 vector<const SMDS_MeshNode *> nodes;
9605 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9606 while(ElemItr->more())
9609 const SMDS_MeshElement* elem = ElemItr->next();
9610 if( !elem ) continue;
9612 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9613 if ( elem->IsQuadratic() )
9616 switch ( aGeomType ) {
9617 case SMDSEntity_Quad_Quadrangle:
9618 case SMDSEntity_Quad_Hexa: alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9619 case SMDSEntity_BiQuad_Quadrangle:
9620 case SMDSEntity_TriQuad_Hexa: alreadyOK = theHelper.GetIsBiQuadratic(); break;
9621 default: alreadyOK = true;
9623 if ( alreadyOK ) continue;
9625 // get elem data needed to re-create it
9627 const int id = elem->GetID();
9628 const int nbNodes = elem->NbCornerNodes();
9629 const SMDSAbs_ElementType aType = elem->GetType();
9630 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9631 if ( aGeomType == SMDSEntity_Polyhedra )
9632 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9633 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9634 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9636 // remove a linear element
9637 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9639 const SMDS_MeshElement* NewElem = 0;
9645 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9653 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9656 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9659 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9664 case SMDSAbs_Volume :
9668 case SMDSEntity_Tetra:
9669 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9671 case SMDSEntity_Pyramid:
9672 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9674 case SMDSEntity_Penta:
9675 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9677 case SMDSEntity_Hexa:
9678 case SMDSEntity_Quad_Hexa:
9679 case SMDSEntity_TriQuad_Hexa:
9680 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9681 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9683 case SMDSEntity_Hexagonal_Prism:
9685 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9692 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9694 theSm->AddElement( NewElem );
9698 //=======================================================================
9699 //function : ConvertToQuadratic
9701 //=======================================================================
9703 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9705 SMESHDS_Mesh* meshDS = GetMeshDS();
9707 SMESH_MesherHelper aHelper(*myMesh);
9709 aHelper.SetIsQuadratic( true );
9710 aHelper.SetIsBiQuadratic( theToBiQuad );
9711 aHelper.SetElementsOnShape(true);
9713 int nbCheckedElems = 0;
9714 if ( myMesh->HasShapeToMesh() )
9716 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9718 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9719 while ( smIt->more() ) {
9720 SMESH_subMesh* sm = smIt->next();
9721 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9722 aHelper.SetSubShape( sm->GetSubShape() );
9723 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9728 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9729 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9731 aHelper.SetElementsOnShape(false);
9732 SMESHDS_SubMesh *smDS = 0;
9733 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9734 while(aEdgeItr->more())
9736 const SMDS_MeshEdge* edge = aEdgeItr->next();
9737 if(edge && !edge->IsQuadratic())
9739 int id = edge->GetID();
9740 //MESSAGE("edge->GetID() " << id);
9741 const SMDS_MeshNode* n1 = edge->GetNode(0);
9742 const SMDS_MeshNode* n2 = edge->GetNode(1);
9744 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9746 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9747 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9750 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9751 while(aFaceItr->more())
9753 const SMDS_MeshFace* face = aFaceItr->next();
9754 if ( !face ) continue;
9756 const SMDSAbs_EntityType type = face->GetEntityType();
9757 if (( theToBiQuad && type == SMDSEntity_BiQuad_Quadrangle ) ||
9758 ( !theToBiQuad && type == SMDSEntity_Quad_Quadrangle ))
9761 const int id = face->GetID();
9762 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9764 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9766 SMDS_MeshFace * NewFace = 0;
9769 case SMDSEntity_Triangle:
9770 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9772 case SMDSEntity_Quadrangle:
9773 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9776 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9778 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9780 vector<int> nbNodeInFaces;
9781 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9782 while(aVolumeItr->more())
9784 const SMDS_MeshVolume* volume = aVolumeItr->next();
9785 if(!volume || volume->IsQuadratic() ) continue;
9787 const SMDSAbs_EntityType type = volume->GetEntityType();
9788 if (( theToBiQuad && type == SMDSEntity_TriQuad_Hexa ) ||
9789 ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
9792 const int id = volume->GetID();
9793 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9794 if ( type == SMDSEntity_Polyhedra )
9795 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9796 else if ( type == SMDSEntity_Hexagonal_Prism )
9797 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9799 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9801 SMDS_MeshVolume * NewVolume = 0;
9804 case SMDSEntity_Tetra:
9805 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9807 case SMDSEntity_Hexa:
9808 case SMDSEntity_Quad_Hexa:
9809 case SMDSEntity_TriQuad_Hexa:
9810 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9811 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9813 case SMDSEntity_Pyramid:
9814 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9815 nodes[3], nodes[4], id, theForce3d);
9817 case SMDSEntity_Penta:
9818 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9819 nodes[3], nodes[4], nodes[5], id, theForce3d);
9821 case SMDSEntity_Hexagonal_Prism:
9823 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9825 ReplaceElemInGroups(volume, NewVolume, meshDS);
9830 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9831 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9832 aHelper.FixQuadraticElements(myError);
9836 //================================================================================
9838 * \brief Makes given elements quadratic
9839 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9840 * \param theElements - elements to make quadratic
9842 //================================================================================
9844 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9845 TIDSortedElemSet& theElements,
9846 const bool theToBiQuad)
9848 if ( theElements.empty() ) return;
9850 // we believe that all theElements are of the same type
9851 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9853 // get all nodes shared by theElements
9854 TIDSortedNodeSet allNodes;
9855 TIDSortedElemSet::iterator eIt = theElements.begin();
9856 for ( ; eIt != theElements.end(); ++eIt )
9857 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9859 // complete theElements with elements of lower dim whose all nodes are in allNodes
9861 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9862 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9863 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9864 for ( ; nIt != allNodes.end(); ++nIt )
9866 const SMDS_MeshNode* n = *nIt;
9867 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9868 while ( invIt->more() )
9870 const SMDS_MeshElement* e = invIt->next();
9871 if ( e->IsQuadratic() )
9874 switch ( e->GetEntityType() ) {
9875 case SMDSEntity_Quad_Quadrangle:
9876 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9877 case SMDSEntity_BiQuad_Quadrangle:
9878 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9879 default: alreadyOK = true;
9883 quadAdjacentElems[ e->GetType() ].insert( e );
9887 if ( e->GetType() >= elemType )
9889 continue; // same type of more complex linear element
9892 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9893 continue; // e is already checked
9897 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9898 while ( nodeIt->more() && allIn )
9899 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9901 theElements.insert(e );
9905 SMESH_MesherHelper helper(*myMesh);
9906 helper.SetIsQuadratic( true );
9907 helper.SetIsBiQuadratic( theToBiQuad );
9909 // add links of quadratic adjacent elements to the helper
9911 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9912 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9913 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9915 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9917 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9918 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9919 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9921 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9923 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9924 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9925 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9927 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9930 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9932 SMESHDS_Mesh* meshDS = GetMeshDS();
9933 SMESHDS_SubMesh* smDS = 0;
9934 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9936 const SMDS_MeshElement* elem = *eIt;
9937 if( elem->NbNodes() < 2 || elem->IsPoly() )
9940 if ( elem->IsQuadratic() )
9943 switch ( elem->GetEntityType() ) {
9944 case SMDSEntity_Quad_Quadrangle:
9945 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9946 case SMDSEntity_BiQuad_Quadrangle:
9947 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9948 default: alreadyOK = true;
9950 if ( alreadyOK ) continue;
9953 const SMDSAbs_ElementType type = elem->GetType();
9954 const int id = elem->GetID();
9955 const int nbNodes = elem->NbCornerNodes();
9956 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9958 if ( !smDS || !smDS->Contains( elem ))
9959 smDS = meshDS->MeshElements( elem->getshapeId() );
9960 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9962 SMDS_MeshElement * newElem = 0;
9965 case 4: // cases for most frequently used element types go first (for optimization)
9966 if ( type == SMDSAbs_Volume )
9967 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9969 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9972 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9973 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9976 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9979 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9982 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9983 nodes[4], id, theForce3d);
9986 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9987 nodes[4], nodes[5], id, theForce3d);
9991 ReplaceElemInGroups( elem, newElem, meshDS);
9992 if( newElem && smDS )
9993 smDS->AddElement( newElem );
9996 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9997 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9998 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9999 helper.FixQuadraticElements( myError );
10003 //=======================================================================
10005 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
10006 * \return int - nb of checked elements
10008 //=======================================================================
10010 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
10011 SMDS_ElemIteratorPtr theItr,
10012 const int theShapeID)
10015 SMESHDS_Mesh* meshDS = GetMeshDS();
10017 while( theItr->more() )
10019 const SMDS_MeshElement* elem = theItr->next();
10021 if( elem && elem->IsQuadratic())
10023 int id = elem->GetID();
10024 int nbCornerNodes = elem->NbCornerNodes();
10025 SMDSAbs_ElementType aType = elem->GetType();
10027 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
10029 //remove a quadratic element
10030 if ( !theSm || !theSm->Contains( elem ))
10031 theSm = meshDS->MeshElements( elem->getshapeId() );
10032 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
10034 // remove medium nodes
10035 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
10036 if ( nodes[i]->NbInverseElements() == 0 )
10037 meshDS->RemoveFreeNode( nodes[i], theSm );
10039 // add a linear element
10040 nodes.resize( nbCornerNodes );
10041 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
10042 ReplaceElemInGroups(elem, newElem, meshDS);
10043 if( theSm && newElem )
10044 theSm->AddElement( newElem );
10050 //=======================================================================
10051 //function : ConvertFromQuadratic
10053 //=======================================================================
10055 bool SMESH_MeshEditor::ConvertFromQuadratic()
10057 int nbCheckedElems = 0;
10058 if ( myMesh->HasShapeToMesh() )
10060 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
10062 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
10063 while ( smIt->more() ) {
10064 SMESH_subMesh* sm = smIt->next();
10065 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
10066 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
10072 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
10073 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10075 SMESHDS_SubMesh *aSM = 0;
10076 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10084 //================================================================================
10086 * \brief Return true if all medium nodes of the element are in the node set
10088 //================================================================================
10090 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10092 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10093 if ( !nodeSet.count( elem->GetNode(i) ))
10099 //================================================================================
10101 * \brief Makes given elements linear
10103 //================================================================================
10105 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10107 if ( theElements.empty() ) return;
10109 // collect IDs of medium nodes of theElements; some of these nodes will be removed
10110 set<int> mediumNodeIDs;
10111 TIDSortedElemSet::iterator eIt = theElements.begin();
10112 for ( ; eIt != theElements.end(); ++eIt )
10114 const SMDS_MeshElement* e = *eIt;
10115 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10116 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10119 // replace given elements by linear ones
10120 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10121 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10122 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10124 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10125 // except those elements sharing medium nodes of quadratic element whose medium nodes
10126 // are not all in mediumNodeIDs
10128 // get remaining medium nodes
10129 TIDSortedNodeSet mediumNodes;
10130 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10131 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10132 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10133 mediumNodes.insert( mediumNodes.end(), n );
10135 // find more quadratic elements to convert
10136 TIDSortedElemSet moreElemsToConvert;
10137 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10138 for ( ; nIt != mediumNodes.end(); ++nIt )
10140 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10141 while ( invIt->more() )
10143 const SMDS_MeshElement* e = invIt->next();
10144 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10146 // find a more complex element including e and
10147 // whose medium nodes are not in mediumNodes
10148 bool complexFound = false;
10149 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10151 SMDS_ElemIteratorPtr invIt2 =
10152 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10153 while ( invIt2->more() )
10155 const SMDS_MeshElement* eComplex = invIt2->next();
10156 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10158 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10159 if ( nbCommonNodes == e->NbNodes())
10161 complexFound = true;
10162 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10168 if ( !complexFound )
10169 moreElemsToConvert.insert( e );
10173 elemIt = SMDS_ElemIteratorPtr
10174 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10175 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10178 //=======================================================================
10179 //function : SewSideElements
10181 //=======================================================================
10183 SMESH_MeshEditor::Sew_Error
10184 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10185 TIDSortedElemSet& theSide2,
10186 const SMDS_MeshNode* theFirstNode1,
10187 const SMDS_MeshNode* theFirstNode2,
10188 const SMDS_MeshNode* theSecondNode1,
10189 const SMDS_MeshNode* theSecondNode2)
10191 myLastCreatedElems.Clear();
10192 myLastCreatedNodes.Clear();
10194 MESSAGE ("::::SewSideElements()");
10195 if ( theSide1.size() != theSide2.size() )
10196 return SEW_DIFF_NB_OF_ELEMENTS;
10198 Sew_Error aResult = SEW_OK;
10200 // 1. Build set of faces representing each side
10201 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10202 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10204 // =======================================================================
10205 // 1. Build set of faces representing each side:
10206 // =======================================================================
10207 // a. build set of nodes belonging to faces
10208 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10209 // c. create temporary faces representing side of volumes if correspondent
10210 // face does not exist
10212 SMESHDS_Mesh* aMesh = GetMeshDS();
10213 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10214 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10215 TIDSortedElemSet faceSet1, faceSet2;
10216 set<const SMDS_MeshElement*> volSet1, volSet2;
10217 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10218 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10219 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10220 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10221 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10222 int iSide, iFace, iNode;
10224 list<const SMDS_MeshElement* > tempFaceList;
10225 for ( iSide = 0; iSide < 2; iSide++ ) {
10226 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10227 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10228 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10229 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10230 set<const SMDS_MeshElement*>::iterator vIt;
10231 TIDSortedElemSet::iterator eIt;
10232 set<const SMDS_MeshNode*>::iterator nIt;
10234 // check that given nodes belong to given elements
10235 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10236 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10237 int firstIndex = -1, secondIndex = -1;
10238 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10239 const SMDS_MeshElement* elem = *eIt;
10240 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10241 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10242 if ( firstIndex > -1 && secondIndex > -1 ) break;
10244 if ( firstIndex < 0 || secondIndex < 0 ) {
10245 // we can simply return until temporary faces created
10246 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10249 // -----------------------------------------------------------
10250 // 1a. Collect nodes of existing faces
10251 // and build set of face nodes in order to detect missing
10252 // faces corresponding to sides of volumes
10253 // -----------------------------------------------------------
10255 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10257 // loop on the given element of a side
10258 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10259 //const SMDS_MeshElement* elem = *eIt;
10260 const SMDS_MeshElement* elem = *eIt;
10261 if ( elem->GetType() == SMDSAbs_Face ) {
10262 faceSet->insert( elem );
10263 set <const SMDS_MeshNode*> faceNodeSet;
10264 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10265 while ( nodeIt->more() ) {
10266 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10267 nodeSet->insert( n );
10268 faceNodeSet.insert( n );
10270 setOfFaceNodeSet.insert( faceNodeSet );
10272 else if ( elem->GetType() == SMDSAbs_Volume )
10273 volSet->insert( elem );
10275 // ------------------------------------------------------------------------------
10276 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10277 // ------------------------------------------------------------------------------
10279 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10280 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10281 while ( fIt->more() ) { // loop on faces sharing a node
10282 const SMDS_MeshElement* f = fIt->next();
10283 if ( faceSet->find( f ) == faceSet->end() ) {
10284 // check if all nodes are in nodeSet and
10285 // complete setOfFaceNodeSet if they are
10286 set <const SMDS_MeshNode*> faceNodeSet;
10287 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10288 bool allInSet = true;
10289 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10290 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10291 if ( nodeSet->find( n ) == nodeSet->end() )
10294 faceNodeSet.insert( n );
10297 faceSet->insert( f );
10298 setOfFaceNodeSet.insert( faceNodeSet );
10304 // -------------------------------------------------------------------------
10305 // 1c. Create temporary faces representing sides of volumes if correspondent
10306 // face does not exist
10307 // -------------------------------------------------------------------------
10309 if ( !volSet->empty() ) {
10310 //int nodeSetSize = nodeSet->size();
10312 // loop on given volumes
10313 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10314 SMDS_VolumeTool vol (*vIt);
10315 // loop on volume faces: find free faces
10316 // --------------------------------------
10317 list<const SMDS_MeshElement* > freeFaceList;
10318 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10319 if ( !vol.IsFreeFace( iFace ))
10321 // check if there is already a face with same nodes in a face set
10322 const SMDS_MeshElement* aFreeFace = 0;
10323 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10324 int nbNodes = vol.NbFaceNodes( iFace );
10325 set <const SMDS_MeshNode*> faceNodeSet;
10326 vol.GetFaceNodes( iFace, faceNodeSet );
10327 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10329 // no such a face is given but it still can exist, check it
10330 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10331 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10333 if ( !aFreeFace ) {
10334 // create a temporary face
10335 if ( nbNodes == 3 ) {
10336 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10337 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10339 else if ( nbNodes == 4 ) {
10340 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10341 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10344 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10345 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10346 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10349 tempFaceList.push_back( aFreeFace );
10353 freeFaceList.push_back( aFreeFace );
10355 } // loop on faces of a volume
10357 // choose one of several free faces of a volume
10358 // --------------------------------------------
10359 if ( freeFaceList.size() > 1 ) {
10360 // choose a face having max nb of nodes shared by other elems of a side
10361 int maxNbNodes = -1;
10362 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10363 while ( fIt != freeFaceList.end() ) { // loop on free faces
10364 int nbSharedNodes = 0;
10365 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10366 while ( nodeIt->more() ) { // loop on free face nodes
10367 const SMDS_MeshNode* n =
10368 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10369 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10370 while ( invElemIt->more() ) {
10371 const SMDS_MeshElement* e = invElemIt->next();
10372 nbSharedNodes += faceSet->count( e );
10373 nbSharedNodes += elemSet->count( e );
10376 if ( nbSharedNodes > maxNbNodes ) {
10377 maxNbNodes = nbSharedNodes;
10378 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10380 else if ( nbSharedNodes == maxNbNodes ) {
10384 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10387 if ( freeFaceList.size() > 1 )
10389 // could not choose one face, use another way
10390 // choose a face most close to the bary center of the opposite side
10391 gp_XYZ aBC( 0., 0., 0. );
10392 set <const SMDS_MeshNode*> addedNodes;
10393 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10394 eIt = elemSet2->begin();
10395 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10396 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10397 while ( nodeIt->more() ) { // loop on free face nodes
10398 const SMDS_MeshNode* n =
10399 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10400 if ( addedNodes.insert( n ).second )
10401 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10404 aBC /= addedNodes.size();
10405 double minDist = DBL_MAX;
10406 fIt = freeFaceList.begin();
10407 while ( fIt != freeFaceList.end() ) { // loop on free faces
10409 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10410 while ( nodeIt->more() ) { // loop on free face nodes
10411 const SMDS_MeshNode* n =
10412 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10413 gp_XYZ p( n->X(),n->Y(),n->Z() );
10414 dist += ( aBC - p ).SquareModulus();
10416 if ( dist < minDist ) {
10418 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10421 fIt = freeFaceList.erase( fIt++ );
10424 } // choose one of several free faces of a volume
10426 if ( freeFaceList.size() == 1 ) {
10427 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10428 faceSet->insert( aFreeFace );
10429 // complete a node set with nodes of a found free face
10430 // for ( iNode = 0; iNode < ; iNode++ )
10431 // nodeSet->insert( fNodes[ iNode ] );
10434 } // loop on volumes of a side
10436 // // complete a set of faces if new nodes in a nodeSet appeared
10437 // // ----------------------------------------------------------
10438 // if ( nodeSetSize != nodeSet->size() ) {
10439 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10440 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10441 // while ( fIt->more() ) { // loop on faces sharing a node
10442 // const SMDS_MeshElement* f = fIt->next();
10443 // if ( faceSet->find( f ) == faceSet->end() ) {
10444 // // check if all nodes are in nodeSet and
10445 // // complete setOfFaceNodeSet if they are
10446 // set <const SMDS_MeshNode*> faceNodeSet;
10447 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10448 // bool allInSet = true;
10449 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10450 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10451 // if ( nodeSet->find( n ) == nodeSet->end() )
10452 // allInSet = false;
10454 // faceNodeSet.insert( n );
10456 // if ( allInSet ) {
10457 // faceSet->insert( f );
10458 // setOfFaceNodeSet.insert( faceNodeSet );
10464 } // Create temporary faces, if there are volumes given
10467 if ( faceSet1.size() != faceSet2.size() ) {
10468 // delete temporary faces: they are in reverseElements of actual nodes
10469 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10470 // while ( tmpFaceIt->more() )
10471 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10472 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10473 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10474 // aMesh->RemoveElement(*tmpFaceIt);
10475 MESSAGE("Diff nb of faces");
10476 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10479 // ============================================================
10480 // 2. Find nodes to merge:
10481 // bind a node to remove to a node to put instead
10482 // ============================================================
10484 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10485 if ( theFirstNode1 != theFirstNode2 )
10486 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10487 if ( theSecondNode1 != theSecondNode2 )
10488 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10490 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10491 set< long > linkIdSet; // links to process
10492 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10494 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10495 list< NLink > linkList[2];
10496 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10497 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10498 // loop on links in linkList; find faces by links and append links
10499 // of the found faces to linkList
10500 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10501 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10503 NLink link[] = { *linkIt[0], *linkIt[1] };
10504 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10505 if ( !linkIdSet.count( linkID ) )
10508 // by links, find faces in the face sets,
10509 // and find indices of link nodes in the found faces;
10510 // in a face set, there is only one or no face sharing a link
10511 // ---------------------------------------------------------------
10513 const SMDS_MeshElement* face[] = { 0, 0 };
10514 vector<const SMDS_MeshNode*> fnodes[2];
10515 int iLinkNode[2][2];
10516 TIDSortedElemSet avoidSet;
10517 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10518 const SMDS_MeshNode* n1 = link[iSide].first;
10519 const SMDS_MeshNode* n2 = link[iSide].second;
10520 //cout << "Side " << iSide << " ";
10521 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10522 // find a face by two link nodes
10523 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10524 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10525 if ( face[ iSide ])
10527 //cout << " F " << face[ iSide]->GetID() <<endl;
10528 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10529 // put face nodes to fnodes
10530 if ( face[ iSide ]->IsQuadratic() )
10532 // use interlaced nodes iterator
10533 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10534 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10535 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10536 while ( nIter->more() )
10537 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10541 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10542 face[ iSide ]->end_nodes() );
10544 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10548 // check similarity of elements of the sides
10549 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10550 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10551 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10552 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10555 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10557 break; // do not return because it's necessary to remove tmp faces
10560 // set nodes to merge
10561 // -------------------
10563 if ( face[0] && face[1] ) {
10564 const int nbNodes = face[0]->NbNodes();
10565 if ( nbNodes != face[1]->NbNodes() ) {
10566 MESSAGE("Diff nb of face nodes");
10567 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10568 break; // do not return because it s necessary to remove tmp faces
10570 bool reverse[] = { false, false }; // order of nodes in the link
10571 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10572 // analyse link orientation in faces
10573 int i1 = iLinkNode[ iSide ][ 0 ];
10574 int i2 = iLinkNode[ iSide ][ 1 ];
10575 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10577 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10578 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10579 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10581 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10582 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10585 // add other links of the faces to linkList
10586 // -----------------------------------------
10588 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10589 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10590 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10591 if ( !iter_isnew.second ) { // already in a set: no need to process
10592 linkIdSet.erase( iter_isnew.first );
10594 else // new in set == encountered for the first time: add
10596 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10597 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10598 linkList[0].push_back ( NLink( n1, n2 ));
10599 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10604 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10607 } // loop on link lists
10609 if ( aResult == SEW_OK &&
10610 ( //linkIt[0] != linkList[0].end() ||
10611 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10612 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10613 " " << (faceSetPtr[1]->empty()));
10614 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10617 // ====================================================================
10618 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10619 // ====================================================================
10621 // delete temporary faces
10622 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10623 // while ( tmpFaceIt->more() )
10624 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10625 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10626 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10627 aMesh->RemoveElement(*tmpFaceIt);
10629 if ( aResult != SEW_OK)
10632 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10633 // loop on nodes replacement map
10634 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10635 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10636 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10637 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10638 nodeIDsToRemove.push_back( nToRemove->GetID() );
10639 // loop on elements sharing nToRemove
10640 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10641 while ( invElemIt->more() ) {
10642 const SMDS_MeshElement* e = invElemIt->next();
10643 // get a new suite of nodes: make replacement
10644 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10645 vector< const SMDS_MeshNode*> nodes( nbNodes );
10646 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10647 while ( nIt->more() ) {
10648 const SMDS_MeshNode* n =
10649 static_cast<const SMDS_MeshNode*>( nIt->next() );
10650 nnIt = nReplaceMap.find( n );
10651 if ( nnIt != nReplaceMap.end() ) {
10653 n = (*nnIt).second;
10657 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10658 // elemIDsToRemove.push_back( e->GetID() );
10662 SMDSAbs_ElementType etyp = e->GetType();
10663 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10666 myLastCreatedElems.Append(newElem);
10667 AddToSameGroups(newElem, e, aMesh);
10668 int aShapeId = e->getshapeId();
10671 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10674 aMesh->RemoveElement(e);
10679 Remove( nodeIDsToRemove, true );
10684 //================================================================================
10686 * \brief Find corresponding nodes in two sets of faces
10687 * \param theSide1 - first face set
10688 * \param theSide2 - second first face
10689 * \param theFirstNode1 - a boundary node of set 1
10690 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10691 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10692 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10693 * \param nReplaceMap - output map of corresponding nodes
10694 * \return bool - is a success or not
10696 //================================================================================
10699 //#define DEBUG_MATCHING_NODES
10702 SMESH_MeshEditor::Sew_Error
10703 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10704 set<const SMDS_MeshElement*>& theSide2,
10705 const SMDS_MeshNode* theFirstNode1,
10706 const SMDS_MeshNode* theFirstNode2,
10707 const SMDS_MeshNode* theSecondNode1,
10708 const SMDS_MeshNode* theSecondNode2,
10709 TNodeNodeMap & nReplaceMap)
10711 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10713 nReplaceMap.clear();
10714 if ( theFirstNode1 != theFirstNode2 )
10715 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10716 if ( theSecondNode1 != theSecondNode2 )
10717 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10719 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10720 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10722 list< NLink > linkList[2];
10723 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10724 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10726 // loop on links in linkList; find faces by links and append links
10727 // of the found faces to linkList
10728 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10729 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10730 NLink link[] = { *linkIt[0], *linkIt[1] };
10731 if ( linkSet.find( link[0] ) == linkSet.end() )
10734 // by links, find faces in the face sets,
10735 // and find indices of link nodes in the found faces;
10736 // in a face set, there is only one or no face sharing a link
10737 // ---------------------------------------------------------------
10739 const SMDS_MeshElement* face[] = { 0, 0 };
10740 list<const SMDS_MeshNode*> notLinkNodes[2];
10741 //bool reverse[] = { false, false }; // order of notLinkNodes
10743 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10745 const SMDS_MeshNode* n1 = link[iSide].first;
10746 const SMDS_MeshNode* n2 = link[iSide].second;
10747 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10748 set< const SMDS_MeshElement* > facesOfNode1;
10749 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10751 // during a loop of the first node, we find all faces around n1,
10752 // during a loop of the second node, we find one face sharing both n1 and n2
10753 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10754 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10755 while ( fIt->more() ) { // loop on faces sharing a node
10756 const SMDS_MeshElement* f = fIt->next();
10757 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10758 ! facesOfNode1.insert( f ).second ) // f encounters twice
10760 if ( face[ iSide ] ) {
10761 MESSAGE( "2 faces per link " );
10762 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10765 faceSet->erase( f );
10767 // get not link nodes
10768 int nbN = f->NbNodes();
10769 if ( f->IsQuadratic() )
10771 nbNodes[ iSide ] = nbN;
10772 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10773 int i1 = f->GetNodeIndex( n1 );
10774 int i2 = f->GetNodeIndex( n2 );
10775 int iEnd = nbN, iBeg = -1, iDelta = 1;
10776 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10778 std::swap( iEnd, iBeg ); iDelta = -1;
10783 if ( i == iEnd ) i = iBeg + iDelta;
10784 if ( i == i1 ) break;
10785 nodes.push_back ( f->GetNode( i ) );
10791 // check similarity of elements of the sides
10792 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10793 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10794 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10795 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10798 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10802 // set nodes to merge
10803 // -------------------
10805 if ( face[0] && face[1] ) {
10806 if ( nbNodes[0] != nbNodes[1] ) {
10807 MESSAGE("Diff nb of face nodes");
10808 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10810 #ifdef DEBUG_MATCHING_NODES
10811 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10812 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10813 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10815 int nbN = nbNodes[0];
10817 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10818 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10819 for ( int i = 0 ; i < nbN - 2; ++i ) {
10820 #ifdef DEBUG_MATCHING_NODES
10821 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10823 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10827 // add other links of the face 1 to linkList
10828 // -----------------------------------------
10830 const SMDS_MeshElement* f0 = face[0];
10831 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10832 for ( int i = 0; i < nbN; i++ )
10834 const SMDS_MeshNode* n2 = f0->GetNode( i );
10835 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10836 linkSet.insert( SMESH_TLink( n1, n2 ));
10837 if ( !iter_isnew.second ) { // already in a set: no need to process
10838 linkSet.erase( iter_isnew.first );
10840 else // new in set == encountered for the first time: add
10842 #ifdef DEBUG_MATCHING_NODES
10843 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10844 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10846 linkList[0].push_back ( NLink( n1, n2 ));
10847 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10852 } // loop on link lists
10857 //================================================================================
10859 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10860 \param theElems - the list of elements (edges or faces) to be replicated
10861 The nodes for duplication could be found from these elements
10862 \param theNodesNot - list of nodes to NOT replicate
10863 \param theAffectedElems - the list of elements (cells and edges) to which the
10864 replicated nodes should be associated to.
10865 \return TRUE if operation has been completed successfully, FALSE otherwise
10867 //================================================================================
10869 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10870 const TIDSortedElemSet& theNodesNot,
10871 const TIDSortedElemSet& theAffectedElems )
10873 myLastCreatedElems.Clear();
10874 myLastCreatedNodes.Clear();
10876 if ( theElems.size() == 0 )
10879 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10884 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10885 // duplicate elements and nodes
10886 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10887 // replce nodes by duplications
10888 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10892 //================================================================================
10894 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10895 \param theMeshDS - mesh instance
10896 \param theElems - the elements replicated or modified (nodes should be changed)
10897 \param theNodesNot - nodes to NOT replicate
10898 \param theNodeNodeMap - relation of old node to new created node
10899 \param theIsDoubleElem - flag os to replicate element or modify
10900 \return TRUE if operation has been completed successfully, FALSE otherwise
10902 //================================================================================
10904 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10905 const TIDSortedElemSet& theElems,
10906 const TIDSortedElemSet& theNodesNot,
10907 std::map< const SMDS_MeshNode*,
10908 const SMDS_MeshNode* >& theNodeNodeMap,
10909 const bool theIsDoubleElem )
10911 MESSAGE("doubleNodes");
10912 // iterate on through element and duplicate them (by nodes duplication)
10914 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10915 for ( ; elemItr != theElems.end(); ++elemItr )
10917 const SMDS_MeshElement* anElem = *elemItr;
10921 bool isDuplicate = false;
10922 // duplicate nodes to duplicate element
10923 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10924 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10926 while ( anIter->more() )
10929 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10930 SMDS_MeshNode* aNewNode = aCurrNode;
10931 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10932 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10933 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10936 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10937 theNodeNodeMap[ aCurrNode ] = aNewNode;
10938 myLastCreatedNodes.Append( aNewNode );
10940 isDuplicate |= (aCurrNode != aNewNode);
10941 newNodes[ ind++ ] = aNewNode;
10943 if ( !isDuplicate )
10946 if ( theIsDoubleElem )
10947 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10950 MESSAGE("ChangeElementNodes");
10951 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10958 //================================================================================
10960 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10961 \param theNodes - identifiers of nodes to be doubled
10962 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10963 nodes. If list of element identifiers is empty then nodes are doubled but
10964 they not assigned to elements
10965 \return TRUE if operation has been completed successfully, FALSE otherwise
10967 //================================================================================
10969 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10970 const std::list< int >& theListOfModifiedElems )
10972 MESSAGE("DoubleNodes");
10973 myLastCreatedElems.Clear();
10974 myLastCreatedNodes.Clear();
10976 if ( theListOfNodes.size() == 0 )
10979 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10983 // iterate through nodes and duplicate them
10985 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10987 std::list< int >::const_iterator aNodeIter;
10988 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10990 int aCurr = *aNodeIter;
10991 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10997 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11000 anOldNodeToNewNode[ aNode ] = aNewNode;
11001 myLastCreatedNodes.Append( aNewNode );
11005 // Create map of new nodes for modified elements
11007 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
11009 std::list< int >::const_iterator anElemIter;
11010 for ( anElemIter = theListOfModifiedElems.begin();
11011 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
11013 int aCurr = *anElemIter;
11014 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
11018 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
11020 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11022 while ( anIter->more() )
11024 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
11025 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
11027 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
11028 aNodeArr[ ind++ ] = aNewNode;
11031 aNodeArr[ ind++ ] = aCurrNode;
11033 anElemToNodes[ anElem ] = aNodeArr;
11036 // Change nodes of elements
11038 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
11039 anElemToNodesIter = anElemToNodes.begin();
11040 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
11042 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
11043 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
11046 MESSAGE("ChangeElementNodes");
11047 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
11056 //================================================================================
11058 \brief Check if element located inside shape
11059 \return TRUE if IN or ON shape, FALSE otherwise
11061 //================================================================================
11063 template<class Classifier>
11064 bool isInside(const SMDS_MeshElement* theElem,
11065 Classifier& theClassifier,
11066 const double theTol)
11068 gp_XYZ centerXYZ (0, 0, 0);
11069 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11070 while (aNodeItr->more())
11071 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
11073 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11074 theClassifier.Perform(aPnt, theTol);
11075 TopAbs_State aState = theClassifier.State();
11076 return (aState == TopAbs_IN || aState == TopAbs_ON );
11079 //================================================================================
11081 * \brief Classifier of the 3D point on the TopoDS_Face
11082 * with interaface suitable for isInside()
11084 //================================================================================
11086 struct _FaceClassifier
11088 Extrema_ExtPS _extremum;
11089 BRepAdaptor_Surface _surface;
11090 TopAbs_State _state;
11092 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11094 _extremum.Initialize( _surface,
11095 _surface.FirstUParameter(), _surface.LastUParameter(),
11096 _surface.FirstVParameter(), _surface.LastVParameter(),
11097 _surface.Tolerance(), _surface.Tolerance() );
11099 void Perform(const gp_Pnt& aPnt, double theTol)
11101 _state = TopAbs_OUT;
11102 _extremum.Perform(aPnt);
11103 if ( _extremum.IsDone() )
11104 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11105 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11106 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11108 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11111 TopAbs_State State() const
11118 //================================================================================
11120 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11121 This method is the first step of DoubleNodeElemGroupsInRegion.
11122 \param theElems - list of groups of elements (edges or faces) to be replicated
11123 \param theNodesNot - list of groups of nodes not to replicated
11124 \param theShape - shape to detect affected elements (element which geometric center
11125 located on or inside shape). If the shape is null, detection is done on faces orientations
11126 (select elements with a gravity center on the side given by faces normals).
11127 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11128 The replicated nodes should be associated to affected elements.
11129 \return groups of affected elements
11130 \sa DoubleNodeElemGroupsInRegion()
11132 //================================================================================
11134 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11135 const TIDSortedElemSet& theNodesNot,
11136 const TopoDS_Shape& theShape,
11137 TIDSortedElemSet& theAffectedElems)
11139 if ( theShape.IsNull() )
11141 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
11142 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
11143 std::set<const SMDS_MeshElement*> edgesToCheck;
11144 alreadyCheckedNodes.clear();
11145 alreadyCheckedElems.clear();
11146 edgesToCheck.clear();
11148 // --- iterates on elements to be replicated and get elements by back references from their nodes
11150 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11152 for ( ; elemItr != theElems.end(); ++elemItr )
11154 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11155 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
11158 SMESH_Algo::FaceNormal( anElem, normal, /*normalized=*/true );
11159 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
11160 std::set<const SMDS_MeshNode*> nodesElem;
11162 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11163 while ( nodeItr->more() )
11165 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11166 nodesElem.insert(aNode);
11168 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
11169 for (; nodit != nodesElem.end(); nodit++)
11171 MESSAGE(" noeud ");
11172 const SMDS_MeshNode* aNode = *nodit;
11173 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11175 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
11177 alreadyCheckedNodes.insert(aNode);
11178 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11179 while ( backElemItr->more() )
11181 MESSAGE(" backelem ");
11182 const SMDS_MeshElement* curElem = backElemItr->next();
11183 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
11185 if (theElems.find(curElem) != theElems.end())
11187 alreadyCheckedElems.insert(curElem);
11188 double x=0, y=0, z=0;
11190 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
11191 while ( nodeItr2->more() )
11193 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
11194 x += anotherNode->X();
11195 y += anotherNode->Y();
11196 z += anotherNode->Z();
11200 p.SetCoord( x/nb -aNode->X(),
11202 z/nb -aNode->Z() );
11203 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
11206 MESSAGE(" --- inserted")
11207 theAffectedElems.insert( curElem );
11209 else if (curElem->GetType() == SMDSAbs_Edge)
11210 edgesToCheck.insert(curElem);
11214 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11215 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11216 for( ; eit != edgesToCheck.end(); eit++)
11218 bool onside = true;
11219 const SMDS_MeshElement* anEdge = *eit;
11220 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11221 while ( nodeItr->more() )
11223 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11224 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11232 MESSAGE(" --- edge onside inserted")
11233 theAffectedElems.insert(anEdge);
11239 const double aTol = Precision::Confusion();
11240 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11241 auto_ptr<_FaceClassifier> aFaceClassifier;
11242 if ( theShape.ShapeType() == TopAbs_SOLID )
11244 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11245 bsc3d->PerformInfinitePoint(aTol);
11247 else if (theShape.ShapeType() == TopAbs_FACE )
11249 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11252 // iterates on indicated elements and get elements by back references from their nodes
11253 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11255 for ( ; elemItr != theElems.end(); ++elemItr )
11257 MESSAGE("element " << ielem++);
11258 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11261 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11262 while ( nodeItr->more() )
11264 MESSAGE(" noeud ");
11265 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11266 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11268 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11269 while ( backElemItr->more() )
11271 MESSAGE(" backelem ");
11272 const SMDS_MeshElement* curElem = backElemItr->next();
11273 if ( curElem && theElems.find(curElem) == theElems.end() &&
11275 isInside( curElem, *bsc3d, aTol ) :
11276 isInside( curElem, *aFaceClassifier, aTol )))
11277 theAffectedElems.insert( curElem );
11285 //================================================================================
11287 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11288 \param theElems - group of of elements (edges or faces) to be replicated
11289 \param theNodesNot - group of nodes not to replicate
11290 \param theShape - shape to detect affected elements (element which geometric center
11291 located on or inside shape).
11292 The replicated nodes should be associated to affected elements.
11293 \return TRUE if operation has been completed successfully, FALSE otherwise
11295 //================================================================================
11297 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11298 const TIDSortedElemSet& theNodesNot,
11299 const TopoDS_Shape& theShape )
11301 if ( theShape.IsNull() )
11304 const double aTol = Precision::Confusion();
11305 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11306 auto_ptr<_FaceClassifier> aFaceClassifier;
11307 if ( theShape.ShapeType() == TopAbs_SOLID )
11309 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11310 bsc3d->PerformInfinitePoint(aTol);
11312 else if (theShape.ShapeType() == TopAbs_FACE )
11314 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11317 // iterates on indicated elements and get elements by back references from their nodes
11318 TIDSortedElemSet anAffected;
11319 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11320 for ( ; elemItr != theElems.end(); ++elemItr )
11322 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11326 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11327 while ( nodeItr->more() )
11329 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11330 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11332 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11333 while ( backElemItr->more() )
11335 const SMDS_MeshElement* curElem = backElemItr->next();
11336 if ( curElem && theElems.find(curElem) == theElems.end() &&
11338 isInside( curElem, *bsc3d, aTol ) :
11339 isInside( curElem, *aFaceClassifier, aTol )))
11340 anAffected.insert( curElem );
11344 return DoubleNodes( theElems, theNodesNot, anAffected );
11348 * \brief compute an oriented angle between two planes defined by four points.
11349 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11350 * @param p0 base of the rotation axe
11351 * @param p1 extremity of the rotation axe
11352 * @param g1 belongs to the first plane
11353 * @param g2 belongs to the second plane
11355 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11357 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11358 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11359 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11360 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11361 gp_Vec vref(p0, p1);
11364 gp_Vec n1 = vref.Crossed(v1);
11365 gp_Vec n2 = vref.Crossed(v2);
11366 return n2.AngleWithRef(n1, vref);
11370 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11371 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11372 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11373 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11374 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11375 * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
11376 * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
11377 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11378 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11379 * @param theElems - list of groups of volumes, where a group of volume is a set of
11380 * SMDS_MeshElements sorted by Id.
11381 * @param createJointElems - if TRUE, create the elements
11382 * @return TRUE if operation has been completed successfully, FALSE otherwise
11384 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11385 bool createJointElems)
11387 MESSAGE("----------------------------------------------");
11388 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11389 MESSAGE("----------------------------------------------");
11391 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11392 meshDS->BuildDownWardConnectivity(true);
11394 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11396 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11397 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11398 // build the list of nodes shared by 2 or more domains, with their domain indexes
11400 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11401 std::map<int,int>celldom; // cell vtkId --> domain
11402 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11403 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11404 faceDomains.clear();
11406 cellDomains.clear();
11407 nodeDomains.clear();
11408 std::map<int,int> emptyMap;
11409 std::set<int> emptySet;
11412 MESSAGE(".. Number of domains :"<<theElems.size());
11414 // Check if the domains do not share an element
11415 for (int idom = 0; idom < theElems.size()-1; idom++)
11417 // MESSAGE("... Check of domain #" << idom);
11418 const TIDSortedElemSet& domain = theElems[idom];
11419 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11420 for (; elemItr != domain.end(); ++elemItr)
11422 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11423 int idombisdeb = idom + 1 ;
11424 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11426 const TIDSortedElemSet& domainbis = theElems[idombis];
11427 if ( domainbis.count(anElem) )
11429 MESSAGE(".... Domain #" << idom);
11430 MESSAGE(".... Domain #" << idombis);
11431 throw SALOME_Exception("The domains are not disjoint.");
11438 for (int idom = 0; idom < theElems.size(); idom++)
11441 // --- build a map (face to duplicate --> volume to modify)
11442 // with all the faces shared by 2 domains (group of elements)
11443 // and corresponding volume of this domain, for each shared face.
11444 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11446 MESSAGE("... Neighbors of domain #" << idom);
11447 const TIDSortedElemSet& domain = theElems[idom];
11448 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11449 for (; elemItr != domain.end(); ++elemItr)
11451 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11454 int vtkId = anElem->getVtkId();
11455 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11456 int neighborsVtkIds[NBMAXNEIGHBORS];
11457 int downIds[NBMAXNEIGHBORS];
11458 unsigned char downTypes[NBMAXNEIGHBORS];
11459 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11460 for (int n = 0; n < nbNeighbors; n++)
11462 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11463 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11464 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11467 for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
11469 // MESSAGE("Domain " << idombis);
11470 const TIDSortedElemSet& domainbis = theElems[idombis];
11471 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11473 if ( ok ) // the characteristics of the face is stored
11475 DownIdType face(downIds[n], downTypes[n]);
11476 if (!faceDomains.count(face))
11477 faceDomains[face] = emptyMap; // create an empty entry for face
11478 if (!faceDomains[face].count(idom))
11480 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11481 celldom[vtkId] = idom;
11482 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11490 //MESSAGE("Number of shared faces " << faceDomains.size());
11491 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11493 // --- explore the shared faces domain by domain,
11494 // explore the nodes of the face and see if they belong to a cell in the domain,
11495 // which has only a node or an edge on the border (not a shared face)
11497 for (int idomain = 0; idomain < theElems.size(); idomain++)
11499 //MESSAGE("Domain " << idomain);
11500 const TIDSortedElemSet& domain = theElems[idomain];
11501 itface = faceDomains.begin();
11502 for (; itface != faceDomains.end(); ++itface)
11504 std::map<int, int> domvol = itface->second;
11505 if (!domvol.count(idomain))
11507 DownIdType face = itface->first;
11508 //MESSAGE(" --- face " << face.cellId);
11509 std::set<int> oldNodes;
11511 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11512 std::set<int>::iterator itn = oldNodes.begin();
11513 for (; itn != oldNodes.end(); ++itn)
11516 //MESSAGE(" node " << oldId);
11517 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11518 for (int i=0; i<l.ncells; i++)
11520 int vtkId = l.cells[i];
11521 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11522 if (!domain.count(anElem))
11524 int vtkType = grid->GetCellType(vtkId);
11525 int downId = grid->CellIdToDownId(vtkId);
11528 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11529 continue; // not OK at this stage of the algorithm:
11530 //no cells created after BuildDownWardConnectivity
11532 DownIdType aCell(downId, vtkType);
11533 if (!cellDomains.count(aCell))
11534 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11535 cellDomains[aCell][idomain] = vtkId;
11536 celldom[vtkId] = idomain;
11537 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11543 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11544 // for each shared face, get the nodes
11545 // for each node, for each domain of the face, create a clone of the node
11547 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11548 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11549 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11551 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11552 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11553 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11555 MESSAGE(".. Duplication of the nodes");
11556 for (int idomain = 0; idomain < theElems.size(); idomain++)
11558 itface = faceDomains.begin();
11559 for (; itface != faceDomains.end(); ++itface)
11561 std::map<int, int> domvol = itface->second;
11562 if (!domvol.count(idomain))
11564 DownIdType face = itface->first;
11565 //MESSAGE(" --- face " << face.cellId);
11566 std::set<int> oldNodes;
11568 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11569 std::set<int>::iterator itn = oldNodes.begin();
11570 for (; itn != oldNodes.end(); ++itn)
11573 //MESSAGE("-+-+-a node " << oldId);
11574 if (!nodeDomains.count(oldId))
11575 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11576 if (nodeDomains[oldId].empty())
11578 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11579 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11581 std::map<int, int>::iterator itdom = domvol.begin();
11582 for (; itdom != domvol.end(); ++itdom)
11584 int idom = itdom->first;
11585 //MESSAGE(" domain " << idom);
11586 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11588 if (nodeDomains[oldId].size() >= 2) // a multiple node
11590 vector<int> orderedDoms;
11591 //MESSAGE("multiple node " << oldId);
11592 if (mutipleNodes.count(oldId))
11593 orderedDoms = mutipleNodes[oldId];
11596 map<int,int>::iterator it = nodeDomains[oldId].begin();
11597 for (; it != nodeDomains[oldId].end(); ++it)
11598 orderedDoms.push_back(it->first);
11600 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11601 //stringstream txt;
11602 //for (int i=0; i<orderedDoms.size(); i++)
11603 // txt << orderedDoms[i] << " ";
11604 //MESSAGE("orderedDoms " << txt.str());
11605 mutipleNodes[oldId] = orderedDoms;
11607 double *coords = grid->GetPoint(oldId);
11608 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11609 int newId = newNode->getVtkId();
11610 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11611 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11618 MESSAGE(".. Creation of elements");
11619 for (int idomain = 0; idomain < theElems.size(); idomain++)
11621 itface = faceDomains.begin();
11622 for (; itface != faceDomains.end(); ++itface)
11624 std::map<int, int> domvol = itface->second;
11625 if (!domvol.count(idomain))
11627 DownIdType face = itface->first;
11628 //MESSAGE(" --- face " << face.cellId);
11629 std::set<int> oldNodes;
11631 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11632 int nbMultipleNodes = 0;
11633 std::set<int>::iterator itn = oldNodes.begin();
11634 for (; itn != oldNodes.end(); ++itn)
11637 if (mutipleNodes.count(oldId))
11640 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11642 //MESSAGE("multiple Nodes detected on a shared face");
11643 int downId = itface->first.cellId;
11644 unsigned char cellType = itface->first.cellType;
11645 // --- shared edge or shared face ?
11646 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11649 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11650 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11651 if (mutipleNodes.count(nodes[i]))
11652 if (!mutipleNodesToFace.count(nodes[i]))
11653 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11655 else // shared face (between two volumes)
11657 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11658 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11659 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11660 for (int ie =0; ie < nbEdges; ie++)
11663 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11664 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11666 vector<int> vn0 = mutipleNodes[nodes[0]];
11667 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11669 for (int i0 = 0; i0 < vn0.size(); i0++)
11670 for (int i1 = 0; i1 < vn1.size(); i1++)
11671 if (vn0[i0] == vn1[i1])
11672 doms.push_back(vn0[i0]);
11673 if (doms.size() >2)
11675 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11676 double *coords = grid->GetPoint(nodes[0]);
11677 gp_Pnt p0(coords[0], coords[1], coords[2]);
11678 coords = grid->GetPoint(nodes[nbNodes - 1]);
11679 gp_Pnt p1(coords[0], coords[1], coords[2]);
11681 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11682 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11683 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11684 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11685 for (int id=0; id < doms.size(); id++)
11687 int idom = doms[id];
11688 for (int ivol=0; ivol<nbvol; ivol++)
11690 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11691 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11692 if (theElems[idom].count(elem))
11694 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11695 domvol[idom] = svol;
11696 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11698 vtkIdType npts = 0;
11699 vtkIdType* pts = 0;
11700 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11701 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11704 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11705 angleDom[idom] = 0;
11709 gp_Pnt g(values[0], values[1], values[2]);
11710 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11711 //MESSAGE(" angle=" << angleDom[idom]);
11717 map<double, int> sortedDom; // sort domains by angle
11718 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11719 sortedDom[ia->second] = ia->first;
11720 vector<int> vnodes;
11722 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11724 vdom.push_back(ib->second);
11725 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11727 for (int ino = 0; ino < nbNodes; ino++)
11728 vnodes.push_back(nodes[ino]);
11729 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11738 // --- iterate on shared faces (volumes to modify, face to extrude)
11739 // get node id's of the face (id SMDS = id VTK)
11740 // create flat element with old and new nodes if requested
11742 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11743 // (domain1 X domain2) = domain1 + MAXINT*domain2
11745 std::map<int, std::map<long,int> > nodeQuadDomains;
11746 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11748 MESSAGE(".. Creation of elements: simple junction");
11749 if (createJointElems)
11752 string joints2DName = "joints2D";
11753 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11754 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11755 string joints3DName = "joints3D";
11756 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11757 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11759 itface = faceDomains.begin();
11760 for (; itface != faceDomains.end(); ++itface)
11762 DownIdType face = itface->first;
11763 std::set<int> oldNodes;
11764 std::set<int>::iterator itn;
11766 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11768 std::map<int, int> domvol = itface->second;
11769 std::map<int, int>::iterator itdom = domvol.begin();
11770 int dom1 = itdom->first;
11771 int vtkVolId = itdom->second;
11773 int dom2 = itdom->first;
11774 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11776 stringstream grpname;
11779 grpname << dom1 << "_" << dom2;
11781 grpname << dom2 << "_" << dom1;
11782 string namegrp = grpname.str();
11783 if (!mapOfJunctionGroups.count(namegrp))
11784 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11785 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11787 sgrp->Add(vol->GetID());
11788 if (vol->GetType() == SMDSAbs_Volume)
11789 joints3DGrp->Add(vol->GetID());
11790 else if (vol->GetType() == SMDSAbs_Face)
11791 joints2DGrp->Add(vol->GetID());
11795 // --- create volumes on multiple domain intersection if requested
11796 // iterate on mutipleNodesToFace
11797 // iterate on edgesMultiDomains
11799 MESSAGE(".. Creation of elements: multiple junction");
11800 if (createJointElems)
11802 // --- iterate on mutipleNodesToFace
11804 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11805 for (; itn != mutipleNodesToFace.end(); ++itn)
11807 int node = itn->first;
11808 vector<int> orderDom = itn->second;
11809 vector<vtkIdType> orderedNodes;
11810 for (int idom = 0; idom <orderDom.size(); idom++)
11811 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11812 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11814 stringstream grpname;
11816 grpname << 0 << "_" << 0;
11818 string namegrp = grpname.str();
11819 if (!mapOfJunctionGroups.count(namegrp))
11820 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11821 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11823 sgrp->Add(face->GetID());
11826 // --- iterate on edgesMultiDomains
11828 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11829 for (; ite != edgesMultiDomains.end(); ++ite)
11831 vector<int> nodes = ite->first;
11832 vector<int> orderDom = ite->second;
11833 vector<vtkIdType> orderedNodes;
11834 if (nodes.size() == 2)
11836 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11837 for (int ino=0; ino < nodes.size(); ino++)
11838 if (orderDom.size() == 3)
11839 for (int idom = 0; idom <orderDom.size(); idom++)
11840 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11842 for (int idom = orderDom.size()-1; idom >=0; idom--)
11843 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11844 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11847 string namegrp = "jointsMultiples";
11848 if (!mapOfJunctionGroups.count(namegrp))
11849 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11850 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11852 sgrp->Add(vol->GetID());
11856 INFOS("Quadratic multiple joints not implemented");
11857 // TODO quadratic nodes
11862 // --- list the explicit faces and edges of the mesh that need to be modified,
11863 // i.e. faces and edges built with one or more duplicated nodes.
11864 // associate these faces or edges to their corresponding domain.
11865 // only the first domain found is kept when a face or edge is shared
11867 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11868 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11869 faceOrEdgeDom.clear();
11872 MESSAGE(".. Modification of elements");
11873 for (int idomain = 0; idomain < theElems.size(); idomain++)
11875 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11876 for (; itnod != nodeDomains.end(); ++itnod)
11878 int oldId = itnod->first;
11879 //MESSAGE(" node " << oldId);
11880 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11881 for (int i = 0; i < l.ncells; i++)
11883 int vtkId = l.cells[i];
11884 int vtkType = grid->GetCellType(vtkId);
11885 int downId = grid->CellIdToDownId(vtkId);
11887 continue; // new cells: not to be modified
11888 DownIdType aCell(downId, vtkType);
11889 int volParents[1000];
11890 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11891 for (int j = 0; j < nbvol; j++)
11892 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11893 if (!feDom.count(vtkId))
11895 feDom[vtkId] = idomain;
11896 faceOrEdgeDom[aCell] = emptyMap;
11897 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11898 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11899 // << " type " << vtkType << " downId " << downId);
11905 // --- iterate on shared faces (volumes to modify, face to extrude)
11906 // get node id's of the face
11907 // replace old nodes by new nodes in volumes, and update inverse connectivity
11909 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11910 for (int m=0; m<3; m++)
11912 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11913 itface = (*amap).begin();
11914 for (; itface != (*amap).end(); ++itface)
11916 DownIdType face = itface->first;
11917 std::set<int> oldNodes;
11918 std::set<int>::iterator itn;
11920 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11921 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11922 std::map<int, int> localClonedNodeIds;
11924 std::map<int, int> domvol = itface->second;
11925 std::map<int, int>::iterator itdom = domvol.begin();
11926 for (; itdom != domvol.end(); ++itdom)
11928 int idom = itdom->first;
11929 int vtkVolId = itdom->second;
11930 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11931 localClonedNodeIds.clear();
11932 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11935 if (nodeDomains[oldId].count(idom))
11937 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11938 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11941 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11946 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11947 grid->BuildLinks();
11955 * \brief Double nodes on some external faces and create flat elements.
11956 * Flat elements are mainly used by some types of mechanic calculations.
11958 * Each group of the list must be constituted of faces.
11959 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11960 * @param theElems - list of groups of faces, where a group of faces is a set of
11961 * SMDS_MeshElements sorted by Id.
11962 * @return TRUE if operation has been completed successfully, FALSE otherwise
11964 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11966 MESSAGE("-------------------------------------------------");
11967 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11968 MESSAGE("-------------------------------------------------");
11970 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11972 // --- For each group of faces
11973 // duplicate the nodes, create a flat element based on the face
11974 // replace the nodes of the faces by their clones
11976 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11977 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11978 clonedNodes.clear();
11979 intermediateNodes.clear();
11980 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11981 mapOfJunctionGroups.clear();
11983 for (int idom = 0; idom < theElems.size(); idom++)
11985 const TIDSortedElemSet& domain = theElems[idom];
11986 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11987 for (; elemItr != domain.end(); ++elemItr)
11989 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11990 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11993 // MESSAGE("aFace=" << aFace->GetID());
11994 bool isQuad = aFace->IsQuadratic();
11995 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11997 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11999 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12000 while (nodeIt->more())
12002 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12003 bool isMedium = isQuad && (aFace->IsMediumNode(node));
12005 ln2.push_back(node);
12007 ln0.push_back(node);
12009 const SMDS_MeshNode* clone = 0;
12010 if (!clonedNodes.count(node))
12012 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12013 clonedNodes[node] = clone;
12016 clone = clonedNodes[node];
12019 ln3.push_back(clone);
12021 ln1.push_back(clone);
12023 const SMDS_MeshNode* inter = 0;
12024 if (isQuad && (!isMedium))
12026 if (!intermediateNodes.count(node))
12028 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12029 intermediateNodes[node] = inter;
12032 inter = intermediateNodes[node];
12033 ln4.push_back(inter);
12037 // --- extrude the face
12039 vector<const SMDS_MeshNode*> ln;
12040 SMDS_MeshVolume* vol = 0;
12041 vtkIdType aType = aFace->GetVtkType();
12045 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12046 // MESSAGE("vol prism " << vol->GetID());
12047 ln.push_back(ln1[0]);
12048 ln.push_back(ln1[1]);
12049 ln.push_back(ln1[2]);
12052 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12053 // MESSAGE("vol hexa " << vol->GetID());
12054 ln.push_back(ln1[0]);
12055 ln.push_back(ln1[1]);
12056 ln.push_back(ln1[2]);
12057 ln.push_back(ln1[3]);
12059 case VTK_QUADRATIC_TRIANGLE:
12060 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12061 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12062 // MESSAGE("vol quad prism " << vol->GetID());
12063 ln.push_back(ln1[0]);
12064 ln.push_back(ln1[1]);
12065 ln.push_back(ln1[2]);
12066 ln.push_back(ln3[0]);
12067 ln.push_back(ln3[1]);
12068 ln.push_back(ln3[2]);
12070 case VTK_QUADRATIC_QUAD:
12071 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12072 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12073 // ln4[0], ln4[1], ln4[2], ln4[3]);
12074 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12075 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12076 ln4[0], ln4[1], ln4[2], ln4[3]);
12077 // MESSAGE("vol quad hexa " << vol->GetID());
12078 ln.push_back(ln1[0]);
12079 ln.push_back(ln1[1]);
12080 ln.push_back(ln1[2]);
12081 ln.push_back(ln1[3]);
12082 ln.push_back(ln3[0]);
12083 ln.push_back(ln3[1]);
12084 ln.push_back(ln3[2]);
12085 ln.push_back(ln3[3]);
12095 stringstream grpname;
12099 string namegrp = grpname.str();
12100 if (!mapOfJunctionGroups.count(namegrp))
12101 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12102 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12104 sgrp->Add(vol->GetID());
12107 // --- modify the face
12109 aFace->ChangeNodes(&ln[0], ln.size());
12116 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12117 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12118 * groups of faces to remove inside the object, (idem edges).
12119 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12121 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12122 const TopoDS_Shape& theShape,
12123 SMESH_NodeSearcher* theNodeSearcher,
12124 const char* groupName,
12125 std::vector<double>& nodesCoords,
12126 std::vector<std::vector<int> >& listOfListOfNodes)
12128 MESSAGE("--------------------------------");
12129 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12130 MESSAGE("--------------------------------");
12132 // --- zone of volumes to remove is given :
12133 // 1 either by a geom shape (one or more vertices) and a radius,
12134 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12135 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12136 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12137 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12138 // defined by it's name.
12140 SMESHDS_GroupBase* groupDS = 0;
12141 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12142 while ( groupIt->more() )
12145 SMESH_Group * group = groupIt->next();
12146 if ( !group ) continue;
12147 groupDS = group->GetGroupDS();
12148 if ( !groupDS || groupDS->IsEmpty() ) continue;
12149 std::string grpName = group->GetName();
12150 //MESSAGE("grpName=" << grpName);
12151 if (grpName == groupName)
12157 bool isNodeGroup = false;
12158 bool isNodeCoords = false;
12161 if (groupDS->GetType() != SMDSAbs_Node)
12163 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12166 if (nodesCoords.size() > 0)
12167 isNodeCoords = true; // a list o nodes given by their coordinates
12168 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12170 // --- define groups to build
12172 int idg; // --- group of SMDS volumes
12173 string grpvName = groupName;
12174 grpvName += "_vol";
12175 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12178 MESSAGE("group not created " << grpvName);
12181 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12183 int idgs; // --- group of SMDS faces on the skin
12184 string grpsName = groupName;
12185 grpsName += "_skin";
12186 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12189 MESSAGE("group not created " << grpsName);
12192 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12194 int idgi; // --- group of SMDS faces internal (several shapes)
12195 string grpiName = groupName;
12196 grpiName += "_internalFaces";
12197 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12200 MESSAGE("group not created " << grpiName);
12203 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12205 int idgei; // --- group of SMDS faces internal (several shapes)
12206 string grpeiName = groupName;
12207 grpeiName += "_internalEdges";
12208 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12211 MESSAGE("group not created " << grpeiName);
12214 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12216 // --- build downward connectivity
12218 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12219 meshDS->BuildDownWardConnectivity(true);
12220 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12222 // --- set of volumes detected inside
12224 std::set<int> setOfInsideVol;
12225 std::set<int> setOfVolToCheck;
12227 std::vector<gp_Pnt> gpnts;
12230 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12232 MESSAGE("group of nodes provided");
12233 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12234 while ( elemIt->more() )
12236 const SMDS_MeshElement* elem = elemIt->next();
12239 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12242 SMDS_MeshElement* vol = 0;
12243 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12244 while (volItr->more())
12246 vol = (SMDS_MeshElement*)volItr->next();
12247 setOfInsideVol.insert(vol->getVtkId());
12248 sgrp->Add(vol->GetID());
12252 else if (isNodeCoords)
12254 MESSAGE("list of nodes coordinates provided");
12257 while (i < nodesCoords.size()-2)
12259 double x = nodesCoords[i++];
12260 double y = nodesCoords[i++];
12261 double z = nodesCoords[i++];
12262 gp_Pnt p = gp_Pnt(x, y ,z);
12263 gpnts.push_back(p);
12264 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12267 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12269 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12270 TopTools_IndexedMapOfShape vertexMap;
12271 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12272 gp_Pnt p = gp_Pnt(0,0,0);
12273 if (vertexMap.Extent() < 1)
12276 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12278 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12279 p = BRep_Tool::Pnt(vertex);
12280 gpnts.push_back(p);
12281 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12285 if (gpnts.size() > 0)
12288 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12290 nodeId = startNode->GetID();
12291 MESSAGE("nodeId " << nodeId);
12293 double radius2 = radius*radius;
12294 MESSAGE("radius2 " << radius2);
12296 // --- volumes on start node
12298 setOfVolToCheck.clear();
12299 SMDS_MeshElement* startVol = 0;
12300 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12301 while (volItr->more())
12303 startVol = (SMDS_MeshElement*)volItr->next();
12304 setOfVolToCheck.insert(startVol->getVtkId());
12306 if (setOfVolToCheck.empty())
12308 MESSAGE("No volumes found");
12312 // --- starting with central volumes then their neighbors, check if they are inside
12313 // or outside the domain, until no more new neighbor volume is inside.
12314 // Fill the group of inside volumes
12316 std::map<int, double> mapOfNodeDistance2;
12317 mapOfNodeDistance2.clear();
12318 std::set<int> setOfOutsideVol;
12319 while (!setOfVolToCheck.empty())
12321 std::set<int>::iterator it = setOfVolToCheck.begin();
12323 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12324 bool volInside = false;
12325 vtkIdType npts = 0;
12326 vtkIdType* pts = 0;
12327 grid->GetCellPoints(vtkId, npts, pts);
12328 for (int i=0; i<npts; i++)
12330 double distance2 = 0;
12331 if (mapOfNodeDistance2.count(pts[i]))
12333 distance2 = mapOfNodeDistance2[pts[i]];
12334 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12338 double *coords = grid->GetPoint(pts[i]);
12339 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12341 for (int j=0; j<gpnts.size(); j++)
12343 double d2 = aPoint.SquareDistance(gpnts[j]);
12344 if (d2 < distance2)
12347 if (distance2 < radius2)
12351 mapOfNodeDistance2[pts[i]] = distance2;
12352 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12354 if (distance2 < radius2)
12356 volInside = true; // one or more nodes inside the domain
12357 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12363 setOfInsideVol.insert(vtkId);
12364 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12365 int neighborsVtkIds[NBMAXNEIGHBORS];
12366 int downIds[NBMAXNEIGHBORS];
12367 unsigned char downTypes[NBMAXNEIGHBORS];
12368 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12369 for (int n = 0; n < nbNeighbors; n++)
12370 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12371 setOfVolToCheck.insert(neighborsVtkIds[n]);
12375 setOfOutsideVol.insert(vtkId);
12376 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12378 setOfVolToCheck.erase(vtkId);
12382 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12383 // If yes, add the volume to the inside set
12385 bool addedInside = true;
12386 std::set<int> setOfVolToReCheck;
12387 while (addedInside)
12389 MESSAGE(" --------------------------- re check");
12390 addedInside = false;
12391 std::set<int>::iterator itv = setOfInsideVol.begin();
12392 for (; itv != setOfInsideVol.end(); ++itv)
12395 int neighborsVtkIds[NBMAXNEIGHBORS];
12396 int downIds[NBMAXNEIGHBORS];
12397 unsigned char downTypes[NBMAXNEIGHBORS];
12398 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12399 for (int n = 0; n < nbNeighbors; n++)
12400 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12401 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12403 setOfVolToCheck = setOfVolToReCheck;
12404 setOfVolToReCheck.clear();
12405 while (!setOfVolToCheck.empty())
12407 std::set<int>::iterator it = setOfVolToCheck.begin();
12409 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12411 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12412 int countInside = 0;
12413 int neighborsVtkIds[NBMAXNEIGHBORS];
12414 int downIds[NBMAXNEIGHBORS];
12415 unsigned char downTypes[NBMAXNEIGHBORS];
12416 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12417 for (int n = 0; n < nbNeighbors; n++)
12418 if (setOfInsideVol.count(neighborsVtkIds[n]))
12420 MESSAGE("countInside " << countInside);
12421 if (countInside > 1)
12423 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12424 setOfInsideVol.insert(vtkId);
12425 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12426 addedInside = true;
12429 setOfVolToReCheck.insert(vtkId);
12431 setOfVolToCheck.erase(vtkId);
12435 // --- map of Downward faces at the boundary, inside the global volume
12436 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12437 // fill group of SMDS faces inside the volume (when several volume shapes)
12438 // fill group of SMDS faces on the skin of the global volume (if skin)
12440 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12441 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12442 std::set<int>::iterator it = setOfInsideVol.begin();
12443 for (; it != setOfInsideVol.end(); ++it)
12446 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12447 int neighborsVtkIds[NBMAXNEIGHBORS];
12448 int downIds[NBMAXNEIGHBORS];
12449 unsigned char downTypes[NBMAXNEIGHBORS];
12450 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12451 for (int n = 0; n < nbNeighbors; n++)
12453 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12454 if (neighborDim == 3)
12456 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12458 DownIdType face(downIds[n], downTypes[n]);
12459 boundaryFaces[face] = vtkId;
12461 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12462 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12463 if (vtkFaceId >= 0)
12465 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12466 // find also the smds edges on this face
12467 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12468 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12469 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12470 for (int i = 0; i < nbEdges; i++)
12472 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12473 if (vtkEdgeId >= 0)
12474 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12478 else if (neighborDim == 2) // skin of the volume
12480 DownIdType face(downIds[n], downTypes[n]);
12481 skinFaces[face] = vtkId;
12482 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12483 if (vtkFaceId >= 0)
12484 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12489 // --- identify the edges constituting the wire of each subshape on the skin
12490 // define polylines with the nodes of edges, equivalent to wires
12491 // project polylines on subshapes, and partition, to get geom faces
12493 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12494 std::set<int> emptySet;
12496 std::set<int> shapeIds;
12498 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12499 while (itelem->more())
12501 const SMDS_MeshElement *elem = itelem->next();
12502 int shapeId = elem->getshapeId();
12503 int vtkId = elem->getVtkId();
12504 if (!shapeIdToVtkIdSet.count(shapeId))
12506 shapeIdToVtkIdSet[shapeId] = emptySet;
12507 shapeIds.insert(shapeId);
12509 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12512 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12513 std::set<DownIdType, DownIdCompare> emptyEdges;
12514 emptyEdges.clear();
12516 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12517 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12519 int shapeId = itShape->first;
12520 MESSAGE(" --- Shape ID --- "<< shapeId);
12521 shapeIdToEdges[shapeId] = emptyEdges;
12523 std::vector<int> nodesEdges;
12525 std::set<int>::iterator its = itShape->second.begin();
12526 for (; its != itShape->second.end(); ++its)
12529 MESSAGE(" " << vtkId);
12530 int neighborsVtkIds[NBMAXNEIGHBORS];
12531 int downIds[NBMAXNEIGHBORS];
12532 unsigned char downTypes[NBMAXNEIGHBORS];
12533 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12534 for (int n = 0; n < nbNeighbors; n++)
12536 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12538 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12539 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12540 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12542 DownIdType edge(downIds[n], downTypes[n]);
12543 if (!shapeIdToEdges[shapeId].count(edge))
12545 shapeIdToEdges[shapeId].insert(edge);
12547 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12548 nodesEdges.push_back(vtkNodeId[0]);
12549 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12550 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12556 std::list<int> order;
12558 if (nodesEdges.size() > 0)
12560 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12561 nodesEdges[0] = -1;
12562 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12563 nodesEdges[1] = -1; // do not reuse this edge
12567 int nodeTofind = order.back(); // try first to push back
12569 for (i = 0; i<nodesEdges.size(); i++)
12570 if (nodesEdges[i] == nodeTofind)
12572 if (i == nodesEdges.size())
12573 found = false; // no follower found on back
12576 if (i%2) // odd ==> use the previous one
12577 if (nodesEdges[i-1] < 0)
12581 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12582 nodesEdges[i-1] = -1;
12584 else // even ==> use the next one
12585 if (nodesEdges[i+1] < 0)
12589 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12590 nodesEdges[i+1] = -1;
12595 // try to push front
12597 nodeTofind = order.front(); // try to push front
12598 for (i = 0; i<nodesEdges.size(); i++)
12599 if (nodesEdges[i] == nodeTofind)
12601 if (i == nodesEdges.size())
12603 found = false; // no predecessor found on front
12606 if (i%2) // odd ==> use the previous one
12607 if (nodesEdges[i-1] < 0)
12611 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12612 nodesEdges[i-1] = -1;
12614 else // even ==> use the next one
12615 if (nodesEdges[i+1] < 0)
12619 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12620 nodesEdges[i+1] = -1;
12626 std::vector<int> nodes;
12627 nodes.push_back(shapeId);
12628 std::list<int>::iterator itl = order.begin();
12629 for (; itl != order.end(); itl++)
12631 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12632 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12634 listOfListOfNodes.push_back(nodes);
12637 // partition geom faces with blocFissure
12638 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12639 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12645 //================================================================================
12647 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12648 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12649 * \return TRUE if operation has been completed successfully, FALSE otherwise
12651 //================================================================================
12653 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12655 // iterates on volume elements and detect all free faces on them
12656 SMESHDS_Mesh* aMesh = GetMeshDS();
12659 //bool res = false;
12660 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12661 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12664 const SMDS_MeshVolume* volume = vIt->next();
12665 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12666 vTool.SetExternalNormal();
12667 //const bool isPoly = volume->IsPoly();
12668 const int iQuad = volume->IsQuadratic();
12669 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12671 if (!vTool.IsFreeFace(iface))
12674 vector<const SMDS_MeshNode *> nodes;
12675 int nbFaceNodes = vTool.NbFaceNodes(iface);
12676 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12678 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12679 nodes.push_back(faceNodes[inode]);
12680 if (iQuad) { // add medium nodes
12681 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12682 nodes.push_back(faceNodes[inode]);
12683 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12684 nodes.push_back(faceNodes[8]);
12686 // add new face based on volume nodes
12687 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12689 continue; // face already exsist
12691 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12695 return ( nbFree==(nbExisted+nbCreated) );
12700 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12702 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12704 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12707 //================================================================================
12709 * \brief Creates missing boundary elements
12710 * \param elements - elements whose boundary is to be checked
12711 * \param dimension - defines type of boundary elements to create
12712 * \param group - a group to store created boundary elements in
12713 * \param targetMesh - a mesh to store created boundary elements in
12714 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12715 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12716 * boundary elements will be copied into the targetMesh
12717 * \param toAddExistingBondary - if true, not only new but also pre-existing
12718 * boundary elements will be added into the new group
12719 * \param aroundElements - if true, elements will be created on boundary of given
12720 * elements else, on boundary of the whole mesh.
12721 * \return nb of added boundary elements
12723 //================================================================================
12725 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12726 Bnd_Dimension dimension,
12727 SMESH_Group* group/*=0*/,
12728 SMESH_Mesh* targetMesh/*=0*/,
12729 bool toCopyElements/*=false*/,
12730 bool toCopyExistingBoundary/*=false*/,
12731 bool toAddExistingBondary/*= false*/,
12732 bool aroundElements/*= false*/)
12734 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12735 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12736 // hope that all elements are of the same type, do not check them all
12737 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12738 throw SALOME_Exception(LOCALIZED("wrong element type"));
12741 toCopyElements = toCopyExistingBoundary = false;
12743 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12744 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12745 int nbAddedBnd = 0;
12747 // editor adding present bnd elements and optionally holding elements to add to the group
12748 SMESH_MeshEditor* presentEditor;
12749 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12750 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12752 SMESH_MesherHelper helper( *myMesh );
12753 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12754 SMDS_VolumeTool vTool;
12755 TIDSortedElemSet avoidSet;
12756 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12759 typedef vector<const SMDS_MeshNode*> TConnectivity;
12761 SMDS_ElemIteratorPtr eIt;
12762 if (elements.empty())
12763 eIt = aMesh->elementsIterator(elemType);
12765 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12767 while (eIt->more())
12769 const SMDS_MeshElement* elem = eIt->next();
12770 const int iQuad = elem->IsQuadratic();
12772 // ------------------------------------------------------------------------------------
12773 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12774 // ------------------------------------------------------------------------------------
12775 vector<const SMDS_MeshElement*> presentBndElems;
12776 vector<TConnectivity> missingBndElems;
12777 TConnectivity nodes;
12778 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12780 vTool.SetExternalNormal();
12781 const SMDS_MeshElement* otherVol = 0;
12782 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12784 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12785 ( !aroundElements || elements.count( otherVol )))
12787 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12788 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12789 if ( missType == SMDSAbs_Edge ) // boundary edges
12791 nodes.resize( 2+iQuad );
12792 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12794 for ( int j = 0; j < nodes.size(); ++j )
12796 if ( const SMDS_MeshElement* edge =
12797 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12798 presentBndElems.push_back( edge );
12800 missingBndElems.push_back( nodes );
12803 else // boundary face
12806 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12807 nodes.push_back( nn[inode] );
12808 if (iQuad) // add medium nodes
12809 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12810 nodes.push_back( nn[inode] );
12811 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12813 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12815 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12816 SMDSAbs_Face, /*noMedium=*/false ))
12817 presentBndElems.push_back( f );
12819 missingBndElems.push_back( nodes );
12821 if ( targetMesh != myMesh )
12823 // add 1D elements on face boundary to be added to a new mesh
12824 const SMDS_MeshElement* edge;
12825 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12828 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12830 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12831 if ( edge && avoidSet.insert( edge ).second )
12832 presentBndElems.push_back( edge );
12838 else // elem is a face ------------------------------------------
12840 avoidSet.clear(), avoidSet.insert( elem );
12841 int nbNodes = elem->NbCornerNodes();
12842 nodes.resize( 2 /*+ iQuad*/);
12843 for ( int i = 0; i < nbNodes; i++ )
12845 nodes[0] = elem->GetNode(i);
12846 nodes[1] = elem->GetNode((i+1)%nbNodes);
12847 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12848 continue; // not free link
12851 //nodes[2] = elem->GetNode( i + nbNodes );
12852 if ( const SMDS_MeshElement* edge =
12853 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12854 presentBndElems.push_back( edge );
12856 missingBndElems.push_back( nodes );
12860 // ---------------------------------
12861 // 2. Add missing boundary elements
12862 // ---------------------------------
12863 if ( targetMesh != myMesh )
12864 // instead of making a map of nodes in this mesh and targetMesh,
12865 // we create nodes with same IDs.
12866 for ( int i = 0; i < missingBndElems.size(); ++i )
12868 TConnectivity& srcNodes = missingBndElems[i];
12869 TConnectivity nodes( srcNodes.size() );
12870 for ( inode = 0; inode < nodes.size(); ++inode )
12871 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12872 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12874 /*noMedium=*/false))
12876 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12880 for ( int i = 0; i < missingBndElems.size(); ++i )
12882 TConnectivity& nodes = missingBndElems[i];
12883 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12885 /*noMedium=*/false))
12887 SMDS_MeshElement* elem =
12888 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12891 // try to set a new element to a shape
12892 if ( myMesh->HasShapeToMesh() )
12895 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12896 const int nbN = nodes.size() / (iQuad+1 );
12897 for ( inode = 0; inode < nbN && ok; ++inode )
12899 pair<int, TopAbs_ShapeEnum> i_stype =
12900 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12901 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12902 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12904 if ( ok && mediumShapes.size() > 1 )
12906 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12907 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12908 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12910 if (( ok = ( stype_i->first != stype_i_0.first )))
12911 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12912 aMesh->IndexToShape( stype_i_0.second ));
12915 if ( ok && mediumShapes.begin()->first == missShapeType )
12916 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12920 // ----------------------------------
12921 // 3. Copy present boundary elements
12922 // ----------------------------------
12923 if ( toCopyExistingBoundary )
12924 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12926 const SMDS_MeshElement* e = presentBndElems[i];
12927 TConnectivity nodes( e->NbNodes() );
12928 for ( inode = 0; inode < nodes.size(); ++inode )
12929 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12930 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12932 else // store present elements to add them to a group
12933 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12935 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12938 } // loop on given elements
12940 // ---------------------------------------------
12941 // 4. Fill group with boundary elements
12942 // ---------------------------------------------
12945 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12946 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12947 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12949 tgtEditor.myLastCreatedElems.Clear();
12950 tgtEditor2.myLastCreatedElems.Clear();
12952 // -----------------------
12953 // 5. Copy given elements
12954 // -----------------------
12955 if ( toCopyElements && targetMesh != myMesh )
12957 if (elements.empty())
12958 eIt = aMesh->elementsIterator(elemType);
12960 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12961 while (eIt->more())
12963 const SMDS_MeshElement* elem = eIt->next();
12964 TConnectivity nodes( elem->NbNodes() );
12965 for ( inode = 0; inode < nodes.size(); ++inode )
12966 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12967 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12969 tgtEditor.myLastCreatedElems.Clear();