1 // Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
86 #include <gp_Trsf.hxx>
100 #include <boost/tuple/tuple.hpp>
102 #include <Standard_Failure.hxx>
103 #include <Standard_ErrorHandler.hxx>
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
110 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
111 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
113 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
115 //=======================================================================
116 //function : SMESH_MeshEditor
118 //=======================================================================
120 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
121 :myMesh( theMesh ) // theMesh may be NULL
125 //================================================================================
127 * \brief Clears myLastCreatedNodes and myLastCreatedElems
129 //================================================================================
131 void SMESH_MeshEditor::CrearLastCreated()
133 myLastCreatedNodes.Clear();
134 myLastCreatedElems.Clear();
138 //=======================================================================
142 //=======================================================================
145 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
146 const SMDSAbs_ElementType type,
149 const double ballDiameter)
151 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
152 SMDS_MeshElement* e = 0;
153 int nbnode = node.size();
154 SMESHDS_Mesh* mesh = GetMeshDS();
159 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
160 else e = mesh->AddFace (node[0], node[1], node[2] );
162 else if (nbnode == 4) {
163 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
164 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
166 else if (nbnode == 6) {
167 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
168 node[4], node[5], ID);
169 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
172 else if (nbnode == 8) {
173 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
174 node[4], node[5], node[6], node[7], ID);
175 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
176 node[4], node[5], node[6], node[7] );
178 else if (nbnode == 9) {
179 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
180 node[4], node[5], node[6], node[7], node[8], ID);
181 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
182 node[4], node[5], node[6], node[7], node[8] );
185 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
186 else e = mesh->AddPolygonalFace (node );
193 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
194 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
196 else if (nbnode == 5) {
197 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
199 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
202 else if (nbnode == 6) {
203 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
204 node[4], node[5], ID);
205 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
208 else if (nbnode == 8) {
209 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], node[7], ID);
211 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], node[7] );
214 else if (nbnode == 10) {
215 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7],
217 node[8], node[9], ID);
218 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7],
222 else if (nbnode == 12) {
223 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7],
225 node[8], node[9], node[10], node[11], ID);
226 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7],
228 node[8], node[9], node[10], node[11] );
230 else if (nbnode == 13) {
231 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232 node[4], node[5], node[6], node[7],
233 node[8], node[9], node[10],node[11],
235 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
236 node[4], node[5], node[6], node[7],
237 node[8], node[9], node[10],node[11],
240 else if (nbnode == 15) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
242 node[4], node[5], node[6], node[7],
243 node[8], node[9], node[10],node[11],
244 node[12],node[13],node[14],ID);
245 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
246 node[4], node[5], node[6], node[7],
247 node[8], node[9], node[10],node[11],
248 node[12],node[13],node[14] );
250 else if (nbnode == 20) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], node[6], node[7],
253 node[8], node[9], node[10],node[11],
254 node[12],node[13],node[14],node[15],
255 node[16],node[17],node[18],node[19],ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
257 node[4], node[5], node[6], node[7],
258 node[8], node[9], node[10],node[11],
259 node[12],node[13],node[14],node[15],
260 node[16],node[17],node[18],node[19] );
262 else if (nbnode == 27) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], node[10],node[11],
266 node[12],node[13],node[14],node[15],
267 node[16],node[17],node[18],node[19],
268 node[20],node[21],node[22],node[23],
269 node[24],node[25],node[26], ID);
270 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
271 node[4], node[5], node[6], node[7],
272 node[8], node[9], node[10],node[11],
273 node[12],node[13],node[14],node[15],
274 node[16],node[17],node[18],node[19],
275 node[20],node[21],node[22],node[23],
276 node[24],node[25],node[26] );
283 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
284 else e = mesh->AddEdge (node[0], node[1] );
286 else if ( nbnode == 3 ) {
287 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
288 else e = mesh->AddEdge (node[0], node[1], node[2] );
292 case SMDSAbs_0DElement:
294 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
295 else e = mesh->Add0DElement (node[0] );
300 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
301 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
305 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
306 else e = mesh->AddBall (node[0], ballDiameter);
311 if ( e ) myLastCreatedElems.Append( e );
315 //=======================================================================
319 //=======================================================================
321 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
322 const SMDSAbs_ElementType type,
326 vector<const SMDS_MeshNode*> nodes;
327 nodes.reserve( nodeIDs.size() );
328 vector<int>::const_iterator id = nodeIDs.begin();
329 while ( id != nodeIDs.end() ) {
330 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
331 nodes.push_back( node );
335 return AddElement( nodes, type, isPoly, ID );
338 //=======================================================================
340 //purpose : Remove a node or an element.
341 // Modify a compute state of sub-meshes which become empty
342 //=======================================================================
344 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
347 myLastCreatedElems.Clear();
348 myLastCreatedNodes.Clear();
350 SMESHDS_Mesh* aMesh = GetMeshDS();
351 set< SMESH_subMesh *> smmap;
354 list<int>::const_iterator it = theIDs.begin();
355 for ( ; it != theIDs.end(); it++ ) {
356 const SMDS_MeshElement * elem;
358 elem = aMesh->FindNode( *it );
360 elem = aMesh->FindElement( *it );
364 // Notify VERTEX sub-meshes about modification
366 const SMDS_MeshNode* node = cast2Node( elem );
367 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
368 if ( int aShapeID = node->getshapeId() )
369 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
372 // Find sub-meshes to notify about modification
373 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
374 // while ( nodeIt->more() ) {
375 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
376 // const SMDS_PositionPtr& aPosition = node->GetPosition();
377 // if ( aPosition.get() ) {
378 // if ( int aShapeID = aPosition->GetShapeId() ) {
379 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
380 // smmap.insert( sm );
387 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
389 aMesh->RemoveElement( elem );
393 // Notify sub-meshes about modification
394 if ( !smmap.empty() ) {
395 set< SMESH_subMesh *>::iterator smIt;
396 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
397 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
400 // // Check if the whole mesh becomes empty
401 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
402 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
407 //================================================================================
409 * \brief Create 0D elements on all nodes of the given object except those
410 * nodes on which a 0D element already exists.
411 * \param elements - Elements on whose nodes to create 0D elements; if empty,
412 * the all mesh is treated
413 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
415 //================================================================================
417 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
418 TIDSortedElemSet& all0DElems )
420 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
421 SMDS_ElemIteratorPtr elemIt;
422 if ( elements.empty() )
423 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
425 elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
427 while ( elemIt->more() )
429 const SMDS_MeshElement* e = elemIt->next();
430 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
431 while ( nodeIt->more() )
433 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
434 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
436 all0DElems.insert( it0D->next() );
438 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
439 all0DElems.insert( myLastCreatedElems.Last() );
445 //=======================================================================
446 //function : FindShape
447 //purpose : Return an index of the shape theElem is on
448 // or zero if a shape not found
449 //=======================================================================
451 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
453 myLastCreatedElems.Clear();
454 myLastCreatedNodes.Clear();
456 SMESHDS_Mesh * aMesh = GetMeshDS();
457 if ( aMesh->ShapeToMesh().IsNull() )
460 int aShapeID = theElem->getshapeId();
464 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
465 if ( sm->Contains( theElem ))
468 if ( theElem->GetType() == SMDSAbs_Node ) {
469 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
472 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
475 TopoDS_Shape aShape; // the shape a node of theElem is on
476 if ( theElem->GetType() != SMDSAbs_Node )
478 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
479 while ( nodeIt->more() ) {
480 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
481 if ((aShapeID = node->getshapeId()) > 0) {
482 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
483 if ( sm->Contains( theElem ))
485 if ( aShape.IsNull() )
486 aShape = aMesh->IndexToShape( aShapeID );
492 // None of nodes is on a proper shape,
493 // find the shape among ancestors of aShape on which a node is
494 if ( !aShape.IsNull() ) {
495 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
496 for ( ; ancIt.More(); ancIt.Next() ) {
497 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
498 if ( sm && sm->Contains( theElem ))
499 return aMesh->ShapeToIndex( ancIt.Value() );
504 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
505 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
506 for ( ; id_sm != id2sm.end(); ++id_sm )
507 if ( id_sm->second->Contains( theElem ))
511 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
515 //=======================================================================
516 //function : IsMedium
518 //=======================================================================
520 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
521 const SMDSAbs_ElementType typeToCheck)
523 bool isMedium = false;
524 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
525 while (it->more() && !isMedium ) {
526 const SMDS_MeshElement* elem = it->next();
527 isMedium = elem->IsMediumNode(node);
532 //=======================================================================
533 //function : ShiftNodesQuadTria
535 // Shift nodes in the array corresponded to quadratic triangle
536 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
537 //=======================================================================
538 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
540 const SMDS_MeshNode* nd1 = aNodes[0];
541 aNodes[0] = aNodes[1];
542 aNodes[1] = aNodes[2];
544 const SMDS_MeshNode* nd2 = aNodes[3];
545 aNodes[3] = aNodes[4];
546 aNodes[4] = aNodes[5];
550 //=======================================================================
551 //function : edgeConnectivity
553 // return number of the edges connected with the theNode.
554 // if theEdges has connections with the other type of the
555 // elements, return -1
556 //=======================================================================
557 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
559 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
561 while(elemIt->more()) {
569 //=======================================================================
570 //function : GetNodesFromTwoTria
572 // Shift nodes in the array corresponded to quadratic triangle
573 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
574 //=======================================================================
575 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
576 const SMDS_MeshElement * theTria2,
577 const SMDS_MeshNode* N1[],
578 const SMDS_MeshNode* N2[])
580 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
583 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
586 if(it->more()) return false;
587 it = theTria2->nodesIterator();
590 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
593 if(it->more()) return false;
595 int sames[3] = {-1,-1,-1};
607 if(nbsames!=2) return false;
609 ShiftNodesQuadTria(N1);
611 ShiftNodesQuadTria(N1);
614 i = sames[0] + sames[1] + sames[2];
616 ShiftNodesQuadTria(N2);
618 // now we receive following N1 and N2 (using numeration as above image)
619 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
620 // i.e. first nodes from both arrays determ new diagonal
624 //=======================================================================
625 //function : InverseDiag
626 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
627 // but having other common link.
628 // Return False if args are improper
629 //=======================================================================
631 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
632 const SMDS_MeshElement * theTria2 )
634 MESSAGE("InverseDiag");
635 myLastCreatedElems.Clear();
636 myLastCreatedNodes.Clear();
638 if (!theTria1 || !theTria2)
641 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
642 if (!F1) return false;
643 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
644 if (!F2) return false;
645 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
646 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
648 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
649 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
653 // put nodes in array and find out indices of the same ones
654 const SMDS_MeshNode* aNodes [6];
655 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
657 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
658 while ( it->more() ) {
659 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
661 if ( i > 2 ) // theTria2
662 // find same node of theTria1
663 for ( int j = 0; j < 3; j++ )
664 if ( aNodes[ i ] == aNodes[ j ]) {
673 return false; // theTria1 is not a triangle
674 it = theTria2->nodesIterator();
676 if ( i == 6 && it->more() )
677 return false; // theTria2 is not a triangle
680 // find indices of 1,2 and of A,B in theTria1
681 int iA = 0, iB = 0, i1 = 0, i2 = 0;
682 for ( i = 0; i < 6; i++ ) {
683 if ( sameInd [ i ] == 0 ) {
692 // nodes 1 and 2 should not be the same
693 if ( aNodes[ i1 ] == aNodes[ i2 ] )
697 aNodes[ iA ] = aNodes[ i2 ];
699 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
701 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
702 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
706 } // end if(F1 && F2)
708 // check case of quadratic faces
709 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
711 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
715 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
716 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
724 const SMDS_MeshNode* N1 [6];
725 const SMDS_MeshNode* N2 [6];
726 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
728 // now we receive following N1 and N2 (using numeration as above image)
729 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
730 // i.e. first nodes from both arrays determ new diagonal
732 const SMDS_MeshNode* N1new [6];
733 const SMDS_MeshNode* N2new [6];
746 // replaces nodes in faces
747 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
748 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
753 //=======================================================================
754 //function : findTriangles
755 //purpose : find triangles sharing theNode1-theNode2 link
756 //=======================================================================
758 static bool findTriangles(const SMDS_MeshNode * theNode1,
759 const SMDS_MeshNode * theNode2,
760 const SMDS_MeshElement*& theTria1,
761 const SMDS_MeshElement*& theTria2)
763 if ( !theNode1 || !theNode2 ) return false;
765 theTria1 = theTria2 = 0;
767 set< const SMDS_MeshElement* > emap;
768 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
770 const SMDS_MeshElement* elem = it->next();
771 if ( elem->NbNodes() == 3 )
774 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
776 const SMDS_MeshElement* elem = it->next();
777 if ( emap.find( elem ) != emap.end() ) {
779 // theTria1 must be element with minimum ID
780 if( theTria1->GetID() < elem->GetID() ) {
794 return ( theTria1 && theTria2 );
797 //=======================================================================
798 //function : InverseDiag
799 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
800 // with ones built on the same 4 nodes but having other common link.
801 // Return false if proper faces not found
802 //=======================================================================
804 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
805 const SMDS_MeshNode * theNode2)
807 myLastCreatedElems.Clear();
808 myLastCreatedNodes.Clear();
810 MESSAGE( "::InverseDiag()" );
812 const SMDS_MeshElement *tr1, *tr2;
813 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
816 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
817 if (!F1) return false;
818 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
819 if (!F2) return false;
820 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
821 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
823 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
824 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
828 // put nodes in array
829 // and find indices of 1,2 and of A in tr1 and of B in tr2
830 int i, iA1 = 0, i1 = 0;
831 const SMDS_MeshNode* aNodes1 [3];
832 SMDS_ElemIteratorPtr it;
833 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
834 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
835 if ( aNodes1[ i ] == theNode1 )
836 iA1 = i; // node A in tr1
837 else if ( aNodes1[ i ] != theNode2 )
841 const SMDS_MeshNode* aNodes2 [3];
842 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
843 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
844 if ( aNodes2[ i ] == theNode2 )
845 iB2 = i; // node B in tr2
846 else if ( aNodes2[ i ] != theNode1 )
850 // nodes 1 and 2 should not be the same
851 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
855 aNodes1[ iA1 ] = aNodes2[ i2 ];
857 aNodes2[ iB2 ] = aNodes1[ i1 ];
859 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
860 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
865 // check case of quadratic faces
866 return InverseDiag(tr1,tr2);
869 //=======================================================================
870 //function : getQuadrangleNodes
871 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
872 // fusion of triangles tr1 and tr2 having shared link on
873 // theNode1 and theNode2
874 //=======================================================================
876 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
877 const SMDS_MeshNode * theNode1,
878 const SMDS_MeshNode * theNode2,
879 const SMDS_MeshElement * tr1,
880 const SMDS_MeshElement * tr2 )
882 if( tr1->NbNodes() != tr2->NbNodes() )
884 // find the 4-th node to insert into tr1
885 const SMDS_MeshNode* n4 = 0;
886 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
888 while ( !n4 && i<3 ) {
889 const SMDS_MeshNode * n = cast2Node( it->next() );
891 bool isDiag = ( n == theNode1 || n == theNode2 );
895 // Make an array of nodes to be in a quadrangle
896 int iNode = 0, iFirstDiag = -1;
897 it = tr1->nodesIterator();
900 const SMDS_MeshNode * n = cast2Node( it->next() );
902 bool isDiag = ( n == theNode1 || n == theNode2 );
904 if ( iFirstDiag < 0 )
906 else if ( iNode - iFirstDiag == 1 )
907 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
909 else if ( n == n4 ) {
910 return false; // tr1 and tr2 should not have all the same nodes
912 theQuadNodes[ iNode++ ] = n;
914 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
915 theQuadNodes[ iNode ] = n4;
920 //=======================================================================
921 //function : DeleteDiag
922 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
923 // with a quadrangle built on the same 4 nodes.
924 // Return false if proper faces not found
925 //=======================================================================
927 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
928 const SMDS_MeshNode * theNode2)
930 myLastCreatedElems.Clear();
931 myLastCreatedNodes.Clear();
933 MESSAGE( "::DeleteDiag()" );
935 const SMDS_MeshElement *tr1, *tr2;
936 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
939 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
940 if (!F1) return false;
941 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
942 if (!F2) return false;
943 SMESHDS_Mesh * aMesh = GetMeshDS();
945 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
946 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
948 const SMDS_MeshNode* aNodes [ 4 ];
949 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
952 const SMDS_MeshElement* newElem = 0;
953 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
954 myLastCreatedElems.Append(newElem);
955 AddToSameGroups( newElem, tr1, aMesh );
956 int aShapeId = tr1->getshapeId();
959 aMesh->SetMeshElementOnShape( newElem, aShapeId );
961 aMesh->RemoveElement( tr1 );
962 aMesh->RemoveElement( tr2 );
967 // check case of quadratic faces
968 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
970 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
974 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
975 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
983 const SMDS_MeshNode* N1 [6];
984 const SMDS_MeshNode* N2 [6];
985 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
987 // now we receive following N1 and N2 (using numeration as above image)
988 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
989 // i.e. first nodes from both arrays determ new diagonal
991 const SMDS_MeshNode* aNodes[8];
1001 const SMDS_MeshElement* newElem = 0;
1002 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1003 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1004 myLastCreatedElems.Append(newElem);
1005 AddToSameGroups( newElem, tr1, aMesh );
1006 int aShapeId = tr1->getshapeId();
1009 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1011 aMesh->RemoveElement( tr1 );
1012 aMesh->RemoveElement( tr2 );
1014 // remove middle node (9)
1015 GetMeshDS()->RemoveNode( N1[4] );
1020 //=======================================================================
1021 //function : Reorient
1022 //purpose : Reverse theElement orientation
1023 //=======================================================================
1025 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1027 MESSAGE("Reorient");
1028 myLastCreatedElems.Clear();
1029 myLastCreatedNodes.Clear();
1033 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1034 if ( !it || !it->more() )
1037 switch ( theElem->GetType() ) {
1040 case SMDSAbs_Face: {
1041 if(!theElem->IsQuadratic()) {
1042 int i = theElem->NbNodes();
1043 vector<const SMDS_MeshNode*> aNodes( i );
1044 while ( it->more() )
1045 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1046 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1049 // quadratic elements
1050 if(theElem->GetType()==SMDSAbs_Edge) {
1051 vector<const SMDS_MeshNode*> aNodes(3);
1052 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1053 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1054 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1055 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1058 int nbn = theElem->NbNodes();
1059 vector<const SMDS_MeshNode*> aNodes(nbn);
1060 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1062 for(; i<nbn/2; i++) {
1063 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1065 for(i=0; i<nbn/2; i++) {
1066 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1068 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1072 case SMDSAbs_Volume: {
1073 if (theElem->IsPoly()) {
1074 // TODO reorient vtk polyhedron
1075 MESSAGE("reorient vtk polyhedron ?");
1076 const SMDS_VtkVolume* aPolyedre =
1077 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1079 MESSAGE("Warning: bad volumic element");
1083 int nbFaces = aPolyedre->NbFaces();
1084 vector<const SMDS_MeshNode *> poly_nodes;
1085 vector<int> quantities (nbFaces);
1087 // reverse each face of the polyedre
1088 for (int iface = 1; iface <= nbFaces; iface++) {
1089 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1090 quantities[iface - 1] = nbFaceNodes;
1092 for (inode = nbFaceNodes; inode >= 1; inode--) {
1093 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1094 poly_nodes.push_back(curNode);
1098 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1102 SMDS_VolumeTool vTool;
1103 if ( !vTool.Set( theElem ))
1106 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1107 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1116 //================================================================================
1118 * \brief Reorient faces.
1119 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1120 * \param theDirection - desired direction of normal of \a theFace
1121 * \param theFace - one of \a theFaces that sould be oriented according to
1122 * \a theDirection and whose orientation defines orientation of other faces
1123 * \return number of reoriented faces.
1125 //================================================================================
1127 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1128 const gp_Dir& theDirection,
1129 const SMDS_MeshElement * theFace)
1132 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1134 if ( theFaces.empty() )
1136 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1137 while ( fIt->more() )
1138 theFaces.insert( theFaces.end(), fIt->next() );
1141 // orient theFace according to theDirection
1143 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1144 if ( normal * theDirection.XYZ() < 0 )
1145 nbReori += Reorient( theFace );
1147 // Orient other faces
1149 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1150 TIDSortedElemSet avoidSet;
1151 set< SMESH_TLink > checkedLinks;
1152 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1154 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1155 theFaces.erase( theFace );
1156 startFaces.insert( theFace );
1158 int nodeInd1, nodeInd2;
1159 const SMDS_MeshElement* otherFace;
1160 vector< const SMDS_MeshElement* > facesNearLink;
1161 vector< std::pair< int, int > > nodeIndsOfFace;
1163 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1164 while ( !startFaces.empty() )
1166 startFace = startFaces.begin();
1167 theFace = *startFace;
1168 startFaces.erase( startFace );
1169 if ( !visitedFaces.insert( theFace ).second )
1173 avoidSet.insert(theFace);
1175 NLink link( theFace->GetNode( 0 ), 0 );
1177 const int nbNodes = theFace->NbCornerNodes();
1178 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1180 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1181 linkIt_isNew = checkedLinks.insert( link );
1182 if ( !linkIt_isNew.second )
1184 // link has already been checked and won't be encountered more
1185 // if the group (theFaces) is manifold
1186 //checkedLinks.erase( linkIt_isNew.first );
1190 facesNearLink.clear();
1191 nodeIndsOfFace.clear();
1192 while (( otherFace = FindFaceInSet( link.first, link.second,
1193 theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1194 if ( otherFace != theFace)
1196 facesNearLink.push_back( otherFace );
1197 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1198 avoidSet.insert( otherFace );
1200 if ( facesNearLink.size() > 1 )
1202 // NON-MANIFOLD mesh shell !
1203 // select a face most co-directed with theFace,
1204 // other faces won't be visited this time
1206 SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1207 double proj, maxProj = -1;
1208 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1209 SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1210 if (( proj = Abs( NF * NOF )) > maxProj ) {
1212 otherFace = facesNearLink[i];
1213 nodeInd1 = nodeIndsOfFace[i].first;
1214 nodeInd2 = nodeIndsOfFace[i].second;
1217 // not to visit rejected faces
1218 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1219 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1220 visitedFaces.insert( facesNearLink[i] );
1222 else if ( facesNearLink.size() == 1 )
1224 otherFace = facesNearLink[0];
1225 nodeInd1 = nodeIndsOfFace.back().first;
1226 nodeInd2 = nodeIndsOfFace.back().second;
1228 if ( otherFace && otherFace != theFace)
1230 // link must be reverse in otherFace if orientation ot otherFace
1231 // is same as that of theFace
1232 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1234 nbReori += Reorient( otherFace );
1236 startFaces.insert( otherFace );
1239 std::swap( link.first, link.second ); // reverse the link
1245 //=======================================================================
1246 //function : getBadRate
1248 //=======================================================================
1250 static double getBadRate (const SMDS_MeshElement* theElem,
1251 SMESH::Controls::NumericalFunctorPtr& theCrit)
1253 SMESH::Controls::TSequenceOfXYZ P;
1254 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1256 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1257 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1260 //=======================================================================
1261 //function : QuadToTri
1262 //purpose : Cut quadrangles into triangles.
1263 // theCrit is used to select a diagonal to cut
1264 //=======================================================================
1266 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1267 SMESH::Controls::NumericalFunctorPtr theCrit)
1269 myLastCreatedElems.Clear();
1270 myLastCreatedNodes.Clear();
1272 MESSAGE( "::QuadToTri()" );
1274 if ( !theCrit.get() )
1277 SMESHDS_Mesh * aMesh = GetMeshDS();
1279 Handle(Geom_Surface) surface;
1280 SMESH_MesherHelper helper( *GetMesh() );
1282 TIDSortedElemSet::iterator itElem;
1283 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1284 const SMDS_MeshElement* elem = *itElem;
1285 if ( !elem || elem->GetType() != SMDSAbs_Face )
1287 if ( elem->NbCornerNodes() != 4 )
1290 // retrieve element nodes
1291 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1293 // compare two sets of possible triangles
1294 double aBadRate1, aBadRate2; // to what extent a set is bad
1295 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1296 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1297 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1299 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1300 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1301 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1303 int aShapeId = FindShape( elem );
1304 const SMDS_MeshElement* newElem1 = 0;
1305 const SMDS_MeshElement* newElem2 = 0;
1307 if( !elem->IsQuadratic() ) {
1309 // split liner quadrangle
1310 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1311 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1312 if ( aBadRate1 <= aBadRate2 ) {
1313 // tr1 + tr2 is better
1314 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1315 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1318 // tr3 + tr4 is better
1319 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1320 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1325 // split quadratic quadrangle
1327 // get surface elem is on
1328 if ( aShapeId != helper.GetSubShapeID() ) {
1332 shape = aMesh->IndexToShape( aShapeId );
1333 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1334 TopoDS_Face face = TopoDS::Face( shape );
1335 surface = BRep_Tool::Surface( face );
1336 if ( !surface.IsNull() )
1337 helper.SetSubShape( shape );
1340 // find middle point for (0,1,2,3)
1341 // and create a node in this point;
1342 const SMDS_MeshNode* newN = 0;
1343 if ( aNodes.size() == 9 )
1345 // SMDSEntity_BiQuad_Quadrangle
1346 newN = aNodes.back();
1351 if ( surface.IsNull() )
1353 for ( int i = 0; i < 4; i++ )
1354 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1359 const SMDS_MeshNode* inFaceNode = 0;
1360 if ( helper.GetNodeUVneedInFaceNode() )
1361 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1362 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1363 inFaceNode = aNodes[ i ];
1365 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1367 for ( int i = 0; i < 4; i++ )
1368 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1370 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1372 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1373 myLastCreatedNodes.Append(newN);
1375 // create a new element
1376 if ( aBadRate1 <= aBadRate2 ) {
1377 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1378 aNodes[6], aNodes[7], newN );
1379 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1380 newN, aNodes[4], aNodes[5] );
1383 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1384 aNodes[7], aNodes[4], newN );
1385 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1386 newN, aNodes[5], aNodes[6] );
1390 // care of a new element
1392 myLastCreatedElems.Append(newElem1);
1393 myLastCreatedElems.Append(newElem2);
1394 AddToSameGroups( newElem1, elem, aMesh );
1395 AddToSameGroups( newElem2, elem, aMesh );
1397 // put a new triangle on the same shape
1400 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1401 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1403 aMesh->RemoveElement( elem );
1408 //=======================================================================
1409 //function : BestSplit
1410 //purpose : Find better diagonal for cutting.
1411 //=======================================================================
1413 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1414 SMESH::Controls::NumericalFunctorPtr theCrit)
1416 myLastCreatedElems.Clear();
1417 myLastCreatedNodes.Clear();
1422 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1425 if( theQuad->NbNodes()==4 ||
1426 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1428 // retrieve element nodes
1429 const SMDS_MeshNode* aNodes [4];
1430 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1432 //while (itN->more())
1434 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1436 // compare two sets of possible triangles
1437 double aBadRate1, aBadRate2; // to what extent a set is bad
1438 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1439 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1440 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1442 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1443 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1444 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1445 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1448 return 1; // diagonal 1-3
1450 return 2; // diagonal 2-4
1457 // Methods of splitting volumes into tetra
1459 const int theHexTo5_1[5*4+1] =
1461 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1463 const int theHexTo5_2[5*4+1] =
1465 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1467 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1469 const int theHexTo6_1[6*4+1] =
1471 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1473 const int theHexTo6_2[6*4+1] =
1475 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1477 const int theHexTo6_3[6*4+1] =
1479 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1481 const int theHexTo6_4[6*4+1] =
1483 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1485 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1487 const int thePyraTo2_1[2*4+1] =
1489 0, 1, 2, 4, 0, 2, 3, 4, -1
1491 const int thePyraTo2_2[2*4+1] =
1493 1, 2, 3, 4, 1, 3, 0, 4, -1
1495 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1497 const int thePentaTo3_1[3*4+1] =
1499 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1501 const int thePentaTo3_2[3*4+1] =
1503 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1505 const int thePentaTo3_3[3*4+1] =
1507 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1509 const int thePentaTo3_4[3*4+1] =
1511 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1513 const int thePentaTo3_5[3*4+1] =
1515 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1517 const int thePentaTo3_6[3*4+1] =
1519 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1521 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1522 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1524 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1527 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1528 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1529 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1534 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1535 bool _baryNode; //!< additional node is to be created at cell barycenter
1536 bool _ownConn; //!< to delete _connectivity in destructor
1537 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1539 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1540 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1541 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1542 bool hasFacet( const TTriangleFacet& facet ) const
1544 const int* tetConn = _connectivity;
1545 for ( ; tetConn[0] >= 0; tetConn += 4 )
1546 if (( facet.contains( tetConn[0] ) +
1547 facet.contains( tetConn[1] ) +
1548 facet.contains( tetConn[2] ) +
1549 facet.contains( tetConn[3] )) == 3 )
1555 //=======================================================================
1557 * \brief return TSplitMethod for the given element
1559 //=======================================================================
1561 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1563 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1565 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1566 // an edge and a face barycenter; tertaherdons are based on triangles and
1567 // a volume barycenter
1568 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1570 // Find out how adjacent volumes are split
1572 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1573 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1574 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1576 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1577 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1578 if ( nbNodes < 4 ) continue;
1580 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1581 const int* nInd = vol.GetFaceNodesIndices( iF );
1584 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1585 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1586 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1587 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1591 int iCom = 0; // common node of triangle faces to split into
1592 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1594 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1595 nInd[ iQ * ( (iCom+1)%nbNodes )],
1596 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1597 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1598 nInd[ iQ * ( (iCom+2)%nbNodes )],
1599 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1600 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1602 triaSplits.push_back( t012 );
1603 triaSplits.push_back( t023 );
1608 if ( !triaSplits.empty() )
1609 hasAdjacentSplits = true;
1612 // Among variants of split method select one compliant with adjacent volumes
1614 TSplitMethod method;
1615 if ( !vol.Element()->IsPoly() && !is24TetMode )
1617 int nbVariants = 2, nbTet = 0;
1618 const int** connVariants = 0;
1619 switch ( vol.Element()->GetEntityType() )
1621 case SMDSEntity_Hexa:
1622 case SMDSEntity_Quad_Hexa:
1623 case SMDSEntity_TriQuad_Hexa:
1624 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1625 connVariants = theHexTo5, nbTet = 5;
1627 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1629 case SMDSEntity_Pyramid:
1630 case SMDSEntity_Quad_Pyramid:
1631 connVariants = thePyraTo2; nbTet = 2;
1633 case SMDSEntity_Penta:
1634 case SMDSEntity_Quad_Penta:
1635 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1640 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1642 // check method compliancy with adjacent tetras,
1643 // all found splits must be among facets of tetras described by this method
1644 method = TSplitMethod( nbTet, connVariants[variant] );
1645 if ( hasAdjacentSplits && method._nbTetra > 0 )
1647 bool facetCreated = true;
1648 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1650 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1651 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1652 facetCreated = method.hasFacet( *facet );
1654 if ( !facetCreated )
1655 method = TSplitMethod(0); // incompatible method
1659 if ( method._nbTetra < 1 )
1661 // No standard method is applicable, use a generic solution:
1662 // each facet of a volume is split into triangles and
1663 // each of triangles and a volume barycenter form a tetrahedron.
1665 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1667 int* connectivity = new int[ maxTetConnSize + 1 ];
1668 method._connectivity = connectivity;
1669 method._ownConn = true;
1670 method._baryNode = !isHex27; // to create central node or not
1673 int baryCenInd = vol.NbNodes() - int( isHex27 );
1674 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1676 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1677 const int* nInd = vol.GetFaceNodesIndices( iF );
1678 // find common node of triangle facets of tetra to create
1679 int iCommon = 0; // index in linear numeration
1680 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1681 if ( !triaSplits.empty() )
1684 const TTriangleFacet* facet = &triaSplits.front();
1685 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1686 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1687 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1690 else if ( nbNodes > 3 && !is24TetMode )
1692 // find the best method of splitting into triangles by aspect ratio
1693 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1694 map< double, int > badness2iCommon;
1695 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1696 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1697 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1700 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1702 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1703 nodes[ iQ*((iLast-1)%nbNodes)],
1704 nodes[ iQ*((iLast )%nbNodes)]);
1705 badness += getBadRate( &tria, aspectRatio );
1707 badness2iCommon.insert( make_pair( badness, iCommon ));
1709 // use iCommon with lowest badness
1710 iCommon = badness2iCommon.begin()->second;
1712 if ( iCommon >= nbNodes )
1713 iCommon = 0; // something wrong
1715 // fill connectivity of tetrahedra based on a current face
1716 int nbTet = nbNodes - 2;
1717 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1722 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1723 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1727 method._faceBaryNode[ iF ] = 0;
1728 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1731 for ( int i = 0; i < nbTet; ++i )
1733 int i1 = i, i2 = (i+1) % nbNodes;
1734 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1735 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1736 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1737 connectivity[ connSize++ ] = faceBaryCenInd;
1738 connectivity[ connSize++ ] = baryCenInd;
1743 for ( int i = 0; i < nbTet; ++i )
1745 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1746 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1747 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1748 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1749 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1750 connectivity[ connSize++ ] = baryCenInd;
1753 method._nbTetra += nbTet;
1755 } // loop on volume faces
1757 connectivity[ connSize++ ] = -1;
1759 } // end of generic solution
1763 //================================================================================
1765 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1767 //================================================================================
1769 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1771 // find the tetrahedron including the three nodes of facet
1772 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1773 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1774 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1775 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1776 while ( volIt1->more() )
1778 const SMDS_MeshElement* v = volIt1->next();
1779 SMDSAbs_EntityType type = v->GetEntityType();
1780 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1782 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1783 continue; // medium node not allowed
1784 const int ind2 = v->GetNodeIndex( n2 );
1785 if ( ind2 < 0 || 3 < ind2 )
1787 const int ind3 = v->GetNodeIndex( n3 );
1788 if ( ind3 < 0 || 3 < ind3 )
1795 //=======================================================================
1797 * \brief A key of a face of volume
1799 //=======================================================================
1801 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1803 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1805 TIDSortedNodeSet sortedNodes;
1806 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1807 int nbNodes = vol.NbFaceNodes( iF );
1808 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1809 for ( int i = 0; i < nbNodes; i += iQ )
1810 sortedNodes.insert( fNodes[i] );
1811 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1812 first.first = (*(n++))->GetID();
1813 first.second = (*(n++))->GetID();
1814 second.first = (*(n++))->GetID();
1815 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1820 //=======================================================================
1821 //function : SplitVolumesIntoTetra
1822 //purpose : Split volume elements into tetrahedra.
1823 //=======================================================================
1825 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1826 const int theMethodFlags)
1828 // std-like iterator on coordinates of nodes of mesh element
1829 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1830 NXyzIterator xyzEnd;
1832 SMDS_VolumeTool volTool;
1833 SMESH_MesherHelper helper( *GetMesh());
1835 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1836 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1838 SMESH_SequenceOfElemPtr newNodes, newElems;
1840 // map face of volume to it's baricenrtic node
1841 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1844 TIDSortedElemSet::const_iterator elem = theElems.begin();
1845 for ( ; elem != theElems.end(); ++elem )
1847 if ( (*elem)->GetType() != SMDSAbs_Volume )
1849 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1850 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1853 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1855 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1856 if ( splitMethod._nbTetra < 1 ) continue;
1858 // find submesh to add new tetras to
1859 if ( !subMesh || !subMesh->Contains( *elem ))
1861 int shapeID = FindShape( *elem );
1862 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1863 subMesh = GetMeshDS()->MeshElements( shapeID );
1866 if ( (*elem)->IsQuadratic() )
1869 // add quadratic links to the helper
1870 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1872 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1873 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1874 for ( int iN = 0; iN < nbN; iN += iQ )
1875 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1877 helper.SetIsQuadratic( true );
1882 helper.SetIsQuadratic( false );
1884 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1885 helper.SetElementsOnShape( true );
1886 if ( splitMethod._baryNode )
1888 // make a node at barycenter
1889 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1890 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1891 nodes.push_back( gcNode );
1892 newNodes.Append( gcNode );
1894 if ( !splitMethod._faceBaryNode.empty() )
1896 // make or find baricentric nodes of faces
1897 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1898 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1900 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1901 volFace2BaryNode.insert
1902 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1905 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1906 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1908 nodes.push_back( iF_n->second = f_n->second );
1913 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1914 const int* tetConn = splitMethod._connectivity;
1915 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1916 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1917 nodes[ tetConn[1] ],
1918 nodes[ tetConn[2] ],
1919 nodes[ tetConn[3] ]));
1921 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1923 // Split faces on sides of the split volume
1925 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1926 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1928 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1929 if ( nbNodes < 4 ) continue;
1931 // find an existing face
1932 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1933 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1934 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1935 /*noMedium=*/false))
1938 helper.SetElementsOnShape( false );
1939 vector< const SMDS_MeshElement* > triangles;
1941 // find submesh to add new triangles in
1942 if ( !fSubMesh || !fSubMesh->Contains( face ))
1944 int shapeID = FindShape( face );
1945 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1947 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1948 if ( iF_n != splitMethod._faceBaryNode.end() )
1950 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1952 const SMDS_MeshNode* n1 = fNodes[iN];
1953 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1954 const SMDS_MeshNode *n3 = iF_n->second;
1955 if ( !volTool.IsFaceExternal( iF ))
1957 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1959 if ( fSubMesh && n3->getshapeId() < 1 )
1960 fSubMesh->AddNode( n3 );
1965 // among possible triangles create ones discribed by split method
1966 const int* nInd = volTool.GetFaceNodesIndices( iF );
1967 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1968 int iCom = 0; // common node of triangle faces to split into
1969 list< TTriangleFacet > facets;
1970 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1972 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1973 nInd[ iQ * ( (iCom+1)%nbNodes )],
1974 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1975 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1976 nInd[ iQ * ( (iCom+2)%nbNodes )],
1977 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1978 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1980 facets.push_back( t012 );
1981 facets.push_back( t023 );
1982 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1983 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1984 nInd[ iQ * ((iLast-1)%nbNodes )],
1985 nInd[ iQ * ((iLast )%nbNodes )]));
1989 list< TTriangleFacet >::iterator facet = facets.begin();
1990 for ( ; facet != facets.end(); ++facet )
1992 if ( !volTool.IsFaceExternal( iF ))
1993 swap( facet->_n2, facet->_n3 );
1994 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1995 volNodes[ facet->_n2 ],
1996 volNodes[ facet->_n3 ]));
1999 for ( int i = 0; i < triangles.size(); ++i )
2001 if ( !triangles[i] ) continue;
2003 fSubMesh->AddElement( triangles[i]);
2004 newElems.Append( triangles[i] );
2006 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2007 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2010 } // loop on volume faces to split them into triangles
2012 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2014 if ( geomType == SMDSEntity_TriQuad_Hexa )
2016 // remove medium nodes that could become free
2017 for ( int i = 20; i < volTool.NbNodes(); ++i )
2018 if ( volNodes[i]->NbInverseElements() == 0 )
2019 GetMeshDS()->RemoveNode( volNodes[i] );
2021 } // loop on volumes to split
2023 myLastCreatedNodes = newNodes;
2024 myLastCreatedElems = newElems;
2027 //=======================================================================
2028 //function : AddToSameGroups
2029 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2030 //=======================================================================
2032 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2033 const SMDS_MeshElement* elemInGroups,
2034 SMESHDS_Mesh * aMesh)
2036 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2037 if (!groups.empty()) {
2038 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2039 for ( ; grIt != groups.end(); grIt++ ) {
2040 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2041 if ( group && group->Contains( elemInGroups ))
2042 group->SMDSGroup().Add( elemToAdd );
2048 //=======================================================================
2049 //function : RemoveElemFromGroups
2050 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2051 //=======================================================================
2052 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2053 SMESHDS_Mesh * aMesh)
2055 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2056 if (!groups.empty())
2058 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2059 for (; GrIt != groups.end(); GrIt++)
2061 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2062 if (!grp || grp->IsEmpty()) continue;
2063 grp->SMDSGroup().Remove(removeelem);
2068 //================================================================================
2070 * \brief Replace elemToRm by elemToAdd in the all groups
2072 //================================================================================
2074 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2075 const SMDS_MeshElement* elemToAdd,
2076 SMESHDS_Mesh * aMesh)
2078 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2079 if (!groups.empty()) {
2080 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2081 for ( ; grIt != groups.end(); grIt++ ) {
2082 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2083 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2084 group->SMDSGroup().Add( elemToAdd );
2089 //================================================================================
2091 * \brief Replace elemToRm by elemToAdd in the all groups
2093 //================================================================================
2095 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2096 const vector<const SMDS_MeshElement*>& elemToAdd,
2097 SMESHDS_Mesh * aMesh)
2099 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2100 if (!groups.empty())
2102 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2103 for ( ; grIt != groups.end(); grIt++ ) {
2104 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2105 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2106 for ( int i = 0; i < elemToAdd.size(); ++i )
2107 group->SMDSGroup().Add( elemToAdd[ i ] );
2112 //=======================================================================
2113 //function : QuadToTri
2114 //purpose : Cut quadrangles into triangles.
2115 // theCrit is used to select a diagonal to cut
2116 //=======================================================================
2118 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2119 const bool the13Diag)
2121 myLastCreatedElems.Clear();
2122 myLastCreatedNodes.Clear();
2124 MESSAGE( "::QuadToTri()" );
2126 SMESHDS_Mesh * aMesh = GetMeshDS();
2128 Handle(Geom_Surface) surface;
2129 SMESH_MesherHelper helper( *GetMesh() );
2131 TIDSortedElemSet::iterator itElem;
2132 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2133 const SMDS_MeshElement* elem = *itElem;
2134 if ( !elem || elem->GetType() != SMDSAbs_Face )
2136 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2137 if(!isquad) continue;
2139 if(elem->NbNodes()==4) {
2140 // retrieve element nodes
2141 const SMDS_MeshNode* aNodes [4];
2142 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2144 while ( itN->more() )
2145 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2147 int aShapeId = FindShape( elem );
2148 const SMDS_MeshElement* newElem1 = 0;
2149 const SMDS_MeshElement* newElem2 = 0;
2151 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2152 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2155 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2156 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2158 myLastCreatedElems.Append(newElem1);
2159 myLastCreatedElems.Append(newElem2);
2160 // put a new triangle on the same shape and add to the same groups
2163 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2164 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2166 AddToSameGroups( newElem1, elem, aMesh );
2167 AddToSameGroups( newElem2, elem, aMesh );
2168 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2169 aMesh->RemoveElement( elem );
2172 // Quadratic quadrangle
2174 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2176 // get surface elem is on
2177 int aShapeId = FindShape( elem );
2178 if ( aShapeId != helper.GetSubShapeID() ) {
2182 shape = aMesh->IndexToShape( aShapeId );
2183 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2184 TopoDS_Face face = TopoDS::Face( shape );
2185 surface = BRep_Tool::Surface( face );
2186 if ( !surface.IsNull() )
2187 helper.SetSubShape( shape );
2191 const SMDS_MeshNode* aNodes [8];
2192 const SMDS_MeshNode* inFaceNode = 0;
2193 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2195 while ( itN->more() ) {
2196 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2197 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2198 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2200 inFaceNode = aNodes[ i-1 ];
2204 // find middle point for (0,1,2,3)
2205 // and create a node in this point;
2207 if ( surface.IsNull() ) {
2209 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2213 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2216 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2218 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2220 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2221 myLastCreatedNodes.Append(newN);
2223 // create a new element
2224 const SMDS_MeshElement* newElem1 = 0;
2225 const SMDS_MeshElement* newElem2 = 0;
2227 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2228 aNodes[6], aNodes[7], newN );
2229 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2230 newN, aNodes[4], aNodes[5] );
2233 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2234 aNodes[7], aNodes[4], newN );
2235 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2236 newN, aNodes[5], aNodes[6] );
2238 myLastCreatedElems.Append(newElem1);
2239 myLastCreatedElems.Append(newElem2);
2240 // put a new triangle on the same shape and add to the same groups
2243 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2244 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2246 AddToSameGroups( newElem1, elem, aMesh );
2247 AddToSameGroups( newElem2, elem, aMesh );
2248 aMesh->RemoveElement( elem );
2255 //=======================================================================
2256 //function : getAngle
2258 //=======================================================================
2260 double getAngle(const SMDS_MeshElement * tr1,
2261 const SMDS_MeshElement * tr2,
2262 const SMDS_MeshNode * n1,
2263 const SMDS_MeshNode * n2)
2265 double angle = 2. * M_PI; // bad angle
2268 SMESH::Controls::TSequenceOfXYZ P1, P2;
2269 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2270 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2273 if(!tr1->IsQuadratic())
2274 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2276 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2277 if ( N1.SquareMagnitude() <= gp::Resolution() )
2279 if(!tr2->IsQuadratic())
2280 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2282 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2283 if ( N2.SquareMagnitude() <= gp::Resolution() )
2286 // find the first diagonal node n1 in the triangles:
2287 // take in account a diagonal link orientation
2288 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2289 for ( int t = 0; t < 2; t++ ) {
2290 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2291 int i = 0, iDiag = -1;
2292 while ( it->more()) {
2293 const SMDS_MeshElement *n = it->next();
2294 if ( n == n1 || n == n2 ) {
2298 if ( i - iDiag == 1 )
2299 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2308 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2311 angle = N1.Angle( N2 );
2316 // =================================================
2317 // class generating a unique ID for a pair of nodes
2318 // and able to return nodes by that ID
2319 // =================================================
2323 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2324 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2327 long GetLinkID (const SMDS_MeshNode * n1,
2328 const SMDS_MeshNode * n2) const
2330 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2333 bool GetNodes (const long theLinkID,
2334 const SMDS_MeshNode* & theNode1,
2335 const SMDS_MeshNode* & theNode2) const
2337 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2338 if ( !theNode1 ) return false;
2339 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2340 if ( !theNode2 ) return false;
2346 const SMESHDS_Mesh* myMesh;
2351 //=======================================================================
2352 //function : TriToQuad
2353 //purpose : Fuse neighbour triangles into quadrangles.
2354 // theCrit is used to select a neighbour to fuse with.
2355 // theMaxAngle is a max angle between element normals at which
2356 // fusion is still performed.
2357 //=======================================================================
2359 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2360 SMESH::Controls::NumericalFunctorPtr theCrit,
2361 const double theMaxAngle)
2363 myLastCreatedElems.Clear();
2364 myLastCreatedNodes.Clear();
2366 MESSAGE( "::TriToQuad()" );
2368 if ( !theCrit.get() )
2371 SMESHDS_Mesh * aMesh = GetMeshDS();
2373 // Prepare data for algo: build
2374 // 1. map of elements with their linkIDs
2375 // 2. map of linkIDs with their elements
2377 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2378 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2379 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2380 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2382 TIDSortedElemSet::iterator itElem;
2383 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2384 const SMDS_MeshElement* elem = *itElem;
2385 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2386 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2387 if(!IsTria) continue;
2389 // retrieve element nodes
2390 const SMDS_MeshNode* aNodes [4];
2391 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2394 aNodes[ i++ ] = cast2Node( itN->next() );
2395 aNodes[ 3 ] = aNodes[ 0 ];
2398 for ( i = 0; i < 3; i++ ) {
2399 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2400 // check if elements sharing a link can be fused
2401 itLE = mapLi_listEl.find( link );
2402 if ( itLE != mapLi_listEl.end() ) {
2403 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2405 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2406 //if ( FindShape( elem ) != FindShape( elem2 ))
2407 // continue; // do not fuse triangles laying on different shapes
2408 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2409 continue; // avoid making badly shaped quads
2410 (*itLE).second.push_back( elem );
2413 mapLi_listEl[ link ].push_back( elem );
2415 mapEl_setLi [ elem ].insert( link );
2418 // Clean the maps from the links shared by a sole element, ie
2419 // links to which only one element is bound in mapLi_listEl
2421 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2422 int nbElems = (*itLE).second.size();
2423 if ( nbElems < 2 ) {
2424 const SMDS_MeshElement* elem = (*itLE).second.front();
2425 SMESH_TLink link = (*itLE).first;
2426 mapEl_setLi[ elem ].erase( link );
2427 if ( mapEl_setLi[ elem ].empty() )
2428 mapEl_setLi.erase( elem );
2432 // Algo: fuse triangles into quadrangles
2434 while ( ! mapEl_setLi.empty() ) {
2435 // Look for the start element:
2436 // the element having the least nb of shared links
2437 const SMDS_MeshElement* startElem = 0;
2439 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2440 int nbLinks = (*itEL).second.size();
2441 if ( nbLinks < minNbLinks ) {
2442 startElem = (*itEL).first;
2443 minNbLinks = nbLinks;
2444 if ( minNbLinks == 1 )
2449 // search elements to fuse starting from startElem or links of elements
2450 // fused earlyer - startLinks
2451 list< SMESH_TLink > startLinks;
2452 while ( startElem || !startLinks.empty() ) {
2453 while ( !startElem && !startLinks.empty() ) {
2454 // Get an element to start, by a link
2455 SMESH_TLink linkId = startLinks.front();
2456 startLinks.pop_front();
2457 itLE = mapLi_listEl.find( linkId );
2458 if ( itLE != mapLi_listEl.end() ) {
2459 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2460 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2461 for ( ; itE != listElem.end() ; itE++ )
2462 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2464 mapLi_listEl.erase( itLE );
2469 // Get candidates to be fused
2470 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2471 const SMESH_TLink *link12, *link13;
2473 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2474 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2475 ASSERT( !setLi.empty() );
2476 set< SMESH_TLink >::iterator itLi;
2477 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2479 const SMESH_TLink & link = (*itLi);
2480 itLE = mapLi_listEl.find( link );
2481 if ( itLE == mapLi_listEl.end() )
2484 const SMDS_MeshElement* elem = (*itLE).second.front();
2486 elem = (*itLE).second.back();
2487 mapLi_listEl.erase( itLE );
2488 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2499 // add other links of elem to list of links to re-start from
2500 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2501 set< SMESH_TLink >::iterator it;
2502 for ( it = links.begin(); it != links.end(); it++ ) {
2503 const SMESH_TLink& link2 = (*it);
2504 if ( link2 != link )
2505 startLinks.push_back( link2 );
2509 // Get nodes of possible quadrangles
2510 const SMDS_MeshNode *n12 [4], *n13 [4];
2511 bool Ok12 = false, Ok13 = false;
2512 const SMDS_MeshNode *linkNode1, *linkNode2;
2514 linkNode1 = link12->first;
2515 linkNode2 = link12->second;
2516 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2520 linkNode1 = link13->first;
2521 linkNode2 = link13->second;
2522 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2526 // Choose a pair to fuse
2527 if ( Ok12 && Ok13 ) {
2528 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2529 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2530 double aBadRate12 = getBadRate( &quad12, theCrit );
2531 double aBadRate13 = getBadRate( &quad13, theCrit );
2532 if ( aBadRate13 < aBadRate12 )
2539 // and remove fused elems and removed links from the maps
2540 mapEl_setLi.erase( tr1 );
2542 mapEl_setLi.erase( tr2 );
2543 mapLi_listEl.erase( *link12 );
2544 if(tr1->NbNodes()==3) {
2545 const SMDS_MeshElement* newElem = 0;
2546 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2547 myLastCreatedElems.Append(newElem);
2548 AddToSameGroups( newElem, tr1, aMesh );
2549 int aShapeId = tr1->getshapeId();
2552 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2554 aMesh->RemoveElement( tr1 );
2555 aMesh->RemoveElement( tr2 );
2558 const SMDS_MeshNode* N1 [6];
2559 const SMDS_MeshNode* N2 [6];
2560 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2561 // now we receive following N1 and N2 (using numeration as above image)
2562 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2563 // i.e. first nodes from both arrays determ new diagonal
2564 const SMDS_MeshNode* aNodes[8];
2573 const SMDS_MeshElement* newElem = 0;
2574 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2575 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2576 myLastCreatedElems.Append(newElem);
2577 AddToSameGroups( newElem, tr1, aMesh );
2578 int aShapeId = tr1->getshapeId();
2581 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2583 aMesh->RemoveElement( tr1 );
2584 aMesh->RemoveElement( tr2 );
2585 // remove middle node (9)
2586 GetMeshDS()->RemoveNode( N1[4] );
2590 mapEl_setLi.erase( tr3 );
2591 mapLi_listEl.erase( *link13 );
2592 if(tr1->NbNodes()==3) {
2593 const SMDS_MeshElement* newElem = 0;
2594 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2595 myLastCreatedElems.Append(newElem);
2596 AddToSameGroups( newElem, tr1, aMesh );
2597 int aShapeId = tr1->getshapeId();
2600 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2602 aMesh->RemoveElement( tr1 );
2603 aMesh->RemoveElement( tr3 );
2606 const SMDS_MeshNode* N1 [6];
2607 const SMDS_MeshNode* N2 [6];
2608 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2609 // now we receive following N1 and N2 (using numeration as above image)
2610 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2611 // i.e. first nodes from both arrays determ new diagonal
2612 const SMDS_MeshNode* aNodes[8];
2621 const SMDS_MeshElement* newElem = 0;
2622 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2623 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2624 myLastCreatedElems.Append(newElem);
2625 AddToSameGroups( newElem, tr1, aMesh );
2626 int aShapeId = tr1->getshapeId();
2629 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2631 aMesh->RemoveElement( tr1 );
2632 aMesh->RemoveElement( tr3 );
2633 // remove middle node (9)
2634 GetMeshDS()->RemoveNode( N1[4] );
2638 // Next element to fuse: the rejected one
2640 startElem = Ok12 ? tr3 : tr2;
2642 } // if ( startElem )
2643 } // while ( startElem || !startLinks.empty() )
2644 } // while ( ! mapEl_setLi.empty() )
2650 /*#define DUMPSO(txt) \
2651 // cout << txt << endl;
2652 //=============================================================================
2656 //=============================================================================
2657 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2661 int tmp = idNodes[ i1 ];
2662 idNodes[ i1 ] = idNodes[ i2 ];
2663 idNodes[ i2 ] = tmp;
2664 gp_Pnt Ptmp = P[ i1 ];
2667 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2670 //=======================================================================
2671 //function : SortQuadNodes
2672 //purpose : Set 4 nodes of a quadrangle face in a good order.
2673 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2675 //=======================================================================
2677 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2682 for ( i = 0; i < 4; i++ ) {
2683 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2685 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2688 gp_Vec V1(P[0], P[1]);
2689 gp_Vec V2(P[0], P[2]);
2690 gp_Vec V3(P[0], P[3]);
2692 gp_Vec Cross1 = V1 ^ V2;
2693 gp_Vec Cross2 = V2 ^ V3;
2696 if (Cross1.Dot(Cross2) < 0)
2701 if (Cross1.Dot(Cross2) < 0)
2705 swap ( i, i + 1, idNodes, P );
2707 // for ( int ii = 0; ii < 4; ii++ ) {
2708 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2709 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2715 //=======================================================================
2716 //function : SortHexaNodes
2717 //purpose : Set 8 nodes of a hexahedron in a good order.
2718 // Return success status
2719 //=======================================================================
2721 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2726 DUMPSO( "INPUT: ========================================");
2727 for ( i = 0; i < 8; i++ ) {
2728 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2729 if ( !n ) return false;
2730 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2731 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2733 DUMPSO( "========================================");
2736 set<int> faceNodes; // ids of bottom face nodes, to be found
2737 set<int> checkedId1; // ids of tried 2-nd nodes
2738 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2739 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2740 int iMin, iLoop1 = 0;
2742 // Loop to try the 2-nd nodes
2744 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2746 // Find not checked 2-nd node
2747 for ( i = 1; i < 8; i++ )
2748 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2749 int id1 = idNodes[i];
2750 swap ( 1, i, idNodes, P );
2751 checkedId1.insert ( id1 );
2755 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2756 // ie that all but meybe one (id3 which is on the same face) nodes
2757 // lay on the same side from the triangle plane.
2759 bool manyInPlane = false; // more than 4 nodes lay in plane
2761 while ( ++iLoop2 < 6 ) {
2763 // get 1-2-3 plane coeffs
2764 Standard_Real A, B, C, D;
2765 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2766 if ( N.SquareMagnitude() > gp::Resolution() )
2768 gp_Pln pln ( P[0], N );
2769 pln.Coefficients( A, B, C, D );
2771 // find the node (iMin) closest to pln
2772 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2774 for ( i = 3; i < 8; i++ ) {
2775 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2776 if ( fabs( dist[i] ) < minDist ) {
2777 minDist = fabs( dist[i] );
2780 if ( fabs( dist[i] ) <= tol )
2781 idInPln.insert( idNodes[i] );
2784 // there should not be more than 4 nodes in bottom plane
2785 if ( idInPln.size() > 1 )
2787 DUMPSO( "### idInPln.size() = " << idInPln.size());
2788 // idInPlane does not contain the first 3 nodes
2789 if ( manyInPlane || idInPln.size() == 5)
2790 return false; // all nodes in one plane
2793 // set the 1-st node to be not in plane
2794 for ( i = 3; i < 8; i++ ) {
2795 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2796 DUMPSO( "### Reset 0-th node");
2797 swap( 0, i, idNodes, P );
2802 // reset to re-check second nodes
2803 leastDist = DBL_MAX;
2807 break; // from iLoop2;
2810 // check that the other 4 nodes are on the same side
2811 bool sameSide = true;
2812 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2813 for ( i = 3; sameSide && i < 8; i++ ) {
2815 sameSide = ( isNeg == dist[i] <= 0.);
2818 // keep best solution
2819 if ( sameSide && minDist < leastDist ) {
2820 leastDist = minDist;
2822 faceNodes.insert( idNodes[ 1 ] );
2823 faceNodes.insert( idNodes[ 2 ] );
2824 faceNodes.insert( idNodes[ iMin ] );
2825 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2826 << " leastDist = " << leastDist);
2827 if ( leastDist <= DBL_MIN )
2832 // set next 3-d node to check
2833 int iNext = 2 + iLoop2;
2835 DUMPSO( "Try 2-nd");
2836 swap ( 2, iNext, idNodes, P );
2838 } // while ( iLoop2 < 6 )
2841 if ( faceNodes.empty() ) return false;
2843 // Put the faceNodes in proper places
2844 for ( i = 4; i < 8; i++ ) {
2845 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2846 // find a place to put
2848 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2850 DUMPSO( "Set faceNodes");
2851 swap ( iTo, i, idNodes, P );
2856 // Set nodes of the found bottom face in good order
2857 DUMPSO( " Found bottom face: ");
2858 i = SortQuadNodes( theMesh, idNodes );
2860 gp_Pnt Ptmp = P[ i ];
2865 // for ( int ii = 0; ii < 4; ii++ ) {
2866 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2867 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2870 // Gravity center of the top and bottom faces
2871 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2872 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2874 // Get direction from the bottom to the top face
2875 gp_Vec upDir ( aGCb, aGCt );
2876 Standard_Real upDirSize = upDir.Magnitude();
2877 if ( upDirSize <= gp::Resolution() ) return false;
2880 // Assure that the bottom face normal points up
2881 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2882 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2883 if ( Nb.Dot( upDir ) < 0 ) {
2884 DUMPSO( "Reverse bottom face");
2885 swap( 1, 3, idNodes, P );
2888 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2889 Standard_Real minDist = DBL_MAX;
2890 for ( i = 4; i < 8; i++ ) {
2891 // projection of P[i] to the plane defined by P[0] and upDir
2892 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2893 Standard_Real sqDist = P[0].SquareDistance( Pp );
2894 if ( sqDist < minDist ) {
2899 DUMPSO( "Set 4-th");
2900 swap ( 4, iMin, idNodes, P );
2902 // Set nodes of the top face in good order
2903 DUMPSO( "Sort top face");
2904 i = SortQuadNodes( theMesh, &idNodes[4] );
2907 gp_Pnt Ptmp = P[ i ];
2912 // Assure that direction of the top face normal is from the bottom face
2913 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2914 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2915 if ( Nt.Dot( upDir ) < 0 ) {
2916 DUMPSO( "Reverse top face");
2917 swap( 5, 7, idNodes, P );
2920 // DUMPSO( "OUTPUT: ========================================");
2921 // for ( i = 0; i < 8; i++ ) {
2922 // float *p = ugrid->GetPoint(idNodes[i]);
2923 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2929 //================================================================================
2931 * \brief Return nodes linked to the given one
2932 * \param theNode - the node
2933 * \param linkedNodes - the found nodes
2934 * \param type - the type of elements to check
2936 * Medium nodes are ignored
2938 //================================================================================
2940 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2941 TIDSortedElemSet & linkedNodes,
2942 SMDSAbs_ElementType type )
2944 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2945 while ( elemIt->more() )
2947 const SMDS_MeshElement* elem = elemIt->next();
2948 if(elem->GetType() == SMDSAbs_0DElement)
2951 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2952 if ( elem->GetType() == SMDSAbs_Volume )
2954 SMDS_VolumeTool vol( elem );
2955 while ( nodeIt->more() ) {
2956 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2957 if ( theNode != n && vol.IsLinked( theNode, n ))
2958 linkedNodes.insert( n );
2963 for ( int i = 0; nodeIt->more(); ++i ) {
2964 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2965 if ( n == theNode ) {
2966 int iBefore = i - 1;
2968 if ( elem->IsQuadratic() ) {
2969 int nb = elem->NbNodes() / 2;
2970 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2971 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2973 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2974 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2981 //=======================================================================
2982 //function : laplacianSmooth
2983 //purpose : pulls theNode toward the center of surrounding nodes directly
2984 // connected to that node along an element edge
2985 //=======================================================================
2987 void laplacianSmooth(const SMDS_MeshNode* theNode,
2988 const Handle(Geom_Surface)& theSurface,
2989 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2991 // find surrounding nodes
2993 TIDSortedElemSet nodeSet;
2994 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2996 // compute new coodrs
2998 double coord[] = { 0., 0., 0. };
2999 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3000 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3001 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3002 if ( theSurface.IsNull() ) { // smooth in 3D
3003 coord[0] += node->X();
3004 coord[1] += node->Y();
3005 coord[2] += node->Z();
3007 else { // smooth in 2D
3008 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3009 gp_XY* uv = theUVMap[ node ];
3010 coord[0] += uv->X();
3011 coord[1] += uv->Y();
3014 int nbNodes = nodeSet.size();
3017 coord[0] /= nbNodes;
3018 coord[1] /= nbNodes;
3020 if ( !theSurface.IsNull() ) {
3021 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3022 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3023 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3029 coord[2] /= nbNodes;
3033 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3036 //=======================================================================
3037 //function : centroidalSmooth
3038 //purpose : pulls theNode toward the element-area-weighted centroid of the
3039 // surrounding elements
3040 //=======================================================================
3042 void centroidalSmooth(const SMDS_MeshNode* theNode,
3043 const Handle(Geom_Surface)& theSurface,
3044 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3046 gp_XYZ aNewXYZ(0.,0.,0.);
3047 SMESH::Controls::Area anAreaFunc;
3048 double totalArea = 0.;
3053 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3054 while ( elemIt->more() )
3056 const SMDS_MeshElement* elem = elemIt->next();
3059 gp_XYZ elemCenter(0.,0.,0.);
3060 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3061 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3062 int nn = elem->NbNodes();
3063 if(elem->IsQuadratic()) nn = nn/2;
3065 //while ( itN->more() ) {
3067 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3069 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3070 aNodePoints.push_back( aP );
3071 if ( !theSurface.IsNull() ) { // smooth in 2D
3072 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3073 gp_XY* uv = theUVMap[ aNode ];
3074 aP.SetCoord( uv->X(), uv->Y(), 0. );
3078 double elemArea = anAreaFunc.GetValue( aNodePoints );
3079 totalArea += elemArea;
3081 aNewXYZ += elemCenter * elemArea;
3083 aNewXYZ /= totalArea;
3084 if ( !theSurface.IsNull() ) {
3085 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3086 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3091 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3094 //=======================================================================
3095 //function : getClosestUV
3096 //purpose : return UV of closest projection
3097 //=======================================================================
3099 static bool getClosestUV (Extrema_GenExtPS& projector,
3100 const gp_Pnt& point,
3103 projector.Perform( point );
3104 if ( projector.IsDone() ) {
3105 double u, v, minVal = DBL_MAX;
3106 for ( int i = projector.NbExt(); i > 0; i-- )
3107 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3108 if ( projector.SquareDistance( i ) < minVal ) {
3109 minVal = projector.SquareDistance( i );
3111 if ( projector.Value( i ) < minVal ) {
3112 minVal = projector.Value( i );
3114 projector.Point( i ).Parameter( u, v );
3116 result.SetCoord( u, v );
3122 //=======================================================================
3124 //purpose : Smooth theElements during theNbIterations or until a worst
3125 // element has aspect ratio <= theTgtAspectRatio.
3126 // Aspect Ratio varies in range [1.0, inf].
3127 // If theElements is empty, the whole mesh is smoothed.
3128 // theFixedNodes contains additionally fixed nodes. Nodes built
3129 // on edges and boundary nodes are always fixed.
3130 //=======================================================================
3132 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3133 set<const SMDS_MeshNode*> & theFixedNodes,
3134 const SmoothMethod theSmoothMethod,
3135 const int theNbIterations,
3136 double theTgtAspectRatio,
3139 myLastCreatedElems.Clear();
3140 myLastCreatedNodes.Clear();
3142 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3144 if ( theTgtAspectRatio < 1.0 )
3145 theTgtAspectRatio = 1.0;
3147 const double disttol = 1.e-16;
3149 SMESH::Controls::AspectRatio aQualityFunc;
3151 SMESHDS_Mesh* aMesh = GetMeshDS();
3153 if ( theElems.empty() ) {
3154 // add all faces to theElems
3155 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3156 while ( fIt->more() ) {
3157 const SMDS_MeshElement* face = fIt->next();
3158 theElems.insert( theElems.end(), face );
3161 // get all face ids theElems are on
3162 set< int > faceIdSet;
3163 TIDSortedElemSet::iterator itElem;
3165 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3166 int fId = FindShape( *itElem );
3167 // check that corresponding submesh exists and a shape is face
3169 faceIdSet.find( fId ) == faceIdSet.end() &&
3170 aMesh->MeshElements( fId )) {
3171 TopoDS_Shape F = aMesh->IndexToShape( fId );
3172 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3173 faceIdSet.insert( fId );
3176 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3178 // ===============================================
3179 // smooth elements on each TopoDS_Face separately
3180 // ===============================================
3182 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3183 for ( ; fId != faceIdSet.rend(); ++fId ) {
3184 // get face surface and submesh
3185 Handle(Geom_Surface) surface;
3186 SMESHDS_SubMesh* faceSubMesh = 0;
3188 double fToler2 = 0, f,l;
3189 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3190 bool isUPeriodic = false, isVPeriodic = false;
3192 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3193 surface = BRep_Tool::Surface( face );
3194 faceSubMesh = aMesh->MeshElements( *fId );
3195 fToler2 = BRep_Tool::Tolerance( face );
3196 fToler2 *= fToler2 * 10.;
3197 isUPeriodic = surface->IsUPeriodic();
3200 isVPeriodic = surface->IsVPeriodic();
3203 surface->Bounds( u1, u2, v1, v2 );
3205 // ---------------------------------------------------------
3206 // for elements on a face, find movable and fixed nodes and
3207 // compute UV for them
3208 // ---------------------------------------------------------
3209 bool checkBoundaryNodes = false;
3210 bool isQuadratic = false;
3211 set<const SMDS_MeshNode*> setMovableNodes;
3212 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3213 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3214 list< const SMDS_MeshElement* > elemsOnFace;
3216 Extrema_GenExtPS projector;
3217 GeomAdaptor_Surface surfAdaptor;
3218 if ( !surface.IsNull() ) {
3219 surfAdaptor.Load( surface );
3220 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3222 int nbElemOnFace = 0;
3223 itElem = theElems.begin();
3224 // loop on not yet smoothed elements: look for elems on a face
3225 while ( itElem != theElems.end() ) {
3226 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3227 break; // all elements found
3229 const SMDS_MeshElement* elem = *itElem;
3230 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3231 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3235 elemsOnFace.push_back( elem );
3236 theElems.erase( itElem++ );
3240 isQuadratic = elem->IsQuadratic();
3242 // get movable nodes of elem
3243 const SMDS_MeshNode* node;
3244 SMDS_TypeOfPosition posType;
3245 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3246 int nn = 0, nbn = elem->NbNodes();
3247 if(elem->IsQuadratic())
3249 while ( nn++ < nbn ) {
3250 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3251 const SMDS_PositionPtr& pos = node->GetPosition();
3252 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3253 if (posType != SMDS_TOP_EDGE &&
3254 posType != SMDS_TOP_VERTEX &&
3255 theFixedNodes.find( node ) == theFixedNodes.end())
3257 // check if all faces around the node are on faceSubMesh
3258 // because a node on edge may be bound to face
3259 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3261 if ( faceSubMesh ) {
3262 while ( eIt->more() && all ) {
3263 const SMDS_MeshElement* e = eIt->next();
3264 all = faceSubMesh->Contains( e );
3268 setMovableNodes.insert( node );
3270 checkBoundaryNodes = true;
3272 if ( posType == SMDS_TOP_3DSPACE )
3273 checkBoundaryNodes = true;
3276 if ( surface.IsNull() )
3279 // get nodes to check UV
3280 list< const SMDS_MeshNode* > uvCheckNodes;
3281 itN = elem->nodesIterator();
3282 nn = 0; nbn = elem->NbNodes();
3283 if(elem->IsQuadratic())
3285 while ( nn++ < nbn ) {
3286 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3287 if ( uvMap.find( node ) == uvMap.end() )
3288 uvCheckNodes.push_back( node );
3289 // add nodes of elems sharing node
3290 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3291 // while ( eIt->more() ) {
3292 // const SMDS_MeshElement* e = eIt->next();
3293 // if ( e != elem ) {
3294 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3295 // while ( nIt->more() ) {
3296 // const SMDS_MeshNode* n =
3297 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3298 // if ( uvMap.find( n ) == uvMap.end() )
3299 // uvCheckNodes.push_back( n );
3305 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3306 for ( ; n != uvCheckNodes.end(); ++n ) {
3309 const SMDS_PositionPtr& pos = node->GetPosition();
3310 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3312 switch ( posType ) {
3313 case SMDS_TOP_FACE: {
3314 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3315 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3318 case SMDS_TOP_EDGE: {
3319 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3320 Handle(Geom2d_Curve) pcurve;
3321 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3322 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3323 if ( !pcurve.IsNull() ) {
3324 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3325 uv = pcurve->Value( u ).XY();
3329 case SMDS_TOP_VERTEX: {
3330 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3331 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3332 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3337 // check existing UV
3338 bool project = true;
3339 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3340 double dist1 = DBL_MAX, dist2 = 0;
3341 if ( posType != SMDS_TOP_3DSPACE ) {
3342 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3343 project = dist1 > fToler2;
3345 if ( project ) { // compute new UV
3347 if ( !getClosestUV( projector, pNode, newUV )) {
3348 MESSAGE("Node Projection Failed " << node);
3352 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3354 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3356 if ( posType != SMDS_TOP_3DSPACE )
3357 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3358 if ( dist2 < dist1 )
3362 // store UV in the map
3363 listUV.push_back( uv );
3364 uvMap.insert( make_pair( node, &listUV.back() ));
3366 } // loop on not yet smoothed elements
3368 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3369 checkBoundaryNodes = true;
3371 // fix nodes on mesh boundary
3373 if ( checkBoundaryNodes ) {
3374 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3375 map< SMESH_TLink, int >::iterator link_nb;
3376 // put all elements links to linkNbMap
3377 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3378 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3379 const SMDS_MeshElement* elem = (*elemIt);
3380 int nbn = elem->NbCornerNodes();
3381 // loop on elem links: insert them in linkNbMap
3382 for ( int iN = 0; iN < nbn; ++iN ) {
3383 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3384 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3385 SMESH_TLink link( n1, n2 );
3386 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3390 // remove nodes that are in links encountered only once from setMovableNodes
3391 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3392 if ( link_nb->second == 1 ) {
3393 setMovableNodes.erase( link_nb->first.node1() );
3394 setMovableNodes.erase( link_nb->first.node2() );
3399 // -----------------------------------------------------
3400 // for nodes on seam edge, compute one more UV ( uvMap2 );
3401 // find movable nodes linked to nodes on seam and which
3402 // are to be smoothed using the second UV ( uvMap2 )
3403 // -----------------------------------------------------
3405 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3406 if ( !surface.IsNull() ) {
3407 TopExp_Explorer eExp( face, TopAbs_EDGE );
3408 for ( ; eExp.More(); eExp.Next() ) {
3409 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3410 if ( !BRep_Tool::IsClosed( edge, face ))
3412 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3413 if ( !sm ) continue;
3414 // find out which parameter varies for a node on seam
3417 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3418 if ( pcurve.IsNull() ) continue;
3419 uv1 = pcurve->Value( f );
3421 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3422 if ( pcurve.IsNull() ) continue;
3423 uv2 = pcurve->Value( f );
3424 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3426 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3427 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3429 // get nodes on seam and its vertices
3430 list< const SMDS_MeshNode* > seamNodes;
3431 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3432 while ( nSeamIt->more() ) {
3433 const SMDS_MeshNode* node = nSeamIt->next();
3434 if ( !isQuadratic || !IsMedium( node ))
3435 seamNodes.push_back( node );
3437 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3438 for ( ; vExp.More(); vExp.Next() ) {
3439 sm = aMesh->MeshElements( vExp.Current() );
3441 nSeamIt = sm->GetNodes();
3442 while ( nSeamIt->more() )
3443 seamNodes.push_back( nSeamIt->next() );
3446 // loop on nodes on seam
3447 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3448 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3449 const SMDS_MeshNode* nSeam = *noSeIt;
3450 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3451 if ( n_uv == uvMap.end() )
3454 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3455 // set the second UV
3456 listUV.push_back( *n_uv->second );
3457 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3458 if ( uvMap2.empty() )
3459 uvMap2 = uvMap; // copy the uvMap contents
3460 uvMap2[ nSeam ] = &listUV.back();
3462 // collect movable nodes linked to ones on seam in nodesNearSeam
3463 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3464 while ( eIt->more() ) {
3465 const SMDS_MeshElement* e = eIt->next();
3466 int nbUseMap1 = 0, nbUseMap2 = 0;
3467 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3468 int nn = 0, nbn = e->NbNodes();
3469 if(e->IsQuadratic()) nbn = nbn/2;
3470 while ( nn++ < nbn )
3472 const SMDS_MeshNode* n =
3473 static_cast<const SMDS_MeshNode*>( nIt->next() );
3475 setMovableNodes.find( n ) == setMovableNodes.end() )
3477 // add only nodes being closer to uv2 than to uv1
3478 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3479 0.5 * ( n->Y() + nSeam->Y() ),
3480 0.5 * ( n->Z() + nSeam->Z() ));
3482 getClosestUV( projector, pMid, uv );
3483 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3484 nodesNearSeam.insert( n );
3490 // for centroidalSmooth all element nodes must
3491 // be on one side of a seam
3492 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3493 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3495 while ( nn++ < nbn ) {
3496 const SMDS_MeshNode* n =
3497 static_cast<const SMDS_MeshNode*>( nIt->next() );
3498 setMovableNodes.erase( n );
3502 } // loop on nodes on seam
3503 } // loop on edge of a face
3504 } // if ( !face.IsNull() )
3506 if ( setMovableNodes.empty() ) {
3507 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3508 continue; // goto next face
3516 double maxRatio = -1., maxDisplacement = -1.;
3517 set<const SMDS_MeshNode*>::iterator nodeToMove;
3518 for ( it = 0; it < theNbIterations; it++ ) {
3519 maxDisplacement = 0.;
3520 nodeToMove = setMovableNodes.begin();
3521 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3522 const SMDS_MeshNode* node = (*nodeToMove);
3523 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3526 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3527 if ( theSmoothMethod == LAPLACIAN )
3528 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3530 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3532 // node displacement
3533 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3534 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3535 if ( aDispl > maxDisplacement )
3536 maxDisplacement = aDispl;
3538 // no node movement => exit
3539 //if ( maxDisplacement < 1.e-16 ) {
3540 if ( maxDisplacement < disttol ) {
3541 MESSAGE("-- no node movement --");
3545 // check elements quality
3547 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3548 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3549 const SMDS_MeshElement* elem = (*elemIt);
3550 if ( !elem || elem->GetType() != SMDSAbs_Face )
3552 SMESH::Controls::TSequenceOfXYZ aPoints;
3553 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3554 double aValue = aQualityFunc.GetValue( aPoints );
3555 if ( aValue > maxRatio )
3559 if ( maxRatio <= theTgtAspectRatio ) {
3560 MESSAGE("-- quality achived --");
3563 if (it+1 == theNbIterations) {
3564 MESSAGE("-- Iteration limit exceeded --");
3566 } // smoothing iterations
3568 MESSAGE(" Face id: " << *fId <<
3569 " Nb iterstions: " << it <<
3570 " Displacement: " << maxDisplacement <<
3571 " Aspect Ratio " << maxRatio);
3573 // ---------------------------------------
3574 // new nodes positions are computed,
3575 // record movement in DS and set new UV
3576 // ---------------------------------------
3577 nodeToMove = setMovableNodes.begin();
3578 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3579 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3580 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3581 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3582 if ( node_uv != uvMap.end() ) {
3583 gp_XY* uv = node_uv->second;
3585 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3589 // move medium nodes of quadratic elements
3592 SMESH_MesherHelper helper( *GetMesh() );
3593 if ( !face.IsNull() )
3594 helper.SetSubShape( face );
3595 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3596 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3597 const SMDS_VtkFace* QF =
3598 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3599 if(QF && QF->IsQuadratic()) {
3600 vector<const SMDS_MeshNode*> Ns;
3601 Ns.reserve(QF->NbNodes()+1);
3602 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3603 while ( anIter->more() )
3604 Ns.push_back( cast2Node(anIter->next()) );
3605 Ns.push_back( Ns[0] );
3607 for(int i=0; i<QF->NbNodes(); i=i+2) {
3608 if ( !surface.IsNull() ) {
3609 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3610 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3611 gp_XY uv = ( uv1 + uv2 ) / 2.;
3612 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3613 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3616 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3617 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3618 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3620 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3621 fabs( Ns[i+1]->Y() - y ) > disttol ||
3622 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3623 // we have to move i+1 node
3624 aMesh->MoveNode( Ns[i+1], x, y, z );
3631 } // loop on face ids
3635 //=======================================================================
3636 //function : isReverse
3637 //purpose : Return true if normal of prevNodes is not co-directied with
3638 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3639 // iNotSame is where prevNodes and nextNodes are different.
3640 // If result is true then future volume orientation is OK
3641 //=======================================================================
3643 static bool isReverse(const SMDS_MeshElement* face,
3644 const vector<const SMDS_MeshNode*>& prevNodes,
3645 const vector<const SMDS_MeshNode*>& nextNodes,
3649 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3650 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3651 gp_XYZ extrDir( pN - pP ), faceNorm;
3652 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3654 return faceNorm * extrDir < 0.0;
3657 //=======================================================================
3659 * \brief Create elements by sweeping an element
3660 * \param elem - element to sweep
3661 * \param newNodesItVec - nodes generated from each node of the element
3662 * \param newElems - generated elements
3663 * \param nbSteps - number of sweeping steps
3664 * \param srcElements - to append elem for each generated element
3666 //=======================================================================
3668 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3669 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3670 list<const SMDS_MeshElement*>& newElems,
3672 SMESH_SequenceOfElemPtr& srcElements)
3674 //MESSAGE("sweepElement " << nbSteps);
3675 SMESHDS_Mesh* aMesh = GetMeshDS();
3677 const int nbNodes = elem->NbNodes();
3678 const int nbCorners = elem->NbCornerNodes();
3679 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3680 polyhedron creation !!! */
3681 // Loop on elem nodes:
3682 // find new nodes and detect same nodes indices
3683 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3684 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3685 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3686 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3688 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3689 vector<int> sames(nbNodes);
3690 vector<bool> isSingleNode(nbNodes);
3692 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3693 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3694 const SMDS_MeshNode* node = nnIt->first;
3695 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3696 if ( listNewNodes.empty() )
3699 itNN [ iNode ] = listNewNodes.begin();
3700 prevNod[ iNode ] = node;
3701 nextNod[ iNode ] = listNewNodes.front();
3703 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3704 corner node of linear */
3705 if ( prevNod[ iNode ] != nextNod [ iNode ])
3706 nbDouble += !isSingleNode[iNode];
3708 if( iNode < nbCorners ) { // check corners only
3709 if ( prevNod[ iNode ] == nextNod [ iNode ])
3710 sames[nbSame++] = iNode;
3712 iNotSameNode = iNode;
3716 if ( nbSame == nbNodes || nbSame > 2) {
3717 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3721 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3723 // fix nodes order to have bottom normal external
3724 if ( baseType == SMDSEntity_Polygon )
3726 std::reverse( itNN.begin(), itNN.end() );
3727 std::reverse( prevNod.begin(), prevNod.end() );
3728 std::reverse( midlNod.begin(), midlNod.end() );
3729 std::reverse( nextNod.begin(), nextNod.end() );
3730 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3734 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3735 SMDS_MeshCell::applyInterlace( ind, itNN );
3736 SMDS_MeshCell::applyInterlace( ind, prevNod );
3737 SMDS_MeshCell::applyInterlace( ind, nextNod );
3738 SMDS_MeshCell::applyInterlace( ind, midlNod );
3739 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3742 sames[nbSame] = iNotSameNode;
3743 for ( int j = 0; j <= nbSame; ++j )
3744 for ( size_t i = 0; i < ind.size(); ++i )
3745 if ( ind[i] == sames[j] )
3750 iNotSameNode = sames[nbSame];
3755 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3757 iSameNode = sames[ nbSame-1 ];
3758 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3759 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3760 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3763 // make new elements
3764 for (int iStep = 0; iStep < nbSteps; iStep++ )
3767 for ( iNode = 0; iNode < nbNodes; iNode++ )
3769 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3770 nextNod[ iNode ] = *itNN[ iNode ]++;
3773 SMDS_MeshElement* aNewElem = 0;
3774 /*if(!elem->IsPoly())*/ {
3775 switch ( baseType ) {
3777 case SMDSEntity_Node: { // sweep NODE
3778 if ( nbSame == 0 ) {
3779 if ( isSingleNode[0] )
3780 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3782 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3788 case SMDSEntity_Edge: { // sweep EDGE
3789 if ( nbDouble == 0 )
3791 if ( nbSame == 0 ) // ---> quadrangle
3792 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3793 nextNod[ 1 ], nextNod[ 0 ] );
3794 else // ---> triangle
3795 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3796 nextNod[ iNotSameNode ] );
3798 else // ---> polygon
3800 vector<const SMDS_MeshNode*> poly_nodes;
3801 poly_nodes.push_back( prevNod[0] );
3802 poly_nodes.push_back( prevNod[1] );
3803 if ( prevNod[1] != nextNod[1] )
3805 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3806 poly_nodes.push_back( nextNod[1] );
3808 if ( prevNod[0] != nextNod[0] )
3810 poly_nodes.push_back( nextNod[0] );
3811 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3813 switch ( poly_nodes.size() ) {
3815 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3818 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3819 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3822 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3827 case SMDSEntity_Triangle: // TRIANGLE --->
3829 if ( nbDouble > 0 ) break;
3830 if ( nbSame == 0 ) // ---> pentahedron
3831 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3832 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3834 else if ( nbSame == 1 ) // ---> pyramid
3835 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3836 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3837 nextNod[ iSameNode ]);
3839 else // 2 same nodes: ---> tetrahedron
3840 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3841 nextNod[ iNotSameNode ]);
3844 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3848 if ( nbDouble+nbSame == 2 )
3850 if(nbSame==0) { // ---> quadratic quadrangle
3851 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3852 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3854 else { //(nbSame==1) // ---> quadratic triangle
3856 return; // medium node on axis
3858 else if(sames[0]==0)
3859 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3860 nextNod[2], midlNod[1], prevNod[2]);
3862 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3863 midlNod[0], nextNod[2], prevNod[2]);
3866 else if ( nbDouble == 3 )
3868 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3869 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3870 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3877 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3878 if ( nbDouble > 0 ) break;
3880 if ( nbSame == 0 ) // ---> hexahedron
3881 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3882 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3884 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3885 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3886 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3887 nextNod[ iSameNode ]);
3888 newElems.push_back( aNewElem );
3889 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3890 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3891 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3893 else if ( nbSame == 2 ) { // ---> pentahedron
3894 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3895 // iBeforeSame is same too
3896 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3897 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3898 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3900 // iAfterSame is same too
3901 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3902 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3903 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3907 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3908 if ( nbDouble+nbSame != 3 ) break;
3910 // ---> pentahedron with 15 nodes
3911 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3912 nextNod[0], nextNod[1], nextNod[2],
3913 prevNod[3], prevNod[4], prevNod[5],
3914 nextNod[3], nextNod[4], nextNod[5],
3915 midlNod[0], midlNod[1], midlNod[2]);
3917 else if(nbSame==1) {
3918 // ---> 2d order pyramid of 13 nodes
3919 int apex = iSameNode;
3920 int i0 = ( apex + 1 ) % nbCorners;
3921 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3925 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3926 nextNod[i0], nextNod[i1], prevNod[apex],
3927 prevNod[i01], midlNod[i0],
3928 nextNod[i01], midlNod[i1],
3929 prevNod[i1a], prevNod[i0a],
3930 nextNod[i0a], nextNod[i1a]);
3932 else if(nbSame==2) {
3933 // ---> 2d order tetrahedron of 10 nodes
3934 int n1 = iNotSameNode;
3935 int n2 = ( n1 + 1 ) % nbCorners;
3936 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3940 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3941 prevNod[n12], prevNod[n23], prevNod[n31],
3942 midlNod[n1], nextNod[n12], nextNod[n31]);
3946 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3948 if ( nbDouble != 4 ) break;
3949 // ---> hexahedron with 20 nodes
3950 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3951 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3952 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3953 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3954 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3956 else if(nbSame==1) {
3957 // ---> pyramid + pentahedron - can not be created since it is needed
3958 // additional middle node at the center of face
3959 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3962 else if( nbSame == 2 ) {
3963 if ( nbDouble != 2 ) break;
3964 // ---> 2d order Pentahedron with 15 nodes
3966 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3967 // iBeforeSame is same too
3974 // iAfterSame is same too
3984 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3985 prevNod[n4], prevNod[n5], nextNod[n5],
3986 prevNod[n12], midlNod[n2], nextNod[n12],
3987 prevNod[n45], midlNod[n5], nextNod[n45],
3988 prevNod[n14], prevNod[n25], nextNod[n25]);
3992 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3994 if( nbSame == 0 && nbDouble == 9 ) {
3995 // ---> tri-quadratic hexahedron with 27 nodes
3996 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3997 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3998 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3999 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4000 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4001 prevNod[8], // bottom center
4002 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4003 nextNod[8], // top center
4004 midlNod[8]);// elem center
4012 case SMDSEntity_Polygon: { // sweep POLYGON
4014 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4015 // ---> hexagonal prism
4016 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4017 prevNod[3], prevNod[4], prevNod[5],
4018 nextNod[0], nextNod[1], nextNod[2],
4019 nextNod[3], nextNod[4], nextNod[5]);
4023 case SMDSEntity_Ball:
4031 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4033 if ( baseType != SMDSEntity_Polygon )
4035 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4036 SMDS_MeshCell::applyInterlace( ind, prevNod );
4037 SMDS_MeshCell::applyInterlace( ind, nextNod );
4038 SMDS_MeshCell::applyInterlace( ind, midlNod );
4039 SMDS_MeshCell::applyInterlace( ind, itNN );
4040 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4041 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4043 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4044 vector<int> quantities (nbNodes + 2);
4045 polyedre_nodes.clear();
4049 for (int inode = 0; inode < nbNodes; inode++)
4050 polyedre_nodes.push_back( prevNod[inode] );
4051 quantities.push_back( nbNodes );
4054 polyedre_nodes.push_back( nextNod[0] );
4055 for (int inode = nbNodes; inode-1; --inode )
4056 polyedre_nodes.push_back( nextNod[inode-1] );
4057 quantities.push_back( nbNodes );
4060 for (int iface = 0; iface < nbNodes; iface++)
4062 const int prevNbNodes = polyedre_nodes.size();
4063 int inextface = (iface+1) % nbNodes;
4064 polyedre_nodes.push_back( prevNod[inextface] );
4065 polyedre_nodes.push_back( prevNod[iface] );
4066 if ( prevNod[iface] != nextNod[iface] )
4068 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4069 polyedre_nodes.push_back( nextNod[iface] );
4071 if ( prevNod[inextface] != nextNod[inextface] )
4073 polyedre_nodes.push_back( nextNod[inextface] );
4074 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4076 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4077 if ( nbFaceNodes > 2 )
4078 quantities.push_back( nbFaceNodes );
4079 else // degenerated face
4080 polyedre_nodes.resize( prevNbNodes );
4082 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4086 newElems.push_back( aNewElem );
4087 myLastCreatedElems.Append(aNewElem);
4088 srcElements.Append( elem );
4091 // set new prev nodes
4092 for ( iNode = 0; iNode < nbNodes; iNode++ )
4093 prevNod[ iNode ] = nextNod[ iNode ];
4098 //=======================================================================
4100 * \brief Create 1D and 2D elements around swept elements
4101 * \param mapNewNodes - source nodes and ones generated from them
4102 * \param newElemsMap - source elements and ones generated from them
4103 * \param elemNewNodesMap - nodes generated from each node of each element
4104 * \param elemSet - all swept elements
4105 * \param nbSteps - number of sweeping steps
4106 * \param srcElements - to append elem for each generated element
4108 //=======================================================================
4110 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4111 TElemOfElemListMap & newElemsMap,
4112 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4113 TIDSortedElemSet& elemSet,
4115 SMESH_SequenceOfElemPtr& srcElements)
4117 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4118 SMESHDS_Mesh* aMesh = GetMeshDS();
4120 // Find nodes belonging to only one initial element - sweep them to get edges.
4122 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4123 for ( ; nList != mapNewNodes.end(); nList++ )
4125 const SMDS_MeshNode* node =
4126 static_cast<const SMDS_MeshNode*>( nList->first );
4127 if ( newElemsMap.count( node ))
4128 continue; // node was extruded into edge
4129 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4130 int nbInitElems = 0;
4131 const SMDS_MeshElement* el = 0;
4132 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4133 while ( eIt->more() && nbInitElems < 2 ) {
4135 SMDSAbs_ElementType type = el->GetType();
4136 if ( type == SMDSAbs_Volume || type < highType ) continue;
4137 if ( type > highType ) {
4141 nbInitElems += elemSet.count(el);
4143 if ( nbInitElems < 2 ) {
4144 bool NotCreateEdge = el && el->IsMediumNode(node);
4145 if(!NotCreateEdge) {
4146 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4147 list<const SMDS_MeshElement*> newEdges;
4148 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4153 // Make a ceiling for each element ie an equal element of last new nodes.
4154 // Find free links of faces - make edges and sweep them into faces.
4156 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4157 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4158 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4160 const SMDS_MeshElement* elem = itElem->first;
4161 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4163 if(itElem->second.size()==0) continue;
4165 const bool isQuadratic = elem->IsQuadratic();
4167 if ( elem->GetType() == SMDSAbs_Edge ) {
4168 // create a ceiling edge
4169 if ( !isQuadratic ) {
4170 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4171 vecNewNodes[ 1 ]->second.back())) {
4172 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4173 vecNewNodes[ 1 ]->second.back()));
4174 srcElements.Append( elem );
4178 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4179 vecNewNodes[ 1 ]->second.back(),
4180 vecNewNodes[ 2 ]->second.back())) {
4181 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4182 vecNewNodes[ 1 ]->second.back(),
4183 vecNewNodes[ 2 ]->second.back()));
4184 srcElements.Append( elem );
4188 if ( elem->GetType() != SMDSAbs_Face )
4191 bool hasFreeLinks = false;
4193 TIDSortedElemSet avoidSet;
4194 avoidSet.insert( elem );
4196 set<const SMDS_MeshNode*> aFaceLastNodes;
4197 int iNode, nbNodes = vecNewNodes.size();
4198 if ( !isQuadratic ) {
4199 // loop on the face nodes
4200 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4202 // look for free links of the face
4203 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4204 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4205 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4206 // check if a link n1-n2 is free
4207 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4208 hasFreeLinks = true;
4209 // make a new edge and a ceiling for a new edge
4210 const SMDS_MeshElement* edge;
4211 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4212 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4213 srcElements.Append( myLastCreatedElems.Last() );
4215 n1 = vecNewNodes[ iNode ]->second.back();
4216 n2 = vecNewNodes[ iNext ]->second.back();
4217 if ( !aMesh->FindEdge( n1, n2 )) {
4218 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4219 srcElements.Append( edge );
4224 else { // elem is quadratic face
4225 int nbn = nbNodes/2;
4226 for ( iNode = 0; iNode < nbn; iNode++ ) {
4227 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4228 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4229 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4230 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4231 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4232 // check if a link is free
4233 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4234 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4235 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4236 hasFreeLinks = true;
4237 // make an edge and a ceiling for a new edge
4239 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4240 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4241 srcElements.Append( elem );
4243 n1 = vecNewNodes[ iNode ]->second.back();
4244 n2 = vecNewNodes[ iNext ]->second.back();
4245 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4246 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4247 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4248 srcElements.Append( elem );
4252 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4253 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4257 // sweep free links into faces
4259 if ( hasFreeLinks ) {
4260 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4261 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4263 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4264 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4265 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4266 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4268 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4269 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4270 std::advance( v, volNb );
4271 // find indices of free faces of a volume and their source edges
4272 list< int > freeInd;
4273 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4274 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4275 int iF, nbF = vTool.NbFaces();
4276 for ( iF = 0; iF < nbF; iF ++ ) {
4277 if (vTool.IsFreeFace( iF ) &&
4278 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4279 initNodeSet != faceNodeSet) // except an initial face
4281 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4283 freeInd.push_back( iF );
4284 // find source edge of a free face iF
4285 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4286 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4287 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4288 initNodeSet.begin(), initNodeSet.end(),
4289 commonNodes.begin());
4290 if ( (*v)->IsQuadratic() )
4291 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4293 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4295 if ( !srcEdges.back() )
4297 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4298 << iF << " of volume #" << vTool.ID() << endl;
4303 if ( freeInd.empty() )
4306 // create faces for all steps;
4307 // if such a face has been already created by sweep of edge,
4308 // assure that its orientation is OK
4309 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4310 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4311 vTool.SetExternalNormal();
4312 const int nextShift = vTool.IsForward() ? +1 : -1;
4313 list< int >::iterator ind = freeInd.begin();
4314 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4315 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4317 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4318 int nbn = vTool.NbFaceNodes( *ind );
4319 const SMDS_MeshElement * f = 0;
4320 if ( nbn == 3 ) ///// triangle
4322 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4324 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4326 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4328 nodes[ 1 + nextShift ] };
4330 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4332 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4336 else if ( nbn == 4 ) ///// quadrangle
4338 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4340 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4342 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4343 nodes[ 2 ], nodes[ 2+nextShift ] };
4345 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4347 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4348 newOrder[ 2 ], newOrder[ 3 ]));
4351 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4353 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4355 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4357 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4359 nodes[2 + 2*nextShift],
4360 nodes[3 - 2*nextShift],
4362 nodes[3 + 2*nextShift]};
4364 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4366 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4374 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4376 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4377 nodes[1], nodes[3], nodes[5], nodes[7] );
4379 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4381 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4382 nodes[4 - 2*nextShift],
4384 nodes[4 + 2*nextShift],
4386 nodes[5 - 2*nextShift],
4388 nodes[5 + 2*nextShift] };
4390 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4392 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4393 newOrder[ 2 ], newOrder[ 3 ],
4394 newOrder[ 4 ], newOrder[ 5 ],
4395 newOrder[ 6 ], newOrder[ 7 ]));
4398 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4400 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4401 SMDSAbs_Face, /*noMedium=*/false);
4403 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4405 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4406 nodes[4 - 2*nextShift],
4408 nodes[4 + 2*nextShift],
4410 nodes[5 - 2*nextShift],
4412 nodes[5 + 2*nextShift],
4415 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4417 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4418 newOrder[ 2 ], newOrder[ 3 ],
4419 newOrder[ 4 ], newOrder[ 5 ],
4420 newOrder[ 6 ], newOrder[ 7 ],
4424 else //////// polygon
4426 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4427 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4429 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4431 if ( !vTool.IsForward() )
4432 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4434 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4436 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4440 while ( srcElements.Length() < myLastCreatedElems.Length() )
4441 srcElements.Append( *srcEdge );
4443 } // loop on free faces
4445 // go to the next volume
4447 while ( iVol++ < nbVolumesByStep ) v++;
4450 } // loop on volumes of one step
4451 } // sweep free links into faces
4453 // Make a ceiling face with a normal external to a volume
4455 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4457 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4459 lastVol.SetExternalNormal();
4460 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4461 int nbn = lastVol.NbFaceNodes( iF );
4463 if (!hasFreeLinks ||
4464 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4465 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4467 else if ( nbn == 4 )
4469 if (!hasFreeLinks ||
4470 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4471 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4473 else if ( nbn == 6 && isQuadratic )
4475 if (!hasFreeLinks ||
4476 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4477 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4478 nodes[1], nodes[3], nodes[5]));
4480 else if ( nbn == 8 && isQuadratic )
4482 if (!hasFreeLinks ||
4483 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4484 nodes[1], nodes[3], nodes[5], nodes[7]) )
4485 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4486 nodes[1], nodes[3], nodes[5], nodes[7]));
4488 else if ( nbn == 9 && isQuadratic )
4490 if (!hasFreeLinks ||
4491 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4492 SMDSAbs_Face, /*noMedium=*/false) )
4493 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4494 nodes[1], nodes[3], nodes[5], nodes[7],
4498 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4499 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4500 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4503 while ( srcElements.Length() < myLastCreatedElems.Length() )
4504 srcElements.Append( elem );
4506 } // loop on swept elements
4509 //=======================================================================
4510 //function : RotationSweep
4512 //=======================================================================
4514 SMESH_MeshEditor::PGroupIDs
4515 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4516 const gp_Ax1& theAxis,
4517 const double theAngle,
4518 const int theNbSteps,
4519 const double theTol,
4520 const bool theMakeGroups,
4521 const bool theMakeWalls)
4523 myLastCreatedElems.Clear();
4524 myLastCreatedNodes.Clear();
4526 // source elements for each generated one
4527 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4529 MESSAGE( "RotationSweep()");
4531 aTrsf.SetRotation( theAxis, theAngle );
4533 aTrsf2.SetRotation( theAxis, theAngle/2. );
4535 gp_Lin aLine( theAxis );
4536 double aSqTol = theTol * theTol;
4538 SMESHDS_Mesh* aMesh = GetMeshDS();
4540 TNodeOfNodeListMap mapNewNodes;
4541 TElemOfVecOfNnlmiMap mapElemNewNodes;
4542 TElemOfElemListMap newElemsMap;
4544 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4545 myMesh->NbFaces(ORDER_QUADRATIC) +
4546 myMesh->NbVolumes(ORDER_QUADRATIC) );
4548 TIDSortedElemSet::iterator itElem;
4549 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4550 const SMDS_MeshElement* elem = *itElem;
4551 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4553 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4554 newNodesItVec.reserve( elem->NbNodes() );
4556 // loop on elem nodes
4557 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4558 while ( itN->more() )
4560 // check if a node has been already sweeped
4561 const SMDS_MeshNode* node = cast2Node( itN->next() );
4563 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4565 aXYZ.Coord( coord[0], coord[1], coord[2] );
4566 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4568 TNodeOfNodeListMapItr nIt =
4569 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4570 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4571 if ( listNewNodes.empty() )
4573 // check if we are to create medium nodes between corner ones
4574 bool needMediumNodes = false;
4575 if ( isQuadraticMesh )
4577 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4578 while (it->more() && !needMediumNodes )
4580 const SMDS_MeshElement* invElem = it->next();
4581 if ( invElem != elem && !theElems.count( invElem )) continue;
4582 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4583 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4584 needMediumNodes = true;
4589 const SMDS_MeshNode * newNode = node;
4590 for ( int i = 0; i < theNbSteps; i++ ) {
4592 if ( needMediumNodes ) // create a medium node
4594 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4595 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4596 myLastCreatedNodes.Append(newNode);
4597 srcNodes.Append( node );
4598 listNewNodes.push_back( newNode );
4599 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4602 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4604 // create a corner node
4605 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4606 myLastCreatedNodes.Append(newNode);
4607 srcNodes.Append( node );
4608 listNewNodes.push_back( newNode );
4611 listNewNodes.push_back( newNode );
4612 // if ( needMediumNodes )
4613 // listNewNodes.push_back( newNode );
4617 newNodesItVec.push_back( nIt );
4619 // make new elements
4620 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4624 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4626 PGroupIDs newGroupIDs;
4627 if ( theMakeGroups )
4628 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4634 //=======================================================================
4635 //function : CreateNode
4637 //=======================================================================
4638 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4641 const double tolnode,
4642 SMESH_SequenceOfNode& aNodes)
4644 // myLastCreatedElems.Clear();
4645 // myLastCreatedNodes.Clear();
4648 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4650 // try to search in sequence of existing nodes
4651 // if aNodes.Length()>0 we 'nave to use given sequence
4652 // else - use all nodes of mesh
4653 if(aNodes.Length()>0) {
4655 for(i=1; i<=aNodes.Length(); i++) {
4656 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4657 if(P1.Distance(P2)<tolnode)
4658 return aNodes.Value(i);
4662 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4663 while(itn->more()) {
4664 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4665 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4666 if(P1.Distance(P2)<tolnode)
4671 // create new node and return it
4672 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4673 //myLastCreatedNodes.Append(NewNode);
4678 //=======================================================================
4679 //function : ExtrusionSweep
4681 //=======================================================================
4683 SMESH_MeshEditor::PGroupIDs
4684 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4685 const gp_Vec& theStep,
4686 const int theNbSteps,
4687 TElemOfElemListMap& newElemsMap,
4688 const bool theMakeGroups,
4690 const double theTolerance)
4692 ExtrusParam aParams;
4693 aParams.myDir = gp_Dir(theStep);
4694 aParams.myNodes.Clear();
4695 aParams.mySteps = new TColStd_HSequenceOfReal;
4697 for(i=1; i<=theNbSteps; i++)
4698 aParams.mySteps->Append(theStep.Magnitude());
4701 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4705 //=======================================================================
4706 //function : ExtrusionSweep
4708 //=======================================================================
4710 SMESH_MeshEditor::PGroupIDs
4711 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4712 ExtrusParam& theParams,
4713 TElemOfElemListMap& newElemsMap,
4714 const bool theMakeGroups,
4716 const double theTolerance)
4718 myLastCreatedElems.Clear();
4719 myLastCreatedNodes.Clear();
4721 // source elements for each generated one
4722 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4724 SMESHDS_Mesh* aMesh = GetMeshDS();
4726 int nbsteps = theParams.mySteps->Length();
4728 TNodeOfNodeListMap mapNewNodes;
4729 //TNodeOfNodeVecMap mapNewNodes;
4730 TElemOfVecOfNnlmiMap mapElemNewNodes;
4731 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4733 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4734 myMesh->NbFaces(ORDER_QUADRATIC) +
4735 myMesh->NbVolumes(ORDER_QUADRATIC) );
4737 TIDSortedElemSet::iterator itElem;
4738 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4739 // check element type
4740 const SMDS_MeshElement* elem = *itElem;
4741 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4744 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4745 newNodesItVec.reserve( elem->NbNodes() );
4747 // loop on elem nodes
4748 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4749 while ( itN->more() )
4751 // check if a node has been already sweeped
4752 const SMDS_MeshNode* node = cast2Node( itN->next() );
4753 TNodeOfNodeListMap::iterator nIt =
4754 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4755 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4756 if ( listNewNodes.empty() )
4760 // check if we are to create medium nodes between corner ones
4761 bool needMediumNodes = false;
4762 if ( isQuadraticMesh )
4764 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4765 while (it->more() && !needMediumNodes )
4767 const SMDS_MeshElement* invElem = it->next();
4768 if ( invElem != elem && !theElems.count( invElem )) continue;
4769 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4770 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4771 needMediumNodes = true;
4775 double coord[] = { node->X(), node->Y(), node->Z() };
4776 for ( int i = 0; i < nbsteps; i++ )
4778 if ( needMediumNodes ) // create a medium node
4780 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4781 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4782 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4783 if( theFlags & EXTRUSION_FLAG_SEW ) {
4784 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4785 theTolerance, theParams.myNodes);
4786 listNewNodes.push_back( newNode );
4789 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4790 myLastCreatedNodes.Append(newNode);
4791 srcNodes.Append( node );
4792 listNewNodes.push_back( newNode );
4795 // create a corner node
4796 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4797 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4798 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4799 if( theFlags & EXTRUSION_FLAG_SEW ) {
4800 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4801 theTolerance, theParams.myNodes);
4802 listNewNodes.push_back( newNode );
4805 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4806 myLastCreatedNodes.Append(newNode);
4807 srcNodes.Append( node );
4808 listNewNodes.push_back( newNode );
4812 newNodesItVec.push_back( nIt );
4814 // make new elements
4815 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4818 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4819 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4821 PGroupIDs newGroupIDs;
4822 if ( theMakeGroups )
4823 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4828 //=======================================================================
4829 //function : ExtrusionAlongTrack
4831 //=======================================================================
4832 SMESH_MeshEditor::Extrusion_Error
4833 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4834 SMESH_subMesh* theTrack,
4835 const SMDS_MeshNode* theN1,
4836 const bool theHasAngles,
4837 list<double>& theAngles,
4838 const bool theLinearVariation,
4839 const bool theHasRefPoint,
4840 const gp_Pnt& theRefPoint,
4841 const bool theMakeGroups)
4843 MESSAGE("ExtrusionAlongTrack");
4844 myLastCreatedElems.Clear();
4845 myLastCreatedNodes.Clear();
4848 std::list<double> aPrms;
4849 TIDSortedElemSet::iterator itElem;
4852 TopoDS_Edge aTrackEdge;
4853 TopoDS_Vertex aV1, aV2;
4855 SMDS_ElemIteratorPtr aItE;
4856 SMDS_NodeIteratorPtr aItN;
4857 SMDSAbs_ElementType aTypeE;
4859 TNodeOfNodeListMap mapNewNodes;
4862 aNbE = theElements.size();
4865 return EXTR_NO_ELEMENTS;
4867 // 1.1 Track Pattern
4870 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4872 aItE = pSubMeshDS->GetElements();
4873 while ( aItE->more() ) {
4874 const SMDS_MeshElement* pE = aItE->next();
4875 aTypeE = pE->GetType();
4876 // Pattern must contain links only
4877 if ( aTypeE != SMDSAbs_Edge )
4878 return EXTR_PATH_NOT_EDGE;
4881 list<SMESH_MeshEditor_PathPoint> fullList;
4883 const TopoDS_Shape& aS = theTrack->GetSubShape();
4884 // Sub-shape for the Pattern must be an Edge or Wire
4885 if( aS.ShapeType() == TopAbs_EDGE ) {
4886 aTrackEdge = TopoDS::Edge( aS );
4887 // the Edge must not be degenerated
4888 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4889 return EXTR_BAD_PATH_SHAPE;
4890 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4891 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4892 const SMDS_MeshNode* aN1 = aItN->next();
4893 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4894 const SMDS_MeshNode* aN2 = aItN->next();
4895 // starting node must be aN1 or aN2
4896 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4897 return EXTR_BAD_STARTING_NODE;
4898 aItN = pSubMeshDS->GetNodes();
4899 while ( aItN->more() ) {
4900 const SMDS_MeshNode* pNode = aItN->next();
4901 const SMDS_EdgePosition* pEPos =
4902 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4903 double aT = pEPos->GetUParameter();
4904 aPrms.push_back( aT );
4906 //Extrusion_Error err =
4907 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4908 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4909 list< SMESH_subMesh* > LSM;
4910 TopTools_SequenceOfShape Edges;
4911 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4912 while(itSM->more()) {
4913 SMESH_subMesh* SM = itSM->next();
4915 const TopoDS_Shape& aS = SM->GetSubShape();
4918 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4919 int startNid = theN1->GetID();
4920 TColStd_MapOfInteger UsedNums;
4922 int NbEdges = Edges.Length();
4924 for(; i<=NbEdges; i++) {
4926 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4927 for(; itLSM!=LSM.end(); itLSM++) {
4929 if(UsedNums.Contains(k)) continue;
4930 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4931 SMESH_subMesh* locTrack = *itLSM;
4932 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4933 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4934 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4935 const SMDS_MeshNode* aN1 = aItN->next();
4936 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4937 const SMDS_MeshNode* aN2 = aItN->next();
4938 // starting node must be aN1 or aN2
4939 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4940 // 2. Collect parameters on the track edge
4942 aItN = locMeshDS->GetNodes();
4943 while ( aItN->more() ) {
4944 const SMDS_MeshNode* pNode = aItN->next();
4945 const SMDS_EdgePosition* pEPos =
4946 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4947 double aT = pEPos->GetUParameter();
4948 aPrms.push_back( aT );
4950 list<SMESH_MeshEditor_PathPoint> LPP;
4951 //Extrusion_Error err =
4952 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4953 LLPPs.push_back(LPP);
4955 // update startN for search following egde
4956 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4957 else startNid = aN1->GetID();
4961 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4962 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4963 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4964 for(; itPP!=firstList.end(); itPP++) {
4965 fullList.push_back( *itPP );
4967 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4968 fullList.pop_back();
4970 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4971 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4972 itPP = currList.begin();
4973 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4974 gp_Dir D1 = PP1.Tangent();
4975 gp_Dir D2 = PP2.Tangent();
4976 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4977 (D1.Z()+D2.Z())/2 ) );
4978 PP1.SetTangent(Dnew);
4979 fullList.push_back(PP1);
4981 for(; itPP!=firstList.end(); itPP++) {
4982 fullList.push_back( *itPP );
4984 PP1 = fullList.back();
4985 fullList.pop_back();
4987 // if wire not closed
4988 fullList.push_back(PP1);
4992 return EXTR_BAD_PATH_SHAPE;
4995 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4996 theHasRefPoint, theRefPoint, theMakeGroups);
5000 //=======================================================================
5001 //function : ExtrusionAlongTrack
5003 //=======================================================================
5004 SMESH_MeshEditor::Extrusion_Error
5005 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5006 SMESH_Mesh* theTrack,
5007 const SMDS_MeshNode* theN1,
5008 const bool theHasAngles,
5009 list<double>& theAngles,
5010 const bool theLinearVariation,
5011 const bool theHasRefPoint,
5012 const gp_Pnt& theRefPoint,
5013 const bool theMakeGroups)
5015 myLastCreatedElems.Clear();
5016 myLastCreatedNodes.Clear();
5019 std::list<double> aPrms;
5020 TIDSortedElemSet::iterator itElem;
5023 TopoDS_Edge aTrackEdge;
5024 TopoDS_Vertex aV1, aV2;
5026 SMDS_ElemIteratorPtr aItE;
5027 SMDS_NodeIteratorPtr aItN;
5028 SMDSAbs_ElementType aTypeE;
5030 TNodeOfNodeListMap mapNewNodes;
5033 aNbE = theElements.size();
5036 return EXTR_NO_ELEMENTS;
5038 // 1.1 Track Pattern
5041 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5043 aItE = pMeshDS->elementsIterator();
5044 while ( aItE->more() ) {
5045 const SMDS_MeshElement* pE = aItE->next();
5046 aTypeE = pE->GetType();
5047 // Pattern must contain links only
5048 if ( aTypeE != SMDSAbs_Edge )
5049 return EXTR_PATH_NOT_EDGE;
5052 list<SMESH_MeshEditor_PathPoint> fullList;
5054 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5056 if( aS == SMESH_Mesh::PseudoShape() ) {
5057 //Mesh without shape
5058 const SMDS_MeshNode* currentNode = NULL;
5059 const SMDS_MeshNode* prevNode = theN1;
5060 std::vector<const SMDS_MeshNode*> aNodesList;
5061 aNodesList.push_back(theN1);
5062 int nbEdges = 0, conn=0;
5063 const SMDS_MeshElement* prevElem = NULL;
5064 const SMDS_MeshElement* currentElem = NULL;
5065 int totalNbEdges = theTrack->NbEdges();
5066 SMDS_ElemIteratorPtr nIt;
5069 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5070 return EXTR_BAD_STARTING_NODE;
5073 conn = nbEdgeConnectivity(theN1);
5075 return EXTR_PATH_NOT_EDGE;
5077 aItE = theN1->GetInverseElementIterator();
5078 prevElem = aItE->next();
5079 currentElem = prevElem;
5081 if(totalNbEdges == 1 ) {
5082 nIt = currentElem->nodesIterator();
5083 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5084 if(currentNode == prevNode)
5085 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5086 aNodesList.push_back(currentNode);
5088 nIt = currentElem->nodesIterator();
5089 while( nIt->more() ) {
5090 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5091 if(currentNode == prevNode)
5092 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5093 aNodesList.push_back(currentNode);
5095 //case of the closed mesh
5096 if(currentNode == theN1) {
5101 conn = nbEdgeConnectivity(currentNode);
5103 return EXTR_PATH_NOT_EDGE;
5104 }else if( conn == 1 && nbEdges > 0 ) {
5109 prevNode = currentNode;
5110 aItE = currentNode->GetInverseElementIterator();
5111 currentElem = aItE->next();
5112 if( currentElem == prevElem)
5113 currentElem = aItE->next();
5114 nIt = currentElem->nodesIterator();
5115 prevElem = currentElem;
5121 if(nbEdges != totalNbEdges)
5122 return EXTR_PATH_NOT_EDGE;
5124 TopTools_SequenceOfShape Edges;
5125 double x1,x2,y1,y2,z1,z2;
5126 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5127 int startNid = theN1->GetID();
5128 for(int i = 1; i < aNodesList.size(); i++) {
5129 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5130 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5131 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5132 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5133 list<SMESH_MeshEditor_PathPoint> LPP;
5135 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5136 LLPPs.push_back(LPP);
5137 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5138 else startNid = aNodesList[i-1]->GetID();
5142 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5143 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5144 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5145 for(; itPP!=firstList.end(); itPP++) {
5146 fullList.push_back( *itPP );
5149 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5150 SMESH_MeshEditor_PathPoint PP2;
5151 fullList.pop_back();
5153 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5154 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5155 itPP = currList.begin();
5156 PP2 = currList.front();
5157 gp_Dir D1 = PP1.Tangent();
5158 gp_Dir D2 = PP2.Tangent();
5159 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5160 (D1.Z()+D2.Z())/2 ) );
5161 PP1.SetTangent(Dnew);
5162 fullList.push_back(PP1);
5164 for(; itPP!=currList.end(); itPP++) {
5165 fullList.push_back( *itPP );
5167 PP1 = fullList.back();
5168 fullList.pop_back();
5170 fullList.push_back(PP1);
5172 } // Sub-shape for the Pattern must be an Edge or Wire
5173 else if( aS.ShapeType() == TopAbs_EDGE ) {
5174 aTrackEdge = TopoDS::Edge( aS );
5175 // the Edge must not be degenerated
5176 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5177 return EXTR_BAD_PATH_SHAPE;
5178 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5179 const SMDS_MeshNode* aN1 = 0;
5180 const SMDS_MeshNode* aN2 = 0;
5181 if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5182 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5185 if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5186 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5189 // starting node must be aN1 or aN2
5190 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5191 return EXTR_BAD_STARTING_NODE;
5192 aItN = pMeshDS->nodesIterator();
5193 while ( aItN->more() ) {
5194 const SMDS_MeshNode* pNode = aItN->next();
5195 if( pNode==aN1 || pNode==aN2 ) continue;
5196 const SMDS_EdgePosition* pEPos =
5197 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5198 double aT = pEPos->GetUParameter();
5199 aPrms.push_back( aT );
5201 //Extrusion_Error err =
5202 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5204 else if( aS.ShapeType() == TopAbs_WIRE ) {
5205 list< SMESH_subMesh* > LSM;
5206 TopTools_SequenceOfShape Edges;
5207 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5208 for(; eExp.More(); eExp.Next()) {
5209 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5210 if( BRep_Tool::Degenerated(E) ) continue;
5211 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5217 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5218 TopoDS_Vertex aVprev;
5219 TColStd_MapOfInteger UsedNums;
5220 int NbEdges = Edges.Length();
5222 for(; i<=NbEdges; i++) {
5224 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5225 for(; itLSM!=LSM.end(); itLSM++) {
5227 if(UsedNums.Contains(k)) continue;
5228 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5229 SMESH_subMesh* locTrack = *itLSM;
5230 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5231 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5232 bool aN1isOK = false, aN2isOK = false;
5233 if ( aVprev.IsNull() ) {
5234 // if previous vertex is not yet defined, it means that we in the beginning of wire
5235 // and we have to find initial vertex corresponding to starting node theN1
5236 const SMDS_MeshNode* aN1 = 0;
5237 const SMDS_MeshNode* aN2 = 0;
5239 if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5240 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5243 if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5244 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5247 // starting node must be aN1 or aN2
5248 aN1isOK = ( aN1 && aN1 == theN1 );
5249 aN2isOK = ( aN2 && aN2 == theN1 );
5252 // we have specified ending vertex of the previous edge on the previous iteration
5253 // and we have just to check that it corresponds to any vertex in current segment
5254 aN1isOK = aVprev.IsSame( aV1 );
5255 aN2isOK = aVprev.IsSame( aV2 );
5257 if ( !aN1isOK && !aN2isOK ) continue;
5258 // 2. Collect parameters on the track edge
5260 aItN = locMeshDS->GetNodes();
5261 while ( aItN->more() ) {
5262 const SMDS_MeshNode* pNode = aItN->next();
5263 const SMDS_EdgePosition* pEPos =
5264 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5265 double aT = pEPos->GetUParameter();
5266 aPrms.push_back( aT );
5268 list<SMESH_MeshEditor_PathPoint> LPP;
5269 //Extrusion_Error err =
5270 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5271 LLPPs.push_back(LPP);
5273 // update startN for search following egde
5274 if ( aN1isOK ) aVprev = aV2;
5279 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5280 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5281 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5282 for(; itPP!=firstList.end(); itPP++) {
5283 fullList.push_back( *itPP );
5285 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5286 fullList.pop_back();
5288 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5289 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5290 itPP = currList.begin();
5291 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5292 gp_Dir D1 = PP1.Tangent();
5293 gp_Dir D2 = PP2.Tangent();
5294 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5295 PP1.SetTangent(Dnew);
5296 fullList.push_back(PP1);
5298 for(; itPP!=currList.end(); itPP++) {
5299 fullList.push_back( *itPP );
5301 PP1 = fullList.back();
5302 fullList.pop_back();
5304 // if wire not closed
5305 fullList.push_back(PP1);
5309 return EXTR_BAD_PATH_SHAPE;
5312 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5313 theHasRefPoint, theRefPoint, theMakeGroups);
5317 //=======================================================================
5318 //function : MakeEdgePathPoints
5319 //purpose : auxilary for ExtrusionAlongTrack
5320 //=======================================================================
5321 SMESH_MeshEditor::Extrusion_Error
5322 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5323 const TopoDS_Edge& aTrackEdge,
5325 list<SMESH_MeshEditor_PathPoint>& LPP)
5327 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5329 aTolVec2=aTolVec*aTolVec;
5331 TopoDS_Vertex aV1, aV2;
5332 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5333 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5334 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5335 // 2. Collect parameters on the track edge
5336 aPrms.push_front( aT1 );
5337 aPrms.push_back( aT2 );
5340 if( FirstIsStart ) {
5351 SMESH_MeshEditor_PathPoint aPP;
5352 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5353 std::list<double>::iterator aItD = aPrms.begin();
5354 for(; aItD != aPrms.end(); ++aItD) {
5358 aC3D->D1( aT, aP3D, aVec );
5359 aL2 = aVec.SquareMagnitude();
5360 if ( aL2 < aTolVec2 )
5361 return EXTR_CANT_GET_TANGENT;
5362 gp_Dir aTgt( aVec );
5364 aPP.SetTangent( aTgt );
5365 aPP.SetParameter( aT );
5372 //=======================================================================
5373 //function : MakeExtrElements
5374 //purpose : auxilary for ExtrusionAlongTrack
5375 //=======================================================================
5376 SMESH_MeshEditor::Extrusion_Error
5377 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5378 list<SMESH_MeshEditor_PathPoint>& fullList,
5379 const bool theHasAngles,
5380 list<double>& theAngles,
5381 const bool theLinearVariation,
5382 const bool theHasRefPoint,
5383 const gp_Pnt& theRefPoint,
5384 const bool theMakeGroups)
5386 MESSAGE("MakeExtrElements");
5387 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5388 int aNbTP = fullList.size();
5389 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5391 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5392 LinearAngleVariation(aNbTP-1, theAngles);
5394 vector<double> aAngles( aNbTP );
5396 for(; j<aNbTP; ++j) {
5399 if ( theHasAngles ) {
5401 std::list<double>::iterator aItD = theAngles.begin();
5402 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5404 aAngles[j] = anAngle;
5407 // fill vector of path points with angles
5408 //aPPs.resize(fullList.size());
5410 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5411 for(; itPP!=fullList.end(); itPP++) {
5413 SMESH_MeshEditor_PathPoint PP = *itPP;
5414 PP.SetAngle(aAngles[j]);
5418 TNodeOfNodeListMap mapNewNodes;
5419 TElemOfVecOfNnlmiMap mapElemNewNodes;
5420 TElemOfElemListMap newElemsMap;
5421 TIDSortedElemSet::iterator itElem;
5424 SMDSAbs_ElementType aTypeE;
5425 // source elements for each generated one
5426 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5428 // 3. Center of rotation aV0
5429 gp_Pnt aV0 = theRefPoint;
5431 if ( !theHasRefPoint ) {
5433 aGC.SetCoord( 0.,0.,0. );
5435 itElem = theElements.begin();
5436 for ( ; itElem != theElements.end(); itElem++ ) {
5437 const SMDS_MeshElement* elem = *itElem;
5439 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5440 while ( itN->more() ) {
5441 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5446 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5447 list<const SMDS_MeshNode*> aLNx;
5448 mapNewNodes[node] = aLNx;
5450 gp_XYZ aXYZ( aX, aY, aZ );
5458 } // if (!theHasRefPoint) {
5459 mapNewNodes.clear();
5461 // 4. Processing the elements
5462 SMESHDS_Mesh* aMesh = GetMeshDS();
5464 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5465 // check element type
5466 const SMDS_MeshElement* elem = *itElem;
5467 aTypeE = elem->GetType();
5468 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5471 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5472 newNodesItVec.reserve( elem->NbNodes() );
5474 // loop on elem nodes
5476 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5477 while ( itN->more() )
5480 // check if a node has been already processed
5481 const SMDS_MeshNode* node =
5482 static_cast<const SMDS_MeshNode*>( itN->next() );
5483 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5484 if ( nIt == mapNewNodes.end() ) {
5485 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5486 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5489 aX = node->X(); aY = node->Y(); aZ = node->Z();
5491 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5492 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5493 gp_Ax1 anAx1, anAxT1T0;
5494 gp_Dir aDT1x, aDT0x, aDT1T0;
5499 aPN0.SetCoord(aX, aY, aZ);
5501 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5503 aDT0x= aPP0.Tangent();
5504 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5506 for ( j = 1; j < aNbTP; ++j ) {
5507 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5509 aDT1x = aPP1.Tangent();
5510 aAngle1x = aPP1.Angle();
5512 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5514 gp_Vec aV01x( aP0x, aP1x );
5515 aTrsf.SetTranslation( aV01x );
5518 aV1x = aV0x.Transformed( aTrsf );
5519 aPN1 = aPN0.Transformed( aTrsf );
5521 // rotation 1 [ T1,T0 ]
5522 aAngleT1T0=-aDT1x.Angle( aDT0x );
5523 if (fabs(aAngleT1T0) > aTolAng) {
5525 anAxT1T0.SetLocation( aV1x );
5526 anAxT1T0.SetDirection( aDT1T0 );
5527 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5529 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5533 if ( theHasAngles ) {
5534 anAx1.SetLocation( aV1x );
5535 anAx1.SetDirection( aDT1x );
5536 aTrsfRot.SetRotation( anAx1, aAngle1x );
5538 aPN1 = aPN1.Transformed( aTrsfRot );
5542 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5543 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5544 // create additional node
5545 double x = ( aPN1.X() + aPN0.X() )/2.;
5546 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5547 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5548 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5549 myLastCreatedNodes.Append(newNode);
5550 srcNodes.Append( node );
5551 listNewNodes.push_back( newNode );
5556 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5557 myLastCreatedNodes.Append(newNode);
5558 srcNodes.Append( node );
5559 listNewNodes.push_back( newNode );
5569 // if current elem is quadratic and current node is not medium
5570 // we have to check - may be it is needed to insert additional nodes
5571 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5572 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5573 if(listNewNodes.size()==aNbTP-1) {
5574 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5575 gp_XYZ P(node->X(), node->Y(), node->Z());
5576 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5578 for(i=0; i<aNbTP-1; i++) {
5579 const SMDS_MeshNode* N = *it;
5580 double x = ( N->X() + P.X() )/2.;
5581 double y = ( N->Y() + P.Y() )/2.;
5582 double z = ( N->Z() + P.Z() )/2.;
5583 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5584 srcNodes.Append( node );
5585 myLastCreatedNodes.Append(newN);
5588 P = gp_XYZ(N->X(),N->Y(),N->Z());
5590 listNewNodes.clear();
5591 for(i=0; i<2*(aNbTP-1); i++) {
5592 listNewNodes.push_back(aNodes[i]);
5598 newNodesItVec.push_back( nIt );
5600 // make new elements
5601 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5602 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5603 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5606 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5608 if ( theMakeGroups )
5609 generateGroups( srcNodes, srcElems, "extruded");
5615 //=======================================================================
5616 //function : LinearAngleVariation
5617 //purpose : auxilary for ExtrusionAlongTrack
5618 //=======================================================================
5619 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5620 list<double>& Angles)
5622 int nbAngles = Angles.size();
5623 if( nbSteps > nbAngles ) {
5624 vector<double> theAngles(nbAngles);
5625 list<double>::iterator it = Angles.begin();
5627 for(; it!=Angles.end(); it++) {
5629 theAngles[i] = (*it);
5632 double rAn2St = double( nbAngles ) / double( nbSteps );
5633 double angPrev = 0, angle;
5634 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5635 double angCur = rAn2St * ( iSt+1 );
5636 double angCurFloor = floor( angCur );
5637 double angPrevFloor = floor( angPrev );
5638 if ( angPrevFloor == angCurFloor )
5639 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5641 int iP = int( angPrevFloor );
5642 double angPrevCeil = ceil(angPrev);
5643 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5645 int iC = int( angCurFloor );
5646 if ( iC < nbAngles )
5647 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5649 iP = int( angPrevCeil );
5651 angle += theAngles[ iC ];
5653 res.push_back(angle);
5658 for(; it!=res.end(); it++)
5659 Angles.push_back( *it );
5664 //================================================================================
5666 * \brief Move or copy theElements applying theTrsf to their nodes
5667 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5668 * \param theTrsf - transformation to apply
5669 * \param theCopy - if true, create translated copies of theElems
5670 * \param theMakeGroups - if true and theCopy, create translated groups
5671 * \param theTargetMesh - mesh to copy translated elements into
5672 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5674 //================================================================================
5676 SMESH_MeshEditor::PGroupIDs
5677 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5678 const gp_Trsf& theTrsf,
5680 const bool theMakeGroups,
5681 SMESH_Mesh* theTargetMesh)
5683 myLastCreatedElems.Clear();
5684 myLastCreatedNodes.Clear();
5686 bool needReverse = false;
5687 string groupPostfix;
5688 switch ( theTrsf.Form() ) {
5690 MESSAGE("gp_PntMirror");
5692 groupPostfix = "mirrored";
5695 MESSAGE("gp_Ax1Mirror");
5696 groupPostfix = "mirrored";
5699 MESSAGE("gp_Ax2Mirror");
5701 groupPostfix = "mirrored";
5704 MESSAGE("gp_Rotation");
5705 groupPostfix = "rotated";
5707 case gp_Translation:
5708 MESSAGE("gp_Translation");
5709 groupPostfix = "translated";
5712 MESSAGE("gp_Scale");
5713 groupPostfix = "scaled";
5715 case gp_CompoundTrsf: // different scale by axis
5716 MESSAGE("gp_CompoundTrsf");
5717 groupPostfix = "scaled";
5721 needReverse = false;
5722 groupPostfix = "transformed";
5725 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5726 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5727 SMESHDS_Mesh* aMesh = GetMeshDS();
5730 // map old node to new one
5731 TNodeNodeMap nodeMap;
5733 // elements sharing moved nodes; those of them which have all
5734 // nodes mirrored but are not in theElems are to be reversed
5735 TIDSortedElemSet inverseElemSet;
5737 // source elements for each generated one
5738 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5740 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5741 TIDSortedElemSet orphanNode;
5743 if ( theElems.empty() ) // transform the whole mesh
5746 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5747 while ( eIt->more() ) theElems.insert( eIt->next() );
5749 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5750 while ( nIt->more() )
5752 const SMDS_MeshNode* node = nIt->next();
5753 if ( node->NbInverseElements() == 0)
5754 orphanNode.insert( node );
5758 // loop on elements to transform nodes : first orphan nodes then elems
5759 TIDSortedElemSet::iterator itElem;
5760 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5761 for (int i=0; i<2; i++)
5762 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5763 const SMDS_MeshElement* elem = *itElem;
5767 // loop on elem nodes
5768 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5769 while ( itN->more() ) {
5771 const SMDS_MeshNode* node = cast2Node( itN->next() );
5772 // check if a node has been already transformed
5773 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5774 nodeMap.insert( make_pair ( node, node ));
5775 if ( !n2n_isnew.second )
5779 coord[0] = node->X();
5780 coord[1] = node->Y();
5781 coord[2] = node->Z();
5782 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5783 if ( theTargetMesh ) {
5784 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5785 n2n_isnew.first->second = newNode;
5786 myLastCreatedNodes.Append(newNode);
5787 srcNodes.Append( node );
5789 else if ( theCopy ) {
5790 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5791 n2n_isnew.first->second = newNode;
5792 myLastCreatedNodes.Append(newNode);
5793 srcNodes.Append( node );
5796 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5797 // node position on shape becomes invalid
5798 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5799 ( SMDS_SpacePosition::originSpacePosition() );
5802 // keep inverse elements
5803 if ( !theCopy && !theTargetMesh && needReverse ) {
5804 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5805 while ( invElemIt->more() ) {
5806 const SMDS_MeshElement* iel = invElemIt->next();
5807 inverseElemSet.insert( iel );
5813 // either create new elements or reverse mirrored ones
5814 if ( !theCopy && !needReverse && !theTargetMesh )
5817 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5818 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5819 theElems.insert( *invElemIt );
5821 // Replicate or reverse elements
5823 std::vector<int> iForw;
5824 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5826 const SMDS_MeshElement* elem = *itElem;
5827 if ( !elem ) continue;
5829 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5830 int nbNodes = elem->NbNodes();
5831 if ( geomType == SMDSGeom_NONE ) continue; // node
5833 switch ( geomType ) {
5835 case SMDSGeom_POLYGON: // ---------------------- polygon
5837 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5839 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5840 while (itN->more()) {
5841 const SMDS_MeshNode* node =
5842 static_cast<const SMDS_MeshNode*>(itN->next());
5843 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5844 if (nodeMapIt == nodeMap.end())
5845 break; // not all nodes transformed
5847 // reverse mirrored faces and volumes
5848 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5850 poly_nodes[iNode] = (*nodeMapIt).second;
5854 if ( iNode != nbNodes )
5855 continue; // not all nodes transformed
5857 if ( theTargetMesh ) {
5858 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5859 srcElems.Append( elem );
5861 else if ( theCopy ) {
5862 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5863 srcElems.Append( elem );
5866 aMesh->ChangePolygonNodes(elem, poly_nodes);
5871 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5873 const SMDS_VtkVolume* aPolyedre =
5874 dynamic_cast<const SMDS_VtkVolume*>( elem );
5876 MESSAGE("Warning: bad volumic element");
5880 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5881 vector<int> quantities; quantities.reserve( nbNodes );
5883 bool allTransformed = true;
5884 int nbFaces = aPolyedre->NbFaces();
5885 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5886 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5887 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5888 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5889 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5890 if (nodeMapIt == nodeMap.end()) {
5891 allTransformed = false; // not all nodes transformed
5893 poly_nodes.push_back((*nodeMapIt).second);
5895 if ( needReverse && allTransformed )
5896 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5898 quantities.push_back(nbFaceNodes);
5900 if ( !allTransformed )
5901 continue; // not all nodes transformed
5903 if ( theTargetMesh ) {
5904 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5905 srcElems.Append( elem );
5907 else if ( theCopy ) {
5908 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5909 srcElems.Append( elem );
5912 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5917 case SMDSGeom_BALL: // -------------------- Ball
5919 if ( !theCopy && !theTargetMesh ) continue;
5921 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5922 if (nodeMapIt == nodeMap.end())
5923 continue; // not all nodes transformed
5925 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5926 if ( theTargetMesh ) {
5927 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5928 srcElems.Append( elem );
5931 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5932 srcElems.Append( elem );
5937 default: // ----------------------- Regular elements
5939 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5940 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5941 const std::vector<int>& i = needReverse ? iRev : iForw;
5943 // find transformed nodes
5944 vector<const SMDS_MeshNode*> nodes(nbNodes);
5946 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5947 while ( itN->more() ) {
5948 const SMDS_MeshNode* node =
5949 static_cast<const SMDS_MeshNode*>( itN->next() );
5950 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5951 if ( nodeMapIt == nodeMap.end() )
5952 break; // not all nodes transformed
5953 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5955 if ( iNode != nbNodes )
5956 continue; // not all nodes transformed
5958 if ( theTargetMesh ) {
5959 if ( SMDS_MeshElement* copy =
5960 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5961 myLastCreatedElems.Append( copy );
5962 srcElems.Append( elem );
5965 else if ( theCopy ) {
5966 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5967 srcElems.Append( elem );
5970 // reverse element as it was reversed by transformation
5972 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5974 } // switch ( geomType )
5976 } // loop on elements
5978 PGroupIDs newGroupIDs;
5980 if ( ( theMakeGroups && theCopy ) ||
5981 ( theMakeGroups && theTargetMesh ) )
5982 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5987 //=======================================================================
5989 * \brief Create groups of elements made during transformation
5990 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5991 * \param elemGens - elements making corresponding myLastCreatedElems
5992 * \param postfix - to append to names of new groups
5994 //=======================================================================
5996 SMESH_MeshEditor::PGroupIDs
5997 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5998 const SMESH_SequenceOfElemPtr& elemGens,
5999 const std::string& postfix,
6000 SMESH_Mesh* targetMesh)
6002 PGroupIDs newGroupIDs( new list<int> );
6003 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6005 // Sort existing groups by types and collect their names
6007 // to store an old group and a generated new ones
6009 using boost::make_tuple;
6010 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6011 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6012 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6014 set< string > groupNames;
6016 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6017 if ( !groupIt->more() ) return newGroupIDs;
6019 int newGroupID = mesh->GetGroupIds().back()+1;
6020 while ( groupIt->more() )
6022 SMESH_Group * group = groupIt->next();
6023 if ( !group ) continue;
6024 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6025 if ( !groupDS || groupDS->IsEmpty() ) continue;
6026 groupNames.insert ( group->GetName() );
6027 groupDS->SetStoreName( group->GetName() );
6028 const SMDSAbs_ElementType type = groupDS->GetType();
6029 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6030 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6031 groupsByType[ groupDS->GetType() ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6032 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6035 // Loop on nodes and elements to add them in new groups
6037 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6039 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6040 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6041 if ( gens.Length() != elems.Length() )
6042 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6044 // loop on created elements
6045 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6047 const SMDS_MeshElement* sourceElem = gens( iElem );
6048 if ( !sourceElem ) {
6049 MESSAGE("generateGroups(): NULL source element");
6052 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6053 if ( groupsOldNew.empty() ) { // no groups of this type at all
6054 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6055 ++iElem; // skip all elements made by sourceElem
6058 // collect all elements made by the iElem-th sourceElem
6059 list< const SMDS_MeshElement* > resultElems;
6060 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6061 if ( resElem != sourceElem )
6062 resultElems.push_back( resElem );
6063 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6064 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6065 if ( resElem != sourceElem )
6066 resultElems.push_back( resElem );
6068 // add resultElems to groups made by ones the sourceElem belongs to
6069 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6070 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6072 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6073 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6075 // fill in a new group
6076 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6077 list< const SMDS_MeshElement* > rejectedElems; // elements of other type
6078 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6079 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6080 if ( !newGroup.Add( *resElemIt ))
6081 rejectedElems.push_back( *resElemIt );
6084 if ( !rejectedElems.empty() )
6086 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6087 resLast = rejectedElems.end();
6088 for ( resElemIt = rejectedElems.begin(); resElemIt != resLast; ++resElemIt )
6089 !newTopGroup.Add( *resElemIt );
6093 } // loop on created elements
6094 }// loop on nodes and elements
6096 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6098 list<int> topGrouIds;
6099 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6101 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6102 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6103 orderedOldNewGroups[i]->get<2>() };
6104 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6105 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6107 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6108 if ( newGroupDS->IsEmpty() )
6110 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6115 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6118 const bool isTop = ( nbNewGroups == 2 &&
6119 newGroupDS->GetType() == oldGroupDS->GetType() );
6120 string name = oldGroupDS->GetStoreName();
6121 if ( !targetMesh ) {
6122 string suffix = ( isTop ? "top": postfix.c_str() );
6126 while ( !groupNames.insert( name ).second ) // name exists
6127 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6132 newGroupDS->SetStoreName( name.c_str() );
6134 // make a SMESH_Groups
6135 mesh->AddGroup( newGroupDS );
6137 topGrouIds.push_back( newGroupDS->GetID() );
6139 newGroupIDs->push_back( newGroupDS->GetID() );
6143 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6148 //================================================================================
6150 * \brief Return list of group of nodes close to each other within theTolerance
6151 * Search among theNodes or in the whole mesh if theNodes is empty using
6152 * an Octree algorithm
6154 //================================================================================
6156 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6157 const double theTolerance,
6158 TListOfListOfNodes & theGroupsOfNodes)
6160 myLastCreatedElems.Clear();
6161 myLastCreatedNodes.Clear();
6163 if ( theNodes.empty() )
6164 { // get all nodes in the mesh
6165 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6166 while ( nIt->more() )
6167 theNodes.insert( theNodes.end(),nIt->next());
6170 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6174 //=======================================================================
6176 * \brief Implementation of search for the node closest to point
6178 //=======================================================================
6180 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6182 //---------------------------------------------------------------------
6184 * \brief Constructor
6186 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6188 myMesh = ( SMESHDS_Mesh* ) theMesh;
6190 TIDSortedNodeSet nodes;
6192 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6193 while ( nIt->more() )
6194 nodes.insert( nodes.end(), nIt->next() );
6196 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6198 // get max size of a leaf box
6199 SMESH_OctreeNode* tree = myOctreeNode;
6200 while ( !tree->isLeaf() )
6202 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6206 myHalfLeafSize = tree->maxSize() / 2.;
6209 //---------------------------------------------------------------------
6211 * \brief Move node and update myOctreeNode accordingly
6213 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6215 myOctreeNode->UpdateByMoveNode( node, toPnt );
6216 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6219 //---------------------------------------------------------------------
6221 * \brief Do it's job
6223 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6225 map<double, const SMDS_MeshNode*> dist2Nodes;
6226 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6227 if ( !dist2Nodes.empty() )
6228 return dist2Nodes.begin()->second;
6229 list<const SMDS_MeshNode*> nodes;
6230 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6232 double minSqDist = DBL_MAX;
6233 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6235 // sort leafs by their distance from thePnt
6236 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6237 TDistTreeMap treeMap;
6238 list< SMESH_OctreeNode* > treeList;
6239 list< SMESH_OctreeNode* >::iterator trIt;
6240 treeList.push_back( myOctreeNode );
6242 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6243 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6244 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6246 SMESH_OctreeNode* tree = *trIt;
6247 if ( !tree->isLeaf() ) // put children to the queue
6249 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6250 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6251 while ( cIt->more() )
6252 treeList.push_back( cIt->next() );
6254 else if ( tree->NbNodes() ) // put a tree to the treeMap
6256 const Bnd_B3d& box = *tree->getBox();
6257 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6258 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6259 if ( !it_in.second ) // not unique distance to box center
6260 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6263 // find distance after which there is no sense to check tree's
6264 double sqLimit = DBL_MAX;
6265 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6266 if ( treeMap.size() > 5 ) {
6267 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6268 const Bnd_B3d& box = *closestTree->getBox();
6269 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6270 sqLimit = limit * limit;
6272 // get all nodes from trees
6273 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6274 if ( sqDist_tree->first > sqLimit )
6276 SMESH_OctreeNode* tree = sqDist_tree->second;
6277 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6280 // find closest among nodes
6281 minSqDist = DBL_MAX;
6282 const SMDS_MeshNode* closestNode = 0;
6283 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6284 for ( ; nIt != nodes.end(); ++nIt ) {
6285 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6286 if ( minSqDist > sqDist ) {
6294 //---------------------------------------------------------------------
6298 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6300 //---------------------------------------------------------------------
6302 * \brief Return the node tree
6304 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6307 SMESH_OctreeNode* myOctreeNode;
6308 SMESHDS_Mesh* myMesh;
6309 double myHalfLeafSize; // max size of a leaf box
6312 //=======================================================================
6314 * \brief Return SMESH_NodeSearcher
6316 //=======================================================================
6318 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6320 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6323 // ========================================================================
6324 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6326 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6327 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6328 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6330 //=======================================================================
6332 * \brief Octal tree of bounding boxes of elements
6334 //=======================================================================
6336 class ElementBndBoxTree : public SMESH_Octree
6340 ElementBndBoxTree(const SMDS_Mesh& mesh,
6341 SMDSAbs_ElementType elemType,
6342 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6343 double tolerance = NodeRadius );
6344 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6345 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6346 void getElementsInSphere ( const gp_XYZ& center,
6347 const double radius, TIDSortedElemSet& foundElems);
6348 size_t getSize() { return std::max( _size, _elements.size() ); }
6349 ~ElementBndBoxTree();
6352 ElementBndBoxTree():_size(0) {}
6353 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6354 void buildChildrenData();
6355 Bnd_B3d* buildRootBox();
6357 //!< Bounding box of element
6358 struct ElementBox : public Bnd_B3d
6360 const SMDS_MeshElement* _element;
6361 int _refCount; // an ElementBox can be included in several tree branches
6362 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6364 vector< ElementBox* > _elements;
6368 //================================================================================
6370 * \brief ElementBndBoxTree creation
6372 //================================================================================
6374 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6375 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6377 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6378 _elements.reserve( nbElems );
6380 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6381 while ( elemIt->more() )
6382 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6387 //================================================================================
6391 //================================================================================
6393 ElementBndBoxTree::~ElementBndBoxTree()
6395 for ( int i = 0; i < _elements.size(); ++i )
6396 if ( --_elements[i]->_refCount <= 0 )
6397 delete _elements[i];
6400 //================================================================================
6402 * \brief Return the maximal box
6404 //================================================================================
6406 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6408 Bnd_B3d* box = new Bnd_B3d;
6409 for ( int i = 0; i < _elements.size(); ++i )
6410 box->Add( *_elements[i] );
6414 //================================================================================
6416 * \brief Redistrubute element boxes among children
6418 //================================================================================
6420 void ElementBndBoxTree::buildChildrenData()
6422 for ( int i = 0; i < _elements.size(); ++i )
6424 for (int j = 0; j < 8; j++)
6426 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6428 _elements[i]->_refCount++;
6429 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6432 _elements[i]->_refCount--;
6434 _size = _elements.size();
6435 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6437 for (int j = 0; j < 8; j++)
6439 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6440 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6441 child->myIsLeaf = true;
6443 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6444 SMESHUtils::CompactVector( child->_elements );
6448 //================================================================================
6450 * \brief Return elements which can include the point
6452 //================================================================================
6454 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6455 TIDSortedElemSet& foundElems)
6457 if ( getBox()->IsOut( point.XYZ() ))
6462 for ( int i = 0; i < _elements.size(); ++i )
6463 if ( !_elements[i]->IsOut( point.XYZ() ))
6464 foundElems.insert( _elements[i]->_element );
6468 for (int i = 0; i < 8; i++)
6469 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6473 //================================================================================
6475 * \brief Return elements which can be intersected by the line
6477 //================================================================================
6479 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6480 TIDSortedElemSet& foundElems)
6482 if ( getBox()->IsOut( line ))
6487 for ( int i = 0; i < _elements.size(); ++i )
6488 if ( !_elements[i]->IsOut( line ))
6489 foundElems.insert( _elements[i]->_element );
6493 for (int i = 0; i < 8; i++)
6494 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6498 //================================================================================
6500 * \brief Return elements from leaves intersecting the sphere
6502 //================================================================================
6504 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6505 const double radius,
6506 TIDSortedElemSet& foundElems)
6508 if ( getBox()->IsOut( center, radius ))
6513 for ( int i = 0; i < _elements.size(); ++i )
6514 if ( !_elements[i]->IsOut( center, radius ))
6515 foundElems.insert( _elements[i]->_element );
6519 for (int i = 0; i < 8; i++)
6520 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6524 //================================================================================
6526 * \brief Construct the element box
6528 //================================================================================
6530 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6534 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6535 while ( nIt->more() )
6536 Add( SMESH_TNodeXYZ( nIt->next() ));
6537 Enlarge( tolerance );
6542 //=======================================================================
6544 * \brief Implementation of search for the elements by point and
6545 * of classification of point in 2D mesh
6547 //=======================================================================
6549 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6551 SMESHDS_Mesh* _mesh;
6552 SMDS_ElemIteratorPtr _meshPartIt;
6553 ElementBndBoxTree* _ebbTree;
6554 SMESH_NodeSearcherImpl* _nodeSearcher;
6555 SMDSAbs_ElementType _elementType;
6557 bool _outerFacesFound;
6558 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6560 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6561 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6562 ~SMESH_ElementSearcherImpl()
6564 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6565 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6567 virtual int FindElementsByPoint(const gp_Pnt& point,
6568 SMDSAbs_ElementType type,
6569 vector< const SMDS_MeshElement* >& foundElements);
6570 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6571 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6572 SMDSAbs_ElementType type );
6574 void GetElementsNearLine( const gp_Ax1& line,
6575 SMDSAbs_ElementType type,
6576 vector< const SMDS_MeshElement* >& foundElems);
6577 double getTolerance();
6578 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6579 const double tolerance, double & param);
6580 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6581 bool isOuterBoundary(const SMDS_MeshElement* face) const
6583 return _outerFaces.empty() || _outerFaces.count(face);
6585 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6587 const SMDS_MeshElement* _face;
6589 bool _coincides; //!< the line lays in face plane
6590 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6591 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6593 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6596 TIDSortedElemSet _faces;
6597 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6598 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6602 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6604 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6605 << ", _coincides="<<i._coincides << ")";
6608 //=======================================================================
6610 * \brief define tolerance for search
6612 //=======================================================================
6614 double SMESH_ElementSearcherImpl::getTolerance()
6616 if ( _tolerance < 0 )
6618 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6621 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6623 double boxSize = _nodeSearcher->getTree()->maxSize();
6624 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6626 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6628 double boxSize = _ebbTree->maxSize();
6629 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6631 if ( _tolerance == 0 )
6633 // define tolerance by size of a most complex element
6634 int complexType = SMDSAbs_Volume;
6635 while ( complexType > SMDSAbs_All &&
6636 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6638 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6640 if ( complexType == int( SMDSAbs_Node ))
6642 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6644 if ( meshInfo.NbNodes() > 2 )
6645 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6649 SMDS_ElemIteratorPtr elemIt =
6650 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6651 const SMDS_MeshElement* elem = elemIt->next();
6652 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6653 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6655 while ( nodeIt->more() )
6657 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6658 elemSize = max( dist, elemSize );
6661 _tolerance = 1e-4 * elemSize;
6667 //================================================================================
6669 * \brief Find intersection of the line and an edge of face and return parameter on line
6671 //================================================================================
6673 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6674 const SMDS_MeshElement* face,
6681 GeomAPI_ExtremaCurveCurve anExtCC;
6682 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6684 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6685 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6687 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6688 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6689 anExtCC.Init( lineCurve, edge);
6690 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6692 Quantity_Parameter pl, pe;
6693 anExtCC.LowerDistanceParameters( pl, pe );
6695 if ( ++nbInts == 2 )
6699 if ( nbInts > 0 ) param /= nbInts;
6702 //================================================================================
6704 * \brief Find all faces belonging to the outer boundary of mesh
6706 //================================================================================
6708 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6710 if ( _outerFacesFound ) return;
6712 // Collect all outer faces by passing from one outer face to another via their links
6713 // and BTW find out if there are internal faces at all.
6715 // checked links and links where outer boundary meets internal one
6716 set< SMESH_TLink > visitedLinks, seamLinks;
6718 // links to treat with already visited faces sharing them
6719 list < TFaceLink > startLinks;
6721 // load startLinks with the first outerFace
6722 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6723 _outerFaces.insert( outerFace );
6725 TIDSortedElemSet emptySet;
6726 while ( !startLinks.empty() )
6728 const SMESH_TLink& link = startLinks.front()._link;
6729 TIDSortedElemSet& faces = startLinks.front()._faces;
6731 outerFace = *faces.begin();
6732 // find other faces sharing the link
6733 const SMDS_MeshElement* f;
6734 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6737 // select another outer face among the found
6738 const SMDS_MeshElement* outerFace2 = 0;
6739 if ( faces.size() == 2 )
6741 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6743 else if ( faces.size() > 2 )
6745 seamLinks.insert( link );
6747 // link direction within the outerFace
6748 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6749 SMESH_TNodeXYZ( link.node2()));
6750 int i1 = outerFace->GetNodeIndex( link.node1() );
6751 int i2 = outerFace->GetNodeIndex( link.node2() );
6752 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6753 if ( rev ) n1n2.Reverse();
6755 gp_XYZ ofNorm, fNorm;
6756 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6758 // direction from the link inside outerFace
6759 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6760 // sort all other faces by angle with the dirInOF
6761 map< double, const SMDS_MeshElement* > angle2Face;
6762 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6763 for ( ; face != faces.end(); ++face )
6765 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6767 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6768 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6769 if ( angle < 0 ) angle += 2. * M_PI;
6770 angle2Face.insert( make_pair( angle, *face ));
6772 if ( !angle2Face.empty() )
6773 outerFace2 = angle2Face.begin()->second;
6776 // store the found outer face and add its links to continue seaching from
6779 _outerFaces.insert( outerFace );
6780 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6781 for ( int i = 0; i < nbNodes; ++i )
6783 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6784 if ( visitedLinks.insert( link2 ).second )
6785 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6788 startLinks.pop_front();
6790 _outerFacesFound = true;
6792 if ( !seamLinks.empty() )
6794 // There are internal boundaries touching the outher one,
6795 // find all faces of internal boundaries in order to find
6796 // faces of boundaries of holes, if any.
6801 _outerFaces.clear();
6805 //=======================================================================
6807 * \brief Find elements of given type where the given point is IN or ON.
6808 * Returns nb of found elements and elements them-selves.
6810 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6812 //=======================================================================
6814 int SMESH_ElementSearcherImpl::
6815 FindElementsByPoint(const gp_Pnt& point,
6816 SMDSAbs_ElementType type,
6817 vector< const SMDS_MeshElement* >& foundElements)
6819 foundElements.clear();
6821 double tolerance = getTolerance();
6823 // =================================================================================
6824 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6826 if ( !_nodeSearcher )
6827 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6829 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6830 if ( !closeNode ) return foundElements.size();
6832 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6833 return foundElements.size(); // to far from any node
6835 if ( type == SMDSAbs_Node )
6837 foundElements.push_back( closeNode );
6841 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6842 while ( elemIt->more() )
6843 foundElements.push_back( elemIt->next() );
6846 // =================================================================================
6847 else // elements more complex than 0D
6849 if ( !_ebbTree || _elementType != type )
6851 if ( _ebbTree ) delete _ebbTree;
6852 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6854 TIDSortedElemSet suspectElems;
6855 _ebbTree->getElementsNearPoint( point, suspectElems );
6856 TIDSortedElemSet::iterator elem = suspectElems.begin();
6857 for ( ; elem != suspectElems.end(); ++elem )
6858 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6859 foundElements.push_back( *elem );
6861 return foundElements.size();
6864 //=======================================================================
6866 * \brief Find an element of given type most close to the given point
6868 * WARNING: Only face search is implemeneted so far
6870 //=======================================================================
6872 const SMDS_MeshElement*
6873 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6874 SMDSAbs_ElementType type )
6876 const SMDS_MeshElement* closestElem = 0;
6878 if ( type == SMDSAbs_Face )
6880 if ( !_ebbTree || _elementType != type )
6882 if ( _ebbTree ) delete _ebbTree;
6883 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6885 TIDSortedElemSet suspectElems;
6886 _ebbTree->getElementsNearPoint( point, suspectElems );
6888 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6890 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6891 _ebbTree->getBox()->CornerMax() );
6893 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6894 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6896 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6897 while ( suspectElems.empty() )
6899 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6903 double minDist = std::numeric_limits<double>::max();
6904 multimap< double, const SMDS_MeshElement* > dist2face;
6905 TIDSortedElemSet::iterator elem = suspectElems.begin();
6906 for ( ; elem != suspectElems.end(); ++elem )
6908 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6910 if ( dist < minDist + 1e-10)
6913 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6916 if ( !dist2face.empty() )
6918 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6919 closestElem = d2f->second;
6920 // if there are several elements at the same distance, select one
6921 // with GC closest to the point
6922 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6923 double minDistToGC = 0;
6924 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6926 if ( minDistToGC == 0 )
6929 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6930 TXyzIterator(), gc ) / closestElem->NbNodes();
6931 minDistToGC = point.SquareDistance( gc );
6934 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6935 TXyzIterator(), gc ) / d2f->second->NbNodes();
6936 double d = point.SquareDistance( gc );
6937 if ( d < minDistToGC )
6940 closestElem = d2f->second;
6943 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6944 // <<closestElem->GetID() << " DIST " << minDist << endl;
6949 // NOT IMPLEMENTED SO FAR
6955 //================================================================================
6957 * \brief Classify the given point in the closed 2D mesh
6959 //================================================================================
6961 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6963 double tolerance = getTolerance();
6964 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6966 if ( _ebbTree ) delete _ebbTree;
6967 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6969 // Algo: analyse transition of a line starting at the point through mesh boundary;
6970 // try three lines parallel to axis of the coordinate system and perform rough
6971 // analysis. If solution is not clear perform thorough analysis.
6973 const int nbAxes = 3;
6974 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6975 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6976 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6977 multimap< int, int > nbInt2Axis; // to find the simplest case
6978 for ( int axis = 0; axis < nbAxes; ++axis )
6980 gp_Ax1 lineAxis( point, axisDir[axis]);
6981 gp_Lin line ( lineAxis );
6983 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6984 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6986 // Intersect faces with the line
6988 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6989 TIDSortedElemSet::iterator face = suspectFaces.begin();
6990 for ( ; face != suspectFaces.end(); ++face )
6994 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6995 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6997 // perform intersection
6998 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6999 if ( !intersection.IsDone() )
7001 if ( intersection.IsInQuadric() )
7003 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7005 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7007 gp_Pnt intersectionPoint = intersection.Point(1);
7008 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7009 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7012 // Analyse intersections roughly
7014 int nbInter = u2inters.size();
7018 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7019 if ( nbInter == 1 ) // not closed mesh
7020 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7022 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7025 if ( (f<0) == (l<0) )
7028 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7029 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
7030 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7033 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7035 if ( _outerFacesFound ) break; // pass to thorough analysis
7037 } // three attempts - loop on CS axes
7039 // Analyse intersections thoroughly.
7040 // We make two loops maximum, on the first one we only exclude touching intersections,
7041 // on the second, if situation is still unclear, we gather and use information on
7042 // position of faces (internal or outer). If faces position is already gathered,
7043 // we make the second loop right away.
7045 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7047 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7048 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7050 int axis = nb_axis->second;
7051 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7053 gp_Ax1 lineAxis( point, axisDir[axis]);
7054 gp_Lin line ( lineAxis );
7056 // add tangent intersections to u2inters
7058 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7059 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7060 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7061 u2inters.insert(make_pair( param, *tgtInt ));
7062 tangentInters[ axis ].clear();
7064 // Count intersections before and after the point excluding touching ones.
7065 // If hasPositionInfo we count intersections of outer boundary only
7067 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7068 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7069 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7070 bool ok = ! u_int1->second._coincides;
7071 while ( ok && u_int1 != u2inters.end() )
7073 double u = u_int1->first;
7074 bool touchingInt = false;
7075 if ( ++u_int2 != u2inters.end() )
7077 // skip intersections at the same point (if the line passes through edge or node)
7079 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7085 // skip tangent intersections
7087 const SMDS_MeshElement* prevFace = u_int1->second._face;
7088 while ( ok && u_int2->second._coincides )
7090 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7096 ok = ( u_int2 != u2inters.end() );
7101 // skip intersections at the same point after tangent intersections
7104 double u2 = u_int2->first;
7106 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7112 // decide if we skipped a touching intersection
7113 if ( nbSamePnt + nbTgt > 0 )
7115 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7116 map< double, TInters >::iterator u_int = u_int1;
7117 for ( ; u_int != u_int2; ++u_int )
7119 if ( u_int->second._coincides ) continue;
7120 double dot = u_int->second._faceNorm * line.Direction();
7121 if ( dot > maxDot ) maxDot = dot;
7122 if ( dot < minDot ) minDot = dot;
7124 touchingInt = ( minDot*maxDot < 0 );
7129 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7140 u_int1 = u_int2; // to next intersection
7142 } // loop on intersections with one line
7146 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7149 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7152 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7153 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7155 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7158 if ( (f<0) == (l<0) )
7161 if ( hasPositionInfo )
7162 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7164 } // loop on intersections of the tree lines - thorough analysis
7166 if ( !hasPositionInfo )
7168 // gather info on faces position - is face in the outer boundary or not
7169 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7170 findOuterBoundary( u2inters.begin()->second._face );
7173 } // two attempts - with and w/o faces position info in the mesh
7175 return TopAbs_UNKNOWN;
7178 //=======================================================================
7180 * \brief Return elements possibly intersecting the line
7182 //=======================================================================
7184 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7185 SMDSAbs_ElementType type,
7186 vector< const SMDS_MeshElement* >& foundElems)
7188 if ( !_ebbTree || _elementType != type )
7190 if ( _ebbTree ) delete _ebbTree;
7191 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7193 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7194 _ebbTree->getElementsNearLine( line, suspectFaces );
7195 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7198 //=======================================================================
7200 * \brief Return SMESH_ElementSearcher
7202 //=======================================================================
7204 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7206 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7209 //=======================================================================
7211 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7213 //=======================================================================
7215 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7217 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7220 //=======================================================================
7222 * \brief Return true if the point is IN or ON of the element
7224 //=======================================================================
7226 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7228 if ( element->GetType() == SMDSAbs_Volume)
7230 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7233 // get ordered nodes
7235 vector< gp_XYZ > xyz;
7236 vector<const SMDS_MeshNode*> nodeList;
7238 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7239 if ( element->IsQuadratic() ) {
7240 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7241 nodeIt = f->interlacedNodesElemIterator();
7242 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7243 nodeIt = e->interlacedNodesElemIterator();
7245 while ( nodeIt->more() )
7247 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7248 xyz.push_back( SMESH_TNodeXYZ(node) );
7249 nodeList.push_back(node);
7252 int i, nbNodes = element->NbNodes();
7254 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7256 // compute face normal
7257 gp_Vec faceNorm(0,0,0);
7258 xyz.push_back( xyz.front() );
7259 nodeList.push_back( nodeList.front() );
7260 for ( i = 0; i < nbNodes; ++i )
7262 gp_Vec edge1( xyz[i+1], xyz[i]);
7263 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7264 faceNorm += edge1 ^ edge2;
7266 double normSize = faceNorm.Magnitude();
7267 if ( normSize <= tol )
7269 // degenerated face: point is out if it is out of all face edges
7270 for ( i = 0; i < nbNodes; ++i )
7272 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7273 if ( !IsOut( &edge, point, tol ))
7278 faceNorm /= normSize;
7280 // check if the point lays on face plane
7281 gp_Vec n2p( xyz[0], point );
7282 if ( fabs( n2p * faceNorm ) > tol )
7283 return true; // not on face plane
7285 // check if point is out of face boundary:
7286 // define it by closest transition of a ray point->infinity through face boundary
7287 // on the face plane.
7288 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7289 // to find intersections of the ray with the boundary.
7291 gp_Vec plnNorm = ray ^ faceNorm;
7292 normSize = plnNorm.Magnitude();
7293 if ( normSize <= tol ) return false; // point coincides with the first node
7294 plnNorm /= normSize;
7295 // for each node of the face, compute its signed distance to the plane
7296 vector<double> dist( nbNodes + 1);
7297 for ( i = 0; i < nbNodes; ++i )
7299 gp_Vec n2p( xyz[i], point );
7300 dist[i] = n2p * plnNorm;
7302 dist.back() = dist.front();
7303 // find the closest intersection
7305 double rClosest, distClosest = 1e100;;
7307 for ( i = 0; i < nbNodes; ++i )
7310 if ( fabs( dist[i]) < tol )
7312 else if ( fabs( dist[i+1]) < tol )
7314 else if ( dist[i] * dist[i+1] < 0 )
7315 r = dist[i] / ( dist[i] - dist[i+1] );
7317 continue; // no intersection
7318 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7319 gp_Vec p2int ( point, pInt);
7320 if ( p2int * ray > -tol ) // right half-space
7322 double intDist = p2int.SquareMagnitude();
7323 if ( intDist < distClosest )
7328 distClosest = intDist;
7333 return true; // no intesections - out
7335 // analyse transition
7336 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7337 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7338 gp_Vec p2int ( point, pClosest );
7339 bool out = (edgeNorm * p2int) < -tol;
7340 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7343 // ray pass through a face node; analyze transition through an adjacent edge
7344 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7345 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7346 gp_Vec edgeAdjacent( p1, p2 );
7347 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7348 bool out2 = (edgeNorm2 * p2int) < -tol;
7350 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7351 return covexCorner ? (out || out2) : (out && out2);
7353 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7355 // point is out of edge if it is NOT ON any straight part of edge
7356 // (we consider quadratic edge as being composed of two straight parts)
7357 for ( i = 1; i < nbNodes; ++i )
7359 gp_Vec edge( xyz[i-1], xyz[i]);
7360 gp_Vec n1p ( xyz[i-1], point);
7361 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7364 gp_Vec n2p( xyz[i], point );
7365 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7367 return false; // point is ON this part
7371 // Node or 0D element -------------------------------------------------------------------------
7373 gp_Vec n2p ( xyz[0], point );
7374 return n2p.Magnitude() <= tol;
7379 //=======================================================================
7383 // Position of a point relative to a segment
7387 // VERTEX 1 o----ON-----> VERTEX 2
7391 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7392 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7396 int _index; // index of vertex or segment
7398 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7399 bool operator < (const PointPos& other ) const
7401 if ( _name == other._name )
7402 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7403 return _name < other._name;
7407 //================================================================================
7409 * \brief Return of a point relative to a segment
7410 * \param point2D - the point to analyze position of
7411 * \param xyVec - end points of segments
7412 * \param index0 - 0-based index of the first point of segment
7413 * \param posToFindOut - flags of positions to detect
7414 * \retval PointPos - point position
7416 //================================================================================
7418 PointPos getPointPosition( const gp_XY& point2D,
7419 const gp_XY* segEnds,
7420 const int index0 = 0,
7421 const int posToFindOut = POS_ALL)
7423 const gp_XY& p1 = segEnds[ index0 ];
7424 const gp_XY& p2 = segEnds[ index0+1 ];
7425 const gp_XY grad = p2 - p1;
7427 if ( posToFindOut & POS_VERTEX )
7429 // check if the point2D is at "vertex 1" zone
7430 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7431 p1.Y() + grad.X() ) };
7432 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7433 return PointPos( POS_VERTEX, index0 );
7435 // check if the point2D is at "vertex 2" zone
7436 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7437 p2.Y() + grad.X() ) };
7438 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7439 return PointPos( POS_VERTEX, index0 + 1);
7441 double edgeEquation =
7442 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7443 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7447 //=======================================================================
7449 * \brief Return minimal distance from a point to a face
7451 * Currently we ignore non-planarity and 2nd order of face
7453 //=======================================================================
7455 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7456 const gp_Pnt& point )
7458 double badDistance = -1;
7459 if ( !face ) return badDistance;
7461 // coordinates of nodes (medium nodes, if any, ignored)
7462 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7463 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7464 xyz.resize( face->NbCornerNodes()+1 );
7466 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7467 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7469 gp_Vec OZ ( xyz[0], xyz[1] );
7470 gp_Vec OX ( xyz[0], xyz[2] );
7471 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7473 if ( xyz.size() < 4 ) return badDistance;
7474 OZ = gp_Vec ( xyz[0], xyz[2] );
7475 OX = gp_Vec ( xyz[0], xyz[3] );
7479 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7481 catch ( Standard_Failure ) {
7484 trsf.SetTransformation( tgtCS );
7486 // move all the nodes to 2D
7487 vector<gp_XY> xy( xyz.size() );
7488 for ( size_t i = 0;i < xyz.size()-1; ++i )
7490 gp_XYZ p3d = xyz[i];
7491 trsf.Transforms( p3d );
7492 xy[i].SetCoord( p3d.X(), p3d.Z() );
7494 xyz.back() = xyz.front();
7495 xy.back() = xy.front();
7497 // // move the point in 2D
7498 gp_XYZ tmpPnt = point.XYZ();
7499 trsf.Transforms( tmpPnt );
7500 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7502 // loop on segments of the face to analyze point position ralative to the face
7503 set< PointPos > pntPosSet;
7504 for ( size_t i = 1; i < xy.size(); ++i )
7506 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7507 pntPosSet.insert( pos );
7511 PointPos pos = *pntPosSet.begin();
7512 // cout << "Face " << face->GetID() << " DIST: ";
7513 switch ( pos._name )
7516 // point is most close to a segment
7517 gp_Vec p0p1( point, xyz[ pos._index ] );
7518 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7520 double projDist = p0p1 * p1p2; // distance projected to the segment
7521 gp_Vec projVec = p1p2 * projDist;
7522 gp_Vec distVec = p0p1 - projVec;
7523 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7524 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7525 return distVec.Magnitude();
7528 // point is inside the face
7529 double distToFacePlane = tmpPnt.Y();
7530 // cout << distToFacePlane << ", INSIDE " << endl;
7531 return Abs( distToFacePlane );
7534 // point is most close to a node
7535 gp_Vec distVec( point, xyz[ pos._index ]);
7536 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7537 return distVec.Magnitude();
7543 //=======================================================================
7544 //function : SimplifyFace
7546 //=======================================================================
7547 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7548 vector<const SMDS_MeshNode *>& poly_nodes,
7549 vector<int>& quantities) const
7551 int nbNodes = faceNodes.size();
7556 set<const SMDS_MeshNode*> nodeSet;
7558 // get simple seq of nodes
7559 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7560 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7561 int iSimple = 0, nbUnique = 0;
7563 simpleNodes[iSimple++] = faceNodes[0];
7565 for (int iCur = 1; iCur < nbNodes; iCur++) {
7566 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7567 simpleNodes[iSimple++] = faceNodes[iCur];
7568 if (nodeSet.insert( faceNodes[iCur] ).second)
7572 int nbSimple = iSimple;
7573 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7583 bool foundLoop = (nbSimple > nbUnique);
7586 set<const SMDS_MeshNode*> loopSet;
7587 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7588 const SMDS_MeshNode* n = simpleNodes[iSimple];
7589 if (!loopSet.insert( n ).second) {
7593 int iC = 0, curLast = iSimple;
7594 for (; iC < curLast; iC++) {
7595 if (simpleNodes[iC] == n) break;
7597 int loopLen = curLast - iC;
7599 // create sub-element
7601 quantities.push_back(loopLen);
7602 for (; iC < curLast; iC++) {
7603 poly_nodes.push_back(simpleNodes[iC]);
7606 // shift the rest nodes (place from the first loop position)
7607 for (iC = curLast + 1; iC < nbSimple; iC++) {
7608 simpleNodes[iC - loopLen] = simpleNodes[iC];
7610 nbSimple -= loopLen;
7613 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7614 } // while (foundLoop)
7618 quantities.push_back(iSimple);
7619 for (int i = 0; i < iSimple; i++)
7620 poly_nodes.push_back(simpleNodes[i]);
7626 //=======================================================================
7627 //function : MergeNodes
7628 //purpose : In each group, the cdr of nodes are substituted by the first one
7630 //=======================================================================
7632 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7634 MESSAGE("MergeNodes");
7635 myLastCreatedElems.Clear();
7636 myLastCreatedNodes.Clear();
7638 SMESHDS_Mesh* aMesh = GetMeshDS();
7640 TNodeNodeMap nodeNodeMap; // node to replace - new node
7641 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7642 list< int > rmElemIds, rmNodeIds;
7644 // Fill nodeNodeMap and elems
7646 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7647 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7648 list<const SMDS_MeshNode*>& nodes = *grIt;
7649 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7650 const SMDS_MeshNode* nToKeep = *nIt;
7651 //MESSAGE("node to keep " << nToKeep->GetID());
7652 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7653 const SMDS_MeshNode* nToRemove = *nIt;
7654 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7655 if ( nToRemove != nToKeep ) {
7656 //MESSAGE(" node to remove " << nToRemove->GetID());
7657 rmNodeIds.push_back( nToRemove->GetID() );
7658 AddToSameGroups( nToKeep, nToRemove, aMesh );
7659 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7660 // after MergeNodes() w/o creating node in place of merged ones.
7661 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7662 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7663 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7664 sm->SetIsAlwaysComputed( true );
7667 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7668 while ( invElemIt->more() ) {
7669 const SMDS_MeshElement* elem = invElemIt->next();
7674 // Change element nodes or remove an element
7676 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7677 for ( ; eIt != elems.end(); eIt++ ) {
7678 const SMDS_MeshElement* elem = *eIt;
7679 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7680 int nbNodes = elem->NbNodes();
7681 int aShapeId = FindShape( elem );
7683 set<const SMDS_MeshNode*> nodeSet;
7684 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7685 int iUnique = 0, iCur = 0, nbRepl = 0;
7686 vector<int> iRepl( nbNodes );
7688 // get new seq of nodes
7689 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7690 while ( itN->more() ) {
7691 const SMDS_MeshNode* n =
7692 static_cast<const SMDS_MeshNode*>( itN->next() );
7694 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7695 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7697 // BUG 0020185: begin
7699 bool stopRecur = false;
7700 set<const SMDS_MeshNode*> nodesRecur;
7701 nodesRecur.insert(n);
7702 while (!stopRecur) {
7703 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7704 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7705 n = (*nnIt_i).second;
7706 if (!nodesRecur.insert(n).second) {
7707 // error: recursive dependancy
7717 curNodes[ iCur ] = n;
7718 bool isUnique = nodeSet.insert( n ).second;
7720 uniqueNodes[ iUnique++ ] = n;
7722 iRepl[ nbRepl++ ] = iCur;
7726 // Analyse element topology after replacement
7729 int nbUniqueNodes = nodeSet.size();
7730 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7731 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7732 // Polygons and Polyhedral volumes
7733 if (elem->IsPoly()) {
7735 if (elem->GetType() == SMDSAbs_Face) {
7737 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7739 for (; inode < nbNodes; inode++) {
7740 face_nodes[inode] = curNodes[inode];
7743 vector<const SMDS_MeshNode *> polygons_nodes;
7744 vector<int> quantities;
7745 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7748 for (int iface = 0; iface < nbNew; iface++) {
7749 int nbNodes = quantities[iface];
7750 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7751 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7752 poly_nodes[ii] = polygons_nodes[inode];
7754 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7755 myLastCreatedElems.Append(newElem);
7757 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7760 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7761 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7762 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7764 if (nbNew > 0) quid = nbNew - 1;
7765 vector<int> newquant(quantities.begin()+quid, quantities.end());
7766 const SMDS_MeshElement* newElem = 0;
7767 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7768 myLastCreatedElems.Append(newElem);
7769 if ( aShapeId && newElem )
7770 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7771 rmElemIds.push_back(elem->GetID());
7774 rmElemIds.push_back(elem->GetID());
7778 else if (elem->GetType() == SMDSAbs_Volume) {
7779 // Polyhedral volume
7780 if (nbUniqueNodes < 4) {
7781 rmElemIds.push_back(elem->GetID());
7784 // each face has to be analyzed in order to check volume validity
7785 const SMDS_VtkVolume* aPolyedre =
7786 dynamic_cast<const SMDS_VtkVolume*>( elem );
7788 int nbFaces = aPolyedre->NbFaces();
7790 vector<const SMDS_MeshNode *> poly_nodes;
7791 vector<int> quantities;
7793 for (int iface = 1; iface <= nbFaces; iface++) {
7794 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7795 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7797 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7798 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7799 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7800 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7801 faceNode = (*nnIt).second;
7803 faceNodes[inode - 1] = faceNode;
7806 SimplifyFace(faceNodes, poly_nodes, quantities);
7809 if (quantities.size() > 3) {
7810 // to be done: remove coincident faces
7813 if (quantities.size() > 3)
7815 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7816 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7817 const SMDS_MeshElement* newElem = 0;
7818 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7819 myLastCreatedElems.Append(newElem);
7820 if ( aShapeId && newElem )
7821 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7822 rmElemIds.push_back(elem->GetID());
7826 rmElemIds.push_back(elem->GetID());
7837 // TODO not all the possible cases are solved. Find something more generic?
7838 switch ( nbNodes ) {
7839 case 2: ///////////////////////////////////// EDGE
7840 isOk = false; break;
7841 case 3: ///////////////////////////////////// TRIANGLE
7842 isOk = false; break;
7844 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7846 else { //////////////////////////////////// QUADRANGLE
7847 if ( nbUniqueNodes < 3 )
7849 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7850 isOk = false; // opposite nodes stick
7851 //MESSAGE("isOk " << isOk);
7854 case 6: ///////////////////////////////////// PENTAHEDRON
7855 if ( nbUniqueNodes == 4 ) {
7856 // ---------------------------------> tetrahedron
7858 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7859 // all top nodes stick: reverse a bottom
7860 uniqueNodes[ 0 ] = curNodes [ 1 ];
7861 uniqueNodes[ 1 ] = curNodes [ 0 ];
7863 else if (nbRepl == 3 &&
7864 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7865 // all bottom nodes stick: set a top before
7866 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7867 uniqueNodes[ 0 ] = curNodes [ 3 ];
7868 uniqueNodes[ 1 ] = curNodes [ 4 ];
7869 uniqueNodes[ 2 ] = curNodes [ 5 ];
7871 else if (nbRepl == 4 &&
7872 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7873 // a lateral face turns into a line: reverse a bottom
7874 uniqueNodes[ 0 ] = curNodes [ 1 ];
7875 uniqueNodes[ 1 ] = curNodes [ 0 ];
7880 else if ( nbUniqueNodes == 5 ) {
7881 // PENTAHEDRON --------------------> 2 tetrahedrons
7882 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7883 // a bottom node sticks with a linked top one
7885 SMDS_MeshElement* newElem =
7886 aMesh->AddVolume(curNodes[ 3 ],
7889 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7890 myLastCreatedElems.Append(newElem);
7892 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7893 // 2. : reverse a bottom
7894 uniqueNodes[ 0 ] = curNodes [ 1 ];
7895 uniqueNodes[ 1 ] = curNodes [ 0 ];
7905 if(elem->IsQuadratic()) { // Quadratic quadrangle
7917 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7920 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7922 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7923 uniqueNodes[0] = curNodes[0];
7924 uniqueNodes[1] = curNodes[2];
7925 uniqueNodes[2] = curNodes[3];
7926 uniqueNodes[3] = curNodes[5];
7927 uniqueNodes[4] = curNodes[6];
7928 uniqueNodes[5] = curNodes[7];
7931 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7932 uniqueNodes[0] = curNodes[0];
7933 uniqueNodes[1] = curNodes[1];
7934 uniqueNodes[2] = curNodes[2];
7935 uniqueNodes[3] = curNodes[4];
7936 uniqueNodes[4] = curNodes[5];
7937 uniqueNodes[5] = curNodes[6];
7940 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7941 uniqueNodes[0] = curNodes[1];
7942 uniqueNodes[1] = curNodes[2];
7943 uniqueNodes[2] = curNodes[3];
7944 uniqueNodes[3] = curNodes[5];
7945 uniqueNodes[4] = curNodes[6];
7946 uniqueNodes[5] = curNodes[0];
7949 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7950 uniqueNodes[0] = curNodes[0];
7951 uniqueNodes[1] = curNodes[1];
7952 uniqueNodes[2] = curNodes[3];
7953 uniqueNodes[3] = curNodes[4];
7954 uniqueNodes[4] = curNodes[6];
7955 uniqueNodes[5] = curNodes[7];
7958 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7959 uniqueNodes[0] = curNodes[0];
7960 uniqueNodes[1] = curNodes[2];
7961 uniqueNodes[2] = curNodes[3];
7962 uniqueNodes[3] = curNodes[1];
7963 uniqueNodes[4] = curNodes[6];
7964 uniqueNodes[5] = curNodes[7];
7967 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7968 uniqueNodes[0] = curNodes[0];
7969 uniqueNodes[1] = curNodes[1];
7970 uniqueNodes[2] = curNodes[2];
7971 uniqueNodes[3] = curNodes[4];
7972 uniqueNodes[4] = curNodes[5];
7973 uniqueNodes[5] = curNodes[7];
7976 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7977 uniqueNodes[0] = curNodes[0];
7978 uniqueNodes[1] = curNodes[1];
7979 uniqueNodes[2] = curNodes[3];
7980 uniqueNodes[3] = curNodes[4];
7981 uniqueNodes[4] = curNodes[2];
7982 uniqueNodes[5] = curNodes[7];
7985 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7986 uniqueNodes[0] = curNodes[0];
7987 uniqueNodes[1] = curNodes[1];
7988 uniqueNodes[2] = curNodes[2];
7989 uniqueNodes[3] = curNodes[4];
7990 uniqueNodes[4] = curNodes[5];
7991 uniqueNodes[5] = curNodes[3];
7996 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7999 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
8003 //////////////////////////////////// HEXAHEDRON
8005 SMDS_VolumeTool hexa (elem);
8006 hexa.SetExternalNormal();
8007 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8008 //////////////////////// HEX ---> 1 tetrahedron
8009 for ( int iFace = 0; iFace < 6; iFace++ ) {
8010 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8011 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8012 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8013 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8014 // one face turns into a point ...
8015 int iOppFace = hexa.GetOppFaceIndex( iFace );
8016 ind = hexa.GetFaceNodesIndices( iOppFace );
8018 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8019 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8022 if ( nbStick == 1 ) {
8023 // ... and the opposite one - into a triangle.
8025 ind = hexa.GetFaceNodesIndices( iFace );
8026 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8033 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8034 //////////////////////// HEX ---> 1 prism
8035 int nbTria = 0, iTria[3];
8036 const int *ind; // indices of face nodes
8037 // look for triangular faces
8038 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8039 ind = hexa.GetFaceNodesIndices( iFace );
8040 TIDSortedNodeSet faceNodes;
8041 for ( iCur = 0; iCur < 4; iCur++ )
8042 faceNodes.insert( curNodes[ind[iCur]] );
8043 if ( faceNodes.size() == 3 )
8044 iTria[ nbTria++ ] = iFace;
8046 // check if triangles are opposite
8047 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8050 // set nodes of the bottom triangle
8051 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8053 for ( iCur = 0; iCur < 4; iCur++ )
8054 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8055 indB.push_back( ind[iCur] );
8056 if ( !hexa.IsForward() )
8057 std::swap( indB[0], indB[2] );
8058 for ( iCur = 0; iCur < 3; iCur++ )
8059 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8060 // set nodes of the top triangle
8061 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8062 for ( iCur = 0; iCur < 3; ++iCur )
8063 for ( int j = 0; j < 4; ++j )
8064 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8066 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8072 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8073 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8074 for ( int iFace = 0; iFace < 6; iFace++ ) {
8075 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8076 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8077 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8078 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8079 // one face turns into a point ...
8080 int iOppFace = hexa.GetOppFaceIndex( iFace );
8081 ind = hexa.GetFaceNodesIndices( iOppFace );
8083 iUnique = 2; // reverse a tetrahedron 1 bottom
8084 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8085 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8087 else if ( iUnique >= 0 )
8088 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8090 if ( nbStick == 0 ) {
8091 // ... and the opposite one is a quadrangle
8093 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8094 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8097 SMDS_MeshElement* newElem =
8098 aMesh->AddVolume(curNodes[ind[ 0 ]],
8101 curNodes[indTop[ 0 ]]);
8102 myLastCreatedElems.Append(newElem);
8104 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8111 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8112 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8113 // find indices of quad and tri faces
8114 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8115 for ( iFace = 0; iFace < 6; iFace++ ) {
8116 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8118 for ( iCur = 0; iCur < 4; iCur++ )
8119 nodeSet.insert( curNodes[ind[ iCur ]] );
8120 nbUniqueNodes = nodeSet.size();
8121 if ( nbUniqueNodes == 3 )
8122 iTriFace[ nbTri++ ] = iFace;
8123 else if ( nbUniqueNodes == 4 )
8124 iQuadFace[ nbQuad++ ] = iFace;
8126 if (nbQuad == 2 && nbTri == 4 &&
8127 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8128 // 2 opposite quadrangles stuck with a diagonal;
8129 // sample groups of merged indices: (0-4)(2-6)
8130 // --------------------------------------------> 2 tetrahedrons
8131 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8132 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8133 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8134 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8135 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8136 // stuck with 0-2 diagonal
8144 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8145 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8146 // stuck with 1-3 diagonal
8158 uniqueNodes[ 0 ] = curNodes [ i0 ];
8159 uniqueNodes[ 1 ] = curNodes [ i1d ];
8160 uniqueNodes[ 2 ] = curNodes [ i3d ];
8161 uniqueNodes[ 3 ] = curNodes [ i0t ];
8164 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8168 myLastCreatedElems.Append(newElem);
8170 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8173 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8174 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8175 // --------------------------------------------> prism
8176 // find 2 opposite triangles
8178 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8179 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8180 // find indices of kept and replaced nodes
8181 // and fill unique nodes of 2 opposite triangles
8182 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8183 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8184 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8185 // fill unique nodes
8188 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8189 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8190 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8192 // iCur of a linked node of the opposite face (make normals co-directed):
8193 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8194 // check that correspondent corners of triangles are linked
8195 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8198 uniqueNodes[ iUnique ] = n;
8199 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8208 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8211 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8218 } // switch ( nbNodes )
8220 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8222 if ( isOk ) { // the elem remains valid after sticking nodes
8223 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8225 // Change nodes of polyedre
8226 const SMDS_VtkVolume* aPolyedre =
8227 dynamic_cast<const SMDS_VtkVolume*>( elem );
8229 int nbFaces = aPolyedre->NbFaces();
8231 vector<const SMDS_MeshNode *> poly_nodes;
8232 vector<int> quantities (nbFaces);
8234 for (int iface = 1; iface <= nbFaces; iface++) {
8235 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8236 quantities[iface - 1] = nbFaceNodes;
8238 for (inode = 1; inode <= nbFaceNodes; inode++) {
8239 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8241 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8242 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8243 curNode = (*nnIt).second;
8245 poly_nodes.push_back(curNode);
8248 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8251 else // replace non-polyhedron elements
8253 const SMDSAbs_ElementType etyp = elem->GetType();
8254 const int elemId = elem->GetID();
8255 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8256 uniqueNodes.resize(nbUniqueNodes);
8258 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8260 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8261 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8262 if ( sm && newElem )
8263 sm->AddElement( newElem );
8264 if ( elem != newElem )
8265 ReplaceElemInGroups( elem, newElem, aMesh );
8269 // Remove invalid regular element or invalid polygon
8270 rmElemIds.push_back( elem->GetID() );
8273 } // loop on elements
8275 // Remove bad elements, then equal nodes (order important)
8277 Remove( rmElemIds, false );
8278 Remove( rmNodeIds, true );
8283 // ========================================================
8284 // class : SortableElement
8285 // purpose : allow sorting elements basing on their nodes
8286 // ========================================================
8287 class SortableElement : public set <const SMDS_MeshElement*>
8291 SortableElement( const SMDS_MeshElement* theElem )
8294 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8295 while ( nodeIt->more() )
8296 this->insert( nodeIt->next() );
8299 const SMDS_MeshElement* Get() const
8302 void Set(const SMDS_MeshElement* e) const
8307 mutable const SMDS_MeshElement* myElem;
8310 //=======================================================================
8311 //function : FindEqualElements
8312 //purpose : Return list of group of elements built on the same nodes.
8313 // Search among theElements or in the whole mesh if theElements is empty
8314 //=======================================================================
8316 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8317 TListOfListOfElementsID & theGroupsOfElementsID)
8319 myLastCreatedElems.Clear();
8320 myLastCreatedNodes.Clear();
8322 typedef map< SortableElement, int > TMapOfNodeSet;
8323 typedef list<int> TGroupOfElems;
8325 if ( theElements.empty() )
8326 { // get all elements in the mesh
8327 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8328 while ( eIt->more() )
8329 theElements.insert( theElements.end(), eIt->next());
8332 vector< TGroupOfElems > arrayOfGroups;
8333 TGroupOfElems groupOfElems;
8334 TMapOfNodeSet mapOfNodeSet;
8336 TIDSortedElemSet::iterator elemIt = theElements.begin();
8337 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8338 const SMDS_MeshElement* curElem = *elemIt;
8339 SortableElement SE(curElem);
8342 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8343 if( !(pp.second) ) {
8344 TMapOfNodeSet::iterator& itSE = pp.first;
8345 ind = (*itSE).second;
8346 arrayOfGroups[ind].push_back(curElem->GetID());
8349 groupOfElems.clear();
8350 groupOfElems.push_back(curElem->GetID());
8351 arrayOfGroups.push_back(groupOfElems);
8356 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8357 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8358 groupOfElems = *groupIt;
8359 if ( groupOfElems.size() > 1 ) {
8360 groupOfElems.sort();
8361 theGroupsOfElementsID.push_back(groupOfElems);
8366 //=======================================================================
8367 //function : MergeElements
8368 //purpose : In each given group, substitute all elements by the first one.
8369 //=======================================================================
8371 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8373 myLastCreatedElems.Clear();
8374 myLastCreatedNodes.Clear();
8376 typedef list<int> TListOfIDs;
8377 TListOfIDs rmElemIds; // IDs of elems to remove
8379 SMESHDS_Mesh* aMesh = GetMeshDS();
8381 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8382 while ( groupsIt != theGroupsOfElementsID.end() ) {
8383 TListOfIDs& aGroupOfElemID = *groupsIt;
8384 aGroupOfElemID.sort();
8385 int elemIDToKeep = aGroupOfElemID.front();
8386 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8387 aGroupOfElemID.pop_front();
8388 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8389 while ( idIt != aGroupOfElemID.end() ) {
8390 int elemIDToRemove = *idIt;
8391 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8392 // add the kept element in groups of removed one (PAL15188)
8393 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8394 rmElemIds.push_back( elemIDToRemove );
8400 Remove( rmElemIds, false );
8403 //=======================================================================
8404 //function : MergeEqualElements
8405 //purpose : Remove all but one of elements built on the same nodes.
8406 //=======================================================================
8408 void SMESH_MeshEditor::MergeEqualElements()
8410 TIDSortedElemSet aMeshElements; /* empty input ==
8411 to merge equal elements in the whole mesh */
8412 TListOfListOfElementsID aGroupsOfElementsID;
8413 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8414 MergeElements(aGroupsOfElementsID);
8417 //=======================================================================
8418 //function : FindFaceInSet
8419 //purpose : Return a face having linked nodes n1 and n2 and which is
8420 // - not in avoidSet,
8421 // - in elemSet provided that !elemSet.empty()
8422 // i1 and i2 optionally returns indices of n1 and n2
8423 //=======================================================================
8425 const SMDS_MeshElement*
8426 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8427 const SMDS_MeshNode* n2,
8428 const TIDSortedElemSet& elemSet,
8429 const TIDSortedElemSet& avoidSet,
8435 const SMDS_MeshElement* face = 0;
8437 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8438 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8439 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8441 //MESSAGE("in while ( invElemIt->more() && !face )");
8442 const SMDS_MeshElement* elem = invElemIt->next();
8443 if (avoidSet.count( elem ))
8445 if ( !elemSet.empty() && !elemSet.count( elem ))
8448 i1 = elem->GetNodeIndex( n1 );
8449 // find a n2 linked to n1
8450 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8451 for ( int di = -1; di < 2 && !face; di += 2 )
8453 i2 = (i1+di+nbN) % nbN;
8454 if ( elem->GetNode( i2 ) == n2 )
8457 if ( !face && elem->IsQuadratic())
8459 // analysis for quadratic elements using all nodes
8460 const SMDS_VtkFace* F =
8461 dynamic_cast<const SMDS_VtkFace*>(elem);
8462 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8463 // use special nodes iterator
8464 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8465 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8466 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8468 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8469 if ( n1 == prevN && n2 == n )
8473 else if ( n2 == prevN && n1 == n )
8475 face = elem; swap( i1, i2 );
8481 if ( n1ind ) *n1ind = i1;
8482 if ( n2ind ) *n2ind = i2;
8486 //=======================================================================
8487 //function : findAdjacentFace
8489 //=======================================================================
8491 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8492 const SMDS_MeshNode* n2,
8493 const SMDS_MeshElement* elem)
8495 TIDSortedElemSet elemSet, avoidSet;
8497 avoidSet.insert ( elem );
8498 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8501 //=======================================================================
8502 //function : FindFreeBorder
8504 //=======================================================================
8506 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8508 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8509 const SMDS_MeshNode* theSecondNode,
8510 const SMDS_MeshNode* theLastNode,
8511 list< const SMDS_MeshNode* > & theNodes,
8512 list< const SMDS_MeshElement* >& theFaces)
8514 if ( !theFirstNode || !theSecondNode )
8516 // find border face between theFirstNode and theSecondNode
8517 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8521 theFaces.push_back( curElem );
8522 theNodes.push_back( theFirstNode );
8523 theNodes.push_back( theSecondNode );
8525 //vector<const SMDS_MeshNode*> nodes;
8526 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8527 TIDSortedElemSet foundElems;
8528 bool needTheLast = ( theLastNode != 0 );
8530 while ( nStart != theLastNode ) {
8531 if ( nStart == theFirstNode )
8532 return !needTheLast;
8534 // find all free border faces sharing form nStart
8536 list< const SMDS_MeshElement* > curElemList;
8537 list< const SMDS_MeshNode* > nStartList;
8538 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8539 while ( invElemIt->more() ) {
8540 const SMDS_MeshElement* e = invElemIt->next();
8541 if ( e == curElem || foundElems.insert( e ).second ) {
8543 int iNode = 0, nbNodes = e->NbNodes();
8544 //const SMDS_MeshNode* nodes[nbNodes+1];
8545 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8547 if(e->IsQuadratic()) {
8548 const SMDS_VtkFace* F =
8549 dynamic_cast<const SMDS_VtkFace*>(e);
8550 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8551 // use special nodes iterator
8552 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8553 while( anIter->more() ) {
8554 nodes[ iNode++ ] = cast2Node(anIter->next());
8558 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8559 while ( nIt->more() )
8560 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8562 nodes[ iNode ] = nodes[ 0 ];
8564 for ( iNode = 0; iNode < nbNodes; iNode++ )
8565 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8566 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8567 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8569 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8570 curElemList.push_back( e );
8574 // analyse the found
8576 int nbNewBorders = curElemList.size();
8577 if ( nbNewBorders == 0 ) {
8578 // no free border furthermore
8579 return !needTheLast;
8581 else if ( nbNewBorders == 1 ) {
8582 // one more element found
8584 nStart = nStartList.front();
8585 curElem = curElemList.front();
8586 theFaces.push_back( curElem );
8587 theNodes.push_back( nStart );
8590 // several continuations found
8591 list< const SMDS_MeshElement* >::iterator curElemIt;
8592 list< const SMDS_MeshNode* >::iterator nStartIt;
8593 // check if one of them reached the last node
8594 if ( needTheLast ) {
8595 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8596 curElemIt!= curElemList.end();
8597 curElemIt++, nStartIt++ )
8598 if ( *nStartIt == theLastNode ) {
8599 theFaces.push_back( *curElemIt );
8600 theNodes.push_back( *nStartIt );
8604 // find the best free border by the continuations
8605 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8606 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8607 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8608 curElemIt!= curElemList.end();
8609 curElemIt++, nStartIt++ )
8611 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8612 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8613 // find one more free border
8614 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8618 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8619 // choice: clear a worse one
8620 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8621 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8622 contNodes[ iWorse ].clear();
8623 contFaces[ iWorse ].clear();
8626 if ( contNodes[0].empty() && contNodes[1].empty() )
8629 // append the best free border
8630 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8631 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8632 theNodes.pop_back(); // remove nIgnore
8633 theNodes.pop_back(); // remove nStart
8634 theFaces.pop_back(); // remove curElem
8635 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8636 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8637 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8638 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8641 } // several continuations found
8642 } // while ( nStart != theLastNode )
8647 //=======================================================================
8648 //function : CheckFreeBorderNodes
8649 //purpose : Return true if the tree nodes are on a free border
8650 //=======================================================================
8652 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8653 const SMDS_MeshNode* theNode2,
8654 const SMDS_MeshNode* theNode3)
8656 list< const SMDS_MeshNode* > nodes;
8657 list< const SMDS_MeshElement* > faces;
8658 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8661 //=======================================================================
8662 //function : SewFreeBorder
8664 //=======================================================================
8666 SMESH_MeshEditor::Sew_Error
8667 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8668 const SMDS_MeshNode* theBordSecondNode,
8669 const SMDS_MeshNode* theBordLastNode,
8670 const SMDS_MeshNode* theSideFirstNode,
8671 const SMDS_MeshNode* theSideSecondNode,
8672 const SMDS_MeshNode* theSideThirdNode,
8673 const bool theSideIsFreeBorder,
8674 const bool toCreatePolygons,
8675 const bool toCreatePolyedrs)
8677 myLastCreatedElems.Clear();
8678 myLastCreatedNodes.Clear();
8680 MESSAGE("::SewFreeBorder()");
8681 Sew_Error aResult = SEW_OK;
8683 // ====================================
8684 // find side nodes and elements
8685 // ====================================
8687 list< const SMDS_MeshNode* > nSide[ 2 ];
8688 list< const SMDS_MeshElement* > eSide[ 2 ];
8689 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8690 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8694 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8695 nSide[0], eSide[0])) {
8696 MESSAGE(" Free Border 1 not found " );
8697 aResult = SEW_BORDER1_NOT_FOUND;
8699 if (theSideIsFreeBorder) {
8702 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8703 nSide[1], eSide[1])) {
8704 MESSAGE(" Free Border 2 not found " );
8705 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8708 if ( aResult != SEW_OK )
8711 if (!theSideIsFreeBorder) {
8715 // -------------------------------------------------------------------------
8717 // 1. If nodes to merge are not coincident, move nodes of the free border
8718 // from the coord sys defined by the direction from the first to last
8719 // nodes of the border to the correspondent sys of the side 2
8720 // 2. On the side 2, find the links most co-directed with the correspondent
8721 // links of the free border
8722 // -------------------------------------------------------------------------
8724 // 1. Since sewing may break if there are volumes to split on the side 2,
8725 // we wont move nodes but just compute new coordinates for them
8726 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8727 TNodeXYZMap nBordXYZ;
8728 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8729 list< const SMDS_MeshNode* >::iterator nBordIt;
8731 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8732 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8733 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8734 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8735 double tol2 = 1.e-8;
8736 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8737 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8738 // Need node movement.
8740 // find X and Z axes to create trsf
8741 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8743 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8745 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8748 gp_Ax3 toBordAx( Pb1, Zb, X );
8749 gp_Ax3 fromSideAx( Ps1, Zs, X );
8750 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8752 gp_Trsf toBordSys, fromSide2Sys;
8753 toBordSys.SetTransformation( toBordAx );
8754 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8755 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8758 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8759 const SMDS_MeshNode* n = *nBordIt;
8760 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8761 toBordSys.Transforms( xyz );
8762 fromSide2Sys.Transforms( xyz );
8763 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8767 // just insert nodes XYZ in the nBordXYZ map
8768 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8769 const SMDS_MeshNode* n = *nBordIt;
8770 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8774 // 2. On the side 2, find the links most co-directed with the correspondent
8775 // links of the free border
8777 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8778 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8779 sideNodes.push_back( theSideFirstNode );
8781 bool hasVolumes = false;
8782 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8783 set<long> foundSideLinkIDs, checkedLinkIDs;
8784 SMDS_VolumeTool volume;
8785 //const SMDS_MeshNode* faceNodes[ 4 ];
8787 const SMDS_MeshNode* sideNode;
8788 const SMDS_MeshElement* sideElem;
8789 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8790 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8791 nBordIt = bordNodes.begin();
8793 // border node position and border link direction to compare with
8794 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8795 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8796 // choose next side node by link direction or by closeness to
8797 // the current border node:
8798 bool searchByDir = ( *nBordIt != theBordLastNode );
8800 // find the next node on the Side 2
8802 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8804 checkedLinkIDs.clear();
8805 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8807 // loop on inverse elements of current node (prevSideNode) on the Side 2
8808 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8809 while ( invElemIt->more() )
8811 const SMDS_MeshElement* elem = invElemIt->next();
8812 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8813 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8814 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8815 bool isVolume = volume.Set( elem );
8816 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8817 if ( isVolume ) // --volume
8819 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8820 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8821 if(elem->IsQuadratic()) {
8822 const SMDS_VtkFace* F =
8823 dynamic_cast<const SMDS_VtkFace*>(elem);
8824 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8825 // use special nodes iterator
8826 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8827 while( anIter->more() ) {
8828 nodes[ iNode ] = cast2Node(anIter->next());
8829 if ( nodes[ iNode++ ] == prevSideNode )
8830 iPrevNode = iNode - 1;
8834 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8835 while ( nIt->more() ) {
8836 nodes[ iNode ] = cast2Node( nIt->next() );
8837 if ( nodes[ iNode++ ] == prevSideNode )
8838 iPrevNode = iNode - 1;
8841 // there are 2 links to check
8846 // loop on links, to be precise, on the second node of links
8847 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8848 const SMDS_MeshNode* n = nodes[ iNode ];
8850 if ( !volume.IsLinked( n, prevSideNode ))
8854 if ( iNode ) // a node before prevSideNode
8855 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8856 else // a node after prevSideNode
8857 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8859 // check if this link was already used
8860 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8861 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8862 if (!isJustChecked &&
8863 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8865 // test a link geometrically
8866 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8867 bool linkIsBetter = false;
8868 double dot = 0.0, dist = 0.0;
8869 if ( searchByDir ) { // choose most co-directed link
8870 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8871 linkIsBetter = ( dot > maxDot );
8873 else { // choose link with the node closest to bordPos
8874 dist = ( nextXYZ - bordPos ).SquareModulus();
8875 linkIsBetter = ( dist < minDist );
8877 if ( linkIsBetter ) {
8886 } // loop on inverse elements of prevSideNode
8889 MESSAGE(" Cant find path by links of the Side 2 ");
8890 return SEW_BAD_SIDE_NODES;
8892 sideNodes.push_back( sideNode );
8893 sideElems.push_back( sideElem );
8894 foundSideLinkIDs.insert ( linkID );
8895 prevSideNode = sideNode;
8897 if ( *nBordIt == theBordLastNode )
8898 searchByDir = false;
8900 // find the next border link to compare with
8901 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8902 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8903 // move to next border node if sideNode is before forward border node (bordPos)
8904 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8905 prevBordNode = *nBordIt;
8907 bordPos = nBordXYZ[ *nBordIt ];
8908 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8909 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8913 while ( sideNode != theSideSecondNode );
8915 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8916 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8917 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8919 } // end nodes search on the side 2
8921 // ============================
8922 // sew the border to the side 2
8923 // ============================
8925 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8926 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8928 TListOfListOfNodes nodeGroupsToMerge;
8929 if ( nbNodes[0] == nbNodes[1] ||
8930 ( theSideIsFreeBorder && !theSideThirdNode)) {
8932 // all nodes are to be merged
8934 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8935 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8936 nIt[0]++, nIt[1]++ )
8938 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8939 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8940 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8945 // insert new nodes into the border and the side to get equal nb of segments
8947 // get normalized parameters of nodes on the borders
8948 //double param[ 2 ][ maxNbNodes ];
8950 param[0] = new double [ maxNbNodes ];
8951 param[1] = new double [ maxNbNodes ];
8953 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8954 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8955 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8956 const SMDS_MeshNode* nPrev = *nIt;
8957 double bordLength = 0;
8958 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8959 const SMDS_MeshNode* nCur = *nIt;
8960 gp_XYZ segment (nCur->X() - nPrev->X(),
8961 nCur->Y() - nPrev->Y(),
8962 nCur->Z() - nPrev->Z());
8963 double segmentLen = segment.Modulus();
8964 bordLength += segmentLen;
8965 param[ iBord ][ iNode ] = bordLength;
8968 // normalize within [0,1]
8969 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8970 param[ iBord ][ iNode ] /= bordLength;
8974 // loop on border segments
8975 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8976 int i[ 2 ] = { 0, 0 };
8977 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8978 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8980 TElemOfNodeListMap insertMap;
8981 TElemOfNodeListMap::iterator insertMapIt;
8983 // key: elem to insert nodes into
8984 // value: 2 nodes to insert between + nodes to be inserted
8986 bool next[ 2 ] = { false, false };
8988 // find min adjacent segment length after sewing
8989 double nextParam = 10., prevParam = 0;
8990 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8991 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8992 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8993 if ( i[ iBord ] > 0 )
8994 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8996 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8997 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8998 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
9000 // choose to insert or to merge nodes
9001 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
9002 if ( Abs( du ) <= minSegLen * 0.2 ) {
9005 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9006 const SMDS_MeshNode* n0 = *nIt[0];
9007 const SMDS_MeshNode* n1 = *nIt[1];
9008 nodeGroupsToMerge.back().push_back( n1 );
9009 nodeGroupsToMerge.back().push_back( n0 );
9010 // position of node of the border changes due to merge
9011 param[ 0 ][ i[0] ] += du;
9012 // move n1 for the sake of elem shape evaluation during insertion.
9013 // n1 will be removed by MergeNodes() anyway
9014 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9015 next[0] = next[1] = true;
9020 int intoBord = ( du < 0 ) ? 0 : 1;
9021 const SMDS_MeshElement* elem = *eIt[ intoBord ];
9022 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
9023 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
9024 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
9025 if ( intoBord == 1 ) {
9026 // move node of the border to be on a link of elem of the side
9027 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9028 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9029 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9030 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9031 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9033 insertMapIt = insertMap.find( elem );
9034 bool notFound = ( insertMapIt == insertMap.end() );
9035 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9037 // insert into another link of the same element:
9038 // 1. perform insertion into the other link of the elem
9039 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9040 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9041 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9042 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9043 // 2. perform insertion into the link of adjacent faces
9045 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9047 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9051 if (toCreatePolyedrs) {
9052 // perform insertion into the links of adjacent volumes
9053 UpdateVolumes(n12, n22, nodeList);
9055 // 3. find an element appeared on n1 and n2 after the insertion
9056 insertMap.erase( elem );
9057 elem = findAdjacentFace( n1, n2, 0 );
9059 if ( notFound || otherLink ) {
9060 // add element and nodes of the side into the insertMap
9061 insertMapIt = insertMap.insert
9062 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9063 (*insertMapIt).second.push_back( n1 );
9064 (*insertMapIt).second.push_back( n2 );
9066 // add node to be inserted into elem
9067 (*insertMapIt).second.push_back( nIns );
9068 next[ 1 - intoBord ] = true;
9071 // go to the next segment
9072 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9073 if ( next[ iBord ] ) {
9074 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9076 nPrev[ iBord ] = *nIt[ iBord ];
9077 nIt[ iBord ]++; i[ iBord ]++;
9081 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9083 // perform insertion of nodes into elements
9085 for (insertMapIt = insertMap.begin();
9086 insertMapIt != insertMap.end();
9089 const SMDS_MeshElement* elem = (*insertMapIt).first;
9090 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9091 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9092 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9094 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9096 if ( !theSideIsFreeBorder ) {
9097 // look for and insert nodes into the faces adjacent to elem
9099 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9101 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9106 if (toCreatePolyedrs) {
9107 // perform insertion into the links of adjacent volumes
9108 UpdateVolumes(n1, n2, nodeList);
9114 } // end: insert new nodes
9116 MergeNodes ( nodeGroupsToMerge );
9121 //=======================================================================
9122 //function : InsertNodesIntoLink
9123 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9124 // and theBetweenNode2 and split theElement
9125 //=======================================================================
9127 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9128 const SMDS_MeshNode* theBetweenNode1,
9129 const SMDS_MeshNode* theBetweenNode2,
9130 list<const SMDS_MeshNode*>& theNodesToInsert,
9131 const bool toCreatePoly)
9133 if ( theFace->GetType() != SMDSAbs_Face ) return;
9135 // find indices of 2 link nodes and of the rest nodes
9136 int iNode = 0, il1, il2, i3, i4;
9137 il1 = il2 = i3 = i4 = -1;
9138 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9139 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9141 if(theFace->IsQuadratic()) {
9142 const SMDS_VtkFace* F =
9143 dynamic_cast<const SMDS_VtkFace*>(theFace);
9144 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9145 // use special nodes iterator
9146 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9147 while( anIter->more() ) {
9148 const SMDS_MeshNode* n = cast2Node(anIter->next());
9149 if ( n == theBetweenNode1 )
9151 else if ( n == theBetweenNode2 )
9157 nodes[ iNode++ ] = n;
9161 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9162 while ( nodeIt->more() ) {
9163 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9164 if ( n == theBetweenNode1 )
9166 else if ( n == theBetweenNode2 )
9172 nodes[ iNode++ ] = n;
9175 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9178 // arrange link nodes to go one after another regarding the face orientation
9179 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9180 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9185 aNodesToInsert.reverse();
9187 // check that not link nodes of a quadrangles are in good order
9188 int nbFaceNodes = theFace->NbNodes();
9189 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9195 if (toCreatePoly || theFace->IsPoly()) {
9198 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9200 // add nodes of face up to first node of link
9203 if(theFace->IsQuadratic()) {
9204 const SMDS_VtkFace* F =
9205 dynamic_cast<const SMDS_VtkFace*>(theFace);
9206 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9207 // use special nodes iterator
9208 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9209 while( anIter->more() && !isFLN ) {
9210 const SMDS_MeshNode* n = cast2Node(anIter->next());
9211 poly_nodes[iNode++] = n;
9212 if (n == nodes[il1]) {
9216 // add nodes to insert
9217 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9218 for (; nIt != aNodesToInsert.end(); nIt++) {
9219 poly_nodes[iNode++] = *nIt;
9221 // add nodes of face starting from last node of link
9222 while ( anIter->more() ) {
9223 poly_nodes[iNode++] = cast2Node(anIter->next());
9227 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9228 while ( nodeIt->more() && !isFLN ) {
9229 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9230 poly_nodes[iNode++] = n;
9231 if (n == nodes[il1]) {
9235 // add nodes to insert
9236 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9237 for (; nIt != aNodesToInsert.end(); nIt++) {
9238 poly_nodes[iNode++] = *nIt;
9240 // add nodes of face starting from last node of link
9241 while ( nodeIt->more() ) {
9242 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9243 poly_nodes[iNode++] = n;
9247 // edit or replace the face
9248 SMESHDS_Mesh *aMesh = GetMeshDS();
9250 if (theFace->IsPoly()) {
9251 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9254 int aShapeId = FindShape( theFace );
9256 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9257 myLastCreatedElems.Append(newElem);
9258 if ( aShapeId && newElem )
9259 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9261 aMesh->RemoveElement(theFace);
9266 SMESHDS_Mesh *aMesh = GetMeshDS();
9267 if( !theFace->IsQuadratic() ) {
9269 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9270 int nbLinkNodes = 2 + aNodesToInsert.size();
9271 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9272 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9273 linkNodes[ 0 ] = nodes[ il1 ];
9274 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9275 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9276 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9277 linkNodes[ iNode++ ] = *nIt;
9279 // decide how to split a quadrangle: compare possible variants
9280 // and choose which of splits to be a quadrangle
9281 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9282 if ( nbFaceNodes == 3 ) {
9283 iBestQuad = nbSplits;
9286 else if ( nbFaceNodes == 4 ) {
9287 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9288 double aBestRate = DBL_MAX;
9289 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9291 double aBadRate = 0;
9292 // evaluate elements quality
9293 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9294 if ( iSplit == iQuad ) {
9295 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9299 aBadRate += getBadRate( &quad, aCrit );
9302 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9304 nodes[ iSplit < iQuad ? i4 : i3 ]);
9305 aBadRate += getBadRate( &tria, aCrit );
9309 if ( aBadRate < aBestRate ) {
9311 aBestRate = aBadRate;
9316 // create new elements
9317 int aShapeId = FindShape( theFace );
9320 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9321 SMDS_MeshElement* newElem = 0;
9322 if ( iSplit == iBestQuad )
9323 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9328 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9330 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9331 myLastCreatedElems.Append(newElem);
9332 if ( aShapeId && newElem )
9333 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9336 // change nodes of theFace
9337 const SMDS_MeshNode* newNodes[ 4 ];
9338 newNodes[ 0 ] = linkNodes[ i1 ];
9339 newNodes[ 1 ] = linkNodes[ i2 ];
9340 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9341 newNodes[ 3 ] = nodes[ i4 ];
9342 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9343 const SMDS_MeshElement* newElem = 0;
9344 if (iSplit == iBestQuad)
9345 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9347 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9348 myLastCreatedElems.Append(newElem);
9349 if ( aShapeId && newElem )
9350 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9351 } // end if(!theFace->IsQuadratic())
9352 else { // theFace is quadratic
9353 // we have to split theFace on simple triangles and one simple quadrangle
9355 int nbshift = tmp*2;
9356 // shift nodes in nodes[] by nbshift
9358 for(i=0; i<nbshift; i++) {
9359 const SMDS_MeshNode* n = nodes[0];
9360 for(j=0; j<nbFaceNodes-1; j++) {
9361 nodes[j] = nodes[j+1];
9363 nodes[nbFaceNodes-1] = n;
9365 il1 = il1 - nbshift;
9366 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9367 // n0 n1 n2 n0 n1 n2
9368 // +-----+-----+ +-----+-----+
9377 // create new elements
9378 int aShapeId = FindShape( theFace );
9381 if(nbFaceNodes==6) { // quadratic triangle
9382 SMDS_MeshElement* newElem =
9383 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9384 myLastCreatedElems.Append(newElem);
9385 if ( aShapeId && newElem )
9386 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9387 if(theFace->IsMediumNode(nodes[il1])) {
9388 // create quadrangle
9389 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9390 myLastCreatedElems.Append(newElem);
9391 if ( aShapeId && newElem )
9392 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9398 // create quadrangle
9399 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9400 myLastCreatedElems.Append(newElem);
9401 if ( aShapeId && newElem )
9402 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9408 else { // nbFaceNodes==8 - quadratic quadrangle
9409 SMDS_MeshElement* newElem =
9410 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9411 myLastCreatedElems.Append(newElem);
9412 if ( aShapeId && newElem )
9413 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9414 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9415 myLastCreatedElems.Append(newElem);
9416 if ( aShapeId && newElem )
9417 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9418 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9419 myLastCreatedElems.Append(newElem);
9420 if ( aShapeId && newElem )
9421 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9422 if(theFace->IsMediumNode(nodes[il1])) {
9423 // create quadrangle
9424 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9425 myLastCreatedElems.Append(newElem);
9426 if ( aShapeId && newElem )
9427 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9433 // create quadrangle
9434 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9435 myLastCreatedElems.Append(newElem);
9436 if ( aShapeId && newElem )
9437 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9443 // create needed triangles using n1,n2,n3 and inserted nodes
9444 int nbn = 2 + aNodesToInsert.size();
9445 //const SMDS_MeshNode* aNodes[nbn];
9446 vector<const SMDS_MeshNode*> aNodes(nbn);
9447 aNodes[0] = nodes[n1];
9448 aNodes[nbn-1] = nodes[n2];
9449 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9450 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9451 aNodes[iNode++] = *nIt;
9453 for(i=1; i<nbn; i++) {
9454 SMDS_MeshElement* newElem =
9455 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9456 myLastCreatedElems.Append(newElem);
9457 if ( aShapeId && newElem )
9458 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9462 aMesh->RemoveElement(theFace);
9465 //=======================================================================
9466 //function : UpdateVolumes
9468 //=======================================================================
9469 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9470 const SMDS_MeshNode* theBetweenNode2,
9471 list<const SMDS_MeshNode*>& theNodesToInsert)
9473 myLastCreatedElems.Clear();
9474 myLastCreatedNodes.Clear();
9476 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9477 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9478 const SMDS_MeshElement* elem = invElemIt->next();
9480 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9481 SMDS_VolumeTool aVolume (elem);
9482 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9485 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9486 int iface, nbFaces = aVolume.NbFaces();
9487 vector<const SMDS_MeshNode *> poly_nodes;
9488 vector<int> quantities (nbFaces);
9490 for (iface = 0; iface < nbFaces; iface++) {
9491 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9492 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9493 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9495 for (int inode = 0; inode < nbFaceNodes; inode++) {
9496 poly_nodes.push_back(faceNodes[inode]);
9498 if (nbInserted == 0) {
9499 if (faceNodes[inode] == theBetweenNode1) {
9500 if (faceNodes[inode + 1] == theBetweenNode2) {
9501 nbInserted = theNodesToInsert.size();
9503 // add nodes to insert
9504 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9505 for (; nIt != theNodesToInsert.end(); nIt++) {
9506 poly_nodes.push_back(*nIt);
9510 else if (faceNodes[inode] == theBetweenNode2) {
9511 if (faceNodes[inode + 1] == theBetweenNode1) {
9512 nbInserted = theNodesToInsert.size();
9514 // add nodes to insert in reversed order
9515 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9517 for (; nIt != theNodesToInsert.begin(); nIt--) {
9518 poly_nodes.push_back(*nIt);
9520 poly_nodes.push_back(*nIt);
9527 quantities[iface] = nbFaceNodes + nbInserted;
9530 // Replace or update the volume
9531 SMESHDS_Mesh *aMesh = GetMeshDS();
9533 if (elem->IsPoly()) {
9534 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9538 int aShapeId = FindShape( elem );
9540 SMDS_MeshElement* newElem =
9541 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9542 myLastCreatedElems.Append(newElem);
9543 if (aShapeId && newElem)
9544 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9546 aMesh->RemoveElement(elem);
9553 //================================================================================
9555 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9557 //================================================================================
9559 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9560 vector<const SMDS_MeshNode *> & nodes,
9561 vector<int> & nbNodeInFaces )
9564 nbNodeInFaces.clear();
9565 SMDS_VolumeTool vTool ( elem );
9566 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9568 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9569 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9570 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9575 //=======================================================================
9577 * \brief Convert elements contained in a submesh to quadratic
9578 * \return int - nb of checked elements
9580 //=======================================================================
9582 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9583 SMESH_MesherHelper& theHelper,
9584 const bool theForce3d)
9587 if( !theSm ) return nbElem;
9589 vector<int> nbNodeInFaces;
9590 vector<const SMDS_MeshNode *> nodes;
9591 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9592 while(ElemItr->more())
9595 const SMDS_MeshElement* elem = ElemItr->next();
9596 if( !elem ) continue;
9598 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9599 if ( elem->IsQuadratic() )
9602 switch ( aGeomType ) {
9603 case SMDSEntity_Quad_Quadrangle:
9604 case SMDSEntity_Quad_Hexa: alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9605 case SMDSEntity_BiQuad_Quadrangle:
9606 case SMDSEntity_TriQuad_Hexa: alreadyOK = theHelper.GetIsBiQuadratic(); break;
9607 default: alreadyOK = true;
9609 if ( alreadyOK ) continue;
9611 // get elem data needed to re-create it
9613 const int id = elem->GetID();
9614 const int nbNodes = elem->NbCornerNodes();
9615 const SMDSAbs_ElementType aType = elem->GetType();
9616 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9617 if ( aGeomType == SMDSEntity_Polyhedra )
9618 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9619 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9620 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9622 // remove a linear element
9623 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9625 const SMDS_MeshElement* NewElem = 0;
9631 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9639 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9642 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9645 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9650 case SMDSAbs_Volume :
9654 case SMDSEntity_Tetra:
9655 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9657 case SMDSEntity_Pyramid:
9658 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9660 case SMDSEntity_Penta:
9661 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9663 case SMDSEntity_Hexa:
9664 case SMDSEntity_Quad_Hexa:
9665 case SMDSEntity_TriQuad_Hexa:
9666 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9667 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9669 case SMDSEntity_Hexagonal_Prism:
9671 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9678 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9680 theSm->AddElement( NewElem );
9684 //=======================================================================
9685 //function : ConvertToQuadratic
9687 //=======================================================================
9689 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9691 SMESHDS_Mesh* meshDS = GetMeshDS();
9693 SMESH_MesherHelper aHelper(*myMesh);
9695 aHelper.SetIsQuadratic( true );
9696 aHelper.SetIsBiQuadratic( theToBiQuad );
9697 aHelper.SetElementsOnShape(true);
9699 int nbCheckedElems = 0;
9700 if ( myMesh->HasShapeToMesh() )
9702 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9704 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9705 while ( smIt->more() ) {
9706 SMESH_subMesh* sm = smIt->next();
9707 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9708 aHelper.SetSubShape( sm->GetSubShape() );
9709 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9714 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9715 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9717 SMESHDS_SubMesh *smDS = 0;
9718 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9719 while(aEdgeItr->more())
9721 const SMDS_MeshEdge* edge = aEdgeItr->next();
9722 if(edge && !edge->IsQuadratic())
9724 int id = edge->GetID();
9725 //MESSAGE("edge->GetID() " << id);
9726 const SMDS_MeshNode* n1 = edge->GetNode(0);
9727 const SMDS_MeshNode* n2 = edge->GetNode(1);
9729 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9731 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9732 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9735 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9736 while(aFaceItr->more())
9738 const SMDS_MeshFace* face = aFaceItr->next();
9739 if ( !face ) continue;
9741 const SMDSAbs_EntityType type = face->GetEntityType();
9742 if (( theToBiQuad && type == SMDSEntity_BiQuad_Quadrangle ) ||
9743 ( !theToBiQuad && type == SMDSEntity_Quad_Quadrangle ))
9746 const int id = face->GetID();
9747 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9749 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9751 SMDS_MeshFace * NewFace = 0;
9754 case SMDSEntity_Triangle:
9755 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9757 case SMDSEntity_Quadrangle:
9758 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9761 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9763 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9765 vector<int> nbNodeInFaces;
9766 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9767 while(aVolumeItr->more())
9769 const SMDS_MeshVolume* volume = aVolumeItr->next();
9770 if(!volume || volume->IsQuadratic() ) continue;
9772 const SMDSAbs_EntityType type = volume->GetEntityType();
9773 if (( theToBiQuad && type == SMDSEntity_TriQuad_Hexa ) ||
9774 ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
9777 const int id = volume->GetID();
9778 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9779 if ( type == SMDSEntity_Polyhedra )
9780 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9781 else if ( type == SMDSEntity_Hexagonal_Prism )
9782 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9784 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9786 SMDS_MeshVolume * NewVolume = 0;
9789 case SMDSEntity_Tetra:
9790 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9792 case SMDSEntity_Hexa:
9793 case SMDSEntity_Quad_Hexa:
9794 case SMDSEntity_TriQuad_Hexa:
9795 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9796 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9798 case SMDSEntity_Pyramid:
9799 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9800 nodes[3], nodes[4], id, theForce3d);
9802 case SMDSEntity_Penta:
9803 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9804 nodes[3], nodes[4], nodes[5], id, theForce3d);
9806 case SMDSEntity_Hexagonal_Prism:
9808 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9810 ReplaceElemInGroups(volume, NewVolume, meshDS);
9815 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9816 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9817 aHelper.FixQuadraticElements(myError);
9821 //================================================================================
9823 * \brief Makes given elements quadratic
9824 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9825 * \param theElements - elements to make quadratic
9827 //================================================================================
9829 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9830 TIDSortedElemSet& theElements,
9831 const bool theToBiQuad)
9833 if ( theElements.empty() ) return;
9835 // we believe that all theElements are of the same type
9836 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9838 // get all nodes shared by theElements
9839 TIDSortedNodeSet allNodes;
9840 TIDSortedElemSet::iterator eIt = theElements.begin();
9841 for ( ; eIt != theElements.end(); ++eIt )
9842 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9844 // complete theElements with elements of lower dim whose all nodes are in allNodes
9846 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9847 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9848 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9849 for ( ; nIt != allNodes.end(); ++nIt )
9851 const SMDS_MeshNode* n = *nIt;
9852 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9853 while ( invIt->more() )
9855 const SMDS_MeshElement* e = invIt->next();
9856 if ( e->IsQuadratic() )
9859 switch ( e->GetEntityType() ) {
9860 case SMDSEntity_Quad_Quadrangle:
9861 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9862 case SMDSEntity_BiQuad_Quadrangle:
9863 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9864 default: alreadyOK = true;
9868 quadAdjacentElems[ e->GetType() ].insert( e );
9872 if ( e->GetType() >= elemType )
9874 continue; // same type of more complex linear element
9877 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9878 continue; // e is already checked
9882 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9883 while ( nodeIt->more() && allIn )
9884 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9886 theElements.insert(e );
9890 SMESH_MesherHelper helper(*myMesh);
9891 helper.SetIsQuadratic( true );
9892 helper.SetIsBiQuadratic( theToBiQuad );
9894 // add links of quadratic adjacent elements to the helper
9896 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9897 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9898 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9900 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9902 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9903 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9904 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9906 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9908 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9909 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9910 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9912 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9915 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9917 SMESHDS_Mesh* meshDS = GetMeshDS();
9918 SMESHDS_SubMesh* smDS = 0;
9919 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9921 const SMDS_MeshElement* elem = *eIt;
9922 if( elem->NbNodes() < 2 || elem->IsPoly() )
9925 if ( elem->IsQuadratic() )
9928 switch ( elem->GetEntityType() ) {
9929 case SMDSEntity_Quad_Quadrangle:
9930 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9931 case SMDSEntity_BiQuad_Quadrangle:
9932 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9933 default: alreadyOK = true;
9935 if ( alreadyOK ) continue;
9938 const SMDSAbs_ElementType type = elem->GetType();
9939 const int id = elem->GetID();
9940 const int nbNodes = elem->NbCornerNodes();
9941 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9943 if ( !smDS || !smDS->Contains( elem ))
9944 smDS = meshDS->MeshElements( elem->getshapeId() );
9945 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9947 SMDS_MeshElement * newElem = 0;
9950 case 4: // cases for most frequently used element types go first (for optimization)
9951 if ( type == SMDSAbs_Volume )
9952 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9954 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9957 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9958 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9961 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9964 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9967 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9968 nodes[4], id, theForce3d);
9971 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9972 nodes[4], nodes[5], id, theForce3d);
9976 ReplaceElemInGroups( elem, newElem, meshDS);
9977 if( newElem && smDS )
9978 smDS->AddElement( newElem );
9981 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9982 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9983 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9984 helper.FixQuadraticElements( myError );
9988 //=======================================================================
9990 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9991 * \return int - nb of checked elements
9993 //=======================================================================
9995 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9996 SMDS_ElemIteratorPtr theItr,
9997 const int theShapeID)
10000 SMESHDS_Mesh* meshDS = GetMeshDS();
10002 while( theItr->more() )
10004 const SMDS_MeshElement* elem = theItr->next();
10006 if( elem && elem->IsQuadratic())
10008 int id = elem->GetID();
10009 int nbCornerNodes = elem->NbCornerNodes();
10010 SMDSAbs_ElementType aType = elem->GetType();
10012 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
10014 //remove a quadratic element
10015 if ( !theSm || !theSm->Contains( elem ))
10016 theSm = meshDS->MeshElements( elem->getshapeId() );
10017 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
10019 // remove medium nodes
10020 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
10021 if ( nodes[i]->NbInverseElements() == 0 )
10022 meshDS->RemoveFreeNode( nodes[i], theSm );
10024 // add a linear element
10025 nodes.resize( nbCornerNodes );
10026 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
10027 ReplaceElemInGroups(elem, newElem, meshDS);
10028 if( theSm && newElem )
10029 theSm->AddElement( newElem );
10035 //=======================================================================
10036 //function : ConvertFromQuadratic
10038 //=======================================================================
10040 bool SMESH_MeshEditor::ConvertFromQuadratic()
10042 int nbCheckedElems = 0;
10043 if ( myMesh->HasShapeToMesh() )
10045 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
10047 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
10048 while ( smIt->more() ) {
10049 SMESH_subMesh* sm = smIt->next();
10050 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
10051 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
10057 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
10058 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10060 SMESHDS_SubMesh *aSM = 0;
10061 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10069 //================================================================================
10071 * \brief Return true if all medium nodes of the element are in the node set
10073 //================================================================================
10075 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10077 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10078 if ( !nodeSet.count( elem->GetNode(i) ))
10084 //================================================================================
10086 * \brief Makes given elements linear
10088 //================================================================================
10090 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10092 if ( theElements.empty() ) return;
10094 // collect IDs of medium nodes of theElements; some of these nodes will be removed
10095 set<int> mediumNodeIDs;
10096 TIDSortedElemSet::iterator eIt = theElements.begin();
10097 for ( ; eIt != theElements.end(); ++eIt )
10099 const SMDS_MeshElement* e = *eIt;
10100 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10101 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10104 // replace given elements by linear ones
10105 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10106 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10107 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10109 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10110 // except those elements sharing medium nodes of quadratic element whose medium nodes
10111 // are not all in mediumNodeIDs
10113 // get remaining medium nodes
10114 TIDSortedNodeSet mediumNodes;
10115 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10116 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10117 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10118 mediumNodes.insert( mediumNodes.end(), n );
10120 // find more quadratic elements to convert
10121 TIDSortedElemSet moreElemsToConvert;
10122 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10123 for ( ; nIt != mediumNodes.end(); ++nIt )
10125 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10126 while ( invIt->more() )
10128 const SMDS_MeshElement* e = invIt->next();
10129 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10131 // find a more complex element including e and
10132 // whose medium nodes are not in mediumNodes
10133 bool complexFound = false;
10134 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10136 SMDS_ElemIteratorPtr invIt2 =
10137 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10138 while ( invIt2->more() )
10140 const SMDS_MeshElement* eComplex = invIt2->next();
10141 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10143 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10144 if ( nbCommonNodes == e->NbNodes())
10146 complexFound = true;
10147 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10153 if ( !complexFound )
10154 moreElemsToConvert.insert( e );
10158 elemIt = SMDS_ElemIteratorPtr
10159 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10160 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10163 //=======================================================================
10164 //function : SewSideElements
10166 //=======================================================================
10168 SMESH_MeshEditor::Sew_Error
10169 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10170 TIDSortedElemSet& theSide2,
10171 const SMDS_MeshNode* theFirstNode1,
10172 const SMDS_MeshNode* theFirstNode2,
10173 const SMDS_MeshNode* theSecondNode1,
10174 const SMDS_MeshNode* theSecondNode2)
10176 myLastCreatedElems.Clear();
10177 myLastCreatedNodes.Clear();
10179 MESSAGE ("::::SewSideElements()");
10180 if ( theSide1.size() != theSide2.size() )
10181 return SEW_DIFF_NB_OF_ELEMENTS;
10183 Sew_Error aResult = SEW_OK;
10185 // 1. Build set of faces representing each side
10186 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10187 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10189 // =======================================================================
10190 // 1. Build set of faces representing each side:
10191 // =======================================================================
10192 // a. build set of nodes belonging to faces
10193 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10194 // c. create temporary faces representing side of volumes if correspondent
10195 // face does not exist
10197 SMESHDS_Mesh* aMesh = GetMeshDS();
10198 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10199 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10200 TIDSortedElemSet faceSet1, faceSet2;
10201 set<const SMDS_MeshElement*> volSet1, volSet2;
10202 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10203 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10204 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10205 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10206 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10207 int iSide, iFace, iNode;
10209 list<const SMDS_MeshElement* > tempFaceList;
10210 for ( iSide = 0; iSide < 2; iSide++ ) {
10211 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10212 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10213 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10214 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10215 set<const SMDS_MeshElement*>::iterator vIt;
10216 TIDSortedElemSet::iterator eIt;
10217 set<const SMDS_MeshNode*>::iterator nIt;
10219 // check that given nodes belong to given elements
10220 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10221 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10222 int firstIndex = -1, secondIndex = -1;
10223 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10224 const SMDS_MeshElement* elem = *eIt;
10225 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10226 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10227 if ( firstIndex > -1 && secondIndex > -1 ) break;
10229 if ( firstIndex < 0 || secondIndex < 0 ) {
10230 // we can simply return until temporary faces created
10231 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10234 // -----------------------------------------------------------
10235 // 1a. Collect nodes of existing faces
10236 // and build set of face nodes in order to detect missing
10237 // faces corresponding to sides of volumes
10238 // -----------------------------------------------------------
10240 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10242 // loop on the given element of a side
10243 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10244 //const SMDS_MeshElement* elem = *eIt;
10245 const SMDS_MeshElement* elem = *eIt;
10246 if ( elem->GetType() == SMDSAbs_Face ) {
10247 faceSet->insert( elem );
10248 set <const SMDS_MeshNode*> faceNodeSet;
10249 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10250 while ( nodeIt->more() ) {
10251 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10252 nodeSet->insert( n );
10253 faceNodeSet.insert( n );
10255 setOfFaceNodeSet.insert( faceNodeSet );
10257 else if ( elem->GetType() == SMDSAbs_Volume )
10258 volSet->insert( elem );
10260 // ------------------------------------------------------------------------------
10261 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10262 // ------------------------------------------------------------------------------
10264 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10265 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10266 while ( fIt->more() ) { // loop on faces sharing a node
10267 const SMDS_MeshElement* f = fIt->next();
10268 if ( faceSet->find( f ) == faceSet->end() ) {
10269 // check if all nodes are in nodeSet and
10270 // complete setOfFaceNodeSet if they are
10271 set <const SMDS_MeshNode*> faceNodeSet;
10272 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10273 bool allInSet = true;
10274 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10275 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10276 if ( nodeSet->find( n ) == nodeSet->end() )
10279 faceNodeSet.insert( n );
10282 faceSet->insert( f );
10283 setOfFaceNodeSet.insert( faceNodeSet );
10289 // -------------------------------------------------------------------------
10290 // 1c. Create temporary faces representing sides of volumes if correspondent
10291 // face does not exist
10292 // -------------------------------------------------------------------------
10294 if ( !volSet->empty() ) {
10295 //int nodeSetSize = nodeSet->size();
10297 // loop on given volumes
10298 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10299 SMDS_VolumeTool vol (*vIt);
10300 // loop on volume faces: find free faces
10301 // --------------------------------------
10302 list<const SMDS_MeshElement* > freeFaceList;
10303 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10304 if ( !vol.IsFreeFace( iFace ))
10306 // check if there is already a face with same nodes in a face set
10307 const SMDS_MeshElement* aFreeFace = 0;
10308 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10309 int nbNodes = vol.NbFaceNodes( iFace );
10310 set <const SMDS_MeshNode*> faceNodeSet;
10311 vol.GetFaceNodes( iFace, faceNodeSet );
10312 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10314 // no such a face is given but it still can exist, check it
10315 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10316 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10318 if ( !aFreeFace ) {
10319 // create a temporary face
10320 if ( nbNodes == 3 ) {
10321 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10322 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10324 else if ( nbNodes == 4 ) {
10325 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10326 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10329 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10330 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10331 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10334 tempFaceList.push_back( aFreeFace );
10338 freeFaceList.push_back( aFreeFace );
10340 } // loop on faces of a volume
10342 // choose one of several free faces of a volume
10343 // --------------------------------------------
10344 if ( freeFaceList.size() > 1 ) {
10345 // choose a face having max nb of nodes shared by other elems of a side
10346 int maxNbNodes = -1;
10347 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10348 while ( fIt != freeFaceList.end() ) { // loop on free faces
10349 int nbSharedNodes = 0;
10350 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10351 while ( nodeIt->more() ) { // loop on free face nodes
10352 const SMDS_MeshNode* n =
10353 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10354 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10355 while ( invElemIt->more() ) {
10356 const SMDS_MeshElement* e = invElemIt->next();
10357 nbSharedNodes += faceSet->count( e );
10358 nbSharedNodes += elemSet->count( e );
10361 if ( nbSharedNodes > maxNbNodes ) {
10362 maxNbNodes = nbSharedNodes;
10363 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10365 else if ( nbSharedNodes == maxNbNodes ) {
10369 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10372 if ( freeFaceList.size() > 1 )
10374 // could not choose one face, use another way
10375 // choose a face most close to the bary center of the opposite side
10376 gp_XYZ aBC( 0., 0., 0. );
10377 set <const SMDS_MeshNode*> addedNodes;
10378 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10379 eIt = elemSet2->begin();
10380 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10381 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10382 while ( nodeIt->more() ) { // loop on free face nodes
10383 const SMDS_MeshNode* n =
10384 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10385 if ( addedNodes.insert( n ).second )
10386 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10389 aBC /= addedNodes.size();
10390 double minDist = DBL_MAX;
10391 fIt = freeFaceList.begin();
10392 while ( fIt != freeFaceList.end() ) { // loop on free faces
10394 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10395 while ( nodeIt->more() ) { // loop on free face nodes
10396 const SMDS_MeshNode* n =
10397 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10398 gp_XYZ p( n->X(),n->Y(),n->Z() );
10399 dist += ( aBC - p ).SquareModulus();
10401 if ( dist < minDist ) {
10403 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10406 fIt = freeFaceList.erase( fIt++ );
10409 } // choose one of several free faces of a volume
10411 if ( freeFaceList.size() == 1 ) {
10412 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10413 faceSet->insert( aFreeFace );
10414 // complete a node set with nodes of a found free face
10415 // for ( iNode = 0; iNode < ; iNode++ )
10416 // nodeSet->insert( fNodes[ iNode ] );
10419 } // loop on volumes of a side
10421 // // complete a set of faces if new nodes in a nodeSet appeared
10422 // // ----------------------------------------------------------
10423 // if ( nodeSetSize != nodeSet->size() ) {
10424 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10425 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10426 // while ( fIt->more() ) { // loop on faces sharing a node
10427 // const SMDS_MeshElement* f = fIt->next();
10428 // if ( faceSet->find( f ) == faceSet->end() ) {
10429 // // check if all nodes are in nodeSet and
10430 // // complete setOfFaceNodeSet if they are
10431 // set <const SMDS_MeshNode*> faceNodeSet;
10432 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10433 // bool allInSet = true;
10434 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10435 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10436 // if ( nodeSet->find( n ) == nodeSet->end() )
10437 // allInSet = false;
10439 // faceNodeSet.insert( n );
10441 // if ( allInSet ) {
10442 // faceSet->insert( f );
10443 // setOfFaceNodeSet.insert( faceNodeSet );
10449 } // Create temporary faces, if there are volumes given
10452 if ( faceSet1.size() != faceSet2.size() ) {
10453 // delete temporary faces: they are in reverseElements of actual nodes
10454 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10455 // while ( tmpFaceIt->more() )
10456 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10457 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10458 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10459 // aMesh->RemoveElement(*tmpFaceIt);
10460 MESSAGE("Diff nb of faces");
10461 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10464 // ============================================================
10465 // 2. Find nodes to merge:
10466 // bind a node to remove to a node to put instead
10467 // ============================================================
10469 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10470 if ( theFirstNode1 != theFirstNode2 )
10471 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10472 if ( theSecondNode1 != theSecondNode2 )
10473 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10475 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10476 set< long > linkIdSet; // links to process
10477 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10479 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10480 list< NLink > linkList[2];
10481 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10482 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10483 // loop on links in linkList; find faces by links and append links
10484 // of the found faces to linkList
10485 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10486 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10488 NLink link[] = { *linkIt[0], *linkIt[1] };
10489 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10490 if ( !linkIdSet.count( linkID ) )
10493 // by links, find faces in the face sets,
10494 // and find indices of link nodes in the found faces;
10495 // in a face set, there is only one or no face sharing a link
10496 // ---------------------------------------------------------------
10498 const SMDS_MeshElement* face[] = { 0, 0 };
10499 vector<const SMDS_MeshNode*> fnodes[2];
10500 int iLinkNode[2][2];
10501 TIDSortedElemSet avoidSet;
10502 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10503 const SMDS_MeshNode* n1 = link[iSide].first;
10504 const SMDS_MeshNode* n2 = link[iSide].second;
10505 //cout << "Side " << iSide << " ";
10506 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10507 // find a face by two link nodes
10508 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10509 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10510 if ( face[ iSide ])
10512 //cout << " F " << face[ iSide]->GetID() <<endl;
10513 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10514 // put face nodes to fnodes
10515 if ( face[ iSide ]->IsQuadratic() )
10517 // use interlaced nodes iterator
10518 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10519 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10520 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10521 while ( nIter->more() )
10522 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10526 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10527 face[ iSide ]->end_nodes() );
10529 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10533 // check similarity of elements of the sides
10534 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10535 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10536 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10537 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10540 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10542 break; // do not return because it's necessary to remove tmp faces
10545 // set nodes to merge
10546 // -------------------
10548 if ( face[0] && face[1] ) {
10549 const int nbNodes = face[0]->NbNodes();
10550 if ( nbNodes != face[1]->NbNodes() ) {
10551 MESSAGE("Diff nb of face nodes");
10552 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10553 break; // do not return because it s necessary to remove tmp faces
10555 bool reverse[] = { false, false }; // order of nodes in the link
10556 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10557 // analyse link orientation in faces
10558 int i1 = iLinkNode[ iSide ][ 0 ];
10559 int i2 = iLinkNode[ iSide ][ 1 ];
10560 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10562 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10563 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10564 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10566 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10567 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10570 // add other links of the faces to linkList
10571 // -----------------------------------------
10573 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10574 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10575 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10576 if ( !iter_isnew.second ) { // already in a set: no need to process
10577 linkIdSet.erase( iter_isnew.first );
10579 else // new in set == encountered for the first time: add
10581 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10582 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10583 linkList[0].push_back ( NLink( n1, n2 ));
10584 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10589 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10592 } // loop on link lists
10594 if ( aResult == SEW_OK &&
10595 ( //linkIt[0] != linkList[0].end() ||
10596 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10597 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10598 " " << (faceSetPtr[1]->empty()));
10599 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10602 // ====================================================================
10603 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10604 // ====================================================================
10606 // delete temporary faces
10607 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10608 // while ( tmpFaceIt->more() )
10609 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10610 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10611 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10612 aMesh->RemoveElement(*tmpFaceIt);
10614 if ( aResult != SEW_OK)
10617 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10618 // loop on nodes replacement map
10619 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10620 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10621 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10622 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10623 nodeIDsToRemove.push_back( nToRemove->GetID() );
10624 // loop on elements sharing nToRemove
10625 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10626 while ( invElemIt->more() ) {
10627 const SMDS_MeshElement* e = invElemIt->next();
10628 // get a new suite of nodes: make replacement
10629 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10630 vector< const SMDS_MeshNode*> nodes( nbNodes );
10631 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10632 while ( nIt->more() ) {
10633 const SMDS_MeshNode* n =
10634 static_cast<const SMDS_MeshNode*>( nIt->next() );
10635 nnIt = nReplaceMap.find( n );
10636 if ( nnIt != nReplaceMap.end() ) {
10638 n = (*nnIt).second;
10642 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10643 // elemIDsToRemove.push_back( e->GetID() );
10647 SMDSAbs_ElementType etyp = e->GetType();
10648 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10651 myLastCreatedElems.Append(newElem);
10652 AddToSameGroups(newElem, e, aMesh);
10653 int aShapeId = e->getshapeId();
10656 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10659 aMesh->RemoveElement(e);
10664 Remove( nodeIDsToRemove, true );
10669 //================================================================================
10671 * \brief Find corresponding nodes in two sets of faces
10672 * \param theSide1 - first face set
10673 * \param theSide2 - second first face
10674 * \param theFirstNode1 - a boundary node of set 1
10675 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10676 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10677 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10678 * \param nReplaceMap - output map of corresponding nodes
10679 * \return bool - is a success or not
10681 //================================================================================
10684 //#define DEBUG_MATCHING_NODES
10687 SMESH_MeshEditor::Sew_Error
10688 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10689 set<const SMDS_MeshElement*>& theSide2,
10690 const SMDS_MeshNode* theFirstNode1,
10691 const SMDS_MeshNode* theFirstNode2,
10692 const SMDS_MeshNode* theSecondNode1,
10693 const SMDS_MeshNode* theSecondNode2,
10694 TNodeNodeMap & nReplaceMap)
10696 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10698 nReplaceMap.clear();
10699 if ( theFirstNode1 != theFirstNode2 )
10700 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10701 if ( theSecondNode1 != theSecondNode2 )
10702 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10704 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10705 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10707 list< NLink > linkList[2];
10708 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10709 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10711 // loop on links in linkList; find faces by links and append links
10712 // of the found faces to linkList
10713 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10714 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10715 NLink link[] = { *linkIt[0], *linkIt[1] };
10716 if ( linkSet.find( link[0] ) == linkSet.end() )
10719 // by links, find faces in the face sets,
10720 // and find indices of link nodes in the found faces;
10721 // in a face set, there is only one or no face sharing a link
10722 // ---------------------------------------------------------------
10724 const SMDS_MeshElement* face[] = { 0, 0 };
10725 list<const SMDS_MeshNode*> notLinkNodes[2];
10726 //bool reverse[] = { false, false }; // order of notLinkNodes
10728 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10730 const SMDS_MeshNode* n1 = link[iSide].first;
10731 const SMDS_MeshNode* n2 = link[iSide].second;
10732 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10733 set< const SMDS_MeshElement* > facesOfNode1;
10734 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10736 // during a loop of the first node, we find all faces around n1,
10737 // during a loop of the second node, we find one face sharing both n1 and n2
10738 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10739 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10740 while ( fIt->more() ) { // loop on faces sharing a node
10741 const SMDS_MeshElement* f = fIt->next();
10742 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10743 ! facesOfNode1.insert( f ).second ) // f encounters twice
10745 if ( face[ iSide ] ) {
10746 MESSAGE( "2 faces per link " );
10747 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10750 faceSet->erase( f );
10752 // get not link nodes
10753 int nbN = f->NbNodes();
10754 if ( f->IsQuadratic() )
10756 nbNodes[ iSide ] = nbN;
10757 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10758 int i1 = f->GetNodeIndex( n1 );
10759 int i2 = f->GetNodeIndex( n2 );
10760 int iEnd = nbN, iBeg = -1, iDelta = 1;
10761 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10763 std::swap( iEnd, iBeg ); iDelta = -1;
10768 if ( i == iEnd ) i = iBeg + iDelta;
10769 if ( i == i1 ) break;
10770 nodes.push_back ( f->GetNode( i ) );
10776 // check similarity of elements of the sides
10777 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10778 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10779 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10780 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10783 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10787 // set nodes to merge
10788 // -------------------
10790 if ( face[0] && face[1] ) {
10791 if ( nbNodes[0] != nbNodes[1] ) {
10792 MESSAGE("Diff nb of face nodes");
10793 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10795 #ifdef DEBUG_MATCHING_NODES
10796 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10797 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10798 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10800 int nbN = nbNodes[0];
10802 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10803 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10804 for ( int i = 0 ; i < nbN - 2; ++i ) {
10805 #ifdef DEBUG_MATCHING_NODES
10806 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10808 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10812 // add other links of the face 1 to linkList
10813 // -----------------------------------------
10815 const SMDS_MeshElement* f0 = face[0];
10816 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10817 for ( int i = 0; i < nbN; i++ )
10819 const SMDS_MeshNode* n2 = f0->GetNode( i );
10820 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10821 linkSet.insert( SMESH_TLink( n1, n2 ));
10822 if ( !iter_isnew.second ) { // already in a set: no need to process
10823 linkSet.erase( iter_isnew.first );
10825 else // new in set == encountered for the first time: add
10827 #ifdef DEBUG_MATCHING_NODES
10828 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10829 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10831 linkList[0].push_back ( NLink( n1, n2 ));
10832 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10837 } // loop on link lists
10842 //================================================================================
10844 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10845 \param theElems - the list of elements (edges or faces) to be replicated
10846 The nodes for duplication could be found from these elements
10847 \param theNodesNot - list of nodes to NOT replicate
10848 \param theAffectedElems - the list of elements (cells and edges) to which the
10849 replicated nodes should be associated to.
10850 \return TRUE if operation has been completed successfully, FALSE otherwise
10852 //================================================================================
10854 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10855 const TIDSortedElemSet& theNodesNot,
10856 const TIDSortedElemSet& theAffectedElems )
10858 myLastCreatedElems.Clear();
10859 myLastCreatedNodes.Clear();
10861 if ( theElems.size() == 0 )
10864 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10869 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10870 // duplicate elements and nodes
10871 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10872 // replce nodes by duplications
10873 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10877 //================================================================================
10879 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10880 \param theMeshDS - mesh instance
10881 \param theElems - the elements replicated or modified (nodes should be changed)
10882 \param theNodesNot - nodes to NOT replicate
10883 \param theNodeNodeMap - relation of old node to new created node
10884 \param theIsDoubleElem - flag os to replicate element or modify
10885 \return TRUE if operation has been completed successfully, FALSE otherwise
10887 //================================================================================
10889 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10890 const TIDSortedElemSet& theElems,
10891 const TIDSortedElemSet& theNodesNot,
10892 std::map< const SMDS_MeshNode*,
10893 const SMDS_MeshNode* >& theNodeNodeMap,
10894 const bool theIsDoubleElem )
10896 MESSAGE("doubleNodes");
10897 // iterate on through element and duplicate them (by nodes duplication)
10899 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10900 for ( ; elemItr != theElems.end(); ++elemItr )
10902 const SMDS_MeshElement* anElem = *elemItr;
10906 bool isDuplicate = false;
10907 // duplicate nodes to duplicate element
10908 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10909 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10911 while ( anIter->more() )
10914 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10915 SMDS_MeshNode* aNewNode = aCurrNode;
10916 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10917 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10918 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10921 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10922 theNodeNodeMap[ aCurrNode ] = aNewNode;
10923 myLastCreatedNodes.Append( aNewNode );
10925 isDuplicate |= (aCurrNode != aNewNode);
10926 newNodes[ ind++ ] = aNewNode;
10928 if ( !isDuplicate )
10931 if ( theIsDoubleElem )
10932 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10935 MESSAGE("ChangeElementNodes");
10936 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10943 //================================================================================
10945 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10946 \param theNodes - identifiers of nodes to be doubled
10947 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10948 nodes. If list of element identifiers is empty then nodes are doubled but
10949 they not assigned to elements
10950 \return TRUE if operation has been completed successfully, FALSE otherwise
10952 //================================================================================
10954 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10955 const std::list< int >& theListOfModifiedElems )
10957 MESSAGE("DoubleNodes");
10958 myLastCreatedElems.Clear();
10959 myLastCreatedNodes.Clear();
10961 if ( theListOfNodes.size() == 0 )
10964 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10968 // iterate through nodes and duplicate them
10970 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10972 std::list< int >::const_iterator aNodeIter;
10973 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10975 int aCurr = *aNodeIter;
10976 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10982 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10985 anOldNodeToNewNode[ aNode ] = aNewNode;
10986 myLastCreatedNodes.Append( aNewNode );
10990 // Create map of new nodes for modified elements
10992 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10994 std::list< int >::const_iterator anElemIter;
10995 for ( anElemIter = theListOfModifiedElems.begin();
10996 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10998 int aCurr = *anElemIter;
10999 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
11003 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
11005 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11007 while ( anIter->more() )
11009 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
11010 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
11012 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
11013 aNodeArr[ ind++ ] = aNewNode;
11016 aNodeArr[ ind++ ] = aCurrNode;
11018 anElemToNodes[ anElem ] = aNodeArr;
11021 // Change nodes of elements
11023 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
11024 anElemToNodesIter = anElemToNodes.begin();
11025 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
11027 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
11028 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
11031 MESSAGE("ChangeElementNodes");
11032 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
11041 //================================================================================
11043 \brief Check if element located inside shape
11044 \return TRUE if IN or ON shape, FALSE otherwise
11046 //================================================================================
11048 template<class Classifier>
11049 bool isInside(const SMDS_MeshElement* theElem,
11050 Classifier& theClassifier,
11051 const double theTol)
11053 gp_XYZ centerXYZ (0, 0, 0);
11054 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11055 while (aNodeItr->more())
11056 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
11058 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11059 theClassifier.Perform(aPnt, theTol);
11060 TopAbs_State aState = theClassifier.State();
11061 return (aState == TopAbs_IN || aState == TopAbs_ON );
11064 //================================================================================
11066 * \brief Classifier of the 3D point on the TopoDS_Face
11067 * with interaface suitable for isInside()
11069 //================================================================================
11071 struct _FaceClassifier
11073 Extrema_ExtPS _extremum;
11074 BRepAdaptor_Surface _surface;
11075 TopAbs_State _state;
11077 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11079 _extremum.Initialize( _surface,
11080 _surface.FirstUParameter(), _surface.LastUParameter(),
11081 _surface.FirstVParameter(), _surface.LastVParameter(),
11082 _surface.Tolerance(), _surface.Tolerance() );
11084 void Perform(const gp_Pnt& aPnt, double theTol)
11086 _state = TopAbs_OUT;
11087 _extremum.Perform(aPnt);
11088 if ( _extremum.IsDone() )
11089 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11090 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11091 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11093 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11096 TopAbs_State State() const
11103 //================================================================================
11105 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11106 This method is the first step of DoubleNodeElemGroupsInRegion.
11107 \param theElems - list of groups of elements (edges or faces) to be replicated
11108 \param theNodesNot - list of groups of nodes not to replicated
11109 \param theShape - shape to detect affected elements (element which geometric center
11110 located on or inside shape).
11111 The replicated nodes should be associated to affected elements.
11112 \return groups of affected elements
11113 \sa DoubleNodeElemGroupsInRegion()
11115 //================================================================================
11117 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11118 const TIDSortedElemSet& theNodesNot,
11119 const TopoDS_Shape& theShape,
11120 TIDSortedElemSet& theAffectedElems)
11122 if ( theShape.IsNull() )
11125 const double aTol = Precision::Confusion();
11126 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11127 auto_ptr<_FaceClassifier> aFaceClassifier;
11128 if ( theShape.ShapeType() == TopAbs_SOLID )
11130 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11131 bsc3d->PerformInfinitePoint(aTol);
11133 else if (theShape.ShapeType() == TopAbs_FACE )
11135 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11138 // iterates on indicated elements and get elements by back references from their nodes
11139 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11140 for ( ; elemItr != theElems.end(); ++elemItr )
11142 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11146 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11147 while ( nodeItr->more() )
11149 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11150 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11152 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11153 while ( backElemItr->more() )
11155 const SMDS_MeshElement* curElem = backElemItr->next();
11156 if ( curElem && theElems.find(curElem) == theElems.end() &&
11158 isInside( curElem, *bsc3d, aTol ) :
11159 isInside( curElem, *aFaceClassifier, aTol )))
11160 theAffectedElems.insert( curElem );
11167 //================================================================================
11169 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11170 \param theElems - group of of elements (edges or faces) to be replicated
11171 \param theNodesNot - group of nodes not to replicate
11172 \param theShape - shape to detect affected elements (element which geometric center
11173 located on or inside shape).
11174 The replicated nodes should be associated to affected elements.
11175 \return TRUE if operation has been completed successfully, FALSE otherwise
11177 //================================================================================
11179 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11180 const TIDSortedElemSet& theNodesNot,
11181 const TopoDS_Shape& theShape )
11183 if ( theShape.IsNull() )
11186 const double aTol = Precision::Confusion();
11187 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11188 auto_ptr<_FaceClassifier> aFaceClassifier;
11189 if ( theShape.ShapeType() == TopAbs_SOLID )
11191 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11192 bsc3d->PerformInfinitePoint(aTol);
11194 else if (theShape.ShapeType() == TopAbs_FACE )
11196 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11199 // iterates on indicated elements and get elements by back references from their nodes
11200 TIDSortedElemSet anAffected;
11201 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11202 for ( ; elemItr != theElems.end(); ++elemItr )
11204 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11208 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11209 while ( nodeItr->more() )
11211 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11212 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11214 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11215 while ( backElemItr->more() )
11217 const SMDS_MeshElement* curElem = backElemItr->next();
11218 if ( curElem && theElems.find(curElem) == theElems.end() &&
11220 isInside( curElem, *bsc3d, aTol ) :
11221 isInside( curElem, *aFaceClassifier, aTol )))
11222 anAffected.insert( curElem );
11226 return DoubleNodes( theElems, theNodesNot, anAffected );
11230 * \brief compute an oriented angle between two planes defined by four points.
11231 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11232 * @param p0 base of the rotation axe
11233 * @param p1 extremity of the rotation axe
11234 * @param g1 belongs to the first plane
11235 * @param g2 belongs to the second plane
11237 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11239 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11240 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11241 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11242 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11243 gp_Vec vref(p0, p1);
11246 gp_Vec n1 = vref.Crossed(v1);
11247 gp_Vec n2 = vref.Crossed(v2);
11248 return n2.AngleWithRef(n1, vref);
11252 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11253 * The list of groups must describe a partition of the mesh volumes.
11254 * The nodes of the internal faces at the boundaries of the groups are doubled.
11255 * In option, the internal faces are replaced by flat elements.
11256 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11257 * The flat elements are stored in groups of volumes.
11258 * @param theElems - list of groups of volumes, where a group of volume is a set of
11259 * SMDS_MeshElements sorted by Id.
11260 * @param createJointElems - if TRUE, create the elements
11261 * @return TRUE if operation has been completed successfully, FALSE otherwise
11263 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11264 bool createJointElems)
11266 MESSAGE("----------------------------------------------");
11267 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11268 MESSAGE("----------------------------------------------");
11270 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11271 meshDS->BuildDownWardConnectivity(true);
11273 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11275 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11276 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11277 // build the list of nodes shared by 2 or more domains, with their domain indexes
11279 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11280 std::map<int,int>celldom; // cell vtkId --> domain
11281 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11282 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11283 faceDomains.clear();
11285 cellDomains.clear();
11286 nodeDomains.clear();
11287 std::map<int,int> emptyMap;
11288 std::set<int> emptySet;
11291 for (int idom = 0; idom < theElems.size(); idom++)
11294 // --- build a map (face to duplicate --> volume to modify)
11295 // with all the faces shared by 2 domains (group of elements)
11296 // and corresponding volume of this domain, for each shared face.
11297 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11299 //MESSAGE("Domain " << idom);
11300 const TIDSortedElemSet& domain = theElems[idom];
11301 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11302 for (; elemItr != domain.end(); ++elemItr)
11304 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11307 int vtkId = anElem->getVtkId();
11308 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11309 int neighborsVtkIds[NBMAXNEIGHBORS];
11310 int downIds[NBMAXNEIGHBORS];
11311 unsigned char downTypes[NBMAXNEIGHBORS];
11312 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11313 for (int n = 0; n < nbNeighbors; n++)
11315 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11316 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11317 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11319 DownIdType face(downIds[n], downTypes[n]);
11320 if (!faceDomains.count(face))
11321 faceDomains[face] = emptyMap; // create an empty entry for face
11322 if (!faceDomains[face].count(idom))
11324 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11325 celldom[vtkId] = idom;
11326 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11333 //MESSAGE("Number of shared faces " << faceDomains.size());
11334 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11336 // --- explore the shared faces domain by domain,
11337 // explore the nodes of the face and see if they belong to a cell in the domain,
11338 // which has only a node or an edge on the border (not a shared face)
11340 for (int idomain = 0; idomain < theElems.size(); idomain++)
11342 //MESSAGE("Domain " << idomain);
11343 const TIDSortedElemSet& domain = theElems[idomain];
11344 itface = faceDomains.begin();
11345 for (; itface != faceDomains.end(); ++itface)
11347 std::map<int, int> domvol = itface->second;
11348 if (!domvol.count(idomain))
11350 DownIdType face = itface->first;
11351 //MESSAGE(" --- face " << face.cellId);
11352 std::set<int> oldNodes;
11354 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11355 std::set<int>::iterator itn = oldNodes.begin();
11356 for (; itn != oldNodes.end(); ++itn)
11359 //MESSAGE(" node " << oldId);
11360 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11361 for (int i=0; i<l.ncells; i++)
11363 int vtkId = l.cells[i];
11364 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11365 if (!domain.count(anElem))
11367 int vtkType = grid->GetCellType(vtkId);
11368 int downId = grid->CellIdToDownId(vtkId);
11371 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11372 continue; // not OK at this stage of the algorithm:
11373 //no cells created after BuildDownWardConnectivity
11375 DownIdType aCell(downId, vtkType);
11376 if (!cellDomains.count(aCell))
11377 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11378 cellDomains[aCell][idomain] = vtkId;
11379 celldom[vtkId] = idomain;
11380 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11386 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11387 // for each shared face, get the nodes
11388 // for each node, for each domain of the face, create a clone of the node
11390 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11391 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11392 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11394 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11395 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11396 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11398 for (int idomain = 0; idomain < theElems.size(); idomain++)
11400 itface = faceDomains.begin();
11401 for (; itface != faceDomains.end(); ++itface)
11403 std::map<int, int> domvol = itface->second;
11404 if (!domvol.count(idomain))
11406 DownIdType face = itface->first;
11407 //MESSAGE(" --- face " << face.cellId);
11408 std::set<int> oldNodes;
11410 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11411 std::set<int>::iterator itn = oldNodes.begin();
11412 for (; itn != oldNodes.end(); ++itn)
11415 //MESSAGE("-+-+-a node " << oldId);
11416 if (!nodeDomains.count(oldId))
11417 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11418 if (nodeDomains[oldId].empty())
11420 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11421 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11423 std::map<int, int>::iterator itdom = domvol.begin();
11424 for (; itdom != domvol.end(); ++itdom)
11426 int idom = itdom->first;
11427 //MESSAGE(" domain " << idom);
11428 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11430 if (nodeDomains[oldId].size() >= 2) // a multiple node
11432 vector<int> orderedDoms;
11433 //MESSAGE("multiple node " << oldId);
11434 if (mutipleNodes.count(oldId))
11435 orderedDoms = mutipleNodes[oldId];
11438 map<int,int>::iterator it = nodeDomains[oldId].begin();
11439 for (; it != nodeDomains[oldId].end(); ++it)
11440 orderedDoms.push_back(it->first);
11442 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11443 //stringstream txt;
11444 //for (int i=0; i<orderedDoms.size(); i++)
11445 // txt << orderedDoms[i] << " ";
11446 //MESSAGE("orderedDoms " << txt.str());
11447 mutipleNodes[oldId] = orderedDoms;
11449 double *coords = grid->GetPoint(oldId);
11450 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11451 int newId = newNode->getVtkId();
11452 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11453 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11460 for (int idomain = 0; idomain < theElems.size(); idomain++)
11462 itface = faceDomains.begin();
11463 for (; itface != faceDomains.end(); ++itface)
11465 std::map<int, int> domvol = itface->second;
11466 if (!domvol.count(idomain))
11468 DownIdType face = itface->first;
11469 //MESSAGE(" --- face " << face.cellId);
11470 std::set<int> oldNodes;
11472 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11473 int nbMultipleNodes = 0;
11474 std::set<int>::iterator itn = oldNodes.begin();
11475 for (; itn != oldNodes.end(); ++itn)
11478 if (mutipleNodes.count(oldId))
11481 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11483 //MESSAGE("multiple Nodes detected on a shared face");
11484 int downId = itface->first.cellId;
11485 unsigned char cellType = itface->first.cellType;
11486 // --- shared edge or shared face ?
11487 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11490 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11491 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11492 if (mutipleNodes.count(nodes[i]))
11493 if (!mutipleNodesToFace.count(nodes[i]))
11494 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11496 else // shared face (between two volumes)
11498 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11499 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11500 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11501 for (int ie =0; ie < nbEdges; ie++)
11504 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11505 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11507 vector<int> vn0 = mutipleNodes[nodes[0]];
11508 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11510 for (int i0 = 0; i0 < vn0.size(); i0++)
11511 for (int i1 = 0; i1 < vn1.size(); i1++)
11512 if (vn0[i0] == vn1[i1])
11513 doms.push_back(vn0[i0]);
11514 if (doms.size() >2)
11516 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11517 double *coords = grid->GetPoint(nodes[0]);
11518 gp_Pnt p0(coords[0], coords[1], coords[2]);
11519 coords = grid->GetPoint(nodes[nbNodes - 1]);
11520 gp_Pnt p1(coords[0], coords[1], coords[2]);
11522 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11523 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11524 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11525 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11526 for (int id=0; id < doms.size(); id++)
11528 int idom = doms[id];
11529 for (int ivol=0; ivol<nbvol; ivol++)
11531 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11532 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11533 if (theElems[idom].count(elem))
11535 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11536 domvol[idom] = svol;
11537 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11539 vtkIdType npts = 0;
11540 vtkIdType* pts = 0;
11541 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11542 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11545 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11546 angleDom[idom] = 0;
11550 gp_Pnt g(values[0], values[1], values[2]);
11551 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11552 //MESSAGE(" angle=" << angleDom[idom]);
11558 map<double, int> sortedDom; // sort domains by angle
11559 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11560 sortedDom[ia->second] = ia->first;
11561 vector<int> vnodes;
11563 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11565 vdom.push_back(ib->second);
11566 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11568 for (int ino = 0; ino < nbNodes; ino++)
11569 vnodes.push_back(nodes[ino]);
11570 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11579 // --- iterate on shared faces (volumes to modify, face to extrude)
11580 // get node id's of the face (id SMDS = id VTK)
11581 // create flat element with old and new nodes if requested
11583 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11584 // (domain1 X domain2) = domain1 + MAXINT*domain2
11586 std::map<int, std::map<long,int> > nodeQuadDomains;
11587 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11589 if (createJointElems)
11592 string joints2DName = "joints2D";
11593 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11594 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11595 string joints3DName = "joints3D";
11596 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11597 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11599 itface = faceDomains.begin();
11600 for (; itface != faceDomains.end(); ++itface)
11602 DownIdType face = itface->first;
11603 std::set<int> oldNodes;
11604 std::set<int>::iterator itn;
11606 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11608 std::map<int, int> domvol = itface->second;
11609 std::map<int, int>::iterator itdom = domvol.begin();
11610 int dom1 = itdom->first;
11611 int vtkVolId = itdom->second;
11613 int dom2 = itdom->first;
11614 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11616 stringstream grpname;
11619 grpname << dom1 << "_" << dom2;
11621 grpname << dom2 << "_" << dom1;
11622 string namegrp = grpname.str();
11623 if (!mapOfJunctionGroups.count(namegrp))
11624 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11625 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11627 sgrp->Add(vol->GetID());
11628 if (vol->GetType() == SMDSAbs_Volume)
11629 joints3DGrp->Add(vol->GetID());
11630 else if (vol->GetType() == SMDSAbs_Face)
11631 joints2DGrp->Add(vol->GetID());
11635 // --- create volumes on multiple domain intersection if requested
11636 // iterate on mutipleNodesToFace
11637 // iterate on edgesMultiDomains
11639 if (createJointElems)
11641 // --- iterate on mutipleNodesToFace
11643 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11644 for (; itn != mutipleNodesToFace.end(); ++itn)
11646 int node = itn->first;
11647 vector<int> orderDom = itn->second;
11648 vector<vtkIdType> orderedNodes;
11649 for (int idom = 0; idom <orderDom.size(); idom++)
11650 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11651 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11653 stringstream grpname;
11655 grpname << 0 << "_" << 0;
11657 string namegrp = grpname.str();
11658 if (!mapOfJunctionGroups.count(namegrp))
11659 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11660 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11662 sgrp->Add(face->GetID());
11665 // --- iterate on edgesMultiDomains
11667 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11668 for (; ite != edgesMultiDomains.end(); ++ite)
11670 vector<int> nodes = ite->first;
11671 vector<int> orderDom = ite->second;
11672 vector<vtkIdType> orderedNodes;
11673 if (nodes.size() == 2)
11675 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11676 for (int ino=0; ino < nodes.size(); ino++)
11677 if (orderDom.size() == 3)
11678 for (int idom = 0; idom <orderDom.size(); idom++)
11679 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11681 for (int idom = orderDom.size()-1; idom >=0; idom--)
11682 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11683 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11686 string namegrp = "jointsMultiples";
11687 if (!mapOfJunctionGroups.count(namegrp))
11688 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11689 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11691 sgrp->Add(vol->GetID());
11695 INFOS("Quadratic multiple joints not implemented");
11696 // TODO quadratic nodes
11701 // --- list the explicit faces and edges of the mesh that need to be modified,
11702 // i.e. faces and edges built with one or more duplicated nodes.
11703 // associate these faces or edges to their corresponding domain.
11704 // only the first domain found is kept when a face or edge is shared
11706 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11707 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11708 faceOrEdgeDom.clear();
11711 for (int idomain = 0; idomain < theElems.size(); idomain++)
11713 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11714 for (; itnod != nodeDomains.end(); ++itnod)
11716 int oldId = itnod->first;
11717 //MESSAGE(" node " << oldId);
11718 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11719 for (int i = 0; i < l.ncells; i++)
11721 int vtkId = l.cells[i];
11722 int vtkType = grid->GetCellType(vtkId);
11723 int downId = grid->CellIdToDownId(vtkId);
11725 continue; // new cells: not to be modified
11726 DownIdType aCell(downId, vtkType);
11727 int volParents[1000];
11728 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11729 for (int j = 0; j < nbvol; j++)
11730 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11731 if (!feDom.count(vtkId))
11733 feDom[vtkId] = idomain;
11734 faceOrEdgeDom[aCell] = emptyMap;
11735 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11736 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11737 // << " type " << vtkType << " downId " << downId);
11743 // --- iterate on shared faces (volumes to modify, face to extrude)
11744 // get node id's of the face
11745 // replace old nodes by new nodes in volumes, and update inverse connectivity
11747 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11748 for (int m=0; m<3; m++)
11750 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11751 itface = (*amap).begin();
11752 for (; itface != (*amap).end(); ++itface)
11754 DownIdType face = itface->first;
11755 std::set<int> oldNodes;
11756 std::set<int>::iterator itn;
11758 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11759 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11760 std::map<int, int> localClonedNodeIds;
11762 std::map<int, int> domvol = itface->second;
11763 std::map<int, int>::iterator itdom = domvol.begin();
11764 for (; itdom != domvol.end(); ++itdom)
11766 int idom = itdom->first;
11767 int vtkVolId = itdom->second;
11768 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11769 localClonedNodeIds.clear();
11770 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11773 if (nodeDomains[oldId].count(idom))
11775 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11776 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11779 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11784 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11785 grid->BuildLinks();
11793 * \brief Double nodes on some external faces and create flat elements.
11794 * Flat elements are mainly used by some types of mechanic calculations.
11796 * Each group of the list must be constituted of faces.
11797 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11798 * @param theElems - list of groups of faces, where a group of faces is a set of
11799 * SMDS_MeshElements sorted by Id.
11800 * @return TRUE if operation has been completed successfully, FALSE otherwise
11802 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11804 MESSAGE("-------------------------------------------------");
11805 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11806 MESSAGE("-------------------------------------------------");
11808 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11810 // --- For each group of faces
11811 // duplicate the nodes, create a flat element based on the face
11812 // replace the nodes of the faces by their clones
11814 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11815 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11816 clonedNodes.clear();
11817 intermediateNodes.clear();
11818 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11819 mapOfJunctionGroups.clear();
11821 for (int idom = 0; idom < theElems.size(); idom++)
11823 const TIDSortedElemSet& domain = theElems[idom];
11824 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11825 for (; elemItr != domain.end(); ++elemItr)
11827 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11828 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11831 // MESSAGE("aFace=" << aFace->GetID());
11832 bool isQuad = aFace->IsQuadratic();
11833 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11835 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11837 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11838 while (nodeIt->more())
11840 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11841 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11843 ln2.push_back(node);
11845 ln0.push_back(node);
11847 const SMDS_MeshNode* clone = 0;
11848 if (!clonedNodes.count(node))
11850 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11851 clonedNodes[node] = clone;
11854 clone = clonedNodes[node];
11857 ln3.push_back(clone);
11859 ln1.push_back(clone);
11861 const SMDS_MeshNode* inter = 0;
11862 if (isQuad && (!isMedium))
11864 if (!intermediateNodes.count(node))
11866 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11867 intermediateNodes[node] = inter;
11870 inter = intermediateNodes[node];
11871 ln4.push_back(inter);
11875 // --- extrude the face
11877 vector<const SMDS_MeshNode*> ln;
11878 SMDS_MeshVolume* vol = 0;
11879 vtkIdType aType = aFace->GetVtkType();
11883 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11884 // MESSAGE("vol prism " << vol->GetID());
11885 ln.push_back(ln1[0]);
11886 ln.push_back(ln1[1]);
11887 ln.push_back(ln1[2]);
11890 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11891 // MESSAGE("vol hexa " << vol->GetID());
11892 ln.push_back(ln1[0]);
11893 ln.push_back(ln1[1]);
11894 ln.push_back(ln1[2]);
11895 ln.push_back(ln1[3]);
11897 case VTK_QUADRATIC_TRIANGLE:
11898 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11899 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11900 // MESSAGE("vol quad prism " << vol->GetID());
11901 ln.push_back(ln1[0]);
11902 ln.push_back(ln1[1]);
11903 ln.push_back(ln1[2]);
11904 ln.push_back(ln3[0]);
11905 ln.push_back(ln3[1]);
11906 ln.push_back(ln3[2]);
11908 case VTK_QUADRATIC_QUAD:
11909 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11910 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11911 // ln4[0], ln4[1], ln4[2], ln4[3]);
11912 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11913 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11914 ln4[0], ln4[1], ln4[2], ln4[3]);
11915 // MESSAGE("vol quad hexa " << vol->GetID());
11916 ln.push_back(ln1[0]);
11917 ln.push_back(ln1[1]);
11918 ln.push_back(ln1[2]);
11919 ln.push_back(ln1[3]);
11920 ln.push_back(ln3[0]);
11921 ln.push_back(ln3[1]);
11922 ln.push_back(ln3[2]);
11923 ln.push_back(ln3[3]);
11933 stringstream grpname;
11937 string namegrp = grpname.str();
11938 if (!mapOfJunctionGroups.count(namegrp))
11939 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11940 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11942 sgrp->Add(vol->GetID());
11945 // --- modify the face
11947 aFace->ChangeNodes(&ln[0], ln.size());
11954 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11955 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11956 * groups of faces to remove inside the object, (idem edges).
11957 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11959 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11960 const TopoDS_Shape& theShape,
11961 SMESH_NodeSearcher* theNodeSearcher,
11962 const char* groupName,
11963 std::vector<double>& nodesCoords,
11964 std::vector<std::vector<int> >& listOfListOfNodes)
11966 MESSAGE("--------------------------------");
11967 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11968 MESSAGE("--------------------------------");
11970 // --- zone of volumes to remove is given :
11971 // 1 either by a geom shape (one or more vertices) and a radius,
11972 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11973 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11974 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11975 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11976 // defined by it's name.
11978 SMESHDS_GroupBase* groupDS = 0;
11979 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11980 while ( groupIt->more() )
11983 SMESH_Group * group = groupIt->next();
11984 if ( !group ) continue;
11985 groupDS = group->GetGroupDS();
11986 if ( !groupDS || groupDS->IsEmpty() ) continue;
11987 std::string grpName = group->GetName();
11988 //MESSAGE("grpName=" << grpName);
11989 if (grpName == groupName)
11995 bool isNodeGroup = false;
11996 bool isNodeCoords = false;
11999 if (groupDS->GetType() != SMDSAbs_Node)
12001 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12004 if (nodesCoords.size() > 0)
12005 isNodeCoords = true; // a list o nodes given by their coordinates
12006 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12008 // --- define groups to build
12010 int idg; // --- group of SMDS volumes
12011 string grpvName = groupName;
12012 grpvName += "_vol";
12013 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12016 MESSAGE("group not created " << grpvName);
12019 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12021 int idgs; // --- group of SMDS faces on the skin
12022 string grpsName = groupName;
12023 grpsName += "_skin";
12024 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12027 MESSAGE("group not created " << grpsName);
12030 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12032 int idgi; // --- group of SMDS faces internal (several shapes)
12033 string grpiName = groupName;
12034 grpiName += "_internalFaces";
12035 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12038 MESSAGE("group not created " << grpiName);
12041 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12043 int idgei; // --- group of SMDS faces internal (several shapes)
12044 string grpeiName = groupName;
12045 grpeiName += "_internalEdges";
12046 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12049 MESSAGE("group not created " << grpeiName);
12052 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12054 // --- build downward connectivity
12056 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12057 meshDS->BuildDownWardConnectivity(true);
12058 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12060 // --- set of volumes detected inside
12062 std::set<int> setOfInsideVol;
12063 std::set<int> setOfVolToCheck;
12065 std::vector<gp_Pnt> gpnts;
12068 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12070 MESSAGE("group of nodes provided");
12071 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12072 while ( elemIt->more() )
12074 const SMDS_MeshElement* elem = elemIt->next();
12077 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12080 SMDS_MeshElement* vol = 0;
12081 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12082 while (volItr->more())
12084 vol = (SMDS_MeshElement*)volItr->next();
12085 setOfInsideVol.insert(vol->getVtkId());
12086 sgrp->Add(vol->GetID());
12090 else if (isNodeCoords)
12092 MESSAGE("list of nodes coordinates provided");
12095 while (i < nodesCoords.size()-2)
12097 double x = nodesCoords[i++];
12098 double y = nodesCoords[i++];
12099 double z = nodesCoords[i++];
12100 gp_Pnt p = gp_Pnt(x, y ,z);
12101 gpnts.push_back(p);
12102 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12105 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12107 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12108 TopTools_IndexedMapOfShape vertexMap;
12109 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12110 gp_Pnt p = gp_Pnt(0,0,0);
12111 if (vertexMap.Extent() < 1)
12114 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12116 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12117 p = BRep_Tool::Pnt(vertex);
12118 gpnts.push_back(p);
12119 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12123 if (gpnts.size() > 0)
12126 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12128 nodeId = startNode->GetID();
12129 MESSAGE("nodeId " << nodeId);
12131 double radius2 = radius*radius;
12132 MESSAGE("radius2 " << radius2);
12134 // --- volumes on start node
12136 setOfVolToCheck.clear();
12137 SMDS_MeshElement* startVol = 0;
12138 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12139 while (volItr->more())
12141 startVol = (SMDS_MeshElement*)volItr->next();
12142 setOfVolToCheck.insert(startVol->getVtkId());
12144 if (setOfVolToCheck.empty())
12146 MESSAGE("No volumes found");
12150 // --- starting with central volumes then their neighbors, check if they are inside
12151 // or outside the domain, until no more new neighbor volume is inside.
12152 // Fill the group of inside volumes
12154 std::map<int, double> mapOfNodeDistance2;
12155 mapOfNodeDistance2.clear();
12156 std::set<int> setOfOutsideVol;
12157 while (!setOfVolToCheck.empty())
12159 std::set<int>::iterator it = setOfVolToCheck.begin();
12161 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12162 bool volInside = false;
12163 vtkIdType npts = 0;
12164 vtkIdType* pts = 0;
12165 grid->GetCellPoints(vtkId, npts, pts);
12166 for (int i=0; i<npts; i++)
12168 double distance2 = 0;
12169 if (mapOfNodeDistance2.count(pts[i]))
12171 distance2 = mapOfNodeDistance2[pts[i]];
12172 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12176 double *coords = grid->GetPoint(pts[i]);
12177 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12179 for (int j=0; j<gpnts.size(); j++)
12181 double d2 = aPoint.SquareDistance(gpnts[j]);
12182 if (d2 < distance2)
12185 if (distance2 < radius2)
12189 mapOfNodeDistance2[pts[i]] = distance2;
12190 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12192 if (distance2 < radius2)
12194 volInside = true; // one or more nodes inside the domain
12195 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12201 setOfInsideVol.insert(vtkId);
12202 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12203 int neighborsVtkIds[NBMAXNEIGHBORS];
12204 int downIds[NBMAXNEIGHBORS];
12205 unsigned char downTypes[NBMAXNEIGHBORS];
12206 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12207 for (int n = 0; n < nbNeighbors; n++)
12208 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12209 setOfVolToCheck.insert(neighborsVtkIds[n]);
12213 setOfOutsideVol.insert(vtkId);
12214 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12216 setOfVolToCheck.erase(vtkId);
12220 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12221 // If yes, add the volume to the inside set
12223 bool addedInside = true;
12224 std::set<int> setOfVolToReCheck;
12225 while (addedInside)
12227 MESSAGE(" --------------------------- re check");
12228 addedInside = false;
12229 std::set<int>::iterator itv = setOfInsideVol.begin();
12230 for (; itv != setOfInsideVol.end(); ++itv)
12233 int neighborsVtkIds[NBMAXNEIGHBORS];
12234 int downIds[NBMAXNEIGHBORS];
12235 unsigned char downTypes[NBMAXNEIGHBORS];
12236 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12237 for (int n = 0; n < nbNeighbors; n++)
12238 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12239 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12241 setOfVolToCheck = setOfVolToReCheck;
12242 setOfVolToReCheck.clear();
12243 while (!setOfVolToCheck.empty())
12245 std::set<int>::iterator it = setOfVolToCheck.begin();
12247 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12249 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12250 int countInside = 0;
12251 int neighborsVtkIds[NBMAXNEIGHBORS];
12252 int downIds[NBMAXNEIGHBORS];
12253 unsigned char downTypes[NBMAXNEIGHBORS];
12254 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12255 for (int n = 0; n < nbNeighbors; n++)
12256 if (setOfInsideVol.count(neighborsVtkIds[n]))
12258 MESSAGE("countInside " << countInside);
12259 if (countInside > 1)
12261 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12262 setOfInsideVol.insert(vtkId);
12263 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12264 addedInside = true;
12267 setOfVolToReCheck.insert(vtkId);
12269 setOfVolToCheck.erase(vtkId);
12273 // --- map of Downward faces at the boundary, inside the global volume
12274 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12275 // fill group of SMDS faces inside the volume (when several volume shapes)
12276 // fill group of SMDS faces on the skin of the global volume (if skin)
12278 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12279 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12280 std::set<int>::iterator it = setOfInsideVol.begin();
12281 for (; it != setOfInsideVol.end(); ++it)
12284 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12285 int neighborsVtkIds[NBMAXNEIGHBORS];
12286 int downIds[NBMAXNEIGHBORS];
12287 unsigned char downTypes[NBMAXNEIGHBORS];
12288 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12289 for (int n = 0; n < nbNeighbors; n++)
12291 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12292 if (neighborDim == 3)
12294 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12296 DownIdType face(downIds[n], downTypes[n]);
12297 boundaryFaces[face] = vtkId;
12299 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12300 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12301 if (vtkFaceId >= 0)
12303 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12304 // find also the smds edges on this face
12305 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12306 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12307 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12308 for (int i = 0; i < nbEdges; i++)
12310 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12311 if (vtkEdgeId >= 0)
12312 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12316 else if (neighborDim == 2) // skin of the volume
12318 DownIdType face(downIds[n], downTypes[n]);
12319 skinFaces[face] = vtkId;
12320 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12321 if (vtkFaceId >= 0)
12322 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12327 // --- identify the edges constituting the wire of each subshape on the skin
12328 // define polylines with the nodes of edges, equivalent to wires
12329 // project polylines on subshapes, and partition, to get geom faces
12331 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12332 std::set<int> emptySet;
12334 std::set<int> shapeIds;
12336 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12337 while (itelem->more())
12339 const SMDS_MeshElement *elem = itelem->next();
12340 int shapeId = elem->getshapeId();
12341 int vtkId = elem->getVtkId();
12342 if (!shapeIdToVtkIdSet.count(shapeId))
12344 shapeIdToVtkIdSet[shapeId] = emptySet;
12345 shapeIds.insert(shapeId);
12347 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12350 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12351 std::set<DownIdType, DownIdCompare> emptyEdges;
12352 emptyEdges.clear();
12354 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12355 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12357 int shapeId = itShape->first;
12358 MESSAGE(" --- Shape ID --- "<< shapeId);
12359 shapeIdToEdges[shapeId] = emptyEdges;
12361 std::vector<int> nodesEdges;
12363 std::set<int>::iterator its = itShape->second.begin();
12364 for (; its != itShape->second.end(); ++its)
12367 MESSAGE(" " << 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++)
12374 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12376 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12377 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12378 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12380 DownIdType edge(downIds[n], downTypes[n]);
12381 if (!shapeIdToEdges[shapeId].count(edge))
12383 shapeIdToEdges[shapeId].insert(edge);
12385 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12386 nodesEdges.push_back(vtkNodeId[0]);
12387 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12388 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12394 std::list<int> order;
12396 if (nodesEdges.size() > 0)
12398 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12399 nodesEdges[0] = -1;
12400 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12401 nodesEdges[1] = -1; // do not reuse this edge
12405 int nodeTofind = order.back(); // try first to push back
12407 for (i = 0; i<nodesEdges.size(); i++)
12408 if (nodesEdges[i] == nodeTofind)
12410 if (i == nodesEdges.size())
12411 found = false; // no follower found on back
12414 if (i%2) // odd ==> use the previous one
12415 if (nodesEdges[i-1] < 0)
12419 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12420 nodesEdges[i-1] = -1;
12422 else // even ==> use the next one
12423 if (nodesEdges[i+1] < 0)
12427 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12428 nodesEdges[i+1] = -1;
12433 // try to push front
12435 nodeTofind = order.front(); // try to push front
12436 for (i = 0; i<nodesEdges.size(); i++)
12437 if (nodesEdges[i] == nodeTofind)
12439 if (i == nodesEdges.size())
12441 found = false; // no predecessor found on front
12444 if (i%2) // odd ==> use the previous one
12445 if (nodesEdges[i-1] < 0)
12449 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12450 nodesEdges[i-1] = -1;
12452 else // even ==> use the next one
12453 if (nodesEdges[i+1] < 0)
12457 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12458 nodesEdges[i+1] = -1;
12464 std::vector<int> nodes;
12465 nodes.push_back(shapeId);
12466 std::list<int>::iterator itl = order.begin();
12467 for (; itl != order.end(); itl++)
12469 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12470 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12472 listOfListOfNodes.push_back(nodes);
12475 // partition geom faces with blocFissure
12476 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12477 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12483 //================================================================================
12485 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12486 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12487 * \return TRUE if operation has been completed successfully, FALSE otherwise
12489 //================================================================================
12491 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12493 // iterates on volume elements and detect all free faces on them
12494 SMESHDS_Mesh* aMesh = GetMeshDS();
12497 //bool res = false;
12498 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12499 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12502 const SMDS_MeshVolume* volume = vIt->next();
12503 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12504 vTool.SetExternalNormal();
12505 //const bool isPoly = volume->IsPoly();
12506 const int iQuad = volume->IsQuadratic();
12507 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12509 if (!vTool.IsFreeFace(iface))
12512 vector<const SMDS_MeshNode *> nodes;
12513 int nbFaceNodes = vTool.NbFaceNodes(iface);
12514 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12516 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12517 nodes.push_back(faceNodes[inode]);
12518 if (iQuad) { // add medium nodes
12519 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12520 nodes.push_back(faceNodes[inode]);
12521 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12522 nodes.push_back(faceNodes[8]);
12524 // add new face based on volume nodes
12525 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12527 continue; // face already exsist
12529 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12533 return ( nbFree==(nbExisted+nbCreated) );
12538 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12540 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12542 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12545 //================================================================================
12547 * \brief Creates missing boundary elements
12548 * \param elements - elements whose boundary is to be checked
12549 * \param dimension - defines type of boundary elements to create
12550 * \param group - a group to store created boundary elements in
12551 * \param targetMesh - a mesh to store created boundary elements in
12552 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12553 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12554 * boundary elements will be copied into the targetMesh
12555 * \param toAddExistingBondary - if true, not only new but also pre-existing
12556 * boundary elements will be added into the new group
12557 * \param aroundElements - if true, elements will be created on boundary of given
12558 * elements else, on boundary of the whole mesh.
12559 * \return nb of added boundary elements
12561 //================================================================================
12563 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12564 Bnd_Dimension dimension,
12565 SMESH_Group* group/*=0*/,
12566 SMESH_Mesh* targetMesh/*=0*/,
12567 bool toCopyElements/*=false*/,
12568 bool toCopyExistingBoundary/*=false*/,
12569 bool toAddExistingBondary/*= false*/,
12570 bool aroundElements/*= false*/)
12572 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12573 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12574 // hope that all elements are of the same type, do not check them all
12575 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12576 throw SALOME_Exception(LOCALIZED("wrong element type"));
12579 toCopyElements = toCopyExistingBoundary = false;
12581 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12582 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12583 int nbAddedBnd = 0;
12585 // editor adding present bnd elements and optionally holding elements to add to the group
12586 SMESH_MeshEditor* presentEditor;
12587 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12588 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12590 SMESH_MesherHelper helper( *myMesh );
12591 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12592 SMDS_VolumeTool vTool;
12593 TIDSortedElemSet avoidSet;
12594 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12597 typedef vector<const SMDS_MeshNode*> TConnectivity;
12599 SMDS_ElemIteratorPtr eIt;
12600 if (elements.empty())
12601 eIt = aMesh->elementsIterator(elemType);
12603 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12605 while (eIt->more())
12607 const SMDS_MeshElement* elem = eIt->next();
12608 const int iQuad = elem->IsQuadratic();
12610 // ------------------------------------------------------------------------------------
12611 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12612 // ------------------------------------------------------------------------------------
12613 vector<const SMDS_MeshElement*> presentBndElems;
12614 vector<TConnectivity> missingBndElems;
12615 TConnectivity nodes;
12616 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12618 vTool.SetExternalNormal();
12619 const SMDS_MeshElement* otherVol = 0;
12620 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12622 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12623 ( !aroundElements || elements.count( otherVol )))
12625 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12626 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12627 if ( missType == SMDSAbs_Edge ) // boundary edges
12629 nodes.resize( 2+iQuad );
12630 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12632 for ( int j = 0; j < nodes.size(); ++j )
12634 if ( const SMDS_MeshElement* edge =
12635 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12636 presentBndElems.push_back( edge );
12638 missingBndElems.push_back( nodes );
12641 else // boundary face
12644 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12645 nodes.push_back( nn[inode] );
12646 if (iQuad) // add medium nodes
12647 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12648 nodes.push_back( nn[inode] );
12649 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12651 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12653 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12654 SMDSAbs_Face, /*noMedium=*/false ))
12655 presentBndElems.push_back( f );
12657 missingBndElems.push_back( nodes );
12659 if ( targetMesh != myMesh )
12661 // add 1D elements on face boundary to be added to a new mesh
12662 const SMDS_MeshElement* edge;
12663 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12666 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12668 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12669 if ( edge && avoidSet.insert( edge ).second )
12670 presentBndElems.push_back( edge );
12676 else // elem is a face ------------------------------------------
12678 avoidSet.clear(), avoidSet.insert( elem );
12679 int nbNodes = elem->NbCornerNodes();
12680 nodes.resize( 2 /*+ iQuad*/);
12681 for ( int i = 0; i < nbNodes; i++ )
12683 nodes[0] = elem->GetNode(i);
12684 nodes[1] = elem->GetNode((i+1)%nbNodes);
12685 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12686 continue; // not free link
12689 //nodes[2] = elem->GetNode( i + nbNodes );
12690 if ( const SMDS_MeshElement* edge =
12691 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12692 presentBndElems.push_back( edge );
12694 missingBndElems.push_back( nodes );
12698 // ---------------------------------
12699 // 2. Add missing boundary elements
12700 // ---------------------------------
12701 if ( targetMesh != myMesh )
12702 // instead of making a map of nodes in this mesh and targetMesh,
12703 // we create nodes with same IDs.
12704 for ( int i = 0; i < missingBndElems.size(); ++i )
12706 TConnectivity& srcNodes = missingBndElems[i];
12707 TConnectivity nodes( srcNodes.size() );
12708 for ( inode = 0; inode < nodes.size(); ++inode )
12709 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12710 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12712 /*noMedium=*/false))
12714 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12718 for ( int i = 0; i < missingBndElems.size(); ++i )
12720 TConnectivity& nodes = missingBndElems[i];
12721 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12723 /*noMedium=*/false))
12725 SMDS_MeshElement* elem =
12726 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12729 // try to set a new element to a shape
12730 if ( myMesh->HasShapeToMesh() )
12733 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12734 const int nbN = nodes.size() / (iQuad+1 );
12735 for ( inode = 0; inode < nbN && ok; ++inode )
12737 pair<int, TopAbs_ShapeEnum> i_stype =
12738 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12739 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12740 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12742 if ( ok && mediumShapes.size() > 1 )
12744 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12745 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12746 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12748 if (( ok = ( stype_i->first != stype_i_0.first )))
12749 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12750 aMesh->IndexToShape( stype_i_0.second ));
12753 if ( ok && mediumShapes.begin()->first == missShapeType )
12754 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12758 // ----------------------------------
12759 // 3. Copy present boundary elements
12760 // ----------------------------------
12761 if ( toCopyExistingBoundary )
12762 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12764 const SMDS_MeshElement* e = presentBndElems[i];
12765 TConnectivity nodes( e->NbNodes() );
12766 for ( inode = 0; inode < nodes.size(); ++inode )
12767 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12768 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12770 else // store present elements to add them to a group
12771 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12773 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12776 } // loop on given elements
12778 // ---------------------------------------------
12779 // 4. Fill group with boundary elements
12780 // ---------------------------------------------
12783 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12784 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12785 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12787 tgtEditor.myLastCreatedElems.Clear();
12788 tgtEditor2.myLastCreatedElems.Clear();
12790 // -----------------------
12791 // 5. Copy given elements
12792 // -----------------------
12793 if ( toCopyElements && targetMesh != myMesh )
12795 if (elements.empty())
12796 eIt = aMesh->elementsIterator(elemType);
12798 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12799 while (eIt->more())
12801 const SMDS_MeshElement* elem = eIt->next();
12802 TConnectivity nodes( elem->NbNodes() );
12803 for ( inode = 0; inode < nodes.size(); ++inode )
12804 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12805 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12807 tgtEditor.myLastCreatedElems.Clear();