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