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).
11126 The replicated nodes should be associated to affected elements.
11127 \return groups of affected elements
11128 \sa DoubleNodeElemGroupsInRegion()
11130 //================================================================================
11132 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11133 const TIDSortedElemSet& theNodesNot,
11134 const TopoDS_Shape& theShape,
11135 TIDSortedElemSet& theAffectedElems)
11137 if ( theShape.IsNull() )
11140 const double aTol = Precision::Confusion();
11141 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11142 auto_ptr<_FaceClassifier> aFaceClassifier;
11143 if ( theShape.ShapeType() == TopAbs_SOLID )
11145 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11146 bsc3d->PerformInfinitePoint(aTol);
11148 else if (theShape.ShapeType() == TopAbs_FACE )
11150 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11153 // iterates on indicated elements and get elements by back references from their nodes
11154 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11155 for ( ; elemItr != theElems.end(); ++elemItr )
11157 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11161 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11162 while ( nodeItr->more() )
11164 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11165 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11167 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11168 while ( backElemItr->more() )
11170 const SMDS_MeshElement* curElem = backElemItr->next();
11171 if ( curElem && theElems.find(curElem) == theElems.end() &&
11173 isInside( curElem, *bsc3d, aTol ) :
11174 isInside( curElem, *aFaceClassifier, aTol )))
11175 theAffectedElems.insert( curElem );
11182 //================================================================================
11184 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11185 \param theElems - group of of elements (edges or faces) to be replicated
11186 \param theNodesNot - group of nodes not to replicate
11187 \param theShape - shape to detect affected elements (element which geometric center
11188 located on or inside shape).
11189 The replicated nodes should be associated to affected elements.
11190 \return TRUE if operation has been completed successfully, FALSE otherwise
11192 //================================================================================
11194 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11195 const TIDSortedElemSet& theNodesNot,
11196 const TopoDS_Shape& theShape )
11198 if ( theShape.IsNull() )
11201 const double aTol = Precision::Confusion();
11202 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11203 auto_ptr<_FaceClassifier> aFaceClassifier;
11204 if ( theShape.ShapeType() == TopAbs_SOLID )
11206 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11207 bsc3d->PerformInfinitePoint(aTol);
11209 else if (theShape.ShapeType() == TopAbs_FACE )
11211 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11214 // iterates on indicated elements and get elements by back references from their nodes
11215 TIDSortedElemSet anAffected;
11216 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11217 for ( ; elemItr != theElems.end(); ++elemItr )
11219 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11223 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11224 while ( nodeItr->more() )
11226 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11227 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11229 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11230 while ( backElemItr->more() )
11232 const SMDS_MeshElement* curElem = backElemItr->next();
11233 if ( curElem && theElems.find(curElem) == theElems.end() &&
11235 isInside( curElem, *bsc3d, aTol ) :
11236 isInside( curElem, *aFaceClassifier, aTol )))
11237 anAffected.insert( curElem );
11241 return DoubleNodes( theElems, theNodesNot, anAffected );
11245 * \brief compute an oriented angle between two planes defined by four points.
11246 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11247 * @param p0 base of the rotation axe
11248 * @param p1 extremity of the rotation axe
11249 * @param g1 belongs to the first plane
11250 * @param g2 belongs to the second plane
11252 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11254 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11255 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11256 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11257 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11258 gp_Vec vref(p0, p1);
11261 gp_Vec n1 = vref.Crossed(v1);
11262 gp_Vec n2 = vref.Crossed(v2);
11263 return n2.AngleWithRef(n1, vref);
11267 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11268 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11269 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11270 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11271 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11272 * 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.
11273 * 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.
11274 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11275 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11276 * @param theElems - list of groups of volumes, where a group of volume is a set of
11277 * SMDS_MeshElements sorted by Id.
11278 * @param createJointElems - if TRUE, create the elements
11279 * @return TRUE if operation has been completed successfully, FALSE otherwise
11281 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11282 bool createJointElems)
11284 MESSAGE("----------------------------------------------");
11285 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11286 MESSAGE("----------------------------------------------");
11288 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11289 meshDS->BuildDownWardConnectivity(true);
11291 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11293 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11294 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11295 // build the list of nodes shared by 2 or more domains, with their domain indexes
11297 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11298 std::map<int,int>celldom; // cell vtkId --> domain
11299 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11300 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11301 faceDomains.clear();
11303 cellDomains.clear();
11304 nodeDomains.clear();
11305 std::map<int,int> emptyMap;
11306 std::set<int> emptySet;
11309 MESSAGE(".. Number of domains :"<<theElems.size());
11311 // Check if the domains do not share an element
11312 for (int idom = 0; idom < theElems.size()-1; idom++)
11314 // MESSAGE("... Check of domain #" << idom);
11315 const TIDSortedElemSet& domain = theElems[idom];
11316 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11317 for (; elemItr != domain.end(); ++elemItr)
11319 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11320 int idombisdeb = idom + 1 ;
11321 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11323 const TIDSortedElemSet& domainbis = theElems[idombis];
11324 if ( domainbis.count(anElem) )
11326 MESSAGE(".... Domain #" << idom);
11327 MESSAGE(".... Domain #" << idombis);
11328 throw SALOME_Exception("The domains are not disjoint.");
11335 for (int idom = 0; idom < theElems.size(); idom++)
11338 // --- build a map (face to duplicate --> volume to modify)
11339 // with all the faces shared by 2 domains (group of elements)
11340 // and corresponding volume of this domain, for each shared face.
11341 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11343 MESSAGE("... Neighbors of domain #" << idom);
11344 const TIDSortedElemSet& domain = theElems[idom];
11345 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11346 for (; elemItr != domain.end(); ++elemItr)
11348 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11351 int vtkId = anElem->getVtkId();
11352 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11353 int neighborsVtkIds[NBMAXNEIGHBORS];
11354 int downIds[NBMAXNEIGHBORS];
11355 unsigned char downTypes[NBMAXNEIGHBORS];
11356 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11357 for (int n = 0; n < nbNeighbors; n++)
11359 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11360 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11361 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11364 for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
11366 // MESSAGE("Domain " << idombis);
11367 const TIDSortedElemSet& domainbis = theElems[idombis];
11368 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11370 if ( ok ) // the characteristics of the face is stored
11372 DownIdType face(downIds[n], downTypes[n]);
11373 if (!faceDomains.count(face))
11374 faceDomains[face] = emptyMap; // create an empty entry for face
11375 if (!faceDomains[face].count(idom))
11377 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11378 celldom[vtkId] = idom;
11379 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11387 //MESSAGE("Number of shared faces " << faceDomains.size());
11388 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11390 // --- explore the shared faces domain by domain,
11391 // explore the nodes of the face and see if they belong to a cell in the domain,
11392 // which has only a node or an edge on the border (not a shared face)
11394 for (int idomain = 0; idomain < theElems.size(); idomain++)
11396 //MESSAGE("Domain " << idomain);
11397 const TIDSortedElemSet& domain = theElems[idomain];
11398 itface = faceDomains.begin();
11399 for (; itface != faceDomains.end(); ++itface)
11401 std::map<int, int> domvol = itface->second;
11402 if (!domvol.count(idomain))
11404 DownIdType face = itface->first;
11405 //MESSAGE(" --- face " << face.cellId);
11406 std::set<int> oldNodes;
11408 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11409 std::set<int>::iterator itn = oldNodes.begin();
11410 for (; itn != oldNodes.end(); ++itn)
11413 //MESSAGE(" node " << oldId);
11414 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11415 for (int i=0; i<l.ncells; i++)
11417 int vtkId = l.cells[i];
11418 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11419 if (!domain.count(anElem))
11421 int vtkType = grid->GetCellType(vtkId);
11422 int downId = grid->CellIdToDownId(vtkId);
11425 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11426 continue; // not OK at this stage of the algorithm:
11427 //no cells created after BuildDownWardConnectivity
11429 DownIdType aCell(downId, vtkType);
11430 if (!cellDomains.count(aCell))
11431 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11432 cellDomains[aCell][idomain] = vtkId;
11433 celldom[vtkId] = idomain;
11434 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11440 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11441 // for each shared face, get the nodes
11442 // for each node, for each domain of the face, create a clone of the node
11444 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11445 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11446 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11448 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11449 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11450 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11452 MESSAGE(".. Duplication of the nodes");
11453 for (int idomain = 0; idomain < theElems.size(); idomain++)
11455 itface = faceDomains.begin();
11456 for (; itface != faceDomains.end(); ++itface)
11458 std::map<int, int> domvol = itface->second;
11459 if (!domvol.count(idomain))
11461 DownIdType face = itface->first;
11462 //MESSAGE(" --- face " << face.cellId);
11463 std::set<int> oldNodes;
11465 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11466 std::set<int>::iterator itn = oldNodes.begin();
11467 for (; itn != oldNodes.end(); ++itn)
11470 //MESSAGE("-+-+-a node " << oldId);
11471 if (!nodeDomains.count(oldId))
11472 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11473 if (nodeDomains[oldId].empty())
11475 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11476 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11478 std::map<int, int>::iterator itdom = domvol.begin();
11479 for (; itdom != domvol.end(); ++itdom)
11481 int idom = itdom->first;
11482 //MESSAGE(" domain " << idom);
11483 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11485 if (nodeDomains[oldId].size() >= 2) // a multiple node
11487 vector<int> orderedDoms;
11488 //MESSAGE("multiple node " << oldId);
11489 if (mutipleNodes.count(oldId))
11490 orderedDoms = mutipleNodes[oldId];
11493 map<int,int>::iterator it = nodeDomains[oldId].begin();
11494 for (; it != nodeDomains[oldId].end(); ++it)
11495 orderedDoms.push_back(it->first);
11497 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11498 //stringstream txt;
11499 //for (int i=0; i<orderedDoms.size(); i++)
11500 // txt << orderedDoms[i] << " ";
11501 //MESSAGE("orderedDoms " << txt.str());
11502 mutipleNodes[oldId] = orderedDoms;
11504 double *coords = grid->GetPoint(oldId);
11505 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11506 int newId = newNode->getVtkId();
11507 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11508 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11515 MESSAGE(".. Creation of elements");
11516 for (int idomain = 0; idomain < theElems.size(); idomain++)
11518 itface = faceDomains.begin();
11519 for (; itface != faceDomains.end(); ++itface)
11521 std::map<int, int> domvol = itface->second;
11522 if (!domvol.count(idomain))
11524 DownIdType face = itface->first;
11525 //MESSAGE(" --- face " << face.cellId);
11526 std::set<int> oldNodes;
11528 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11529 int nbMultipleNodes = 0;
11530 std::set<int>::iterator itn = oldNodes.begin();
11531 for (; itn != oldNodes.end(); ++itn)
11534 if (mutipleNodes.count(oldId))
11537 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11539 //MESSAGE("multiple Nodes detected on a shared face");
11540 int downId = itface->first.cellId;
11541 unsigned char cellType = itface->first.cellType;
11542 // --- shared edge or shared face ?
11543 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11546 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11547 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11548 if (mutipleNodes.count(nodes[i]))
11549 if (!mutipleNodesToFace.count(nodes[i]))
11550 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11552 else // shared face (between two volumes)
11554 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11555 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11556 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11557 for (int ie =0; ie < nbEdges; ie++)
11560 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11561 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11563 vector<int> vn0 = mutipleNodes[nodes[0]];
11564 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11566 for (int i0 = 0; i0 < vn0.size(); i0++)
11567 for (int i1 = 0; i1 < vn1.size(); i1++)
11568 if (vn0[i0] == vn1[i1])
11569 doms.push_back(vn0[i0]);
11570 if (doms.size() >2)
11572 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11573 double *coords = grid->GetPoint(nodes[0]);
11574 gp_Pnt p0(coords[0], coords[1], coords[2]);
11575 coords = grid->GetPoint(nodes[nbNodes - 1]);
11576 gp_Pnt p1(coords[0], coords[1], coords[2]);
11578 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11579 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11580 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11581 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11582 for (int id=0; id < doms.size(); id++)
11584 int idom = doms[id];
11585 for (int ivol=0; ivol<nbvol; ivol++)
11587 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11588 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11589 if (theElems[idom].count(elem))
11591 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11592 domvol[idom] = svol;
11593 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11595 vtkIdType npts = 0;
11596 vtkIdType* pts = 0;
11597 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11598 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11601 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11602 angleDom[idom] = 0;
11606 gp_Pnt g(values[0], values[1], values[2]);
11607 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11608 //MESSAGE(" angle=" << angleDom[idom]);
11614 map<double, int> sortedDom; // sort domains by angle
11615 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11616 sortedDom[ia->second] = ia->first;
11617 vector<int> vnodes;
11619 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11621 vdom.push_back(ib->second);
11622 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11624 for (int ino = 0; ino < nbNodes; ino++)
11625 vnodes.push_back(nodes[ino]);
11626 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11635 // --- iterate on shared faces (volumes to modify, face to extrude)
11636 // get node id's of the face (id SMDS = id VTK)
11637 // create flat element with old and new nodes if requested
11639 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11640 // (domain1 X domain2) = domain1 + MAXINT*domain2
11642 std::map<int, std::map<long,int> > nodeQuadDomains;
11643 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11645 MESSAGE(".. Creation of elements: simple junction");
11646 if (createJointElems)
11649 string joints2DName = "joints2D";
11650 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11651 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11652 string joints3DName = "joints3D";
11653 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11654 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11656 itface = faceDomains.begin();
11657 for (; itface != faceDomains.end(); ++itface)
11659 DownIdType face = itface->first;
11660 std::set<int> oldNodes;
11661 std::set<int>::iterator itn;
11663 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11665 std::map<int, int> domvol = itface->second;
11666 std::map<int, int>::iterator itdom = domvol.begin();
11667 int dom1 = itdom->first;
11668 int vtkVolId = itdom->second;
11670 int dom2 = itdom->first;
11671 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11673 stringstream grpname;
11676 grpname << dom1 << "_" << dom2;
11678 grpname << dom2 << "_" << dom1;
11679 string namegrp = grpname.str();
11680 if (!mapOfJunctionGroups.count(namegrp))
11681 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11682 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11684 sgrp->Add(vol->GetID());
11685 if (vol->GetType() == SMDSAbs_Volume)
11686 joints3DGrp->Add(vol->GetID());
11687 else if (vol->GetType() == SMDSAbs_Face)
11688 joints2DGrp->Add(vol->GetID());
11692 // --- create volumes on multiple domain intersection if requested
11693 // iterate on mutipleNodesToFace
11694 // iterate on edgesMultiDomains
11696 MESSAGE(".. Creation of elements: multiple junction");
11697 if (createJointElems)
11699 // --- iterate on mutipleNodesToFace
11701 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11702 for (; itn != mutipleNodesToFace.end(); ++itn)
11704 int node = itn->first;
11705 vector<int> orderDom = itn->second;
11706 vector<vtkIdType> orderedNodes;
11707 for (int idom = 0; idom <orderDom.size(); idom++)
11708 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11709 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11711 stringstream grpname;
11713 grpname << 0 << "_" << 0;
11715 string namegrp = grpname.str();
11716 if (!mapOfJunctionGroups.count(namegrp))
11717 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11718 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11720 sgrp->Add(face->GetID());
11723 // --- iterate on edgesMultiDomains
11725 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11726 for (; ite != edgesMultiDomains.end(); ++ite)
11728 vector<int> nodes = ite->first;
11729 vector<int> orderDom = ite->second;
11730 vector<vtkIdType> orderedNodes;
11731 if (nodes.size() == 2)
11733 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11734 for (int ino=0; ino < nodes.size(); ino++)
11735 if (orderDom.size() == 3)
11736 for (int idom = 0; idom <orderDom.size(); idom++)
11737 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11739 for (int idom = orderDom.size()-1; idom >=0; idom--)
11740 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11741 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11744 string namegrp = "jointsMultiples";
11745 if (!mapOfJunctionGroups.count(namegrp))
11746 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11747 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11749 sgrp->Add(vol->GetID());
11753 INFOS("Quadratic multiple joints not implemented");
11754 // TODO quadratic nodes
11759 // --- list the explicit faces and edges of the mesh that need to be modified,
11760 // i.e. faces and edges built with one or more duplicated nodes.
11761 // associate these faces or edges to their corresponding domain.
11762 // only the first domain found is kept when a face or edge is shared
11764 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11765 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11766 faceOrEdgeDom.clear();
11769 MESSAGE(".. Modification of elements");
11770 for (int idomain = 0; idomain < theElems.size(); idomain++)
11772 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11773 for (; itnod != nodeDomains.end(); ++itnod)
11775 int oldId = itnod->first;
11776 //MESSAGE(" node " << oldId);
11777 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11778 for (int i = 0; i < l.ncells; i++)
11780 int vtkId = l.cells[i];
11781 int vtkType = grid->GetCellType(vtkId);
11782 int downId = grid->CellIdToDownId(vtkId);
11784 continue; // new cells: not to be modified
11785 DownIdType aCell(downId, vtkType);
11786 int volParents[1000];
11787 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11788 for (int j = 0; j < nbvol; j++)
11789 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11790 if (!feDom.count(vtkId))
11792 feDom[vtkId] = idomain;
11793 faceOrEdgeDom[aCell] = emptyMap;
11794 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11795 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11796 // << " type " << vtkType << " downId " << downId);
11802 // --- iterate on shared faces (volumes to modify, face to extrude)
11803 // get node id's of the face
11804 // replace old nodes by new nodes in volumes, and update inverse connectivity
11806 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11807 for (int m=0; m<3; m++)
11809 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11810 itface = (*amap).begin();
11811 for (; itface != (*amap).end(); ++itface)
11813 DownIdType face = itface->first;
11814 std::set<int> oldNodes;
11815 std::set<int>::iterator itn;
11817 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11818 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11819 std::map<int, int> localClonedNodeIds;
11821 std::map<int, int> domvol = itface->second;
11822 std::map<int, int>::iterator itdom = domvol.begin();
11823 for (; itdom != domvol.end(); ++itdom)
11825 int idom = itdom->first;
11826 int vtkVolId = itdom->second;
11827 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11828 localClonedNodeIds.clear();
11829 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11832 if (nodeDomains[oldId].count(idom))
11834 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11835 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11838 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11843 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11844 grid->BuildLinks();
11852 * \brief Double nodes on some external faces and create flat elements.
11853 * Flat elements are mainly used by some types of mechanic calculations.
11855 * Each group of the list must be constituted of faces.
11856 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11857 * @param theElems - list of groups of faces, where a group of faces is a set of
11858 * SMDS_MeshElements sorted by Id.
11859 * @return TRUE if operation has been completed successfully, FALSE otherwise
11861 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11863 MESSAGE("-------------------------------------------------");
11864 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11865 MESSAGE("-------------------------------------------------");
11867 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11869 // --- For each group of faces
11870 // duplicate the nodes, create a flat element based on the face
11871 // replace the nodes of the faces by their clones
11873 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11874 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11875 clonedNodes.clear();
11876 intermediateNodes.clear();
11877 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11878 mapOfJunctionGroups.clear();
11880 for (int idom = 0; idom < theElems.size(); idom++)
11882 const TIDSortedElemSet& domain = theElems[idom];
11883 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11884 for (; elemItr != domain.end(); ++elemItr)
11886 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11887 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11890 // MESSAGE("aFace=" << aFace->GetID());
11891 bool isQuad = aFace->IsQuadratic();
11892 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11894 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11896 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11897 while (nodeIt->more())
11899 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11900 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11902 ln2.push_back(node);
11904 ln0.push_back(node);
11906 const SMDS_MeshNode* clone = 0;
11907 if (!clonedNodes.count(node))
11909 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11910 clonedNodes[node] = clone;
11913 clone = clonedNodes[node];
11916 ln3.push_back(clone);
11918 ln1.push_back(clone);
11920 const SMDS_MeshNode* inter = 0;
11921 if (isQuad && (!isMedium))
11923 if (!intermediateNodes.count(node))
11925 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11926 intermediateNodes[node] = inter;
11929 inter = intermediateNodes[node];
11930 ln4.push_back(inter);
11934 // --- extrude the face
11936 vector<const SMDS_MeshNode*> ln;
11937 SMDS_MeshVolume* vol = 0;
11938 vtkIdType aType = aFace->GetVtkType();
11942 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11943 // MESSAGE("vol prism " << vol->GetID());
11944 ln.push_back(ln1[0]);
11945 ln.push_back(ln1[1]);
11946 ln.push_back(ln1[2]);
11949 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11950 // MESSAGE("vol hexa " << vol->GetID());
11951 ln.push_back(ln1[0]);
11952 ln.push_back(ln1[1]);
11953 ln.push_back(ln1[2]);
11954 ln.push_back(ln1[3]);
11956 case VTK_QUADRATIC_TRIANGLE:
11957 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11958 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11959 // MESSAGE("vol quad prism " << vol->GetID());
11960 ln.push_back(ln1[0]);
11961 ln.push_back(ln1[1]);
11962 ln.push_back(ln1[2]);
11963 ln.push_back(ln3[0]);
11964 ln.push_back(ln3[1]);
11965 ln.push_back(ln3[2]);
11967 case VTK_QUADRATIC_QUAD:
11968 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11969 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11970 // ln4[0], ln4[1], ln4[2], ln4[3]);
11971 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11972 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11973 ln4[0], ln4[1], ln4[2], ln4[3]);
11974 // MESSAGE("vol quad hexa " << vol->GetID());
11975 ln.push_back(ln1[0]);
11976 ln.push_back(ln1[1]);
11977 ln.push_back(ln1[2]);
11978 ln.push_back(ln1[3]);
11979 ln.push_back(ln3[0]);
11980 ln.push_back(ln3[1]);
11981 ln.push_back(ln3[2]);
11982 ln.push_back(ln3[3]);
11992 stringstream grpname;
11996 string namegrp = grpname.str();
11997 if (!mapOfJunctionGroups.count(namegrp))
11998 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11999 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12001 sgrp->Add(vol->GetID());
12004 // --- modify the face
12006 aFace->ChangeNodes(&ln[0], ln.size());
12013 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12014 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12015 * groups of faces to remove inside the object, (idem edges).
12016 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12018 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12019 const TopoDS_Shape& theShape,
12020 SMESH_NodeSearcher* theNodeSearcher,
12021 const char* groupName,
12022 std::vector<double>& nodesCoords,
12023 std::vector<std::vector<int> >& listOfListOfNodes)
12025 MESSAGE("--------------------------------");
12026 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12027 MESSAGE("--------------------------------");
12029 // --- zone of volumes to remove is given :
12030 // 1 either by a geom shape (one or more vertices) and a radius,
12031 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12032 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12033 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12034 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12035 // defined by it's name.
12037 SMESHDS_GroupBase* groupDS = 0;
12038 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12039 while ( groupIt->more() )
12042 SMESH_Group * group = groupIt->next();
12043 if ( !group ) continue;
12044 groupDS = group->GetGroupDS();
12045 if ( !groupDS || groupDS->IsEmpty() ) continue;
12046 std::string grpName = group->GetName();
12047 //MESSAGE("grpName=" << grpName);
12048 if (grpName == groupName)
12054 bool isNodeGroup = false;
12055 bool isNodeCoords = false;
12058 if (groupDS->GetType() != SMDSAbs_Node)
12060 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12063 if (nodesCoords.size() > 0)
12064 isNodeCoords = true; // a list o nodes given by their coordinates
12065 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12067 // --- define groups to build
12069 int idg; // --- group of SMDS volumes
12070 string grpvName = groupName;
12071 grpvName += "_vol";
12072 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12075 MESSAGE("group not created " << grpvName);
12078 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12080 int idgs; // --- group of SMDS faces on the skin
12081 string grpsName = groupName;
12082 grpsName += "_skin";
12083 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12086 MESSAGE("group not created " << grpsName);
12089 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12091 int idgi; // --- group of SMDS faces internal (several shapes)
12092 string grpiName = groupName;
12093 grpiName += "_internalFaces";
12094 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12097 MESSAGE("group not created " << grpiName);
12100 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12102 int idgei; // --- group of SMDS faces internal (several shapes)
12103 string grpeiName = groupName;
12104 grpeiName += "_internalEdges";
12105 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12108 MESSAGE("group not created " << grpeiName);
12111 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12113 // --- build downward connectivity
12115 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12116 meshDS->BuildDownWardConnectivity(true);
12117 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12119 // --- set of volumes detected inside
12121 std::set<int> setOfInsideVol;
12122 std::set<int> setOfVolToCheck;
12124 std::vector<gp_Pnt> gpnts;
12127 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12129 MESSAGE("group of nodes provided");
12130 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12131 while ( elemIt->more() )
12133 const SMDS_MeshElement* elem = elemIt->next();
12136 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12139 SMDS_MeshElement* vol = 0;
12140 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12141 while (volItr->more())
12143 vol = (SMDS_MeshElement*)volItr->next();
12144 setOfInsideVol.insert(vol->getVtkId());
12145 sgrp->Add(vol->GetID());
12149 else if (isNodeCoords)
12151 MESSAGE("list of nodes coordinates provided");
12154 while (i < nodesCoords.size()-2)
12156 double x = nodesCoords[i++];
12157 double y = nodesCoords[i++];
12158 double z = nodesCoords[i++];
12159 gp_Pnt p = gp_Pnt(x, y ,z);
12160 gpnts.push_back(p);
12161 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12164 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12166 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12167 TopTools_IndexedMapOfShape vertexMap;
12168 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12169 gp_Pnt p = gp_Pnt(0,0,0);
12170 if (vertexMap.Extent() < 1)
12173 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12175 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12176 p = BRep_Tool::Pnt(vertex);
12177 gpnts.push_back(p);
12178 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12182 if (gpnts.size() > 0)
12185 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12187 nodeId = startNode->GetID();
12188 MESSAGE("nodeId " << nodeId);
12190 double radius2 = radius*radius;
12191 MESSAGE("radius2 " << radius2);
12193 // --- volumes on start node
12195 setOfVolToCheck.clear();
12196 SMDS_MeshElement* startVol = 0;
12197 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12198 while (volItr->more())
12200 startVol = (SMDS_MeshElement*)volItr->next();
12201 setOfVolToCheck.insert(startVol->getVtkId());
12203 if (setOfVolToCheck.empty())
12205 MESSAGE("No volumes found");
12209 // --- starting with central volumes then their neighbors, check if they are inside
12210 // or outside the domain, until no more new neighbor volume is inside.
12211 // Fill the group of inside volumes
12213 std::map<int, double> mapOfNodeDistance2;
12214 mapOfNodeDistance2.clear();
12215 std::set<int> setOfOutsideVol;
12216 while (!setOfVolToCheck.empty())
12218 std::set<int>::iterator it = setOfVolToCheck.begin();
12220 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12221 bool volInside = false;
12222 vtkIdType npts = 0;
12223 vtkIdType* pts = 0;
12224 grid->GetCellPoints(vtkId, npts, pts);
12225 for (int i=0; i<npts; i++)
12227 double distance2 = 0;
12228 if (mapOfNodeDistance2.count(pts[i]))
12230 distance2 = mapOfNodeDistance2[pts[i]];
12231 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12235 double *coords = grid->GetPoint(pts[i]);
12236 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12238 for (int j=0; j<gpnts.size(); j++)
12240 double d2 = aPoint.SquareDistance(gpnts[j]);
12241 if (d2 < distance2)
12244 if (distance2 < radius2)
12248 mapOfNodeDistance2[pts[i]] = distance2;
12249 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12251 if (distance2 < radius2)
12253 volInside = true; // one or more nodes inside the domain
12254 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12260 setOfInsideVol.insert(vtkId);
12261 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12262 int neighborsVtkIds[NBMAXNEIGHBORS];
12263 int downIds[NBMAXNEIGHBORS];
12264 unsigned char downTypes[NBMAXNEIGHBORS];
12265 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12266 for (int n = 0; n < nbNeighbors; n++)
12267 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12268 setOfVolToCheck.insert(neighborsVtkIds[n]);
12272 setOfOutsideVol.insert(vtkId);
12273 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12275 setOfVolToCheck.erase(vtkId);
12279 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12280 // If yes, add the volume to the inside set
12282 bool addedInside = true;
12283 std::set<int> setOfVolToReCheck;
12284 while (addedInside)
12286 MESSAGE(" --------------------------- re check");
12287 addedInside = false;
12288 std::set<int>::iterator itv = setOfInsideVol.begin();
12289 for (; itv != setOfInsideVol.end(); ++itv)
12292 int neighborsVtkIds[NBMAXNEIGHBORS];
12293 int downIds[NBMAXNEIGHBORS];
12294 unsigned char downTypes[NBMAXNEIGHBORS];
12295 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12296 for (int n = 0; n < nbNeighbors; n++)
12297 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12298 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12300 setOfVolToCheck = setOfVolToReCheck;
12301 setOfVolToReCheck.clear();
12302 while (!setOfVolToCheck.empty())
12304 std::set<int>::iterator it = setOfVolToCheck.begin();
12306 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12308 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12309 int countInside = 0;
12310 int neighborsVtkIds[NBMAXNEIGHBORS];
12311 int downIds[NBMAXNEIGHBORS];
12312 unsigned char downTypes[NBMAXNEIGHBORS];
12313 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12314 for (int n = 0; n < nbNeighbors; n++)
12315 if (setOfInsideVol.count(neighborsVtkIds[n]))
12317 MESSAGE("countInside " << countInside);
12318 if (countInside > 1)
12320 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12321 setOfInsideVol.insert(vtkId);
12322 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12323 addedInside = true;
12326 setOfVolToReCheck.insert(vtkId);
12328 setOfVolToCheck.erase(vtkId);
12332 // --- map of Downward faces at the boundary, inside the global volume
12333 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12334 // fill group of SMDS faces inside the volume (when several volume shapes)
12335 // fill group of SMDS faces on the skin of the global volume (if skin)
12337 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12338 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12339 std::set<int>::iterator it = setOfInsideVol.begin();
12340 for (; it != setOfInsideVol.end(); ++it)
12343 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12344 int neighborsVtkIds[NBMAXNEIGHBORS];
12345 int downIds[NBMAXNEIGHBORS];
12346 unsigned char downTypes[NBMAXNEIGHBORS];
12347 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12348 for (int n = 0; n < nbNeighbors; n++)
12350 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12351 if (neighborDim == 3)
12353 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12355 DownIdType face(downIds[n], downTypes[n]);
12356 boundaryFaces[face] = vtkId;
12358 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12359 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12360 if (vtkFaceId >= 0)
12362 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12363 // find also the smds edges on this face
12364 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12365 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12366 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12367 for (int i = 0; i < nbEdges; i++)
12369 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12370 if (vtkEdgeId >= 0)
12371 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12375 else if (neighborDim == 2) // skin of the volume
12377 DownIdType face(downIds[n], downTypes[n]);
12378 skinFaces[face] = vtkId;
12379 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12380 if (vtkFaceId >= 0)
12381 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12386 // --- identify the edges constituting the wire of each subshape on the skin
12387 // define polylines with the nodes of edges, equivalent to wires
12388 // project polylines on subshapes, and partition, to get geom faces
12390 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12391 std::set<int> emptySet;
12393 std::set<int> shapeIds;
12395 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12396 while (itelem->more())
12398 const SMDS_MeshElement *elem = itelem->next();
12399 int shapeId = elem->getshapeId();
12400 int vtkId = elem->getVtkId();
12401 if (!shapeIdToVtkIdSet.count(shapeId))
12403 shapeIdToVtkIdSet[shapeId] = emptySet;
12404 shapeIds.insert(shapeId);
12406 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12409 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12410 std::set<DownIdType, DownIdCompare> emptyEdges;
12411 emptyEdges.clear();
12413 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12414 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12416 int shapeId = itShape->first;
12417 MESSAGE(" --- Shape ID --- "<< shapeId);
12418 shapeIdToEdges[shapeId] = emptyEdges;
12420 std::vector<int> nodesEdges;
12422 std::set<int>::iterator its = itShape->second.begin();
12423 for (; its != itShape->second.end(); ++its)
12426 MESSAGE(" " << vtkId);
12427 int neighborsVtkIds[NBMAXNEIGHBORS];
12428 int downIds[NBMAXNEIGHBORS];
12429 unsigned char downTypes[NBMAXNEIGHBORS];
12430 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12431 for (int n = 0; n < nbNeighbors; n++)
12433 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12435 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12436 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12437 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12439 DownIdType edge(downIds[n], downTypes[n]);
12440 if (!shapeIdToEdges[shapeId].count(edge))
12442 shapeIdToEdges[shapeId].insert(edge);
12444 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12445 nodesEdges.push_back(vtkNodeId[0]);
12446 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12447 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12453 std::list<int> order;
12455 if (nodesEdges.size() > 0)
12457 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12458 nodesEdges[0] = -1;
12459 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12460 nodesEdges[1] = -1; // do not reuse this edge
12464 int nodeTofind = order.back(); // try first to push back
12466 for (i = 0; i<nodesEdges.size(); i++)
12467 if (nodesEdges[i] == nodeTofind)
12469 if (i == nodesEdges.size())
12470 found = false; // no follower found on back
12473 if (i%2) // odd ==> use the previous one
12474 if (nodesEdges[i-1] < 0)
12478 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12479 nodesEdges[i-1] = -1;
12481 else // even ==> use the next one
12482 if (nodesEdges[i+1] < 0)
12486 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12487 nodesEdges[i+1] = -1;
12492 // try to push front
12494 nodeTofind = order.front(); // try to push front
12495 for (i = 0; i<nodesEdges.size(); i++)
12496 if (nodesEdges[i] == nodeTofind)
12498 if (i == nodesEdges.size())
12500 found = false; // no predecessor found on front
12503 if (i%2) // odd ==> use the previous one
12504 if (nodesEdges[i-1] < 0)
12508 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12509 nodesEdges[i-1] = -1;
12511 else // even ==> use the next one
12512 if (nodesEdges[i+1] < 0)
12516 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12517 nodesEdges[i+1] = -1;
12523 std::vector<int> nodes;
12524 nodes.push_back(shapeId);
12525 std::list<int>::iterator itl = order.begin();
12526 for (; itl != order.end(); itl++)
12528 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12529 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12531 listOfListOfNodes.push_back(nodes);
12534 // partition geom faces with blocFissure
12535 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12536 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12542 //================================================================================
12544 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12545 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12546 * \return TRUE if operation has been completed successfully, FALSE otherwise
12548 //================================================================================
12550 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12552 // iterates on volume elements and detect all free faces on them
12553 SMESHDS_Mesh* aMesh = GetMeshDS();
12556 //bool res = false;
12557 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12558 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12561 const SMDS_MeshVolume* volume = vIt->next();
12562 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12563 vTool.SetExternalNormal();
12564 //const bool isPoly = volume->IsPoly();
12565 const int iQuad = volume->IsQuadratic();
12566 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12568 if (!vTool.IsFreeFace(iface))
12571 vector<const SMDS_MeshNode *> nodes;
12572 int nbFaceNodes = vTool.NbFaceNodes(iface);
12573 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12575 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12576 nodes.push_back(faceNodes[inode]);
12577 if (iQuad) { // add medium nodes
12578 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12579 nodes.push_back(faceNodes[inode]);
12580 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12581 nodes.push_back(faceNodes[8]);
12583 // add new face based on volume nodes
12584 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12586 continue; // face already exsist
12588 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12592 return ( nbFree==(nbExisted+nbCreated) );
12597 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12599 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12601 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12604 //================================================================================
12606 * \brief Creates missing boundary elements
12607 * \param elements - elements whose boundary is to be checked
12608 * \param dimension - defines type of boundary elements to create
12609 * \param group - a group to store created boundary elements in
12610 * \param targetMesh - a mesh to store created boundary elements in
12611 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12612 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12613 * boundary elements will be copied into the targetMesh
12614 * \param toAddExistingBondary - if true, not only new but also pre-existing
12615 * boundary elements will be added into the new group
12616 * \param aroundElements - if true, elements will be created on boundary of given
12617 * elements else, on boundary of the whole mesh.
12618 * \return nb of added boundary elements
12620 //================================================================================
12622 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12623 Bnd_Dimension dimension,
12624 SMESH_Group* group/*=0*/,
12625 SMESH_Mesh* targetMesh/*=0*/,
12626 bool toCopyElements/*=false*/,
12627 bool toCopyExistingBoundary/*=false*/,
12628 bool toAddExistingBondary/*= false*/,
12629 bool aroundElements/*= false*/)
12631 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12632 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12633 // hope that all elements are of the same type, do not check them all
12634 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12635 throw SALOME_Exception(LOCALIZED("wrong element type"));
12638 toCopyElements = toCopyExistingBoundary = false;
12640 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12641 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12642 int nbAddedBnd = 0;
12644 // editor adding present bnd elements and optionally holding elements to add to the group
12645 SMESH_MeshEditor* presentEditor;
12646 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12647 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12649 SMESH_MesherHelper helper( *myMesh );
12650 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12651 SMDS_VolumeTool vTool;
12652 TIDSortedElemSet avoidSet;
12653 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12656 typedef vector<const SMDS_MeshNode*> TConnectivity;
12658 SMDS_ElemIteratorPtr eIt;
12659 if (elements.empty())
12660 eIt = aMesh->elementsIterator(elemType);
12662 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12664 while (eIt->more())
12666 const SMDS_MeshElement* elem = eIt->next();
12667 const int iQuad = elem->IsQuadratic();
12669 // ------------------------------------------------------------------------------------
12670 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12671 // ------------------------------------------------------------------------------------
12672 vector<const SMDS_MeshElement*> presentBndElems;
12673 vector<TConnectivity> missingBndElems;
12674 TConnectivity nodes;
12675 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12677 vTool.SetExternalNormal();
12678 const SMDS_MeshElement* otherVol = 0;
12679 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12681 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12682 ( !aroundElements || elements.count( otherVol )))
12684 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12685 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12686 if ( missType == SMDSAbs_Edge ) // boundary edges
12688 nodes.resize( 2+iQuad );
12689 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12691 for ( int j = 0; j < nodes.size(); ++j )
12693 if ( const SMDS_MeshElement* edge =
12694 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12695 presentBndElems.push_back( edge );
12697 missingBndElems.push_back( nodes );
12700 else // boundary face
12703 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12704 nodes.push_back( nn[inode] );
12705 if (iQuad) // add medium nodes
12706 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12707 nodes.push_back( nn[inode] );
12708 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12710 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12712 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12713 SMDSAbs_Face, /*noMedium=*/false ))
12714 presentBndElems.push_back( f );
12716 missingBndElems.push_back( nodes );
12718 if ( targetMesh != myMesh )
12720 // add 1D elements on face boundary to be added to a new mesh
12721 const SMDS_MeshElement* edge;
12722 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12725 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12727 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12728 if ( edge && avoidSet.insert( edge ).second )
12729 presentBndElems.push_back( edge );
12735 else // elem is a face ------------------------------------------
12737 avoidSet.clear(), avoidSet.insert( elem );
12738 int nbNodes = elem->NbCornerNodes();
12739 nodes.resize( 2 /*+ iQuad*/);
12740 for ( int i = 0; i < nbNodes; i++ )
12742 nodes[0] = elem->GetNode(i);
12743 nodes[1] = elem->GetNode((i+1)%nbNodes);
12744 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12745 continue; // not free link
12748 //nodes[2] = elem->GetNode( i + nbNodes );
12749 if ( const SMDS_MeshElement* edge =
12750 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12751 presentBndElems.push_back( edge );
12753 missingBndElems.push_back( nodes );
12757 // ---------------------------------
12758 // 2. Add missing boundary elements
12759 // ---------------------------------
12760 if ( targetMesh != myMesh )
12761 // instead of making a map of nodes in this mesh and targetMesh,
12762 // we create nodes with same IDs.
12763 for ( int i = 0; i < missingBndElems.size(); ++i )
12765 TConnectivity& srcNodes = missingBndElems[i];
12766 TConnectivity nodes( srcNodes.size() );
12767 for ( inode = 0; inode < nodes.size(); ++inode )
12768 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12769 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12771 /*noMedium=*/false))
12773 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12777 for ( int i = 0; i < missingBndElems.size(); ++i )
12779 TConnectivity& nodes = missingBndElems[i];
12780 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12782 /*noMedium=*/false))
12784 SMDS_MeshElement* elem =
12785 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12788 // try to set a new element to a shape
12789 if ( myMesh->HasShapeToMesh() )
12792 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12793 const int nbN = nodes.size() / (iQuad+1 );
12794 for ( inode = 0; inode < nbN && ok; ++inode )
12796 pair<int, TopAbs_ShapeEnum> i_stype =
12797 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12798 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12799 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12801 if ( ok && mediumShapes.size() > 1 )
12803 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12804 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12805 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12807 if (( ok = ( stype_i->first != stype_i_0.first )))
12808 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12809 aMesh->IndexToShape( stype_i_0.second ));
12812 if ( ok && mediumShapes.begin()->first == missShapeType )
12813 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12817 // ----------------------------------
12818 // 3. Copy present boundary elements
12819 // ----------------------------------
12820 if ( toCopyExistingBoundary )
12821 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12823 const SMDS_MeshElement* e = presentBndElems[i];
12824 TConnectivity nodes( e->NbNodes() );
12825 for ( inode = 0; inode < nodes.size(); ++inode )
12826 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12827 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12829 else // store present elements to add them to a group
12830 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12832 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12835 } // loop on given elements
12837 // ---------------------------------------------
12838 // 4. Fill group with boundary elements
12839 // ---------------------------------------------
12842 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12843 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12844 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12846 tgtEditor.myLastCreatedElems.Clear();
12847 tgtEditor2.myLastCreatedElems.Clear();
12849 // -----------------------
12850 // 5. Copy given elements
12851 // -----------------------
12852 if ( toCopyElements && targetMesh != myMesh )
12854 if (elements.empty())
12855 eIt = aMesh->elementsIterator(elemType);
12857 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12858 while (eIt->more())
12860 const SMDS_MeshElement* elem = eIt->next();
12861 TConnectivity nodes( elem->NbNodes() );
12862 for ( inode = 0; inode < nodes.size(); ++inode )
12863 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12864 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12866 tgtEditor.myLastCreatedElems.Clear();