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 <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
109 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
111 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
113 //=======================================================================
114 //function : SMESH_MeshEditor
116 //=======================================================================
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119 :myMesh( theMesh ) // theMesh may be NULL
123 //================================================================================
125 * \brief Clears myLastCreatedNodes and myLastCreatedElems
127 //================================================================================
129 void SMESH_MeshEditor::CrearLastCreated()
131 myLastCreatedNodes.Clear();
132 myLastCreatedElems.Clear();
136 //=======================================================================
140 //=======================================================================
143 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
144 const SMDSAbs_ElementType type,
147 const double ballDiameter)
149 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
150 SMDS_MeshElement* e = 0;
151 int nbnode = node.size();
152 SMESHDS_Mesh* mesh = GetMeshDS();
157 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
158 else e = mesh->AddFace (node[0], node[1], node[2] );
160 else if (nbnode == 4) {
161 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
162 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
164 else if (nbnode == 6) {
165 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
166 node[4], node[5], ID);
167 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
170 else if (nbnode == 8) {
171 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
172 node[4], node[5], node[6], node[7], ID);
173 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
174 node[4], node[5], node[6], node[7] );
176 else if (nbnode == 9) {
177 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
178 node[4], node[5], node[6], node[7], node[8], ID);
179 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
180 node[4], node[5], node[6], node[7], node[8] );
183 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
184 else e = mesh->AddPolygonalFace (node );
191 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
192 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
194 else if (nbnode == 5) {
195 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
197 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
200 else if (nbnode == 6) {
201 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
202 node[4], node[5], ID);
203 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
206 else if (nbnode == 8) {
207 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208 node[4], node[5], node[6], node[7], ID);
209 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], node[7] );
212 else if (nbnode == 10) {
213 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6], node[7],
215 node[8], node[9], ID);
216 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7],
220 else if (nbnode == 12) {
221 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7],
223 node[8], node[9], node[10], node[11], ID);
224 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7],
226 node[8], node[9], node[10], node[11] );
228 else if (nbnode == 13) {
229 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
230 node[4], node[5], node[6], node[7],
231 node[8], node[9], node[10],node[11],
233 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
234 node[4], node[5], node[6], node[7],
235 node[8], node[9], node[10],node[11],
238 else if (nbnode == 15) {
239 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240 node[4], node[5], node[6], node[7],
241 node[8], node[9], node[10],node[11],
242 node[12],node[13],node[14],ID);
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
244 node[4], node[5], node[6], node[7],
245 node[8], node[9], node[10],node[11],
246 node[12],node[13],node[14] );
248 else if (nbnode == 20) {
249 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 node[4], node[5], node[6], node[7],
251 node[8], node[9], node[10],node[11],
252 node[12],node[13],node[14],node[15],
253 node[16],node[17],node[18],node[19],ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
255 node[4], node[5], node[6], node[7],
256 node[8], node[9], node[10],node[11],
257 node[12],node[13],node[14],node[15],
258 node[16],node[17],node[18],node[19] );
260 else if (nbnode == 27) {
261 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262 node[4], node[5], node[6], node[7],
263 node[8], node[9], node[10],node[11],
264 node[12],node[13],node[14],node[15],
265 node[16],node[17],node[18],node[19],
266 node[20],node[21],node[22],node[23],
267 node[24],node[25],node[26], ID);
268 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7],
270 node[8], node[9], node[10],node[11],
271 node[12],node[13],node[14],node[15],
272 node[16],node[17],node[18],node[19],
273 node[20],node[21],node[22],node[23],
274 node[24],node[25],node[26] );
281 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
282 else e = mesh->AddEdge (node[0], node[1] );
284 else if ( nbnode == 3 ) {
285 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
286 else e = mesh->AddEdge (node[0], node[1], node[2] );
290 case SMDSAbs_0DElement:
292 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
293 else e = mesh->Add0DElement (node[0] );
298 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
299 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
303 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
304 else e = mesh->AddBall (node[0], ballDiameter);
309 if ( e ) myLastCreatedElems.Append( e );
313 //=======================================================================
317 //=======================================================================
319 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
320 const SMDSAbs_ElementType type,
324 vector<const SMDS_MeshNode*> nodes;
325 nodes.reserve( nodeIDs.size() );
326 vector<int>::const_iterator id = nodeIDs.begin();
327 while ( id != nodeIDs.end() ) {
328 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
329 nodes.push_back( node );
333 return AddElement( nodes, type, isPoly, ID );
336 //=======================================================================
338 //purpose : Remove a node or an element.
339 // Modify a compute state of sub-meshes which become empty
340 //=======================================================================
342 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
345 myLastCreatedElems.Clear();
346 myLastCreatedNodes.Clear();
348 SMESHDS_Mesh* aMesh = GetMeshDS();
349 set< SMESH_subMesh *> smmap;
352 list<int>::const_iterator it = theIDs.begin();
353 for ( ; it != theIDs.end(); it++ ) {
354 const SMDS_MeshElement * elem;
356 elem = aMesh->FindNode( *it );
358 elem = aMesh->FindElement( *it );
362 // Notify VERTEX sub-meshes about modification
364 const SMDS_MeshNode* node = cast2Node( elem );
365 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
366 if ( int aShapeID = node->getshapeId() )
367 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
370 // Find sub-meshes to notify about modification
371 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
372 // while ( nodeIt->more() ) {
373 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
374 // const SMDS_PositionPtr& aPosition = node->GetPosition();
375 // if ( aPosition.get() ) {
376 // if ( int aShapeID = aPosition->GetShapeId() ) {
377 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378 // smmap.insert( sm );
385 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
387 aMesh->RemoveElement( elem );
391 // Notify sub-meshes about modification
392 if ( !smmap.empty() ) {
393 set< SMESH_subMesh *>::iterator smIt;
394 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
395 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
398 // // Check if the whole mesh becomes empty
399 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
400 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
405 //================================================================================
407 * \brief Create 0D elements on all nodes of the given object except those
408 * nodes on which a 0D element already exists.
409 * \param elements - Elements on whose nodes to create 0D elements; if empty,
410 * the all mesh is treated
411 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
413 //================================================================================
415 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
416 TIDSortedElemSet& all0DElems )
418 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
419 SMDS_ElemIteratorPtr elemIt;
420 if ( elements.empty() )
421 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
423 elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
425 while ( elemIt->more() )
427 const SMDS_MeshElement* e = elemIt->next();
428 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
429 while ( nodeIt->more() )
431 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
432 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
434 all0DElems.insert( it0D->next() );
436 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
437 all0DElems.insert( myLastCreatedElems.Last() );
443 //=======================================================================
444 //function : FindShape
445 //purpose : Return an index of the shape theElem is on
446 // or zero if a shape not found
447 //=======================================================================
449 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
451 myLastCreatedElems.Clear();
452 myLastCreatedNodes.Clear();
454 SMESHDS_Mesh * aMesh = GetMeshDS();
455 if ( aMesh->ShapeToMesh().IsNull() )
458 int aShapeID = theElem->getshapeId();
462 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
463 if ( sm->Contains( theElem ))
466 if ( theElem->GetType() == SMDSAbs_Node ) {
467 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
470 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
473 TopoDS_Shape aShape; // the shape a node of theElem is on
474 if ( theElem->GetType() != SMDSAbs_Node )
476 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
477 while ( nodeIt->more() ) {
478 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
479 if ((aShapeID = node->getshapeId()) > 0) {
480 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
481 if ( sm->Contains( theElem ))
483 if ( aShape.IsNull() )
484 aShape = aMesh->IndexToShape( aShapeID );
490 // None of nodes is on a proper shape,
491 // find the shape among ancestors of aShape on which a node is
492 if ( !aShape.IsNull() ) {
493 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
494 for ( ; ancIt.More(); ancIt.Next() ) {
495 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
496 if ( sm && sm->Contains( theElem ))
497 return aMesh->ShapeToIndex( ancIt.Value() );
502 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
503 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
504 for ( ; id_sm != id2sm.end(); ++id_sm )
505 if ( id_sm->second->Contains( theElem ))
509 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
513 //=======================================================================
514 //function : IsMedium
516 //=======================================================================
518 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
519 const SMDSAbs_ElementType typeToCheck)
521 bool isMedium = false;
522 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
523 while (it->more() && !isMedium ) {
524 const SMDS_MeshElement* elem = it->next();
525 isMedium = elem->IsMediumNode(node);
530 //=======================================================================
531 //function : ShiftNodesQuadTria
533 // Shift nodes in the array corresponded to quadratic triangle
534 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
535 //=======================================================================
536 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
538 const SMDS_MeshNode* nd1 = aNodes[0];
539 aNodes[0] = aNodes[1];
540 aNodes[1] = aNodes[2];
542 const SMDS_MeshNode* nd2 = aNodes[3];
543 aNodes[3] = aNodes[4];
544 aNodes[4] = aNodes[5];
548 //=======================================================================
549 //function : edgeConnectivity
551 // return number of the edges connected with the theNode.
552 // if theEdges has connections with the other type of the
553 // elements, return -1
554 //=======================================================================
555 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
557 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
559 while(elemIt->more()) {
567 //=======================================================================
568 //function : GetNodesFromTwoTria
570 // Shift nodes in the array corresponded to quadratic triangle
571 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
572 //=======================================================================
573 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
574 const SMDS_MeshElement * theTria2,
575 const SMDS_MeshNode* N1[],
576 const SMDS_MeshNode* N2[])
578 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
581 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
584 if(it->more()) return false;
585 it = theTria2->nodesIterator();
588 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
591 if(it->more()) return false;
593 int sames[3] = {-1,-1,-1};
605 if(nbsames!=2) return false;
607 ShiftNodesQuadTria(N1);
609 ShiftNodesQuadTria(N1);
612 i = sames[0] + sames[1] + sames[2];
614 ShiftNodesQuadTria(N2);
616 // now we receive following N1 and N2 (using numeration as above image)
617 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
618 // i.e. first nodes from both arrays determ new diagonal
622 //=======================================================================
623 //function : InverseDiag
624 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
625 // but having other common link.
626 // Return False if args are improper
627 //=======================================================================
629 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
630 const SMDS_MeshElement * theTria2 )
632 MESSAGE("InverseDiag");
633 myLastCreatedElems.Clear();
634 myLastCreatedNodes.Clear();
636 if (!theTria1 || !theTria2)
639 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
640 if (!F1) return false;
641 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
642 if (!F2) return false;
643 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
644 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
646 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
647 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
651 // put nodes in array and find out indices of the same ones
652 const SMDS_MeshNode* aNodes [6];
653 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
655 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
656 while ( it->more() ) {
657 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
659 if ( i > 2 ) // theTria2
660 // find same node of theTria1
661 for ( int j = 0; j < 3; j++ )
662 if ( aNodes[ i ] == aNodes[ j ]) {
671 return false; // theTria1 is not a triangle
672 it = theTria2->nodesIterator();
674 if ( i == 6 && it->more() )
675 return false; // theTria2 is not a triangle
678 // find indices of 1,2 and of A,B in theTria1
679 int iA = 0, iB = 0, i1 = 0, i2 = 0;
680 for ( i = 0; i < 6; i++ ) {
681 if ( sameInd [ i ] == 0 ) {
690 // nodes 1 and 2 should not be the same
691 if ( aNodes[ i1 ] == aNodes[ i2 ] )
695 aNodes[ iA ] = aNodes[ i2 ];
697 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
699 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
700 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
704 } // end if(F1 && F2)
706 // check case of quadratic faces
707 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
709 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
713 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
714 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
722 const SMDS_MeshNode* N1 [6];
723 const SMDS_MeshNode* N2 [6];
724 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
726 // now we receive following N1 and N2 (using numeration as above image)
727 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
728 // i.e. first nodes from both arrays determ new diagonal
730 const SMDS_MeshNode* N1new [6];
731 const SMDS_MeshNode* N2new [6];
744 // replaces nodes in faces
745 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
746 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
751 //=======================================================================
752 //function : findTriangles
753 //purpose : find triangles sharing theNode1-theNode2 link
754 //=======================================================================
756 static bool findTriangles(const SMDS_MeshNode * theNode1,
757 const SMDS_MeshNode * theNode2,
758 const SMDS_MeshElement*& theTria1,
759 const SMDS_MeshElement*& theTria2)
761 if ( !theNode1 || !theNode2 ) return false;
763 theTria1 = theTria2 = 0;
765 set< const SMDS_MeshElement* > emap;
766 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
768 const SMDS_MeshElement* elem = it->next();
769 if ( elem->NbNodes() == 3 )
772 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
774 const SMDS_MeshElement* elem = it->next();
775 if ( emap.find( elem ) != emap.end() ) {
777 // theTria1 must be element with minimum ID
778 if( theTria1->GetID() < elem->GetID() ) {
792 return ( theTria1 && theTria2 );
795 //=======================================================================
796 //function : InverseDiag
797 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
798 // with ones built on the same 4 nodes but having other common link.
799 // Return false if proper faces not found
800 //=======================================================================
802 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
803 const SMDS_MeshNode * theNode2)
805 myLastCreatedElems.Clear();
806 myLastCreatedNodes.Clear();
808 MESSAGE( "::InverseDiag()" );
810 const SMDS_MeshElement *tr1, *tr2;
811 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
814 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
815 if (!F1) return false;
816 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
817 if (!F2) return false;
818 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
819 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
821 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
822 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
826 // put nodes in array
827 // and find indices of 1,2 and of A in tr1 and of B in tr2
828 int i, iA1 = 0, i1 = 0;
829 const SMDS_MeshNode* aNodes1 [3];
830 SMDS_ElemIteratorPtr it;
831 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
832 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
833 if ( aNodes1[ i ] == theNode1 )
834 iA1 = i; // node A in tr1
835 else if ( aNodes1[ i ] != theNode2 )
839 const SMDS_MeshNode* aNodes2 [3];
840 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
841 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
842 if ( aNodes2[ i ] == theNode2 )
843 iB2 = i; // node B in tr2
844 else if ( aNodes2[ i ] != theNode1 )
848 // nodes 1 and 2 should not be the same
849 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
853 aNodes1[ iA1 ] = aNodes2[ i2 ];
855 aNodes2[ iB2 ] = aNodes1[ i1 ];
857 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
858 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
863 // check case of quadratic faces
864 return InverseDiag(tr1,tr2);
867 //=======================================================================
868 //function : getQuadrangleNodes
869 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
870 // fusion of triangles tr1 and tr2 having shared link on
871 // theNode1 and theNode2
872 //=======================================================================
874 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
875 const SMDS_MeshNode * theNode1,
876 const SMDS_MeshNode * theNode2,
877 const SMDS_MeshElement * tr1,
878 const SMDS_MeshElement * tr2 )
880 if( tr1->NbNodes() != tr2->NbNodes() )
882 // find the 4-th node to insert into tr1
883 const SMDS_MeshNode* n4 = 0;
884 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
886 while ( !n4 && i<3 ) {
887 const SMDS_MeshNode * n = cast2Node( it->next() );
889 bool isDiag = ( n == theNode1 || n == theNode2 );
893 // Make an array of nodes to be in a quadrangle
894 int iNode = 0, iFirstDiag = -1;
895 it = tr1->nodesIterator();
898 const SMDS_MeshNode * n = cast2Node( it->next() );
900 bool isDiag = ( n == theNode1 || n == theNode2 );
902 if ( iFirstDiag < 0 )
904 else if ( iNode - iFirstDiag == 1 )
905 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
907 else if ( n == n4 ) {
908 return false; // tr1 and tr2 should not have all the same nodes
910 theQuadNodes[ iNode++ ] = n;
912 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
913 theQuadNodes[ iNode ] = n4;
918 //=======================================================================
919 //function : DeleteDiag
920 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
921 // with a quadrangle built on the same 4 nodes.
922 // Return false if proper faces not found
923 //=======================================================================
925 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
926 const SMDS_MeshNode * theNode2)
928 myLastCreatedElems.Clear();
929 myLastCreatedNodes.Clear();
931 MESSAGE( "::DeleteDiag()" );
933 const SMDS_MeshElement *tr1, *tr2;
934 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
937 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
938 if (!F1) return false;
939 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
940 if (!F2) return false;
941 SMESHDS_Mesh * aMesh = GetMeshDS();
943 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
944 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
946 const SMDS_MeshNode* aNodes [ 4 ];
947 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
950 const SMDS_MeshElement* newElem = 0;
951 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
952 myLastCreatedElems.Append(newElem);
953 AddToSameGroups( newElem, tr1, aMesh );
954 int aShapeId = tr1->getshapeId();
957 aMesh->SetMeshElementOnShape( newElem, aShapeId );
959 aMesh->RemoveElement( tr1 );
960 aMesh->RemoveElement( tr2 );
965 // check case of quadratic faces
966 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
968 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
972 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
973 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
981 const SMDS_MeshNode* N1 [6];
982 const SMDS_MeshNode* N2 [6];
983 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
985 // now we receive following N1 and N2 (using numeration as above image)
986 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
987 // i.e. first nodes from both arrays determ new diagonal
989 const SMDS_MeshNode* aNodes[8];
999 const SMDS_MeshElement* newElem = 0;
1000 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1001 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1002 myLastCreatedElems.Append(newElem);
1003 AddToSameGroups( newElem, tr1, aMesh );
1004 int aShapeId = tr1->getshapeId();
1007 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1009 aMesh->RemoveElement( tr1 );
1010 aMesh->RemoveElement( tr2 );
1012 // remove middle node (9)
1013 GetMeshDS()->RemoveNode( N1[4] );
1018 //=======================================================================
1019 //function : Reorient
1020 //purpose : Reverse theElement orientation
1021 //=======================================================================
1023 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1025 MESSAGE("Reorient");
1026 myLastCreatedElems.Clear();
1027 myLastCreatedNodes.Clear();
1031 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1032 if ( !it || !it->more() )
1035 switch ( theElem->GetType() ) {
1038 case SMDSAbs_Face: {
1039 if(!theElem->IsQuadratic()) {
1040 int i = theElem->NbNodes();
1041 vector<const SMDS_MeshNode*> aNodes( i );
1042 while ( it->more() )
1043 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1044 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1047 // quadratic elements
1048 if(theElem->GetType()==SMDSAbs_Edge) {
1049 vector<const SMDS_MeshNode*> aNodes(3);
1050 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1051 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1052 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1053 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1056 int nbn = theElem->NbNodes();
1057 vector<const SMDS_MeshNode*> aNodes(nbn);
1058 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1060 for(; i<nbn/2; i++) {
1061 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1063 for(i=0; i<nbn/2; i++) {
1064 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1066 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1070 case SMDSAbs_Volume: {
1071 if (theElem->IsPoly()) {
1072 // TODO reorient vtk polyhedron
1073 MESSAGE("reorient vtk polyhedron ?");
1074 const SMDS_VtkVolume* aPolyedre =
1075 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1077 MESSAGE("Warning: bad volumic element");
1081 int nbFaces = aPolyedre->NbFaces();
1082 vector<const SMDS_MeshNode *> poly_nodes;
1083 vector<int> quantities (nbFaces);
1085 // reverse each face of the polyedre
1086 for (int iface = 1; iface <= nbFaces; iface++) {
1087 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1088 quantities[iface - 1] = nbFaceNodes;
1090 for (inode = nbFaceNodes; inode >= 1; inode--) {
1091 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1092 poly_nodes.push_back(curNode);
1096 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100 SMDS_VolumeTool vTool;
1101 if ( !vTool.Set( theElem ))
1104 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1105 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1114 //================================================================================
1116 * \brief Reorient faces.
1117 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1118 * \param theDirection - desired direction of normal of \a theFace
1119 * \param theFace - one of \a theFaces that sould be oriented according to
1120 * \a theDirection and whose orientation defines orientation of other faces
1121 * \return number of reoriented faces.
1123 //================================================================================
1125 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1126 const gp_Dir& theDirection,
1127 const SMDS_MeshElement * theFace)
1130 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1132 if ( theFaces.empty() )
1134 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1135 while ( fIt->more() )
1136 theFaces.insert( theFaces.end(), fIt->next() );
1139 // orient theFace according to theDirection
1141 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1142 if ( normal * theDirection.XYZ() < 0 )
1143 nbReori += Reorient( theFace );
1145 // Orient other faces
1147 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1148 TIDSortedElemSet avoidSet;
1149 set< SMESH_TLink > checkedLinks;
1150 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1152 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1153 theFaces.erase( theFace );
1154 startFaces.insert( theFace );
1156 int nodeInd1, nodeInd2;
1157 const SMDS_MeshElement* otherFace;
1158 vector< const SMDS_MeshElement* > facesNearLink;
1159 vector< std::pair< int, int > > nodeIndsOfFace;
1161 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1162 while ( !startFaces.empty() )
1164 startFace = startFaces.begin();
1165 theFace = *startFace;
1166 startFaces.erase( startFace );
1167 if ( !visitedFaces.insert( theFace ).second )
1171 avoidSet.insert(theFace);
1173 NLink link( theFace->GetNode( 0 ), 0 );
1175 const int nbNodes = theFace->NbCornerNodes();
1176 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1178 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1179 linkIt_isNew = checkedLinks.insert( link );
1180 if ( !linkIt_isNew.second )
1182 // link has already been checked and won't be encountered more
1183 // if the group (theFaces) is manifold
1184 //checkedLinks.erase( linkIt_isNew.first );
1188 facesNearLink.clear();
1189 nodeIndsOfFace.clear();
1190 while (( otherFace = FindFaceInSet( link.first, link.second,
1191 theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1192 if ( otherFace != theFace)
1194 facesNearLink.push_back( otherFace );
1195 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1196 avoidSet.insert( otherFace );
1198 if ( facesNearLink.size() > 1 )
1200 // NON-MANIFOLD mesh shell !
1201 // select a face most co-directed with theFace,
1202 // other faces won't be visited this time
1204 SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1205 double proj, maxProj = -1;
1206 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1207 SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1208 if (( proj = Abs( NF * NOF )) > maxProj ) {
1210 otherFace = facesNearLink[i];
1211 nodeInd1 = nodeIndsOfFace[i].first;
1212 nodeInd2 = nodeIndsOfFace[i].second;
1215 // not to visit rejected faces
1216 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1217 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1218 visitedFaces.insert( facesNearLink[i] );
1220 else if ( facesNearLink.size() == 1 )
1222 otherFace = facesNearLink[0];
1223 nodeInd1 = nodeIndsOfFace.back().first;
1224 nodeInd2 = nodeIndsOfFace.back().second;
1226 if ( otherFace && otherFace != theFace)
1228 // link must be reverse in otherFace if orientation ot otherFace
1229 // is same as that of theFace
1230 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1232 nbReori += Reorient( otherFace );
1234 startFaces.insert( otherFace );
1237 std::swap( link.first, link.second ); // reverse the link
1243 //=======================================================================
1244 //function : getBadRate
1246 //=======================================================================
1248 static double getBadRate (const SMDS_MeshElement* theElem,
1249 SMESH::Controls::NumericalFunctorPtr& theCrit)
1251 SMESH::Controls::TSequenceOfXYZ P;
1252 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1254 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1255 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1258 //=======================================================================
1259 //function : QuadToTri
1260 //purpose : Cut quadrangles into triangles.
1261 // theCrit is used to select a diagonal to cut
1262 //=======================================================================
1264 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1265 SMESH::Controls::NumericalFunctorPtr theCrit)
1267 myLastCreatedElems.Clear();
1268 myLastCreatedNodes.Clear();
1270 MESSAGE( "::QuadToTri()" );
1272 if ( !theCrit.get() )
1275 SMESHDS_Mesh * aMesh = GetMeshDS();
1277 Handle(Geom_Surface) surface;
1278 SMESH_MesherHelper helper( *GetMesh() );
1280 TIDSortedElemSet::iterator itElem;
1281 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1282 const SMDS_MeshElement* elem = *itElem;
1283 if ( !elem || elem->GetType() != SMDSAbs_Face )
1285 if ( elem->NbCornerNodes() != 4 )
1288 // retrieve element nodes
1289 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1291 // compare two sets of possible triangles
1292 double aBadRate1, aBadRate2; // to what extent a set is bad
1293 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1294 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1295 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1297 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1298 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1299 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1301 int aShapeId = FindShape( elem );
1302 const SMDS_MeshElement* newElem1 = 0;
1303 const SMDS_MeshElement* newElem2 = 0;
1305 if( !elem->IsQuadratic() ) {
1307 // split liner quadrangle
1308 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1309 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1310 if ( aBadRate1 <= aBadRate2 ) {
1311 // tr1 + tr2 is better
1312 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1313 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1316 // tr3 + tr4 is better
1317 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1318 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1323 // split quadratic quadrangle
1325 // get surface elem is on
1326 if ( aShapeId != helper.GetSubShapeID() ) {
1330 shape = aMesh->IndexToShape( aShapeId );
1331 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1332 TopoDS_Face face = TopoDS::Face( shape );
1333 surface = BRep_Tool::Surface( face );
1334 if ( !surface.IsNull() )
1335 helper.SetSubShape( shape );
1338 // find middle point for (0,1,2,3)
1339 // and create a node in this point;
1340 const SMDS_MeshNode* newN = 0;
1341 if ( aNodes.size() == 9 )
1343 // SMDSEntity_BiQuad_Quadrangle
1344 newN = aNodes.back();
1349 if ( surface.IsNull() )
1351 for ( int i = 0; i < 4; i++ )
1352 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1357 const SMDS_MeshNode* inFaceNode = 0;
1358 if ( helper.GetNodeUVneedInFaceNode() )
1359 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1360 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1361 inFaceNode = aNodes[ i ];
1363 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1365 for ( int i = 0; i < 4; i++ )
1366 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1368 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1370 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1371 myLastCreatedNodes.Append(newN);
1373 // create a new element
1374 if ( aBadRate1 <= aBadRate2 ) {
1375 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1376 aNodes[6], aNodes[7], newN );
1377 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1378 newN, aNodes[4], aNodes[5] );
1381 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1382 aNodes[7], aNodes[4], newN );
1383 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1384 newN, aNodes[5], aNodes[6] );
1388 // care of a new element
1390 myLastCreatedElems.Append(newElem1);
1391 myLastCreatedElems.Append(newElem2);
1392 AddToSameGroups( newElem1, elem, aMesh );
1393 AddToSameGroups( newElem2, elem, aMesh );
1395 // put a new triangle on the same shape
1398 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1399 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1401 aMesh->RemoveElement( elem );
1406 //=======================================================================
1407 //function : BestSplit
1408 //purpose : Find better diagonal for cutting.
1409 //=======================================================================
1411 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1412 SMESH::Controls::NumericalFunctorPtr theCrit)
1414 myLastCreatedElems.Clear();
1415 myLastCreatedNodes.Clear();
1420 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1423 if( theQuad->NbNodes()==4 ||
1424 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1426 // retrieve element nodes
1427 const SMDS_MeshNode* aNodes [4];
1428 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1430 //while (itN->more())
1432 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1434 // compare two sets of possible triangles
1435 double aBadRate1, aBadRate2; // to what extent a set is bad
1436 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1437 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1438 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1440 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1441 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1442 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1446 return 1; // diagonal 1-3
1448 return 2; // diagonal 2-4
1455 // Methods of splitting volumes into tetra
1457 const int theHexTo5_1[5*4+1] =
1459 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1461 const int theHexTo5_2[5*4+1] =
1463 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1465 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1467 const int theHexTo6_1[6*4+1] =
1469 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
1471 const int theHexTo6_2[6*4+1] =
1473 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
1475 const int theHexTo6_3[6*4+1] =
1477 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
1479 const int theHexTo6_4[6*4+1] =
1481 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
1483 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1485 const int thePyraTo2_1[2*4+1] =
1487 0, 1, 2, 4, 0, 2, 3, 4, -1
1489 const int thePyraTo2_2[2*4+1] =
1491 1, 2, 3, 4, 1, 3, 0, 4, -1
1493 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1495 const int thePentaTo3_1[3*4+1] =
1497 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1499 const int thePentaTo3_2[3*4+1] =
1501 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1503 const int thePentaTo3_3[3*4+1] =
1505 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1507 const int thePentaTo3_4[3*4+1] =
1509 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1511 const int thePentaTo3_5[3*4+1] =
1513 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1515 const int thePentaTo3_6[3*4+1] =
1517 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1519 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1520 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1522 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1525 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1526 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1527 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1532 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1533 bool _baryNode; //!< additional node is to be created at cell barycenter
1534 bool _ownConn; //!< to delete _connectivity in destructor
1535 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1537 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1538 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1539 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1540 bool hasFacet( const TTriangleFacet& facet ) const
1542 const int* tetConn = _connectivity;
1543 for ( ; tetConn[0] >= 0; tetConn += 4 )
1544 if (( facet.contains( tetConn[0] ) +
1545 facet.contains( tetConn[1] ) +
1546 facet.contains( tetConn[2] ) +
1547 facet.contains( tetConn[3] )) == 3 )
1553 //=======================================================================
1555 * \brief return TSplitMethod for the given element
1557 //=======================================================================
1559 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1561 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1563 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1564 // an edge and a face barycenter; tertaherdons are based on triangles and
1565 // a volume barycenter
1566 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1568 // Find out how adjacent volumes are split
1570 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1571 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1572 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1574 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1575 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1576 if ( nbNodes < 4 ) continue;
1578 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1579 const int* nInd = vol.GetFaceNodesIndices( iF );
1582 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1583 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1584 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1585 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1589 int iCom = 0; // common node of triangle faces to split into
1590 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1592 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1593 nInd[ iQ * ( (iCom+1)%nbNodes )],
1594 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1595 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1596 nInd[ iQ * ( (iCom+2)%nbNodes )],
1597 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1598 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1600 triaSplits.push_back( t012 );
1601 triaSplits.push_back( t023 );
1606 if ( !triaSplits.empty() )
1607 hasAdjacentSplits = true;
1610 // Among variants of split method select one compliant with adjacent volumes
1612 TSplitMethod method;
1613 if ( !vol.Element()->IsPoly() && !is24TetMode )
1615 int nbVariants = 2, nbTet = 0;
1616 const int** connVariants = 0;
1617 switch ( vol.Element()->GetEntityType() )
1619 case SMDSEntity_Hexa:
1620 case SMDSEntity_Quad_Hexa:
1621 case SMDSEntity_TriQuad_Hexa:
1622 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1623 connVariants = theHexTo5, nbTet = 5;
1625 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1627 case SMDSEntity_Pyramid:
1628 case SMDSEntity_Quad_Pyramid:
1629 connVariants = thePyraTo2; nbTet = 2;
1631 case SMDSEntity_Penta:
1632 case SMDSEntity_Quad_Penta:
1633 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1638 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1640 // check method compliancy with adjacent tetras,
1641 // all found splits must be among facets of tetras described by this method
1642 method = TSplitMethod( nbTet, connVariants[variant] );
1643 if ( hasAdjacentSplits && method._nbTetra > 0 )
1645 bool facetCreated = true;
1646 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1648 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1649 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1650 facetCreated = method.hasFacet( *facet );
1652 if ( !facetCreated )
1653 method = TSplitMethod(0); // incompatible method
1657 if ( method._nbTetra < 1 )
1659 // No standard method is applicable, use a generic solution:
1660 // each facet of a volume is split into triangles and
1661 // each of triangles and a volume barycenter form a tetrahedron.
1663 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1665 int* connectivity = new int[ maxTetConnSize + 1 ];
1666 method._connectivity = connectivity;
1667 method._ownConn = true;
1668 method._baryNode = !isHex27; // to create central node or not
1671 int baryCenInd = vol.NbNodes() - int( isHex27 );
1672 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1674 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1675 const int* nInd = vol.GetFaceNodesIndices( iF );
1676 // find common node of triangle facets of tetra to create
1677 int iCommon = 0; // index in linear numeration
1678 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1679 if ( !triaSplits.empty() )
1682 const TTriangleFacet* facet = &triaSplits.front();
1683 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1684 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1685 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1688 else if ( nbNodes > 3 && !is24TetMode )
1690 // find the best method of splitting into triangles by aspect ratio
1691 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1692 map< double, int > badness2iCommon;
1693 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1694 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1695 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1698 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1700 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1701 nodes[ iQ*((iLast-1)%nbNodes)],
1702 nodes[ iQ*((iLast )%nbNodes)]);
1703 badness += getBadRate( &tria, aspectRatio );
1705 badness2iCommon.insert( make_pair( badness, iCommon ));
1707 // use iCommon with lowest badness
1708 iCommon = badness2iCommon.begin()->second;
1710 if ( iCommon >= nbNodes )
1711 iCommon = 0; // something wrong
1713 // fill connectivity of tetrahedra based on a current face
1714 int nbTet = nbNodes - 2;
1715 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1720 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1721 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1725 method._faceBaryNode[ iF ] = 0;
1726 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1729 for ( int i = 0; i < nbTet; ++i )
1731 int i1 = i, i2 = (i+1) % nbNodes;
1732 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1733 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1734 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1735 connectivity[ connSize++ ] = faceBaryCenInd;
1736 connectivity[ connSize++ ] = baryCenInd;
1741 for ( int i = 0; i < nbTet; ++i )
1743 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1744 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1745 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1746 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1747 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1748 connectivity[ connSize++ ] = baryCenInd;
1751 method._nbTetra += nbTet;
1753 } // loop on volume faces
1755 connectivity[ connSize++ ] = -1;
1757 } // end of generic solution
1761 //================================================================================
1763 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1765 //================================================================================
1767 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1769 // find the tetrahedron including the three nodes of facet
1770 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1771 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1772 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1773 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1774 while ( volIt1->more() )
1776 const SMDS_MeshElement* v = volIt1->next();
1777 SMDSAbs_EntityType type = v->GetEntityType();
1778 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1780 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1781 continue; // medium node not allowed
1782 const int ind2 = v->GetNodeIndex( n2 );
1783 if ( ind2 < 0 || 3 < ind2 )
1785 const int ind3 = v->GetNodeIndex( n3 );
1786 if ( ind3 < 0 || 3 < ind3 )
1793 //=======================================================================
1795 * \brief A key of a face of volume
1797 //=======================================================================
1799 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1801 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1803 TIDSortedNodeSet sortedNodes;
1804 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1805 int nbNodes = vol.NbFaceNodes( iF );
1806 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1807 for ( int i = 0; i < nbNodes; i += iQ )
1808 sortedNodes.insert( fNodes[i] );
1809 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1810 first.first = (*(n++))->GetID();
1811 first.second = (*(n++))->GetID();
1812 second.first = (*(n++))->GetID();
1813 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1818 //=======================================================================
1819 //function : SplitVolumesIntoTetra
1820 //purpose : Split volume elements into tetrahedra.
1821 //=======================================================================
1823 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1824 const int theMethodFlags)
1826 // std-like iterator on coordinates of nodes of mesh element
1827 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1828 NXyzIterator xyzEnd;
1830 SMDS_VolumeTool volTool;
1831 SMESH_MesherHelper helper( *GetMesh());
1833 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1834 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1836 SMESH_SequenceOfElemPtr newNodes, newElems;
1838 // map face of volume to it's baricenrtic node
1839 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1842 TIDSortedElemSet::const_iterator elem = theElems.begin();
1843 for ( ; elem != theElems.end(); ++elem )
1845 if ( (*elem)->GetType() != SMDSAbs_Volume )
1847 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1848 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1851 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1853 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1854 if ( splitMethod._nbTetra < 1 ) continue;
1856 // find submesh to add new tetras to
1857 if ( !subMesh || !subMesh->Contains( *elem ))
1859 int shapeID = FindShape( *elem );
1860 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1861 subMesh = GetMeshDS()->MeshElements( shapeID );
1864 if ( (*elem)->IsQuadratic() )
1867 // add quadratic links to the helper
1868 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1870 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1871 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1872 for ( int iN = 0; iN < nbN; iN += iQ )
1873 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1875 helper.SetIsQuadratic( true );
1880 helper.SetIsQuadratic( false );
1882 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1883 helper.SetElementsOnShape( true );
1884 if ( splitMethod._baryNode )
1886 // make a node at barycenter
1887 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1888 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1889 nodes.push_back( gcNode );
1890 newNodes.Append( gcNode );
1892 if ( !splitMethod._faceBaryNode.empty() )
1894 // make or find baricentric nodes of faces
1895 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1896 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1898 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1899 volFace2BaryNode.insert
1900 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1903 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1904 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1906 nodes.push_back( iF_n->second = f_n->second );
1911 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1912 const int* tetConn = splitMethod._connectivity;
1913 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1914 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1915 nodes[ tetConn[1] ],
1916 nodes[ tetConn[2] ],
1917 nodes[ tetConn[3] ]));
1919 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1921 // Split faces on sides of the split volume
1923 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1924 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1926 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1927 if ( nbNodes < 4 ) continue;
1929 // find an existing face
1930 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1931 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1932 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1933 /*noMedium=*/false))
1936 helper.SetElementsOnShape( false );
1937 vector< const SMDS_MeshElement* > triangles;
1939 // find submesh to add new triangles in
1940 if ( !fSubMesh || !fSubMesh->Contains( face ))
1942 int shapeID = FindShape( face );
1943 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1945 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1946 if ( iF_n != splitMethod._faceBaryNode.end() )
1948 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1950 const SMDS_MeshNode* n1 = fNodes[iN];
1951 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1952 const SMDS_MeshNode *n3 = iF_n->second;
1953 if ( !volTool.IsFaceExternal( iF ))
1955 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1957 if ( fSubMesh && n3->getshapeId() < 1 )
1958 fSubMesh->AddNode( n3 );
1963 // among possible triangles create ones discribed by split method
1964 const int* nInd = volTool.GetFaceNodesIndices( iF );
1965 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1966 int iCom = 0; // common node of triangle faces to split into
1967 list< TTriangleFacet > facets;
1968 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1970 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1971 nInd[ iQ * ( (iCom+1)%nbNodes )],
1972 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1973 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1974 nInd[ iQ * ( (iCom+2)%nbNodes )],
1975 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1976 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1978 facets.push_back( t012 );
1979 facets.push_back( t023 );
1980 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1981 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1982 nInd[ iQ * ((iLast-1)%nbNodes )],
1983 nInd[ iQ * ((iLast )%nbNodes )]));
1987 list< TTriangleFacet >::iterator facet = facets.begin();
1988 for ( ; facet != facets.end(); ++facet )
1990 if ( !volTool.IsFaceExternal( iF ))
1991 swap( facet->_n2, facet->_n3 );
1992 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1993 volNodes[ facet->_n2 ],
1994 volNodes[ facet->_n3 ]));
1997 for ( int i = 0; i < triangles.size(); ++i )
1999 if ( !triangles[i] ) continue;
2001 fSubMesh->AddElement( triangles[i]);
2002 newElems.Append( triangles[i] );
2004 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2005 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2008 } // loop on volume faces to split them into triangles
2010 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2012 if ( geomType == SMDSEntity_TriQuad_Hexa )
2014 // remove medium nodes that could become free
2015 for ( int i = 20; i < volTool.NbNodes(); ++i )
2016 if ( volNodes[i]->NbInverseElements() == 0 )
2017 GetMeshDS()->RemoveNode( volNodes[i] );
2019 } // loop on volumes to split
2021 myLastCreatedNodes = newNodes;
2022 myLastCreatedElems = newElems;
2025 //=======================================================================
2026 //function : AddToSameGroups
2027 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2028 //=======================================================================
2030 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2031 const SMDS_MeshElement* elemInGroups,
2032 SMESHDS_Mesh * aMesh)
2034 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2035 if (!groups.empty()) {
2036 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2037 for ( ; grIt != groups.end(); grIt++ ) {
2038 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2039 if ( group && group->Contains( elemInGroups ))
2040 group->SMDSGroup().Add( elemToAdd );
2046 //=======================================================================
2047 //function : RemoveElemFromGroups
2048 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2049 //=======================================================================
2050 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2051 SMESHDS_Mesh * aMesh)
2053 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2054 if (!groups.empty())
2056 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2057 for (; GrIt != groups.end(); GrIt++)
2059 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2060 if (!grp || grp->IsEmpty()) continue;
2061 grp->SMDSGroup().Remove(removeelem);
2066 //================================================================================
2068 * \brief Replace elemToRm by elemToAdd in the all groups
2070 //================================================================================
2072 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2073 const SMDS_MeshElement* elemToAdd,
2074 SMESHDS_Mesh * aMesh)
2076 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2077 if (!groups.empty()) {
2078 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2079 for ( ; grIt != groups.end(); grIt++ ) {
2080 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2081 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2082 group->SMDSGroup().Add( elemToAdd );
2087 //================================================================================
2089 * \brief Replace elemToRm by elemToAdd in the all groups
2091 //================================================================================
2093 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2094 const vector<const SMDS_MeshElement*>& elemToAdd,
2095 SMESHDS_Mesh * aMesh)
2097 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2098 if (!groups.empty())
2100 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2101 for ( ; grIt != groups.end(); grIt++ ) {
2102 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2103 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2104 for ( int i = 0; i < elemToAdd.size(); ++i )
2105 group->SMDSGroup().Add( elemToAdd[ i ] );
2110 //=======================================================================
2111 //function : QuadToTri
2112 //purpose : Cut quadrangles into triangles.
2113 // theCrit is used to select a diagonal to cut
2114 //=======================================================================
2116 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2117 const bool the13Diag)
2119 myLastCreatedElems.Clear();
2120 myLastCreatedNodes.Clear();
2122 MESSAGE( "::QuadToTri()" );
2124 SMESHDS_Mesh * aMesh = GetMeshDS();
2126 Handle(Geom_Surface) surface;
2127 SMESH_MesherHelper helper( *GetMesh() );
2129 TIDSortedElemSet::iterator itElem;
2130 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2131 const SMDS_MeshElement* elem = *itElem;
2132 if ( !elem || elem->GetType() != SMDSAbs_Face )
2134 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2135 if(!isquad) continue;
2137 if(elem->NbNodes()==4) {
2138 // retrieve element nodes
2139 const SMDS_MeshNode* aNodes [4];
2140 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2142 while ( itN->more() )
2143 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2145 int aShapeId = FindShape( elem );
2146 const SMDS_MeshElement* newElem1 = 0;
2147 const SMDS_MeshElement* newElem2 = 0;
2149 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2150 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2153 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2154 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2156 myLastCreatedElems.Append(newElem1);
2157 myLastCreatedElems.Append(newElem2);
2158 // put a new triangle on the same shape and add to the same groups
2161 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2162 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2164 AddToSameGroups( newElem1, elem, aMesh );
2165 AddToSameGroups( newElem2, elem, aMesh );
2166 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2167 aMesh->RemoveElement( elem );
2170 // Quadratic quadrangle
2172 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2174 // get surface elem is on
2175 int aShapeId = FindShape( elem );
2176 if ( aShapeId != helper.GetSubShapeID() ) {
2180 shape = aMesh->IndexToShape( aShapeId );
2181 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2182 TopoDS_Face face = TopoDS::Face( shape );
2183 surface = BRep_Tool::Surface( face );
2184 if ( !surface.IsNull() )
2185 helper.SetSubShape( shape );
2189 const SMDS_MeshNode* aNodes [8];
2190 const SMDS_MeshNode* inFaceNode = 0;
2191 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2193 while ( itN->more() ) {
2194 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2195 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2196 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2198 inFaceNode = aNodes[ i-1 ];
2202 // find middle point for (0,1,2,3)
2203 // and create a node in this point;
2205 if ( surface.IsNull() ) {
2207 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2211 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2214 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2216 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2218 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2219 myLastCreatedNodes.Append(newN);
2221 // create a new element
2222 const SMDS_MeshElement* newElem1 = 0;
2223 const SMDS_MeshElement* newElem2 = 0;
2225 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2226 aNodes[6], aNodes[7], newN );
2227 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2228 newN, aNodes[4], aNodes[5] );
2231 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2232 aNodes[7], aNodes[4], newN );
2233 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2234 newN, aNodes[5], aNodes[6] );
2236 myLastCreatedElems.Append(newElem1);
2237 myLastCreatedElems.Append(newElem2);
2238 // put a new triangle on the same shape and add to the same groups
2241 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2242 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2244 AddToSameGroups( newElem1, elem, aMesh );
2245 AddToSameGroups( newElem2, elem, aMesh );
2246 aMesh->RemoveElement( elem );
2253 //=======================================================================
2254 //function : getAngle
2256 //=======================================================================
2258 double getAngle(const SMDS_MeshElement * tr1,
2259 const SMDS_MeshElement * tr2,
2260 const SMDS_MeshNode * n1,
2261 const SMDS_MeshNode * n2)
2263 double angle = 2. * M_PI; // bad angle
2266 SMESH::Controls::TSequenceOfXYZ P1, P2;
2267 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2268 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2271 if(!tr1->IsQuadratic())
2272 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2274 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2275 if ( N1.SquareMagnitude() <= gp::Resolution() )
2277 if(!tr2->IsQuadratic())
2278 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2280 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2281 if ( N2.SquareMagnitude() <= gp::Resolution() )
2284 // find the first diagonal node n1 in the triangles:
2285 // take in account a diagonal link orientation
2286 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2287 for ( int t = 0; t < 2; t++ ) {
2288 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2289 int i = 0, iDiag = -1;
2290 while ( it->more()) {
2291 const SMDS_MeshElement *n = it->next();
2292 if ( n == n1 || n == n2 ) {
2296 if ( i - iDiag == 1 )
2297 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2306 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2309 angle = N1.Angle( N2 );
2314 // =================================================
2315 // class generating a unique ID for a pair of nodes
2316 // and able to return nodes by that ID
2317 // =================================================
2321 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2322 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2325 long GetLinkID (const SMDS_MeshNode * n1,
2326 const SMDS_MeshNode * n2) const
2328 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2331 bool GetNodes (const long theLinkID,
2332 const SMDS_MeshNode* & theNode1,
2333 const SMDS_MeshNode* & theNode2) const
2335 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2336 if ( !theNode1 ) return false;
2337 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2338 if ( !theNode2 ) return false;
2344 const SMESHDS_Mesh* myMesh;
2349 //=======================================================================
2350 //function : TriToQuad
2351 //purpose : Fuse neighbour triangles into quadrangles.
2352 // theCrit is used to select a neighbour to fuse with.
2353 // theMaxAngle is a max angle between element normals at which
2354 // fusion is still performed.
2355 //=======================================================================
2357 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2358 SMESH::Controls::NumericalFunctorPtr theCrit,
2359 const double theMaxAngle)
2361 myLastCreatedElems.Clear();
2362 myLastCreatedNodes.Clear();
2364 MESSAGE( "::TriToQuad()" );
2366 if ( !theCrit.get() )
2369 SMESHDS_Mesh * aMesh = GetMeshDS();
2371 // Prepare data for algo: build
2372 // 1. map of elements with their linkIDs
2373 // 2. map of linkIDs with their elements
2375 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2376 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2377 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2378 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2380 TIDSortedElemSet::iterator itElem;
2381 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2382 const SMDS_MeshElement* elem = *itElem;
2383 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2384 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2385 if(!IsTria) continue;
2387 // retrieve element nodes
2388 const SMDS_MeshNode* aNodes [4];
2389 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2392 aNodes[ i++ ] = cast2Node( itN->next() );
2393 aNodes[ 3 ] = aNodes[ 0 ];
2396 for ( i = 0; i < 3; i++ ) {
2397 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2398 // check if elements sharing a link can be fused
2399 itLE = mapLi_listEl.find( link );
2400 if ( itLE != mapLi_listEl.end() ) {
2401 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2403 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2404 //if ( FindShape( elem ) != FindShape( elem2 ))
2405 // continue; // do not fuse triangles laying on different shapes
2406 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2407 continue; // avoid making badly shaped quads
2408 (*itLE).second.push_back( elem );
2411 mapLi_listEl[ link ].push_back( elem );
2413 mapEl_setLi [ elem ].insert( link );
2416 // Clean the maps from the links shared by a sole element, ie
2417 // links to which only one element is bound in mapLi_listEl
2419 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2420 int nbElems = (*itLE).second.size();
2421 if ( nbElems < 2 ) {
2422 const SMDS_MeshElement* elem = (*itLE).second.front();
2423 SMESH_TLink link = (*itLE).first;
2424 mapEl_setLi[ elem ].erase( link );
2425 if ( mapEl_setLi[ elem ].empty() )
2426 mapEl_setLi.erase( elem );
2430 // Algo: fuse triangles into quadrangles
2432 while ( ! mapEl_setLi.empty() ) {
2433 // Look for the start element:
2434 // the element having the least nb of shared links
2435 const SMDS_MeshElement* startElem = 0;
2437 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2438 int nbLinks = (*itEL).second.size();
2439 if ( nbLinks < minNbLinks ) {
2440 startElem = (*itEL).first;
2441 minNbLinks = nbLinks;
2442 if ( minNbLinks == 1 )
2447 // search elements to fuse starting from startElem or links of elements
2448 // fused earlyer - startLinks
2449 list< SMESH_TLink > startLinks;
2450 while ( startElem || !startLinks.empty() ) {
2451 while ( !startElem && !startLinks.empty() ) {
2452 // Get an element to start, by a link
2453 SMESH_TLink linkId = startLinks.front();
2454 startLinks.pop_front();
2455 itLE = mapLi_listEl.find( linkId );
2456 if ( itLE != mapLi_listEl.end() ) {
2457 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2458 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2459 for ( ; itE != listElem.end() ; itE++ )
2460 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2462 mapLi_listEl.erase( itLE );
2467 // Get candidates to be fused
2468 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2469 const SMESH_TLink *link12, *link13;
2471 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2472 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2473 ASSERT( !setLi.empty() );
2474 set< SMESH_TLink >::iterator itLi;
2475 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2477 const SMESH_TLink & link = (*itLi);
2478 itLE = mapLi_listEl.find( link );
2479 if ( itLE == mapLi_listEl.end() )
2482 const SMDS_MeshElement* elem = (*itLE).second.front();
2484 elem = (*itLE).second.back();
2485 mapLi_listEl.erase( itLE );
2486 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2497 // add other links of elem to list of links to re-start from
2498 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2499 set< SMESH_TLink >::iterator it;
2500 for ( it = links.begin(); it != links.end(); it++ ) {
2501 const SMESH_TLink& link2 = (*it);
2502 if ( link2 != link )
2503 startLinks.push_back( link2 );
2507 // Get nodes of possible quadrangles
2508 const SMDS_MeshNode *n12 [4], *n13 [4];
2509 bool Ok12 = false, Ok13 = false;
2510 const SMDS_MeshNode *linkNode1, *linkNode2;
2512 linkNode1 = link12->first;
2513 linkNode2 = link12->second;
2514 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2518 linkNode1 = link13->first;
2519 linkNode2 = link13->second;
2520 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2524 // Choose a pair to fuse
2525 if ( Ok12 && Ok13 ) {
2526 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2527 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2528 double aBadRate12 = getBadRate( &quad12, theCrit );
2529 double aBadRate13 = getBadRate( &quad13, theCrit );
2530 if ( aBadRate13 < aBadRate12 )
2537 // and remove fused elems and removed links from the maps
2538 mapEl_setLi.erase( tr1 );
2540 mapEl_setLi.erase( tr2 );
2541 mapLi_listEl.erase( *link12 );
2542 if(tr1->NbNodes()==3) {
2543 const SMDS_MeshElement* newElem = 0;
2544 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2545 myLastCreatedElems.Append(newElem);
2546 AddToSameGroups( newElem, tr1, aMesh );
2547 int aShapeId = tr1->getshapeId();
2550 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2552 aMesh->RemoveElement( tr1 );
2553 aMesh->RemoveElement( tr2 );
2556 const SMDS_MeshNode* N1 [6];
2557 const SMDS_MeshNode* N2 [6];
2558 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2559 // now we receive following N1 and N2 (using numeration as above image)
2560 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2561 // i.e. first nodes from both arrays determ new diagonal
2562 const SMDS_MeshNode* aNodes[8];
2571 const SMDS_MeshElement* newElem = 0;
2572 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2573 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2574 myLastCreatedElems.Append(newElem);
2575 AddToSameGroups( newElem, tr1, aMesh );
2576 int aShapeId = tr1->getshapeId();
2579 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2581 aMesh->RemoveElement( tr1 );
2582 aMesh->RemoveElement( tr2 );
2583 // remove middle node (9)
2584 GetMeshDS()->RemoveNode( N1[4] );
2588 mapEl_setLi.erase( tr3 );
2589 mapLi_listEl.erase( *link13 );
2590 if(tr1->NbNodes()==3) {
2591 const SMDS_MeshElement* newElem = 0;
2592 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2593 myLastCreatedElems.Append(newElem);
2594 AddToSameGroups( newElem, tr1, aMesh );
2595 int aShapeId = tr1->getshapeId();
2598 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2600 aMesh->RemoveElement( tr1 );
2601 aMesh->RemoveElement( tr3 );
2604 const SMDS_MeshNode* N1 [6];
2605 const SMDS_MeshNode* N2 [6];
2606 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2607 // now we receive following N1 and N2 (using numeration as above image)
2608 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2609 // i.e. first nodes from both arrays determ new diagonal
2610 const SMDS_MeshNode* aNodes[8];
2619 const SMDS_MeshElement* newElem = 0;
2620 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2621 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2622 myLastCreatedElems.Append(newElem);
2623 AddToSameGroups( newElem, tr1, aMesh );
2624 int aShapeId = tr1->getshapeId();
2627 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2629 aMesh->RemoveElement( tr1 );
2630 aMesh->RemoveElement( tr3 );
2631 // remove middle node (9)
2632 GetMeshDS()->RemoveNode( N1[4] );
2636 // Next element to fuse: the rejected one
2638 startElem = Ok12 ? tr3 : tr2;
2640 } // if ( startElem )
2641 } // while ( startElem || !startLinks.empty() )
2642 } // while ( ! mapEl_setLi.empty() )
2648 /*#define DUMPSO(txt) \
2649 // cout << txt << endl;
2650 //=============================================================================
2654 //=============================================================================
2655 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2659 int tmp = idNodes[ i1 ];
2660 idNodes[ i1 ] = idNodes[ i2 ];
2661 idNodes[ i2 ] = tmp;
2662 gp_Pnt Ptmp = P[ i1 ];
2665 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2668 //=======================================================================
2669 //function : SortQuadNodes
2670 //purpose : Set 4 nodes of a quadrangle face in a good order.
2671 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2673 //=======================================================================
2675 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2680 for ( i = 0; i < 4; i++ ) {
2681 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2683 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2686 gp_Vec V1(P[0], P[1]);
2687 gp_Vec V2(P[0], P[2]);
2688 gp_Vec V3(P[0], P[3]);
2690 gp_Vec Cross1 = V1 ^ V2;
2691 gp_Vec Cross2 = V2 ^ V3;
2694 if (Cross1.Dot(Cross2) < 0)
2699 if (Cross1.Dot(Cross2) < 0)
2703 swap ( i, i + 1, idNodes, P );
2705 // for ( int ii = 0; ii < 4; ii++ ) {
2706 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2707 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2713 //=======================================================================
2714 //function : SortHexaNodes
2715 //purpose : Set 8 nodes of a hexahedron in a good order.
2716 // Return success status
2717 //=======================================================================
2719 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2724 DUMPSO( "INPUT: ========================================");
2725 for ( i = 0; i < 8; i++ ) {
2726 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2727 if ( !n ) return false;
2728 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2729 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2731 DUMPSO( "========================================");
2734 set<int> faceNodes; // ids of bottom face nodes, to be found
2735 set<int> checkedId1; // ids of tried 2-nd nodes
2736 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2737 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2738 int iMin, iLoop1 = 0;
2740 // Loop to try the 2-nd nodes
2742 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2744 // Find not checked 2-nd node
2745 for ( i = 1; i < 8; i++ )
2746 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2747 int id1 = idNodes[i];
2748 swap ( 1, i, idNodes, P );
2749 checkedId1.insert ( id1 );
2753 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2754 // ie that all but meybe one (id3 which is on the same face) nodes
2755 // lay on the same side from the triangle plane.
2757 bool manyInPlane = false; // more than 4 nodes lay in plane
2759 while ( ++iLoop2 < 6 ) {
2761 // get 1-2-3 plane coeffs
2762 Standard_Real A, B, C, D;
2763 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2764 if ( N.SquareMagnitude() > gp::Resolution() )
2766 gp_Pln pln ( P[0], N );
2767 pln.Coefficients( A, B, C, D );
2769 // find the node (iMin) closest to pln
2770 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2772 for ( i = 3; i < 8; i++ ) {
2773 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2774 if ( fabs( dist[i] ) < minDist ) {
2775 minDist = fabs( dist[i] );
2778 if ( fabs( dist[i] ) <= tol )
2779 idInPln.insert( idNodes[i] );
2782 // there should not be more than 4 nodes in bottom plane
2783 if ( idInPln.size() > 1 )
2785 DUMPSO( "### idInPln.size() = " << idInPln.size());
2786 // idInPlane does not contain the first 3 nodes
2787 if ( manyInPlane || idInPln.size() == 5)
2788 return false; // all nodes in one plane
2791 // set the 1-st node to be not in plane
2792 for ( i = 3; i < 8; i++ ) {
2793 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2794 DUMPSO( "### Reset 0-th node");
2795 swap( 0, i, idNodes, P );
2800 // reset to re-check second nodes
2801 leastDist = DBL_MAX;
2805 break; // from iLoop2;
2808 // check that the other 4 nodes are on the same side
2809 bool sameSide = true;
2810 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2811 for ( i = 3; sameSide && i < 8; i++ ) {
2813 sameSide = ( isNeg == dist[i] <= 0.);
2816 // keep best solution
2817 if ( sameSide && minDist < leastDist ) {
2818 leastDist = minDist;
2820 faceNodes.insert( idNodes[ 1 ] );
2821 faceNodes.insert( idNodes[ 2 ] );
2822 faceNodes.insert( idNodes[ iMin ] );
2823 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2824 << " leastDist = " << leastDist);
2825 if ( leastDist <= DBL_MIN )
2830 // set next 3-d node to check
2831 int iNext = 2 + iLoop2;
2833 DUMPSO( "Try 2-nd");
2834 swap ( 2, iNext, idNodes, P );
2836 } // while ( iLoop2 < 6 )
2839 if ( faceNodes.empty() ) return false;
2841 // Put the faceNodes in proper places
2842 for ( i = 4; i < 8; i++ ) {
2843 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2844 // find a place to put
2846 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2848 DUMPSO( "Set faceNodes");
2849 swap ( iTo, i, idNodes, P );
2854 // Set nodes of the found bottom face in good order
2855 DUMPSO( " Found bottom face: ");
2856 i = SortQuadNodes( theMesh, idNodes );
2858 gp_Pnt Ptmp = P[ i ];
2863 // for ( int ii = 0; ii < 4; ii++ ) {
2864 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2865 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2868 // Gravity center of the top and bottom faces
2869 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2870 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2872 // Get direction from the bottom to the top face
2873 gp_Vec upDir ( aGCb, aGCt );
2874 Standard_Real upDirSize = upDir.Magnitude();
2875 if ( upDirSize <= gp::Resolution() ) return false;
2878 // Assure that the bottom face normal points up
2879 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2880 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2881 if ( Nb.Dot( upDir ) < 0 ) {
2882 DUMPSO( "Reverse bottom face");
2883 swap( 1, 3, idNodes, P );
2886 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2887 Standard_Real minDist = DBL_MAX;
2888 for ( i = 4; i < 8; i++ ) {
2889 // projection of P[i] to the plane defined by P[0] and upDir
2890 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2891 Standard_Real sqDist = P[0].SquareDistance( Pp );
2892 if ( sqDist < minDist ) {
2897 DUMPSO( "Set 4-th");
2898 swap ( 4, iMin, idNodes, P );
2900 // Set nodes of the top face in good order
2901 DUMPSO( "Sort top face");
2902 i = SortQuadNodes( theMesh, &idNodes[4] );
2905 gp_Pnt Ptmp = P[ i ];
2910 // Assure that direction of the top face normal is from the bottom face
2911 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2912 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2913 if ( Nt.Dot( upDir ) < 0 ) {
2914 DUMPSO( "Reverse top face");
2915 swap( 5, 7, idNodes, P );
2918 // DUMPSO( "OUTPUT: ========================================");
2919 // for ( i = 0; i < 8; i++ ) {
2920 // float *p = ugrid->GetPoint(idNodes[i]);
2921 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2927 //================================================================================
2929 * \brief Return nodes linked to the given one
2930 * \param theNode - the node
2931 * \param linkedNodes - the found nodes
2932 * \param type - the type of elements to check
2934 * Medium nodes are ignored
2936 //================================================================================
2938 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2939 TIDSortedElemSet & linkedNodes,
2940 SMDSAbs_ElementType type )
2942 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2943 while ( elemIt->more() )
2945 const SMDS_MeshElement* elem = elemIt->next();
2946 if(elem->GetType() == SMDSAbs_0DElement)
2949 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2950 if ( elem->GetType() == SMDSAbs_Volume )
2952 SMDS_VolumeTool vol( elem );
2953 while ( nodeIt->more() ) {
2954 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2955 if ( theNode != n && vol.IsLinked( theNode, n ))
2956 linkedNodes.insert( n );
2961 for ( int i = 0; nodeIt->more(); ++i ) {
2962 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2963 if ( n == theNode ) {
2964 int iBefore = i - 1;
2966 if ( elem->IsQuadratic() ) {
2967 int nb = elem->NbNodes() / 2;
2968 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2969 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2971 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2972 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2979 //=======================================================================
2980 //function : laplacianSmooth
2981 //purpose : pulls theNode toward the center of surrounding nodes directly
2982 // connected to that node along an element edge
2983 //=======================================================================
2985 void laplacianSmooth(const SMDS_MeshNode* theNode,
2986 const Handle(Geom_Surface)& theSurface,
2987 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2989 // find surrounding nodes
2991 TIDSortedElemSet nodeSet;
2992 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2994 // compute new coodrs
2996 double coord[] = { 0., 0., 0. };
2997 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2998 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2999 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3000 if ( theSurface.IsNull() ) { // smooth in 3D
3001 coord[0] += node->X();
3002 coord[1] += node->Y();
3003 coord[2] += node->Z();
3005 else { // smooth in 2D
3006 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3007 gp_XY* uv = theUVMap[ node ];
3008 coord[0] += uv->X();
3009 coord[1] += uv->Y();
3012 int nbNodes = nodeSet.size();
3015 coord[0] /= nbNodes;
3016 coord[1] /= nbNodes;
3018 if ( !theSurface.IsNull() ) {
3019 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3020 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3021 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3027 coord[2] /= nbNodes;
3031 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3034 //=======================================================================
3035 //function : centroidalSmooth
3036 //purpose : pulls theNode toward the element-area-weighted centroid of the
3037 // surrounding elements
3038 //=======================================================================
3040 void centroidalSmooth(const SMDS_MeshNode* theNode,
3041 const Handle(Geom_Surface)& theSurface,
3042 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3044 gp_XYZ aNewXYZ(0.,0.,0.);
3045 SMESH::Controls::Area anAreaFunc;
3046 double totalArea = 0.;
3051 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3052 while ( elemIt->more() )
3054 const SMDS_MeshElement* elem = elemIt->next();
3057 gp_XYZ elemCenter(0.,0.,0.);
3058 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3059 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3060 int nn = elem->NbNodes();
3061 if(elem->IsQuadratic()) nn = nn/2;
3063 //while ( itN->more() ) {
3065 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3067 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3068 aNodePoints.push_back( aP );
3069 if ( !theSurface.IsNull() ) { // smooth in 2D
3070 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3071 gp_XY* uv = theUVMap[ aNode ];
3072 aP.SetCoord( uv->X(), uv->Y(), 0. );
3076 double elemArea = anAreaFunc.GetValue( aNodePoints );
3077 totalArea += elemArea;
3079 aNewXYZ += elemCenter * elemArea;
3081 aNewXYZ /= totalArea;
3082 if ( !theSurface.IsNull() ) {
3083 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3084 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3089 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3092 //=======================================================================
3093 //function : getClosestUV
3094 //purpose : return UV of closest projection
3095 //=======================================================================
3097 static bool getClosestUV (Extrema_GenExtPS& projector,
3098 const gp_Pnt& point,
3101 projector.Perform( point );
3102 if ( projector.IsDone() ) {
3103 double u, v, minVal = DBL_MAX;
3104 for ( int i = projector.NbExt(); i > 0; i-- )
3105 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3106 if ( projector.SquareDistance( i ) < minVal ) {
3107 minVal = projector.SquareDistance( i );
3109 if ( projector.Value( i ) < minVal ) {
3110 minVal = projector.Value( i );
3112 projector.Point( i ).Parameter( u, v );
3114 result.SetCoord( u, v );
3120 //=======================================================================
3122 //purpose : Smooth theElements during theNbIterations or until a worst
3123 // element has aspect ratio <= theTgtAspectRatio.
3124 // Aspect Ratio varies in range [1.0, inf].
3125 // If theElements is empty, the whole mesh is smoothed.
3126 // theFixedNodes contains additionally fixed nodes. Nodes built
3127 // on edges and boundary nodes are always fixed.
3128 //=======================================================================
3130 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3131 set<const SMDS_MeshNode*> & theFixedNodes,
3132 const SmoothMethod theSmoothMethod,
3133 const int theNbIterations,
3134 double theTgtAspectRatio,
3137 myLastCreatedElems.Clear();
3138 myLastCreatedNodes.Clear();
3140 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3142 if ( theTgtAspectRatio < 1.0 )
3143 theTgtAspectRatio = 1.0;
3145 const double disttol = 1.e-16;
3147 SMESH::Controls::AspectRatio aQualityFunc;
3149 SMESHDS_Mesh* aMesh = GetMeshDS();
3151 if ( theElems.empty() ) {
3152 // add all faces to theElems
3153 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3154 while ( fIt->more() ) {
3155 const SMDS_MeshElement* face = fIt->next();
3156 theElems.insert( theElems.end(), face );
3159 // get all face ids theElems are on
3160 set< int > faceIdSet;
3161 TIDSortedElemSet::iterator itElem;
3163 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3164 int fId = FindShape( *itElem );
3165 // check that corresponding submesh exists and a shape is face
3167 faceIdSet.find( fId ) == faceIdSet.end() &&
3168 aMesh->MeshElements( fId )) {
3169 TopoDS_Shape F = aMesh->IndexToShape( fId );
3170 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3171 faceIdSet.insert( fId );
3174 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3176 // ===============================================
3177 // smooth elements on each TopoDS_Face separately
3178 // ===============================================
3180 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3181 for ( ; fId != faceIdSet.rend(); ++fId ) {
3182 // get face surface and submesh
3183 Handle(Geom_Surface) surface;
3184 SMESHDS_SubMesh* faceSubMesh = 0;
3186 double fToler2 = 0, f,l;
3187 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3188 bool isUPeriodic = false, isVPeriodic = false;
3190 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3191 surface = BRep_Tool::Surface( face );
3192 faceSubMesh = aMesh->MeshElements( *fId );
3193 fToler2 = BRep_Tool::Tolerance( face );
3194 fToler2 *= fToler2 * 10.;
3195 isUPeriodic = surface->IsUPeriodic();
3198 isVPeriodic = surface->IsVPeriodic();
3201 surface->Bounds( u1, u2, v1, v2 );
3203 // ---------------------------------------------------------
3204 // for elements on a face, find movable and fixed nodes and
3205 // compute UV for them
3206 // ---------------------------------------------------------
3207 bool checkBoundaryNodes = false;
3208 bool isQuadratic = false;
3209 set<const SMDS_MeshNode*> setMovableNodes;
3210 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3211 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3212 list< const SMDS_MeshElement* > elemsOnFace;
3214 Extrema_GenExtPS projector;
3215 GeomAdaptor_Surface surfAdaptor;
3216 if ( !surface.IsNull() ) {
3217 surfAdaptor.Load( surface );
3218 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3220 int nbElemOnFace = 0;
3221 itElem = theElems.begin();
3222 // loop on not yet smoothed elements: look for elems on a face
3223 while ( itElem != theElems.end() ) {
3224 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3225 break; // all elements found
3227 const SMDS_MeshElement* elem = *itElem;
3228 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3229 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3233 elemsOnFace.push_back( elem );
3234 theElems.erase( itElem++ );
3238 isQuadratic = elem->IsQuadratic();
3240 // get movable nodes of elem
3241 const SMDS_MeshNode* node;
3242 SMDS_TypeOfPosition posType;
3243 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3244 int nn = 0, nbn = elem->NbNodes();
3245 if(elem->IsQuadratic())
3247 while ( nn++ < nbn ) {
3248 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3249 const SMDS_PositionPtr& pos = node->GetPosition();
3250 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3251 if (posType != SMDS_TOP_EDGE &&
3252 posType != SMDS_TOP_VERTEX &&
3253 theFixedNodes.find( node ) == theFixedNodes.end())
3255 // check if all faces around the node are on faceSubMesh
3256 // because a node on edge may be bound to face
3257 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3259 if ( faceSubMesh ) {
3260 while ( eIt->more() && all ) {
3261 const SMDS_MeshElement* e = eIt->next();
3262 all = faceSubMesh->Contains( e );
3266 setMovableNodes.insert( node );
3268 checkBoundaryNodes = true;
3270 if ( posType == SMDS_TOP_3DSPACE )
3271 checkBoundaryNodes = true;
3274 if ( surface.IsNull() )
3277 // get nodes to check UV
3278 list< const SMDS_MeshNode* > uvCheckNodes;
3279 itN = elem->nodesIterator();
3280 nn = 0; nbn = elem->NbNodes();
3281 if(elem->IsQuadratic())
3283 while ( nn++ < nbn ) {
3284 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3285 if ( uvMap.find( node ) == uvMap.end() )
3286 uvCheckNodes.push_back( node );
3287 // add nodes of elems sharing node
3288 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3289 // while ( eIt->more() ) {
3290 // const SMDS_MeshElement* e = eIt->next();
3291 // if ( e != elem ) {
3292 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3293 // while ( nIt->more() ) {
3294 // const SMDS_MeshNode* n =
3295 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3296 // if ( uvMap.find( n ) == uvMap.end() )
3297 // uvCheckNodes.push_back( n );
3303 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3304 for ( ; n != uvCheckNodes.end(); ++n ) {
3307 const SMDS_PositionPtr& pos = node->GetPosition();
3308 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3310 switch ( posType ) {
3311 case SMDS_TOP_FACE: {
3312 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3313 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3316 case SMDS_TOP_EDGE: {
3317 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3318 Handle(Geom2d_Curve) pcurve;
3319 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3320 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3321 if ( !pcurve.IsNull() ) {
3322 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3323 uv = pcurve->Value( u ).XY();
3327 case SMDS_TOP_VERTEX: {
3328 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3329 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3330 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3335 // check existing UV
3336 bool project = true;
3337 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3338 double dist1 = DBL_MAX, dist2 = 0;
3339 if ( posType != SMDS_TOP_3DSPACE ) {
3340 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3341 project = dist1 > fToler2;
3343 if ( project ) { // compute new UV
3345 if ( !getClosestUV( projector, pNode, newUV )) {
3346 MESSAGE("Node Projection Failed " << node);
3350 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3352 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3354 if ( posType != SMDS_TOP_3DSPACE )
3355 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3356 if ( dist2 < dist1 )
3360 // store UV in the map
3361 listUV.push_back( uv );
3362 uvMap.insert( make_pair( node, &listUV.back() ));
3364 } // loop on not yet smoothed elements
3366 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3367 checkBoundaryNodes = true;
3369 // fix nodes on mesh boundary
3371 if ( checkBoundaryNodes ) {
3372 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3373 map< SMESH_TLink, int >::iterator link_nb;
3374 // put all elements links to linkNbMap
3375 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3376 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3377 const SMDS_MeshElement* elem = (*elemIt);
3378 int nbn = elem->NbCornerNodes();
3379 // loop on elem links: insert them in linkNbMap
3380 for ( int iN = 0; iN < nbn; ++iN ) {
3381 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3382 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3383 SMESH_TLink link( n1, n2 );
3384 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3388 // remove nodes that are in links encountered only once from setMovableNodes
3389 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3390 if ( link_nb->second == 1 ) {
3391 setMovableNodes.erase( link_nb->first.node1() );
3392 setMovableNodes.erase( link_nb->first.node2() );
3397 // -----------------------------------------------------
3398 // for nodes on seam edge, compute one more UV ( uvMap2 );
3399 // find movable nodes linked to nodes on seam and which
3400 // are to be smoothed using the second UV ( uvMap2 )
3401 // -----------------------------------------------------
3403 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3404 if ( !surface.IsNull() ) {
3405 TopExp_Explorer eExp( face, TopAbs_EDGE );
3406 for ( ; eExp.More(); eExp.Next() ) {
3407 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3408 if ( !BRep_Tool::IsClosed( edge, face ))
3410 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3411 if ( !sm ) continue;
3412 // find out which parameter varies for a node on seam
3415 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3416 if ( pcurve.IsNull() ) continue;
3417 uv1 = pcurve->Value( f );
3419 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3420 if ( pcurve.IsNull() ) continue;
3421 uv2 = pcurve->Value( f );
3422 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3424 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3425 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3427 // get nodes on seam and its vertices
3428 list< const SMDS_MeshNode* > seamNodes;
3429 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3430 while ( nSeamIt->more() ) {
3431 const SMDS_MeshNode* node = nSeamIt->next();
3432 if ( !isQuadratic || !IsMedium( node ))
3433 seamNodes.push_back( node );
3435 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3436 for ( ; vExp.More(); vExp.Next() ) {
3437 sm = aMesh->MeshElements( vExp.Current() );
3439 nSeamIt = sm->GetNodes();
3440 while ( nSeamIt->more() )
3441 seamNodes.push_back( nSeamIt->next() );
3444 // loop on nodes on seam
3445 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3446 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3447 const SMDS_MeshNode* nSeam = *noSeIt;
3448 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3449 if ( n_uv == uvMap.end() )
3452 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3453 // set the second UV
3454 listUV.push_back( *n_uv->second );
3455 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3456 if ( uvMap2.empty() )
3457 uvMap2 = uvMap; // copy the uvMap contents
3458 uvMap2[ nSeam ] = &listUV.back();
3460 // collect movable nodes linked to ones on seam in nodesNearSeam
3461 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3462 while ( eIt->more() ) {
3463 const SMDS_MeshElement* e = eIt->next();
3464 int nbUseMap1 = 0, nbUseMap2 = 0;
3465 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3466 int nn = 0, nbn = e->NbNodes();
3467 if(e->IsQuadratic()) nbn = nbn/2;
3468 while ( nn++ < nbn )
3470 const SMDS_MeshNode* n =
3471 static_cast<const SMDS_MeshNode*>( nIt->next() );
3473 setMovableNodes.find( n ) == setMovableNodes.end() )
3475 // add only nodes being closer to uv2 than to uv1
3476 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3477 0.5 * ( n->Y() + nSeam->Y() ),
3478 0.5 * ( n->Z() + nSeam->Z() ));
3480 getClosestUV( projector, pMid, uv );
3481 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3482 nodesNearSeam.insert( n );
3488 // for centroidalSmooth all element nodes must
3489 // be on one side of a seam
3490 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3491 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3493 while ( nn++ < nbn ) {
3494 const SMDS_MeshNode* n =
3495 static_cast<const SMDS_MeshNode*>( nIt->next() );
3496 setMovableNodes.erase( n );
3500 } // loop on nodes on seam
3501 } // loop on edge of a face
3502 } // if ( !face.IsNull() )
3504 if ( setMovableNodes.empty() ) {
3505 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3506 continue; // goto next face
3514 double maxRatio = -1., maxDisplacement = -1.;
3515 set<const SMDS_MeshNode*>::iterator nodeToMove;
3516 for ( it = 0; it < theNbIterations; it++ ) {
3517 maxDisplacement = 0.;
3518 nodeToMove = setMovableNodes.begin();
3519 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3520 const SMDS_MeshNode* node = (*nodeToMove);
3521 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3524 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3525 if ( theSmoothMethod == LAPLACIAN )
3526 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3528 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3530 // node displacement
3531 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3532 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3533 if ( aDispl > maxDisplacement )
3534 maxDisplacement = aDispl;
3536 // no node movement => exit
3537 //if ( maxDisplacement < 1.e-16 ) {
3538 if ( maxDisplacement < disttol ) {
3539 MESSAGE("-- no node movement --");
3543 // check elements quality
3545 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3546 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3547 const SMDS_MeshElement* elem = (*elemIt);
3548 if ( !elem || elem->GetType() != SMDSAbs_Face )
3550 SMESH::Controls::TSequenceOfXYZ aPoints;
3551 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3552 double aValue = aQualityFunc.GetValue( aPoints );
3553 if ( aValue > maxRatio )
3557 if ( maxRatio <= theTgtAspectRatio ) {
3558 MESSAGE("-- quality achived --");
3561 if (it+1 == theNbIterations) {
3562 MESSAGE("-- Iteration limit exceeded --");
3564 } // smoothing iterations
3566 MESSAGE(" Face id: " << *fId <<
3567 " Nb iterstions: " << it <<
3568 " Displacement: " << maxDisplacement <<
3569 " Aspect Ratio " << maxRatio);
3571 // ---------------------------------------
3572 // new nodes positions are computed,
3573 // record movement in DS and set new UV
3574 // ---------------------------------------
3575 nodeToMove = setMovableNodes.begin();
3576 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3577 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3578 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3579 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3580 if ( node_uv != uvMap.end() ) {
3581 gp_XY* uv = node_uv->second;
3583 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3587 // move medium nodes of quadratic elements
3590 SMESH_MesherHelper helper( *GetMesh() );
3591 if ( !face.IsNull() )
3592 helper.SetSubShape( face );
3593 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3594 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3595 const SMDS_VtkFace* QF =
3596 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3597 if(QF && QF->IsQuadratic()) {
3598 vector<const SMDS_MeshNode*> Ns;
3599 Ns.reserve(QF->NbNodes()+1);
3600 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3601 while ( anIter->more() )
3602 Ns.push_back( cast2Node(anIter->next()) );
3603 Ns.push_back( Ns[0] );
3605 for(int i=0; i<QF->NbNodes(); i=i+2) {
3606 if ( !surface.IsNull() ) {
3607 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3608 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3609 gp_XY uv = ( uv1 + uv2 ) / 2.;
3610 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3611 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3614 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3615 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3616 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3618 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3619 fabs( Ns[i+1]->Y() - y ) > disttol ||
3620 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3621 // we have to move i+1 node
3622 aMesh->MoveNode( Ns[i+1], x, y, z );
3629 } // loop on face ids
3633 //=======================================================================
3634 //function : isReverse
3635 //purpose : Return true if normal of prevNodes is not co-directied with
3636 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3637 // iNotSame is where prevNodes and nextNodes are different.
3638 // If result is true then future volume orientation is OK
3639 //=======================================================================
3641 static bool isReverse(const SMDS_MeshElement* face,
3642 const vector<const SMDS_MeshNode*>& prevNodes,
3643 const vector<const SMDS_MeshNode*>& nextNodes,
3647 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3648 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3649 gp_XYZ extrDir( pN - pP ), faceNorm;
3650 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3652 return faceNorm * extrDir < 0.0;
3655 //=======================================================================
3657 * \brief Create elements by sweeping an element
3658 * \param elem - element to sweep
3659 * \param newNodesItVec - nodes generated from each node of the element
3660 * \param newElems - generated elements
3661 * \param nbSteps - number of sweeping steps
3662 * \param srcElements - to append elem for each generated element
3664 //=======================================================================
3666 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3667 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3668 list<const SMDS_MeshElement*>& newElems,
3670 SMESH_SequenceOfElemPtr& srcElements)
3672 //MESSAGE("sweepElement " << nbSteps);
3673 SMESHDS_Mesh* aMesh = GetMeshDS();
3675 const int nbNodes = elem->NbNodes();
3676 const int nbCorners = elem->NbCornerNodes();
3677 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3678 polyhedron creation !!! */
3679 // Loop on elem nodes:
3680 // find new nodes and detect same nodes indices
3681 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3682 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3683 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3684 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3686 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3687 vector<int> sames(nbNodes);
3688 vector<bool> isSingleNode(nbNodes);
3690 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3691 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3692 const SMDS_MeshNode* node = nnIt->first;
3693 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3694 if ( listNewNodes.empty() )
3697 itNN [ iNode ] = listNewNodes.begin();
3698 prevNod[ iNode ] = node;
3699 nextNod[ iNode ] = listNewNodes.front();
3701 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3702 corner node of linear */
3703 if ( prevNod[ iNode ] != nextNod [ iNode ])
3704 nbDouble += !isSingleNode[iNode];
3706 if( iNode < nbCorners ) { // check corners only
3707 if ( prevNod[ iNode ] == nextNod [ iNode ])
3708 sames[nbSame++] = iNode;
3710 iNotSameNode = iNode;
3714 if ( nbSame == nbNodes || nbSame > 2) {
3715 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3719 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3721 // fix nodes order to have bottom normal external
3722 if ( baseType == SMDSEntity_Polygon )
3724 std::reverse( itNN.begin(), itNN.end() );
3725 std::reverse( prevNod.begin(), prevNod.end() );
3726 std::reverse( midlNod.begin(), midlNod.end() );
3727 std::reverse( nextNod.begin(), nextNod.end() );
3728 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3732 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3733 SMDS_MeshCell::applyInterlace( ind, itNN );
3734 SMDS_MeshCell::applyInterlace( ind, prevNod );
3735 SMDS_MeshCell::applyInterlace( ind, nextNod );
3736 SMDS_MeshCell::applyInterlace( ind, midlNod );
3737 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3740 sames[nbSame] = iNotSameNode;
3741 for ( int j = 0; j <= nbSame; ++j )
3742 for ( size_t i = 0; i < ind.size(); ++i )
3743 if ( ind[i] == sames[j] )
3748 iNotSameNode = sames[nbSame];
3753 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3755 iSameNode = sames[ nbSame-1 ];
3756 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3757 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3758 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3761 // make new elements
3762 for (int iStep = 0; iStep < nbSteps; iStep++ )
3765 for ( iNode = 0; iNode < nbNodes; iNode++ )
3767 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3768 nextNod[ iNode ] = *itNN[ iNode ]++;
3771 SMDS_MeshElement* aNewElem = 0;
3772 /*if(!elem->IsPoly())*/ {
3773 switch ( baseType ) {
3775 case SMDSEntity_Node: { // sweep NODE
3776 if ( nbSame == 0 ) {
3777 if ( isSingleNode[0] )
3778 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3780 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3786 case SMDSEntity_Edge: { // sweep EDGE
3787 if ( nbDouble == 0 )
3789 if ( nbSame == 0 ) // ---> quadrangle
3790 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3791 nextNod[ 1 ], nextNod[ 0 ] );
3792 else // ---> triangle
3793 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3794 nextNod[ iNotSameNode ] );
3796 else // ---> polygon
3798 vector<const SMDS_MeshNode*> poly_nodes;
3799 poly_nodes.push_back( prevNod[0] );
3800 poly_nodes.push_back( prevNod[1] );
3801 if ( prevNod[1] != nextNod[1] )
3803 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3804 poly_nodes.push_back( nextNod[1] );
3806 if ( prevNod[0] != nextNod[0] )
3808 poly_nodes.push_back( nextNod[0] );
3809 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3811 switch ( poly_nodes.size() ) {
3813 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3816 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3817 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3820 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3825 case SMDSEntity_Triangle: // TRIANGLE --->
3827 if ( nbDouble > 0 ) break;
3828 if ( nbSame == 0 ) // ---> pentahedron
3829 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3830 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3832 else if ( nbSame == 1 ) // ---> pyramid
3833 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3834 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3835 nextNod[ iSameNode ]);
3837 else // 2 same nodes: ---> tetrahedron
3838 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3839 nextNod[ iNotSameNode ]);
3842 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3846 if ( nbDouble+nbSame == 2 )
3848 if(nbSame==0) { // ---> quadratic quadrangle
3849 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3850 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3852 else { //(nbSame==1) // ---> quadratic triangle
3854 return; // medium node on axis
3856 else if(sames[0]==0)
3857 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3858 nextNod[2], midlNod[1], prevNod[2]);
3860 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3861 midlNod[0], nextNod[2], prevNod[2]);
3864 else if ( nbDouble == 3 )
3866 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3867 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3868 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3875 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3876 if ( nbDouble > 0 ) break;
3878 if ( nbSame == 0 ) // ---> hexahedron
3879 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3880 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3882 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3883 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3884 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3885 nextNod[ iSameNode ]);
3886 newElems.push_back( aNewElem );
3887 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3888 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3889 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3891 else if ( nbSame == 2 ) { // ---> pentahedron
3892 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3893 // iBeforeSame is same too
3894 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3895 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3896 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3898 // iAfterSame is same too
3899 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3900 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3901 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3905 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3906 if ( nbDouble+nbSame != 3 ) break;
3908 // ---> pentahedron with 15 nodes
3909 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3910 nextNod[0], nextNod[1], nextNod[2],
3911 prevNod[3], prevNod[4], prevNod[5],
3912 nextNod[3], nextNod[4], nextNod[5],
3913 midlNod[0], midlNod[1], midlNod[2]);
3915 else if(nbSame==1) {
3916 // ---> 2d order pyramid of 13 nodes
3917 int apex = iSameNode;
3918 int i0 = ( apex + 1 ) % nbCorners;
3919 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3923 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3924 nextNod[i0], nextNod[i1], prevNod[apex],
3925 prevNod[i01], midlNod[i0],
3926 nextNod[i01], midlNod[i1],
3927 prevNod[i1a], prevNod[i0a],
3928 nextNod[i0a], nextNod[i1a]);
3930 else if(nbSame==2) {
3931 // ---> 2d order tetrahedron of 10 nodes
3932 int n1 = iNotSameNode;
3933 int n2 = ( n1 + 1 ) % nbCorners;
3934 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3938 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3939 prevNod[n12], prevNod[n23], prevNod[n31],
3940 midlNod[n1], nextNod[n12], nextNod[n31]);
3944 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3946 if ( nbDouble != 4 ) break;
3947 // ---> hexahedron with 20 nodes
3948 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3949 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3950 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3951 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3952 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3954 else if(nbSame==1) {
3955 // ---> pyramid + pentahedron - can not be created since it is needed
3956 // additional middle node at the center of face
3957 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3960 else if( nbSame == 2 ) {
3961 if ( nbDouble != 2 ) break;
3962 // ---> 2d order Pentahedron with 15 nodes
3964 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3965 // iBeforeSame is same too
3972 // iAfterSame is same too
3982 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3983 prevNod[n4], prevNod[n5], nextNod[n5],
3984 prevNod[n12], midlNod[n2], nextNod[n12],
3985 prevNod[n45], midlNod[n5], nextNod[n45],
3986 prevNod[n14], prevNod[n25], nextNod[n25]);
3990 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3992 if( nbSame == 0 && nbDouble == 9 ) {
3993 // ---> tri-quadratic hexahedron with 27 nodes
3994 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3995 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3996 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3997 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3998 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3999 prevNod[8], // bottom center
4000 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4001 nextNod[8], // top center
4002 midlNod[8]);// elem center
4010 case SMDSEntity_Polygon: { // sweep POLYGON
4012 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4013 // ---> hexagonal prism
4014 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4015 prevNod[3], prevNod[4], prevNod[5],
4016 nextNod[0], nextNod[1], nextNod[2],
4017 nextNod[3], nextNod[4], nextNod[5]);
4021 case SMDSEntity_Ball:
4029 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4031 if ( baseType != SMDSEntity_Polygon )
4033 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4034 SMDS_MeshCell::applyInterlace( ind, prevNod );
4035 SMDS_MeshCell::applyInterlace( ind, nextNod );
4036 SMDS_MeshCell::applyInterlace( ind, midlNod );
4037 SMDS_MeshCell::applyInterlace( ind, itNN );
4038 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4039 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4041 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4042 vector<int> quantities (nbNodes + 2);
4043 polyedre_nodes.clear();
4047 for (int inode = 0; inode < nbNodes; inode++)
4048 polyedre_nodes.push_back( prevNod[inode] );
4049 quantities.push_back( nbNodes );
4052 polyedre_nodes.push_back( nextNod[0] );
4053 for (int inode = nbNodes; inode-1; --inode )
4054 polyedre_nodes.push_back( nextNod[inode-1] );
4055 quantities.push_back( nbNodes );
4058 for (int iface = 0; iface < nbNodes; iface++)
4060 const int prevNbNodes = polyedre_nodes.size();
4061 int inextface = (iface+1) % nbNodes;
4062 polyedre_nodes.push_back( prevNod[inextface] );
4063 polyedre_nodes.push_back( prevNod[iface] );
4064 if ( prevNod[iface] != nextNod[iface] )
4066 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4067 polyedre_nodes.push_back( nextNod[iface] );
4069 if ( prevNod[inextface] != nextNod[inextface] )
4071 polyedre_nodes.push_back( nextNod[inextface] );
4072 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4074 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4075 if ( nbFaceNodes > 2 )
4076 quantities.push_back( nbFaceNodes );
4077 else // degenerated face
4078 polyedre_nodes.resize( prevNbNodes );
4080 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4084 newElems.push_back( aNewElem );
4085 myLastCreatedElems.Append(aNewElem);
4086 srcElements.Append( elem );
4089 // set new prev nodes
4090 for ( iNode = 0; iNode < nbNodes; iNode++ )
4091 prevNod[ iNode ] = nextNod[ iNode ];
4096 //=======================================================================
4098 * \brief Create 1D and 2D elements around swept elements
4099 * \param mapNewNodes - source nodes and ones generated from them
4100 * \param newElemsMap - source elements and ones generated from them
4101 * \param elemNewNodesMap - nodes generated from each node of each element
4102 * \param elemSet - all swept elements
4103 * \param nbSteps - number of sweeping steps
4104 * \param srcElements - to append elem for each generated element
4106 //=======================================================================
4108 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4109 TElemOfElemListMap & newElemsMap,
4110 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4111 TIDSortedElemSet& elemSet,
4113 SMESH_SequenceOfElemPtr& srcElements)
4115 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4116 SMESHDS_Mesh* aMesh = GetMeshDS();
4118 // Find nodes belonging to only one initial element - sweep them to get edges.
4120 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4121 for ( ; nList != mapNewNodes.end(); nList++ )
4123 const SMDS_MeshNode* node =
4124 static_cast<const SMDS_MeshNode*>( nList->first );
4125 if ( newElemsMap.count( node ))
4126 continue; // node was extruded into edge
4127 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4128 int nbInitElems = 0;
4129 const SMDS_MeshElement* el = 0;
4130 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4131 while ( eIt->more() && nbInitElems < 2 ) {
4133 SMDSAbs_ElementType type = el->GetType();
4134 if ( type == SMDSAbs_Volume || type < highType ) continue;
4135 if ( type > highType ) {
4139 nbInitElems += elemSet.count(el);
4141 if ( nbInitElems < 2 ) {
4142 bool NotCreateEdge = el && el->IsMediumNode(node);
4143 if(!NotCreateEdge) {
4144 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4145 list<const SMDS_MeshElement*> newEdges;
4146 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4151 // Make a ceiling for each element ie an equal element of last new nodes.
4152 // Find free links of faces - make edges and sweep them into faces.
4154 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4155 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4156 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4158 const SMDS_MeshElement* elem = itElem->first;
4159 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4161 if(itElem->second.size()==0) continue;
4163 const bool isQuadratic = elem->IsQuadratic();
4165 if ( elem->GetType() == SMDSAbs_Edge ) {
4166 // create a ceiling edge
4167 if ( !isQuadratic ) {
4168 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4169 vecNewNodes[ 1 ]->second.back())) {
4170 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4171 vecNewNodes[ 1 ]->second.back()));
4172 srcElements.Append( myLastCreatedElems.Last() );
4176 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4177 vecNewNodes[ 1 ]->second.back(),
4178 vecNewNodes[ 2 ]->second.back())) {
4179 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4180 vecNewNodes[ 1 ]->second.back(),
4181 vecNewNodes[ 2 ]->second.back()));
4182 srcElements.Append( myLastCreatedElems.Last() );
4186 if ( elem->GetType() != SMDSAbs_Face )
4189 bool hasFreeLinks = false;
4191 TIDSortedElemSet avoidSet;
4192 avoidSet.insert( elem );
4194 set<const SMDS_MeshNode*> aFaceLastNodes;
4195 int iNode, nbNodes = vecNewNodes.size();
4196 if ( !isQuadratic ) {
4197 // loop on the face nodes
4198 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4199 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4200 // look for free links of the face
4201 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4202 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4203 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4204 // check if a link is free
4205 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4206 hasFreeLinks = true;
4207 // make an edge and a ceiling for a new edge
4208 if ( !aMesh->FindEdge( n1, n2 )) {
4209 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4210 srcElements.Append( myLastCreatedElems.Last() );
4212 n1 = vecNewNodes[ iNode ]->second.back();
4213 n2 = vecNewNodes[ iNext ]->second.back();
4214 if ( !aMesh->FindEdge( n1, n2 )) {
4215 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4216 srcElements.Append( myLastCreatedElems.Last() );
4221 else { // elem is quadratic face
4222 int nbn = nbNodes/2;
4223 for ( iNode = 0; iNode < nbn; iNode++ ) {
4224 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4225 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4226 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4227 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4228 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4229 // check if a link is free
4230 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4231 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4232 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4233 hasFreeLinks = true;
4234 // make an edge and a ceiling for a new edge
4236 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4237 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4238 srcElements.Append( myLastCreatedElems.Last() );
4240 n1 = vecNewNodes[ iNode ]->second.back();
4241 n2 = vecNewNodes[ iNext ]->second.back();
4242 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4243 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4244 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4245 srcElements.Append( myLastCreatedElems.Last() );
4249 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4250 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4254 // sweep free links into faces
4256 if ( hasFreeLinks ) {
4257 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4258 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4260 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4261 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4262 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4263 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4265 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4266 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4267 std::advance( v, volNb );
4268 // find indices of free faces of a volume and their source edges
4269 list< int > freeInd;
4270 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4271 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4272 int iF, nbF = vTool.NbFaces();
4273 for ( iF = 0; iF < nbF; iF ++ ) {
4274 if (vTool.IsFreeFace( iF ) &&
4275 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4276 initNodeSet != faceNodeSet) // except an initial face
4278 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4280 freeInd.push_back( iF );
4281 // find source edge of a free face iF
4282 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4283 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4284 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4285 initNodeSet.begin(), initNodeSet.end(),
4286 commonNodes.begin());
4287 if ( (*v)->IsQuadratic() )
4288 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4290 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4292 if ( !srcEdges.back() )
4294 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4295 << iF << " of volume #" << vTool.ID() << endl;
4300 if ( freeInd.empty() )
4303 // create faces for all steps;
4304 // if such a face has been already created by sweep of edge,
4305 // assure that its orientation is OK
4306 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4307 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4308 vTool.SetExternalNormal();
4309 const int nextShift = vTool.IsForward() ? +1 : -1;
4310 list< int >::iterator ind = freeInd.begin();
4311 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4312 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4314 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4315 int nbn = vTool.NbFaceNodes( *ind );
4316 const SMDS_MeshElement * f = 0;
4317 if ( nbn == 3 ) ///// triangle
4319 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4321 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4323 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4325 nodes[ 1 + nextShift ] };
4327 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4329 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4333 else if ( nbn == 4 ) ///// quadrangle
4335 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4337 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4339 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4340 nodes[ 2 ], nodes[ 2+nextShift ] };
4342 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4344 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4345 newOrder[ 2 ], newOrder[ 3 ]));
4348 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4350 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4352 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4354 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4356 nodes[2 + 2*nextShift],
4357 nodes[3 - 2*nextShift],
4359 nodes[3 + 2*nextShift]};
4361 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4363 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4371 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4373 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4374 nodes[1], nodes[3], nodes[5], nodes[7] );
4376 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4378 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4379 nodes[4 - 2*nextShift],
4381 nodes[4 + 2*nextShift],
4383 nodes[5 - 2*nextShift],
4385 nodes[5 + 2*nextShift] };
4387 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4389 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4390 newOrder[ 2 ], newOrder[ 3 ],
4391 newOrder[ 4 ], newOrder[ 5 ],
4392 newOrder[ 6 ], newOrder[ 7 ]));
4395 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4397 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4398 SMDSAbs_Face, /*noMedium=*/false);
4400 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4402 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4403 nodes[4 - 2*nextShift],
4405 nodes[4 + 2*nextShift],
4407 nodes[5 - 2*nextShift],
4409 nodes[5 + 2*nextShift],
4412 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4414 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4415 newOrder[ 2 ], newOrder[ 3 ],
4416 newOrder[ 4 ], newOrder[ 5 ],
4417 newOrder[ 6 ], newOrder[ 7 ],
4421 else //////// polygon
4423 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4424 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4426 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4428 if ( !vTool.IsForward() )
4429 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4431 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4433 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4437 while ( srcElements.Length() < myLastCreatedElems.Length() )
4438 srcElements.Append( *srcEdge );
4440 } // loop on free faces
4442 // go to the next volume
4444 while ( iVol++ < nbVolumesByStep ) v++;
4447 } // loop on volumes of one step
4448 } // sweep free links into faces
4450 // Make a ceiling face with a normal external to a volume
4452 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4454 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4456 lastVol.SetExternalNormal();
4457 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4458 int nbn = lastVol.NbFaceNodes( iF );
4460 if (!hasFreeLinks ||
4461 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4462 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4464 else if ( nbn == 4 )
4466 if (!hasFreeLinks ||
4467 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4468 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4470 else if ( nbn == 6 && isQuadratic )
4472 if (!hasFreeLinks ||
4473 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4474 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4475 nodes[1], nodes[3], nodes[5]));
4477 else if ( nbn == 8 && isQuadratic )
4479 if (!hasFreeLinks ||
4480 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4481 nodes[1], nodes[3], nodes[5], nodes[7]) )
4482 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4483 nodes[1], nodes[3], nodes[5], nodes[7]));
4485 else if ( nbn == 9 && isQuadratic )
4487 if (!hasFreeLinks ||
4488 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4489 SMDSAbs_Face, /*noMedium=*/false) )
4490 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4491 nodes[1], nodes[3], nodes[5], nodes[7],
4495 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4496 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4497 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4500 while ( srcElements.Length() < myLastCreatedElems.Length() )
4501 srcElements.Append( myLastCreatedElems.Last() );
4503 } // loop on swept elements
4506 //=======================================================================
4507 //function : RotationSweep
4509 //=======================================================================
4511 SMESH_MeshEditor::PGroupIDs
4512 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4513 const gp_Ax1& theAxis,
4514 const double theAngle,
4515 const int theNbSteps,
4516 const double theTol,
4517 const bool theMakeGroups,
4518 const bool theMakeWalls)
4520 myLastCreatedElems.Clear();
4521 myLastCreatedNodes.Clear();
4523 // source elements for each generated one
4524 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4526 MESSAGE( "RotationSweep()");
4528 aTrsf.SetRotation( theAxis, theAngle );
4530 aTrsf2.SetRotation( theAxis, theAngle/2. );
4532 gp_Lin aLine( theAxis );
4533 double aSqTol = theTol * theTol;
4535 SMESHDS_Mesh* aMesh = GetMeshDS();
4537 TNodeOfNodeListMap mapNewNodes;
4538 TElemOfVecOfNnlmiMap mapElemNewNodes;
4539 TElemOfElemListMap newElemsMap;
4541 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4542 myMesh->NbFaces(ORDER_QUADRATIC) +
4543 myMesh->NbVolumes(ORDER_QUADRATIC) );
4545 TIDSortedElemSet::iterator itElem;
4546 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4547 const SMDS_MeshElement* elem = *itElem;
4548 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4550 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4551 newNodesItVec.reserve( elem->NbNodes() );
4553 // loop on elem nodes
4554 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4555 while ( itN->more() )
4557 // check if a node has been already sweeped
4558 const SMDS_MeshNode* node = cast2Node( itN->next() );
4560 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4562 aXYZ.Coord( coord[0], coord[1], coord[2] );
4563 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4565 TNodeOfNodeListMapItr nIt =
4566 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4567 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4568 if ( listNewNodes.empty() )
4570 // check if we are to create medium nodes between corner ones
4571 bool needMediumNodes = false;
4572 if ( isQuadraticMesh )
4574 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4575 while (it->more() && !needMediumNodes )
4577 const SMDS_MeshElement* invElem = it->next();
4578 if ( invElem != elem && !theElems.count( invElem )) continue;
4579 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4580 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4581 needMediumNodes = true;
4586 const SMDS_MeshNode * newNode = node;
4587 for ( int i = 0; i < theNbSteps; i++ ) {
4589 if ( needMediumNodes ) // create a medium node
4591 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4592 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4593 myLastCreatedNodes.Append(newNode);
4594 srcNodes.Append( node );
4595 listNewNodes.push_back( newNode );
4596 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4599 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4601 // create a corner node
4602 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4603 myLastCreatedNodes.Append(newNode);
4604 srcNodes.Append( node );
4605 listNewNodes.push_back( newNode );
4608 listNewNodes.push_back( newNode );
4609 // if ( needMediumNodes )
4610 // listNewNodes.push_back( newNode );
4614 newNodesItVec.push_back( nIt );
4616 // make new elements
4617 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4621 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4623 PGroupIDs newGroupIDs;
4624 if ( theMakeGroups )
4625 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4631 //=======================================================================
4632 //function : CreateNode
4634 //=======================================================================
4635 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4638 const double tolnode,
4639 SMESH_SequenceOfNode& aNodes)
4641 // myLastCreatedElems.Clear();
4642 // myLastCreatedNodes.Clear();
4645 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4647 // try to search in sequence of existing nodes
4648 // if aNodes.Length()>0 we 'nave to use given sequence
4649 // else - use all nodes of mesh
4650 if(aNodes.Length()>0) {
4652 for(i=1; i<=aNodes.Length(); i++) {
4653 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4654 if(P1.Distance(P2)<tolnode)
4655 return aNodes.Value(i);
4659 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4660 while(itn->more()) {
4661 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4662 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4663 if(P1.Distance(P2)<tolnode)
4668 // create new node and return it
4669 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4670 //myLastCreatedNodes.Append(NewNode);
4675 //=======================================================================
4676 //function : ExtrusionSweep
4678 //=======================================================================
4680 SMESH_MeshEditor::PGroupIDs
4681 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4682 const gp_Vec& theStep,
4683 const int theNbSteps,
4684 TElemOfElemListMap& newElemsMap,
4685 const bool theMakeGroups,
4687 const double theTolerance)
4689 ExtrusParam aParams;
4690 aParams.myDir = gp_Dir(theStep);
4691 aParams.myNodes.Clear();
4692 aParams.mySteps = new TColStd_HSequenceOfReal;
4694 for(i=1; i<=theNbSteps; i++)
4695 aParams.mySteps->Append(theStep.Magnitude());
4698 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4702 //=======================================================================
4703 //function : ExtrusionSweep
4705 //=======================================================================
4707 SMESH_MeshEditor::PGroupIDs
4708 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4709 ExtrusParam& theParams,
4710 TElemOfElemListMap& newElemsMap,
4711 const bool theMakeGroups,
4713 const double theTolerance)
4715 myLastCreatedElems.Clear();
4716 myLastCreatedNodes.Clear();
4718 // source elements for each generated one
4719 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4721 SMESHDS_Mesh* aMesh = GetMeshDS();
4723 int nbsteps = theParams.mySteps->Length();
4725 TNodeOfNodeListMap mapNewNodes;
4726 //TNodeOfNodeVecMap mapNewNodes;
4727 TElemOfVecOfNnlmiMap mapElemNewNodes;
4728 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4730 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4731 myMesh->NbFaces(ORDER_QUADRATIC) +
4732 myMesh->NbVolumes(ORDER_QUADRATIC) );
4734 TIDSortedElemSet::iterator itElem;
4735 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4736 // check element type
4737 const SMDS_MeshElement* elem = *itElem;
4738 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4741 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4742 newNodesItVec.reserve( elem->NbNodes() );
4744 // loop on elem nodes
4745 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4746 while ( itN->more() )
4748 // check if a node has been already sweeped
4749 const SMDS_MeshNode* node = cast2Node( itN->next() );
4750 TNodeOfNodeListMap::iterator nIt =
4751 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4752 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4753 if ( listNewNodes.empty() )
4757 // check if we are to create medium nodes between corner ones
4758 bool needMediumNodes = false;
4759 if ( isQuadraticMesh )
4761 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4762 while (it->more() && !needMediumNodes )
4764 const SMDS_MeshElement* invElem = it->next();
4765 if ( invElem != elem && !theElems.count( invElem )) continue;
4766 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4767 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4768 needMediumNodes = true;
4772 double coord[] = { node->X(), node->Y(), node->Z() };
4773 for ( int i = 0; i < nbsteps; i++ )
4775 if ( needMediumNodes ) // create a medium node
4777 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4778 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4779 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4780 if( theFlags & EXTRUSION_FLAG_SEW ) {
4781 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4782 theTolerance, theParams.myNodes);
4783 listNewNodes.push_back( newNode );
4786 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4787 myLastCreatedNodes.Append(newNode);
4788 srcNodes.Append( node );
4789 listNewNodes.push_back( newNode );
4792 // create a corner node
4793 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4794 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4795 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4796 if( theFlags & EXTRUSION_FLAG_SEW ) {
4797 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4798 theTolerance, theParams.myNodes);
4799 listNewNodes.push_back( newNode );
4802 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4803 myLastCreatedNodes.Append(newNode);
4804 srcNodes.Append( node );
4805 listNewNodes.push_back( newNode );
4809 newNodesItVec.push_back( nIt );
4811 // make new elements
4812 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4815 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4816 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4818 PGroupIDs newGroupIDs;
4819 if ( theMakeGroups )
4820 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4825 //=======================================================================
4826 //function : ExtrusionAlongTrack
4828 //=======================================================================
4829 SMESH_MeshEditor::Extrusion_Error
4830 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4831 SMESH_subMesh* theTrack,
4832 const SMDS_MeshNode* theN1,
4833 const bool theHasAngles,
4834 list<double>& theAngles,
4835 const bool theLinearVariation,
4836 const bool theHasRefPoint,
4837 const gp_Pnt& theRefPoint,
4838 const bool theMakeGroups)
4840 MESSAGE("ExtrusionAlongTrack");
4841 myLastCreatedElems.Clear();
4842 myLastCreatedNodes.Clear();
4845 std::list<double> aPrms;
4846 TIDSortedElemSet::iterator itElem;
4849 TopoDS_Edge aTrackEdge;
4850 TopoDS_Vertex aV1, aV2;
4852 SMDS_ElemIteratorPtr aItE;
4853 SMDS_NodeIteratorPtr aItN;
4854 SMDSAbs_ElementType aTypeE;
4856 TNodeOfNodeListMap mapNewNodes;
4859 aNbE = theElements.size();
4862 return EXTR_NO_ELEMENTS;
4864 // 1.1 Track Pattern
4867 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4869 aItE = pSubMeshDS->GetElements();
4870 while ( aItE->more() ) {
4871 const SMDS_MeshElement* pE = aItE->next();
4872 aTypeE = pE->GetType();
4873 // Pattern must contain links only
4874 if ( aTypeE != SMDSAbs_Edge )
4875 return EXTR_PATH_NOT_EDGE;
4878 list<SMESH_MeshEditor_PathPoint> fullList;
4880 const TopoDS_Shape& aS = theTrack->GetSubShape();
4881 // Sub-shape for the Pattern must be an Edge or Wire
4882 if( aS.ShapeType() == TopAbs_EDGE ) {
4883 aTrackEdge = TopoDS::Edge( aS );
4884 // the Edge must not be degenerated
4885 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4886 return EXTR_BAD_PATH_SHAPE;
4887 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4888 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4889 const SMDS_MeshNode* aN1 = aItN->next();
4890 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4891 const SMDS_MeshNode* aN2 = aItN->next();
4892 // starting node must be aN1 or aN2
4893 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4894 return EXTR_BAD_STARTING_NODE;
4895 aItN = pSubMeshDS->GetNodes();
4896 while ( aItN->more() ) {
4897 const SMDS_MeshNode* pNode = aItN->next();
4898 const SMDS_EdgePosition* pEPos =
4899 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4900 double aT = pEPos->GetUParameter();
4901 aPrms.push_back( aT );
4903 //Extrusion_Error err =
4904 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4905 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4906 list< SMESH_subMesh* > LSM;
4907 TopTools_SequenceOfShape Edges;
4908 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4909 while(itSM->more()) {
4910 SMESH_subMesh* SM = itSM->next();
4912 const TopoDS_Shape& aS = SM->GetSubShape();
4915 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4916 int startNid = theN1->GetID();
4917 TColStd_MapOfInteger UsedNums;
4919 int NbEdges = Edges.Length();
4921 for(; i<=NbEdges; i++) {
4923 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4924 for(; itLSM!=LSM.end(); itLSM++) {
4926 if(UsedNums.Contains(k)) continue;
4927 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4928 SMESH_subMesh* locTrack = *itLSM;
4929 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4930 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4931 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4932 const SMDS_MeshNode* aN1 = aItN->next();
4933 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4934 const SMDS_MeshNode* aN2 = aItN->next();
4935 // starting node must be aN1 or aN2
4936 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4937 // 2. Collect parameters on the track edge
4939 aItN = locMeshDS->GetNodes();
4940 while ( aItN->more() ) {
4941 const SMDS_MeshNode* pNode = aItN->next();
4942 const SMDS_EdgePosition* pEPos =
4943 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4944 double aT = pEPos->GetUParameter();
4945 aPrms.push_back( aT );
4947 list<SMESH_MeshEditor_PathPoint> LPP;
4948 //Extrusion_Error err =
4949 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4950 LLPPs.push_back(LPP);
4952 // update startN for search following egde
4953 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4954 else startNid = aN1->GetID();
4958 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4959 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4960 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4961 for(; itPP!=firstList.end(); itPP++) {
4962 fullList.push_back( *itPP );
4964 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4965 fullList.pop_back();
4967 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4968 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4969 itPP = currList.begin();
4970 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4971 gp_Dir D1 = PP1.Tangent();
4972 gp_Dir D2 = PP2.Tangent();
4973 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4974 (D1.Z()+D2.Z())/2 ) );
4975 PP1.SetTangent(Dnew);
4976 fullList.push_back(PP1);
4978 for(; itPP!=firstList.end(); itPP++) {
4979 fullList.push_back( *itPP );
4981 PP1 = fullList.back();
4982 fullList.pop_back();
4984 // if wire not closed
4985 fullList.push_back(PP1);
4989 return EXTR_BAD_PATH_SHAPE;
4992 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4993 theHasRefPoint, theRefPoint, theMakeGroups);
4997 //=======================================================================
4998 //function : ExtrusionAlongTrack
5000 //=======================================================================
5001 SMESH_MeshEditor::Extrusion_Error
5002 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5003 SMESH_Mesh* theTrack,
5004 const SMDS_MeshNode* theN1,
5005 const bool theHasAngles,
5006 list<double>& theAngles,
5007 const bool theLinearVariation,
5008 const bool theHasRefPoint,
5009 const gp_Pnt& theRefPoint,
5010 const bool theMakeGroups)
5012 myLastCreatedElems.Clear();
5013 myLastCreatedNodes.Clear();
5016 std::list<double> aPrms;
5017 TIDSortedElemSet::iterator itElem;
5020 TopoDS_Edge aTrackEdge;
5021 TopoDS_Vertex aV1, aV2;
5023 SMDS_ElemIteratorPtr aItE;
5024 SMDS_NodeIteratorPtr aItN;
5025 SMDSAbs_ElementType aTypeE;
5027 TNodeOfNodeListMap mapNewNodes;
5030 aNbE = theElements.size();
5033 return EXTR_NO_ELEMENTS;
5035 // 1.1 Track Pattern
5038 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5040 aItE = pMeshDS->elementsIterator();
5041 while ( aItE->more() ) {
5042 const SMDS_MeshElement* pE = aItE->next();
5043 aTypeE = pE->GetType();
5044 // Pattern must contain links only
5045 if ( aTypeE != SMDSAbs_Edge )
5046 return EXTR_PATH_NOT_EDGE;
5049 list<SMESH_MeshEditor_PathPoint> fullList;
5051 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5053 if( aS == SMESH_Mesh::PseudoShape() ) {
5054 //Mesh without shape
5055 const SMDS_MeshNode* currentNode = NULL;
5056 const SMDS_MeshNode* prevNode = theN1;
5057 std::vector<const SMDS_MeshNode*> aNodesList;
5058 aNodesList.push_back(theN1);
5059 int nbEdges = 0, conn=0;
5060 const SMDS_MeshElement* prevElem = NULL;
5061 const SMDS_MeshElement* currentElem = NULL;
5062 int totalNbEdges = theTrack->NbEdges();
5063 SMDS_ElemIteratorPtr nIt;
5066 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5067 return EXTR_BAD_STARTING_NODE;
5070 conn = nbEdgeConnectivity(theN1);
5072 return EXTR_PATH_NOT_EDGE;
5074 aItE = theN1->GetInverseElementIterator();
5075 prevElem = aItE->next();
5076 currentElem = prevElem;
5078 if(totalNbEdges == 1 ) {
5079 nIt = currentElem->nodesIterator();
5080 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5081 if(currentNode == prevNode)
5082 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5083 aNodesList.push_back(currentNode);
5085 nIt = currentElem->nodesIterator();
5086 while( nIt->more() ) {
5087 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5088 if(currentNode == prevNode)
5089 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5090 aNodesList.push_back(currentNode);
5092 //case of the closed mesh
5093 if(currentNode == theN1) {
5098 conn = nbEdgeConnectivity(currentNode);
5100 return EXTR_PATH_NOT_EDGE;
5101 }else if( conn == 1 && nbEdges > 0 ) {
5106 prevNode = currentNode;
5107 aItE = currentNode->GetInverseElementIterator();
5108 currentElem = aItE->next();
5109 if( currentElem == prevElem)
5110 currentElem = aItE->next();
5111 nIt = currentElem->nodesIterator();
5112 prevElem = currentElem;
5118 if(nbEdges != totalNbEdges)
5119 return EXTR_PATH_NOT_EDGE;
5121 TopTools_SequenceOfShape Edges;
5122 double x1,x2,y1,y2,z1,z2;
5123 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5124 int startNid = theN1->GetID();
5125 for(int i = 1; i < aNodesList.size(); i++) {
5126 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5127 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5128 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5129 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5130 list<SMESH_MeshEditor_PathPoint> LPP;
5132 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5133 LLPPs.push_back(LPP);
5134 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5135 else startNid = aNodesList[i-1]->GetID();
5139 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5140 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5141 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5142 for(; itPP!=firstList.end(); itPP++) {
5143 fullList.push_back( *itPP );
5146 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5147 SMESH_MeshEditor_PathPoint PP2;
5148 fullList.pop_back();
5150 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5151 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5152 itPP = currList.begin();
5153 PP2 = currList.front();
5154 gp_Dir D1 = PP1.Tangent();
5155 gp_Dir D2 = PP2.Tangent();
5156 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5157 (D1.Z()+D2.Z())/2 ) );
5158 PP1.SetTangent(Dnew);
5159 fullList.push_back(PP1);
5161 for(; itPP!=currList.end(); itPP++) {
5162 fullList.push_back( *itPP );
5164 PP1 = fullList.back();
5165 fullList.pop_back();
5167 fullList.push_back(PP1);
5169 } // Sub-shape for the Pattern must be an Edge or Wire
5170 else if( aS.ShapeType() == TopAbs_EDGE ) {
5171 aTrackEdge = TopoDS::Edge( aS );
5172 // the Edge must not be degenerated
5173 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5174 return EXTR_BAD_PATH_SHAPE;
5175 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5176 const SMDS_MeshNode* aN1 = 0;
5177 const SMDS_MeshNode* aN2 = 0;
5178 if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5179 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5182 if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5183 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5186 // starting node must be aN1 or aN2
5187 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5188 return EXTR_BAD_STARTING_NODE;
5189 aItN = pMeshDS->nodesIterator();
5190 while ( aItN->more() ) {
5191 const SMDS_MeshNode* pNode = aItN->next();
5192 if( pNode==aN1 || pNode==aN2 ) continue;
5193 const SMDS_EdgePosition* pEPos =
5194 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5195 double aT = pEPos->GetUParameter();
5196 aPrms.push_back( aT );
5198 //Extrusion_Error err =
5199 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5201 else if( aS.ShapeType() == TopAbs_WIRE ) {
5202 list< SMESH_subMesh* > LSM;
5203 TopTools_SequenceOfShape Edges;
5204 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5205 for(; eExp.More(); eExp.Next()) {
5206 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5207 if( BRep_Tool::Degenerated(E) ) continue;
5208 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5214 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5215 TopoDS_Vertex aVprev;
5216 TColStd_MapOfInteger UsedNums;
5217 int NbEdges = Edges.Length();
5219 for(; i<=NbEdges; i++) {
5221 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5222 for(; itLSM!=LSM.end(); itLSM++) {
5224 if(UsedNums.Contains(k)) continue;
5225 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5226 SMESH_subMesh* locTrack = *itLSM;
5227 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5228 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5229 bool aN1isOK = false, aN2isOK = false;
5230 if ( aVprev.IsNull() ) {
5231 // if previous vertex is not yet defined, it means that we in the beginning of wire
5232 // and we have to find initial vertex corresponding to starting node theN1
5233 const SMDS_MeshNode* aN1 = 0;
5234 const SMDS_MeshNode* aN2 = 0;
5236 if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5237 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5240 if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5241 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5244 // starting node must be aN1 or aN2
5245 aN1isOK = ( aN1 && aN1 == theN1 );
5246 aN2isOK = ( aN2 && aN2 == theN1 );
5249 // we have specified ending vertex of the previous edge on the previous iteration
5250 // and we have just to check that it corresponds to any vertex in current segment
5251 aN1isOK = aVprev.IsSame( aV1 );
5252 aN2isOK = aVprev.IsSame( aV2 );
5254 if ( !aN1isOK && !aN2isOK ) continue;
5255 // 2. Collect parameters on the track edge
5257 aItN = locMeshDS->GetNodes();
5258 while ( aItN->more() ) {
5259 const SMDS_MeshNode* pNode = aItN->next();
5260 const SMDS_EdgePosition* pEPos =
5261 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5262 double aT = pEPos->GetUParameter();
5263 aPrms.push_back( aT );
5265 list<SMESH_MeshEditor_PathPoint> LPP;
5266 //Extrusion_Error err =
5267 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5268 LLPPs.push_back(LPP);
5270 // update startN for search following egde
5271 if ( aN1isOK ) aVprev = aV2;
5276 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5277 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5278 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5279 for(; itPP!=firstList.end(); itPP++) {
5280 fullList.push_back( *itPP );
5282 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5283 fullList.pop_back();
5285 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5286 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5287 itPP = currList.begin();
5288 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5289 gp_Dir D1 = PP1.Tangent();
5290 gp_Dir D2 = PP2.Tangent();
5291 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5292 PP1.SetTangent(Dnew);
5293 fullList.push_back(PP1);
5295 for(; itPP!=currList.end(); itPP++) {
5296 fullList.push_back( *itPP );
5298 PP1 = fullList.back();
5299 fullList.pop_back();
5301 // if wire not closed
5302 fullList.push_back(PP1);
5306 return EXTR_BAD_PATH_SHAPE;
5309 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5310 theHasRefPoint, theRefPoint, theMakeGroups);
5314 //=======================================================================
5315 //function : MakeEdgePathPoints
5316 //purpose : auxilary for ExtrusionAlongTrack
5317 //=======================================================================
5318 SMESH_MeshEditor::Extrusion_Error
5319 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5320 const TopoDS_Edge& aTrackEdge,
5322 list<SMESH_MeshEditor_PathPoint>& LPP)
5324 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5326 aTolVec2=aTolVec*aTolVec;
5328 TopoDS_Vertex aV1, aV2;
5329 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5330 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5331 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5332 // 2. Collect parameters on the track edge
5333 aPrms.push_front( aT1 );
5334 aPrms.push_back( aT2 );
5337 if( FirstIsStart ) {
5348 SMESH_MeshEditor_PathPoint aPP;
5349 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5350 std::list<double>::iterator aItD = aPrms.begin();
5351 for(; aItD != aPrms.end(); ++aItD) {
5355 aC3D->D1( aT, aP3D, aVec );
5356 aL2 = aVec.SquareMagnitude();
5357 if ( aL2 < aTolVec2 )
5358 return EXTR_CANT_GET_TANGENT;
5359 gp_Dir aTgt( aVec );
5361 aPP.SetTangent( aTgt );
5362 aPP.SetParameter( aT );
5369 //=======================================================================
5370 //function : MakeExtrElements
5371 //purpose : auxilary for ExtrusionAlongTrack
5372 //=======================================================================
5373 SMESH_MeshEditor::Extrusion_Error
5374 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5375 list<SMESH_MeshEditor_PathPoint>& fullList,
5376 const bool theHasAngles,
5377 list<double>& theAngles,
5378 const bool theLinearVariation,
5379 const bool theHasRefPoint,
5380 const gp_Pnt& theRefPoint,
5381 const bool theMakeGroups)
5383 MESSAGE("MakeExtrElements");
5384 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5385 int aNbTP = fullList.size();
5386 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5388 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5389 LinearAngleVariation(aNbTP-1, theAngles);
5391 vector<double> aAngles( aNbTP );
5393 for(; j<aNbTP; ++j) {
5396 if ( theHasAngles ) {
5398 std::list<double>::iterator aItD = theAngles.begin();
5399 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5401 aAngles[j] = anAngle;
5404 // fill vector of path points with angles
5405 //aPPs.resize(fullList.size());
5407 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5408 for(; itPP!=fullList.end(); itPP++) {
5410 SMESH_MeshEditor_PathPoint PP = *itPP;
5411 PP.SetAngle(aAngles[j]);
5415 TNodeOfNodeListMap mapNewNodes;
5416 TElemOfVecOfNnlmiMap mapElemNewNodes;
5417 TElemOfElemListMap newElemsMap;
5418 TIDSortedElemSet::iterator itElem;
5421 SMDSAbs_ElementType aTypeE;
5422 // source elements for each generated one
5423 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5425 // 3. Center of rotation aV0
5426 gp_Pnt aV0 = theRefPoint;
5428 if ( !theHasRefPoint ) {
5430 aGC.SetCoord( 0.,0.,0. );
5432 itElem = theElements.begin();
5433 for ( ; itElem != theElements.end(); itElem++ ) {
5434 const SMDS_MeshElement* elem = *itElem;
5436 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5437 while ( itN->more() ) {
5438 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5443 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5444 list<const SMDS_MeshNode*> aLNx;
5445 mapNewNodes[node] = aLNx;
5447 gp_XYZ aXYZ( aX, aY, aZ );
5455 } // if (!theHasRefPoint) {
5456 mapNewNodes.clear();
5458 // 4. Processing the elements
5459 SMESHDS_Mesh* aMesh = GetMeshDS();
5461 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5462 // check element type
5463 const SMDS_MeshElement* elem = *itElem;
5464 aTypeE = elem->GetType();
5465 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5468 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5469 newNodesItVec.reserve( elem->NbNodes() );
5471 // loop on elem nodes
5473 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5474 while ( itN->more() )
5477 // check if a node has been already processed
5478 const SMDS_MeshNode* node =
5479 static_cast<const SMDS_MeshNode*>( itN->next() );
5480 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5481 if ( nIt == mapNewNodes.end() ) {
5482 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5483 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5486 aX = node->X(); aY = node->Y(); aZ = node->Z();
5488 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5489 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5490 gp_Ax1 anAx1, anAxT1T0;
5491 gp_Dir aDT1x, aDT0x, aDT1T0;
5496 aPN0.SetCoord(aX, aY, aZ);
5498 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5500 aDT0x= aPP0.Tangent();
5501 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5503 for ( j = 1; j < aNbTP; ++j ) {
5504 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5506 aDT1x = aPP1.Tangent();
5507 aAngle1x = aPP1.Angle();
5509 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5511 gp_Vec aV01x( aP0x, aP1x );
5512 aTrsf.SetTranslation( aV01x );
5515 aV1x = aV0x.Transformed( aTrsf );
5516 aPN1 = aPN0.Transformed( aTrsf );
5518 // rotation 1 [ T1,T0 ]
5519 aAngleT1T0=-aDT1x.Angle( aDT0x );
5520 if (fabs(aAngleT1T0) > aTolAng) {
5522 anAxT1T0.SetLocation( aV1x );
5523 anAxT1T0.SetDirection( aDT1T0 );
5524 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5526 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5530 if ( theHasAngles ) {
5531 anAx1.SetLocation( aV1x );
5532 anAx1.SetDirection( aDT1x );
5533 aTrsfRot.SetRotation( anAx1, aAngle1x );
5535 aPN1 = aPN1.Transformed( aTrsfRot );
5539 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5540 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5541 // create additional node
5542 double x = ( aPN1.X() + aPN0.X() )/2.;
5543 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5544 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5545 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5546 myLastCreatedNodes.Append(newNode);
5547 srcNodes.Append( node );
5548 listNewNodes.push_back( newNode );
5553 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5554 myLastCreatedNodes.Append(newNode);
5555 srcNodes.Append( node );
5556 listNewNodes.push_back( newNode );
5566 // if current elem is quadratic and current node is not medium
5567 // we have to check - may be it is needed to insert additional nodes
5568 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5569 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5570 if(listNewNodes.size()==aNbTP-1) {
5571 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5572 gp_XYZ P(node->X(), node->Y(), node->Z());
5573 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5575 for(i=0; i<aNbTP-1; i++) {
5576 const SMDS_MeshNode* N = *it;
5577 double x = ( N->X() + P.X() )/2.;
5578 double y = ( N->Y() + P.Y() )/2.;
5579 double z = ( N->Z() + P.Z() )/2.;
5580 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5581 srcNodes.Append( node );
5582 myLastCreatedNodes.Append(newN);
5585 P = gp_XYZ(N->X(),N->Y(),N->Z());
5587 listNewNodes.clear();
5588 for(i=0; i<2*(aNbTP-1); i++) {
5589 listNewNodes.push_back(aNodes[i]);
5595 newNodesItVec.push_back( nIt );
5597 // make new elements
5598 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5599 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5600 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5603 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5605 if ( theMakeGroups )
5606 generateGroups( srcNodes, srcElems, "extruded");
5612 //=======================================================================
5613 //function : LinearAngleVariation
5614 //purpose : auxilary for ExtrusionAlongTrack
5615 //=======================================================================
5616 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5617 list<double>& Angles)
5619 int nbAngles = Angles.size();
5620 if( nbSteps > nbAngles ) {
5621 vector<double> theAngles(nbAngles);
5622 list<double>::iterator it = Angles.begin();
5624 for(; it!=Angles.end(); it++) {
5626 theAngles[i] = (*it);
5629 double rAn2St = double( nbAngles ) / double( nbSteps );
5630 double angPrev = 0, angle;
5631 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5632 double angCur = rAn2St * ( iSt+1 );
5633 double angCurFloor = floor( angCur );
5634 double angPrevFloor = floor( angPrev );
5635 if ( angPrevFloor == angCurFloor )
5636 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5638 int iP = int( angPrevFloor );
5639 double angPrevCeil = ceil(angPrev);
5640 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5642 int iC = int( angCurFloor );
5643 if ( iC < nbAngles )
5644 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5646 iP = int( angPrevCeil );
5648 angle += theAngles[ iC ];
5650 res.push_back(angle);
5655 for(; it!=res.end(); it++)
5656 Angles.push_back( *it );
5661 //================================================================================
5663 * \brief Move or copy theElements applying theTrsf to their nodes
5664 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5665 * \param theTrsf - transformation to apply
5666 * \param theCopy - if true, create translated copies of theElems
5667 * \param theMakeGroups - if true and theCopy, create translated groups
5668 * \param theTargetMesh - mesh to copy translated elements into
5669 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5671 //================================================================================
5673 SMESH_MeshEditor::PGroupIDs
5674 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5675 const gp_Trsf& theTrsf,
5677 const bool theMakeGroups,
5678 SMESH_Mesh* theTargetMesh)
5680 myLastCreatedElems.Clear();
5681 myLastCreatedNodes.Clear();
5683 bool needReverse = false;
5684 string groupPostfix;
5685 switch ( theTrsf.Form() ) {
5687 MESSAGE("gp_PntMirror");
5689 groupPostfix = "mirrored";
5692 MESSAGE("gp_Ax1Mirror");
5693 groupPostfix = "mirrored";
5696 MESSAGE("gp_Ax2Mirror");
5698 groupPostfix = "mirrored";
5701 MESSAGE("gp_Rotation");
5702 groupPostfix = "rotated";
5704 case gp_Translation:
5705 MESSAGE("gp_Translation");
5706 groupPostfix = "translated";
5709 MESSAGE("gp_Scale");
5710 groupPostfix = "scaled";
5712 case gp_CompoundTrsf: // different scale by axis
5713 MESSAGE("gp_CompoundTrsf");
5714 groupPostfix = "scaled";
5718 needReverse = false;
5719 groupPostfix = "transformed";
5722 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5723 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5724 SMESHDS_Mesh* aMesh = GetMeshDS();
5727 // map old node to new one
5728 TNodeNodeMap nodeMap;
5730 // elements sharing moved nodes; those of them which have all
5731 // nodes mirrored but are not in theElems are to be reversed
5732 TIDSortedElemSet inverseElemSet;
5734 // source elements for each generated one
5735 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5737 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5738 TIDSortedElemSet orphanNode;
5740 if ( theElems.empty() ) // transform the whole mesh
5743 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5744 while ( eIt->more() ) theElems.insert( eIt->next() );
5746 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5747 while ( nIt->more() )
5749 const SMDS_MeshNode* node = nIt->next();
5750 if ( node->NbInverseElements() == 0)
5751 orphanNode.insert( node );
5755 // loop on elements to transform nodes : first orphan nodes then elems
5756 TIDSortedElemSet::iterator itElem;
5757 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5758 for (int i=0; i<2; i++)
5759 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5760 const SMDS_MeshElement* elem = *itElem;
5764 // loop on elem nodes
5765 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5766 while ( itN->more() ) {
5768 const SMDS_MeshNode* node = cast2Node( itN->next() );
5769 // check if a node has been already transformed
5770 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5771 nodeMap.insert( make_pair ( node, node ));
5772 if ( !n2n_isnew.second )
5776 coord[0] = node->X();
5777 coord[1] = node->Y();
5778 coord[2] = node->Z();
5779 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5780 if ( theTargetMesh ) {
5781 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5782 n2n_isnew.first->second = newNode;
5783 myLastCreatedNodes.Append(newNode);
5784 srcNodes.Append( node );
5786 else if ( theCopy ) {
5787 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5788 n2n_isnew.first->second = newNode;
5789 myLastCreatedNodes.Append(newNode);
5790 srcNodes.Append( node );
5793 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5794 // node position on shape becomes invalid
5795 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5796 ( SMDS_SpacePosition::originSpacePosition() );
5799 // keep inverse elements
5800 if ( !theCopy && !theTargetMesh && needReverse ) {
5801 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5802 while ( invElemIt->more() ) {
5803 const SMDS_MeshElement* iel = invElemIt->next();
5804 inverseElemSet.insert( iel );
5810 // either create new elements or reverse mirrored ones
5811 if ( !theCopy && !needReverse && !theTargetMesh )
5814 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5815 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5816 theElems.insert( *invElemIt );
5818 // Replicate or reverse elements
5820 std::vector<int> iForw;
5821 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5823 const SMDS_MeshElement* elem = *itElem;
5824 if ( !elem ) continue;
5826 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5827 int nbNodes = elem->NbNodes();
5828 if ( geomType == SMDSGeom_NONE ) continue; // node
5830 switch ( geomType ) {
5832 case SMDSGeom_POLYGON: // ---------------------- polygon
5834 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5836 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5837 while (itN->more()) {
5838 const SMDS_MeshNode* node =
5839 static_cast<const SMDS_MeshNode*>(itN->next());
5840 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5841 if (nodeMapIt == nodeMap.end())
5842 break; // not all nodes transformed
5844 // reverse mirrored faces and volumes
5845 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5847 poly_nodes[iNode] = (*nodeMapIt).second;
5851 if ( iNode != nbNodes )
5852 continue; // not all nodes transformed
5854 if ( theTargetMesh ) {
5855 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5856 srcElems.Append( elem );
5858 else if ( theCopy ) {
5859 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5860 srcElems.Append( elem );
5863 aMesh->ChangePolygonNodes(elem, poly_nodes);
5868 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5870 const SMDS_VtkVolume* aPolyedre =
5871 dynamic_cast<const SMDS_VtkVolume*>( elem );
5873 MESSAGE("Warning: bad volumic element");
5877 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5878 vector<int> quantities; quantities.reserve( nbNodes );
5880 bool allTransformed = true;
5881 int nbFaces = aPolyedre->NbFaces();
5882 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5883 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5884 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5885 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5886 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5887 if (nodeMapIt == nodeMap.end()) {
5888 allTransformed = false; // not all nodes transformed
5890 poly_nodes.push_back((*nodeMapIt).second);
5892 if ( needReverse && allTransformed )
5893 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5895 quantities.push_back(nbFaceNodes);
5897 if ( !allTransformed )
5898 continue; // not all nodes transformed
5900 if ( theTargetMesh ) {
5901 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5902 srcElems.Append( elem );
5904 else if ( theCopy ) {
5905 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5906 srcElems.Append( elem );
5909 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5914 case SMDSGeom_BALL: // -------------------- Ball
5916 if ( !theCopy && !theTargetMesh ) continue;
5918 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5919 if (nodeMapIt == nodeMap.end())
5920 continue; // not all nodes transformed
5922 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5923 if ( theTargetMesh ) {
5924 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5925 srcElems.Append( elem );
5928 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5929 srcElems.Append( elem );
5934 default: // ----------------------- Regular elements
5936 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5937 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5938 const std::vector<int>& i = needReverse ? iRev : iForw;
5940 // find transformed nodes
5941 vector<const SMDS_MeshNode*> nodes(nbNodes);
5943 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5944 while ( itN->more() ) {
5945 const SMDS_MeshNode* node =
5946 static_cast<const SMDS_MeshNode*>( itN->next() );
5947 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5948 if ( nodeMapIt == nodeMap.end() )
5949 break; // not all nodes transformed
5950 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5952 if ( iNode != nbNodes )
5953 continue; // not all nodes transformed
5955 if ( theTargetMesh ) {
5956 if ( SMDS_MeshElement* copy =
5957 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5958 myLastCreatedElems.Append( copy );
5959 srcElems.Append( elem );
5962 else if ( theCopy ) {
5963 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5964 srcElems.Append( elem );
5967 // reverse element as it was reversed by transformation
5969 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5971 } // switch ( geomType )
5973 } // loop on elements
5975 PGroupIDs newGroupIDs;
5977 if ( ( theMakeGroups && theCopy ) ||
5978 ( theMakeGroups && theTargetMesh ) )
5979 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5984 //=======================================================================
5986 * \brief Create groups of elements made during transformation
5987 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5988 * \param elemGens - elements making corresponding myLastCreatedElems
5989 * \param postfix - to append to names of new groups
5991 //=======================================================================
5993 SMESH_MeshEditor::PGroupIDs
5994 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5995 const SMESH_SequenceOfElemPtr& elemGens,
5996 const std::string& postfix,
5997 SMESH_Mesh* targetMesh)
5999 PGroupIDs newGroupIDs( new list<int> );
6000 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6002 // Sort existing groups by types and collect their names
6004 // to store an old group and a generated new one
6005 typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
6006 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6007 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6009 set< string > groupNames;
6011 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6012 if ( !groupIt->more() ) return newGroupIDs;
6014 int newGroupID = mesh->GetGroupIds().back()+1;
6015 while ( groupIt->more() )
6017 SMESH_Group * group = groupIt->next();
6018 if ( !group ) continue;
6019 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6020 if ( !groupDS || groupDS->IsEmpty() ) continue;
6021 groupNames.insert( group->GetName() );
6022 groupDS->SetStoreName( group->GetName() );
6023 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
6024 groupDS->GetType() );
6025 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
6026 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6029 // Loop on nodes and elements to add them in new groups
6031 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6033 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6034 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6035 if ( gens.Length() != elems.Length() )
6036 throw SALOME_Exception(LOCALIZED("invalid args"));
6038 // loop on created elements
6039 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6041 const SMDS_MeshElement* sourceElem = gens( iElem );
6042 if ( !sourceElem ) {
6043 MESSAGE("generateGroups(): NULL source element");
6046 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6047 if ( groupsOldNew.empty() ) { // no groups of this type at all
6048 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6049 ++iElem; // skip all elements made by sourceElem
6052 // collect all elements made by sourceElem
6053 list< const SMDS_MeshElement* > resultElems;
6054 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6055 if ( resElem != sourceElem )
6056 resultElems.push_back( resElem );
6057 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6058 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6059 if ( resElem != sourceElem )
6060 resultElems.push_back( resElem );
6062 // add resultElems to groups made by ones the sourceElem belongs to
6063 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6064 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6066 SMESHDS_GroupBase* oldGroup = gOldNew->first;
6067 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6069 // fill in a new group
6070 SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6071 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6072 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6073 newGroup.Add( *resElemIt );
6076 } // loop on created elements
6077 }// loop on nodes and elements
6079 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6081 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6083 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6084 SMESHDS_Group* newGroupDS = orderedOldNewGroups[i]->second;
6085 if ( newGroupDS->IsEmpty() )
6087 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6092 string name = oldGroupDS->GetStoreName();
6093 if ( !targetMesh ) {
6097 while ( !groupNames.insert( name ).second ) // name exists
6098 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6100 newGroupDS->SetStoreName( name.c_str() );
6102 // make a SMESH_Groups
6103 mesh->AddGroup( newGroupDS );
6104 newGroupIDs->push_back( newGroupDS->GetID() );
6107 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6114 //================================================================================
6116 * \brief Return list of group of nodes close to each other within theTolerance
6117 * Search among theNodes or in the whole mesh if theNodes is empty using
6118 * an Octree algorithm
6120 //================================================================================
6122 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6123 const double theTolerance,
6124 TListOfListOfNodes & theGroupsOfNodes)
6126 myLastCreatedElems.Clear();
6127 myLastCreatedNodes.Clear();
6129 if ( theNodes.empty() )
6130 { // get all nodes in the mesh
6131 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6132 while ( nIt->more() )
6133 theNodes.insert( theNodes.end(),nIt->next());
6136 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6140 //=======================================================================
6142 * \brief Implementation of search for the node closest to point
6144 //=======================================================================
6146 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6148 //---------------------------------------------------------------------
6150 * \brief Constructor
6152 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6154 myMesh = ( SMESHDS_Mesh* ) theMesh;
6156 TIDSortedNodeSet nodes;
6158 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6159 while ( nIt->more() )
6160 nodes.insert( nodes.end(), nIt->next() );
6162 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6164 // get max size of a leaf box
6165 SMESH_OctreeNode* tree = myOctreeNode;
6166 while ( !tree->isLeaf() )
6168 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6172 myHalfLeafSize = tree->maxSize() / 2.;
6175 //---------------------------------------------------------------------
6177 * \brief Move node and update myOctreeNode accordingly
6179 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6181 myOctreeNode->UpdateByMoveNode( node, toPnt );
6182 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6185 //---------------------------------------------------------------------
6187 * \brief Do it's job
6189 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6191 map<double, const SMDS_MeshNode*> dist2Nodes;
6192 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6193 if ( !dist2Nodes.empty() )
6194 return dist2Nodes.begin()->second;
6195 list<const SMDS_MeshNode*> nodes;
6196 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6198 double minSqDist = DBL_MAX;
6199 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6201 // sort leafs by their distance from thePnt
6202 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6203 TDistTreeMap treeMap;
6204 list< SMESH_OctreeNode* > treeList;
6205 list< SMESH_OctreeNode* >::iterator trIt;
6206 treeList.push_back( myOctreeNode );
6208 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6209 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6210 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6212 SMESH_OctreeNode* tree = *trIt;
6213 if ( !tree->isLeaf() ) // put children to the queue
6215 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6216 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6217 while ( cIt->more() )
6218 treeList.push_back( cIt->next() );
6220 else if ( tree->NbNodes() ) // put a tree to the treeMap
6222 const Bnd_B3d& box = *tree->getBox();
6223 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6224 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6225 if ( !it_in.second ) // not unique distance to box center
6226 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6229 // find distance after which there is no sense to check tree's
6230 double sqLimit = DBL_MAX;
6231 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6232 if ( treeMap.size() > 5 ) {
6233 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6234 const Bnd_B3d& box = *closestTree->getBox();
6235 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6236 sqLimit = limit * limit;
6238 // get all nodes from trees
6239 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6240 if ( sqDist_tree->first > sqLimit )
6242 SMESH_OctreeNode* tree = sqDist_tree->second;
6243 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6246 // find closest among nodes
6247 minSqDist = DBL_MAX;
6248 const SMDS_MeshNode* closestNode = 0;
6249 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6250 for ( ; nIt != nodes.end(); ++nIt ) {
6251 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6252 if ( minSqDist > sqDist ) {
6260 //---------------------------------------------------------------------
6264 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6266 //---------------------------------------------------------------------
6268 * \brief Return the node tree
6270 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6273 SMESH_OctreeNode* myOctreeNode;
6274 SMESHDS_Mesh* myMesh;
6275 double myHalfLeafSize; // max size of a leaf box
6278 //=======================================================================
6280 * \brief Return SMESH_NodeSearcher
6282 //=======================================================================
6284 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6286 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6289 // ========================================================================
6290 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6292 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6293 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6294 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6296 //=======================================================================
6298 * \brief Octal tree of bounding boxes of elements
6300 //=======================================================================
6302 class ElementBndBoxTree : public SMESH_Octree
6306 ElementBndBoxTree(const SMDS_Mesh& mesh,
6307 SMDSAbs_ElementType elemType,
6308 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6309 double tolerance = NodeRadius );
6310 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6311 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6312 void getElementsInSphere ( const gp_XYZ& center,
6313 const double radius, TIDSortedElemSet& foundElems);
6314 size_t getSize() { return std::max( _size, _elements.size() ); }
6315 ~ElementBndBoxTree();
6318 ElementBndBoxTree():_size(0) {}
6319 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6320 void buildChildrenData();
6321 Bnd_B3d* buildRootBox();
6323 //!< Bounding box of element
6324 struct ElementBox : public Bnd_B3d
6326 const SMDS_MeshElement* _element;
6327 int _refCount; // an ElementBox can be included in several tree branches
6328 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6330 vector< ElementBox* > _elements;
6334 //================================================================================
6336 * \brief ElementBndBoxTree creation
6338 //================================================================================
6340 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6341 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6343 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6344 _elements.reserve( nbElems );
6346 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6347 while ( elemIt->more() )
6348 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6353 //================================================================================
6357 //================================================================================
6359 ElementBndBoxTree::~ElementBndBoxTree()
6361 for ( int i = 0; i < _elements.size(); ++i )
6362 if ( --_elements[i]->_refCount <= 0 )
6363 delete _elements[i];
6366 //================================================================================
6368 * \brief Return the maximal box
6370 //================================================================================
6372 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6374 Bnd_B3d* box = new Bnd_B3d;
6375 for ( int i = 0; i < _elements.size(); ++i )
6376 box->Add( *_elements[i] );
6380 //================================================================================
6382 * \brief Redistrubute element boxes among children
6384 //================================================================================
6386 void ElementBndBoxTree::buildChildrenData()
6388 for ( int i = 0; i < _elements.size(); ++i )
6390 for (int j = 0; j < 8; j++)
6392 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6394 _elements[i]->_refCount++;
6395 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6398 _elements[i]->_refCount--;
6400 _size = _elements.size();
6401 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6403 for (int j = 0; j < 8; j++)
6405 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6406 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6407 child->myIsLeaf = true;
6409 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6410 SMESHUtils::CompactVector( child->_elements );
6414 //================================================================================
6416 * \brief Return elements which can include the point
6418 //================================================================================
6420 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6421 TIDSortedElemSet& foundElems)
6423 if ( getBox()->IsOut( point.XYZ() ))
6428 for ( int i = 0; i < _elements.size(); ++i )
6429 if ( !_elements[i]->IsOut( point.XYZ() ))
6430 foundElems.insert( _elements[i]->_element );
6434 for (int i = 0; i < 8; i++)
6435 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6439 //================================================================================
6441 * \brief Return elements which can be intersected by the line
6443 //================================================================================
6445 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6446 TIDSortedElemSet& foundElems)
6448 if ( getBox()->IsOut( line ))
6453 for ( int i = 0; i < _elements.size(); ++i )
6454 if ( !_elements[i]->IsOut( line ))
6455 foundElems.insert( _elements[i]->_element );
6459 for (int i = 0; i < 8; i++)
6460 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6464 //================================================================================
6466 * \brief Return elements from leaves intersecting the sphere
6468 //================================================================================
6470 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6471 const double radius,
6472 TIDSortedElemSet& foundElems)
6474 if ( getBox()->IsOut( center, radius ))
6479 for ( int i = 0; i < _elements.size(); ++i )
6480 if ( !_elements[i]->IsOut( center, radius ))
6481 foundElems.insert( _elements[i]->_element );
6485 for (int i = 0; i < 8; i++)
6486 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6490 //================================================================================
6492 * \brief Construct the element box
6494 //================================================================================
6496 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6500 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6501 while ( nIt->more() )
6502 Add( SMESH_TNodeXYZ( nIt->next() ));
6503 Enlarge( tolerance );
6508 //=======================================================================
6510 * \brief Implementation of search for the elements by point and
6511 * of classification of point in 2D mesh
6513 //=======================================================================
6515 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6517 SMESHDS_Mesh* _mesh;
6518 SMDS_ElemIteratorPtr _meshPartIt;
6519 ElementBndBoxTree* _ebbTree;
6520 SMESH_NodeSearcherImpl* _nodeSearcher;
6521 SMDSAbs_ElementType _elementType;
6523 bool _outerFacesFound;
6524 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6526 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6527 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6528 ~SMESH_ElementSearcherImpl()
6530 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6531 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6533 virtual int FindElementsByPoint(const gp_Pnt& point,
6534 SMDSAbs_ElementType type,
6535 vector< const SMDS_MeshElement* >& foundElements);
6536 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6537 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6538 SMDSAbs_ElementType type );
6540 void GetElementsNearLine( const gp_Ax1& line,
6541 SMDSAbs_ElementType type,
6542 vector< const SMDS_MeshElement* >& foundElems);
6543 double getTolerance();
6544 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6545 const double tolerance, double & param);
6546 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6547 bool isOuterBoundary(const SMDS_MeshElement* face) const
6549 return _outerFaces.empty() || _outerFaces.count(face);
6551 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6553 const SMDS_MeshElement* _face;
6555 bool _coincides; //!< the line lays in face plane
6556 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6557 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6559 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6562 TIDSortedElemSet _faces;
6563 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6564 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6568 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6570 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6571 << ", _coincides="<<i._coincides << ")";
6574 //=======================================================================
6576 * \brief define tolerance for search
6578 //=======================================================================
6580 double SMESH_ElementSearcherImpl::getTolerance()
6582 if ( _tolerance < 0 )
6584 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6587 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6589 double boxSize = _nodeSearcher->getTree()->maxSize();
6590 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6592 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6594 double boxSize = _ebbTree->maxSize();
6595 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6597 if ( _tolerance == 0 )
6599 // define tolerance by size of a most complex element
6600 int complexType = SMDSAbs_Volume;
6601 while ( complexType > SMDSAbs_All &&
6602 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6604 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6606 if ( complexType == int( SMDSAbs_Node ))
6608 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6610 if ( meshInfo.NbNodes() > 2 )
6611 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6615 SMDS_ElemIteratorPtr elemIt =
6616 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6617 const SMDS_MeshElement* elem = elemIt->next();
6618 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6619 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6621 while ( nodeIt->more() )
6623 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6624 elemSize = max( dist, elemSize );
6627 _tolerance = 1e-4 * elemSize;
6633 //================================================================================
6635 * \brief Find intersection of the line and an edge of face and return parameter on line
6637 //================================================================================
6639 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6640 const SMDS_MeshElement* face,
6647 GeomAPI_ExtremaCurveCurve anExtCC;
6648 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6650 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6651 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6653 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6654 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6655 anExtCC.Init( lineCurve, edge);
6656 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6658 Quantity_Parameter pl, pe;
6659 anExtCC.LowerDistanceParameters( pl, pe );
6661 if ( ++nbInts == 2 )
6665 if ( nbInts > 0 ) param /= nbInts;
6668 //================================================================================
6670 * \brief Find all faces belonging to the outer boundary of mesh
6672 //================================================================================
6674 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6676 if ( _outerFacesFound ) return;
6678 // Collect all outer faces by passing from one outer face to another via their links
6679 // and BTW find out if there are internal faces at all.
6681 // checked links and links where outer boundary meets internal one
6682 set< SMESH_TLink > visitedLinks, seamLinks;
6684 // links to treat with already visited faces sharing them
6685 list < TFaceLink > startLinks;
6687 // load startLinks with the first outerFace
6688 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6689 _outerFaces.insert( outerFace );
6691 TIDSortedElemSet emptySet;
6692 while ( !startLinks.empty() )
6694 const SMESH_TLink& link = startLinks.front()._link;
6695 TIDSortedElemSet& faces = startLinks.front()._faces;
6697 outerFace = *faces.begin();
6698 // find other faces sharing the link
6699 const SMDS_MeshElement* f;
6700 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6703 // select another outer face among the found
6704 const SMDS_MeshElement* outerFace2 = 0;
6705 if ( faces.size() == 2 )
6707 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6709 else if ( faces.size() > 2 )
6711 seamLinks.insert( link );
6713 // link direction within the outerFace
6714 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6715 SMESH_TNodeXYZ( link.node2()));
6716 int i1 = outerFace->GetNodeIndex( link.node1() );
6717 int i2 = outerFace->GetNodeIndex( link.node2() );
6718 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6719 if ( rev ) n1n2.Reverse();
6721 gp_XYZ ofNorm, fNorm;
6722 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6724 // direction from the link inside outerFace
6725 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6726 // sort all other faces by angle with the dirInOF
6727 map< double, const SMDS_MeshElement* > angle2Face;
6728 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6729 for ( ; face != faces.end(); ++face )
6731 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6733 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6734 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6735 if ( angle < 0 ) angle += 2. * M_PI;
6736 angle2Face.insert( make_pair( angle, *face ));
6738 if ( !angle2Face.empty() )
6739 outerFace2 = angle2Face.begin()->second;
6742 // store the found outer face and add its links to continue seaching from
6745 _outerFaces.insert( outerFace );
6746 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6747 for ( int i = 0; i < nbNodes; ++i )
6749 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6750 if ( visitedLinks.insert( link2 ).second )
6751 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6754 startLinks.pop_front();
6756 _outerFacesFound = true;
6758 if ( !seamLinks.empty() )
6760 // There are internal boundaries touching the outher one,
6761 // find all faces of internal boundaries in order to find
6762 // faces of boundaries of holes, if any.
6767 _outerFaces.clear();
6771 //=======================================================================
6773 * \brief Find elements of given type where the given point is IN or ON.
6774 * Returns nb of found elements and elements them-selves.
6776 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6778 //=======================================================================
6780 int SMESH_ElementSearcherImpl::
6781 FindElementsByPoint(const gp_Pnt& point,
6782 SMDSAbs_ElementType type,
6783 vector< const SMDS_MeshElement* >& foundElements)
6785 foundElements.clear();
6787 double tolerance = getTolerance();
6789 // =================================================================================
6790 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6792 if ( !_nodeSearcher )
6793 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6795 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6796 if ( !closeNode ) return foundElements.size();
6798 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6799 return foundElements.size(); // to far from any node
6801 if ( type == SMDSAbs_Node )
6803 foundElements.push_back( closeNode );
6807 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6808 while ( elemIt->more() )
6809 foundElements.push_back( elemIt->next() );
6812 // =================================================================================
6813 else // elements more complex than 0D
6815 if ( !_ebbTree || _elementType != type )
6817 if ( _ebbTree ) delete _ebbTree;
6818 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6820 TIDSortedElemSet suspectElems;
6821 _ebbTree->getElementsNearPoint( point, suspectElems );
6822 TIDSortedElemSet::iterator elem = suspectElems.begin();
6823 for ( ; elem != suspectElems.end(); ++elem )
6824 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6825 foundElements.push_back( *elem );
6827 return foundElements.size();
6830 //=======================================================================
6832 * \brief Find an element of given type most close to the given point
6834 * WARNING: Only face search is implemeneted so far
6836 //=======================================================================
6838 const SMDS_MeshElement*
6839 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6840 SMDSAbs_ElementType type )
6842 const SMDS_MeshElement* closestElem = 0;
6844 if ( type == SMDSAbs_Face )
6846 if ( !_ebbTree || _elementType != type )
6848 if ( _ebbTree ) delete _ebbTree;
6849 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6851 TIDSortedElemSet suspectElems;
6852 _ebbTree->getElementsNearPoint( point, suspectElems );
6854 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6856 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6857 _ebbTree->getBox()->CornerMax() );
6859 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6860 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6862 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6863 while ( suspectElems.empty() )
6865 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6869 double minDist = std::numeric_limits<double>::max();
6870 multimap< double, const SMDS_MeshElement* > dist2face;
6871 TIDSortedElemSet::iterator elem = suspectElems.begin();
6872 for ( ; elem != suspectElems.end(); ++elem )
6874 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6876 if ( dist < minDist + 1e-10)
6879 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6882 if ( !dist2face.empty() )
6884 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6885 closestElem = d2f->second;
6886 // if there are several elements at the same distance, select one
6887 // with GC closest to the point
6888 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6889 double minDistToGC = 0;
6890 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6892 if ( minDistToGC == 0 )
6895 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6896 TXyzIterator(), gc ) / closestElem->NbNodes();
6897 minDistToGC = point.SquareDistance( gc );
6900 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6901 TXyzIterator(), gc ) / d2f->second->NbNodes();
6902 double d = point.SquareDistance( gc );
6903 if ( d < minDistToGC )
6906 closestElem = d2f->second;
6909 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6910 // <<closestElem->GetID() << " DIST " << minDist << endl;
6915 // NOT IMPLEMENTED SO FAR
6921 //================================================================================
6923 * \brief Classify the given point in the closed 2D mesh
6925 //================================================================================
6927 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6929 double tolerance = getTolerance();
6930 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6932 if ( _ebbTree ) delete _ebbTree;
6933 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6935 // Algo: analyse transition of a line starting at the point through mesh boundary;
6936 // try three lines parallel to axis of the coordinate system and perform rough
6937 // analysis. If solution is not clear perform thorough analysis.
6939 const int nbAxes = 3;
6940 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6941 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6942 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6943 multimap< int, int > nbInt2Axis; // to find the simplest case
6944 for ( int axis = 0; axis < nbAxes; ++axis )
6946 gp_Ax1 lineAxis( point, axisDir[axis]);
6947 gp_Lin line ( lineAxis );
6949 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6950 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6952 // Intersect faces with the line
6954 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6955 TIDSortedElemSet::iterator face = suspectFaces.begin();
6956 for ( ; face != suspectFaces.end(); ++face )
6960 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6961 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6963 // perform intersection
6964 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6965 if ( !intersection.IsDone() )
6967 if ( intersection.IsInQuadric() )
6969 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6971 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6973 gp_Pnt intersectionPoint = intersection.Point(1);
6974 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6975 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6978 // Analyse intersections roughly
6980 int nbInter = u2inters.size();
6984 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6985 if ( nbInter == 1 ) // not closed mesh
6986 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6988 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6991 if ( (f<0) == (l<0) )
6994 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6995 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6996 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6999 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7001 if ( _outerFacesFound ) break; // pass to thorough analysis
7003 } // three attempts - loop on CS axes
7005 // Analyse intersections thoroughly.
7006 // We make two loops maximum, on the first one we only exclude touching intersections,
7007 // on the second, if situation is still unclear, we gather and use information on
7008 // position of faces (internal or outer). If faces position is already gathered,
7009 // we make the second loop right away.
7011 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7013 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7014 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7016 int axis = nb_axis->second;
7017 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7019 gp_Ax1 lineAxis( point, axisDir[axis]);
7020 gp_Lin line ( lineAxis );
7022 // add tangent intersections to u2inters
7024 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7025 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7026 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7027 u2inters.insert(make_pair( param, *tgtInt ));
7028 tangentInters[ axis ].clear();
7030 // Count intersections before and after the point excluding touching ones.
7031 // If hasPositionInfo we count intersections of outer boundary only
7033 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7034 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7035 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7036 bool ok = ! u_int1->second._coincides;
7037 while ( ok && u_int1 != u2inters.end() )
7039 double u = u_int1->first;
7040 bool touchingInt = false;
7041 if ( ++u_int2 != u2inters.end() )
7043 // skip intersections at the same point (if the line passes through edge or node)
7045 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7051 // skip tangent intersections
7053 const SMDS_MeshElement* prevFace = u_int1->second._face;
7054 while ( ok && u_int2->second._coincides )
7056 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7062 ok = ( u_int2 != u2inters.end() );
7067 // skip intersections at the same point after tangent intersections
7070 double u2 = u_int2->first;
7072 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7078 // decide if we skipped a touching intersection
7079 if ( nbSamePnt + nbTgt > 0 )
7081 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7082 map< double, TInters >::iterator u_int = u_int1;
7083 for ( ; u_int != u_int2; ++u_int )
7085 if ( u_int->second._coincides ) continue;
7086 double dot = u_int->second._faceNorm * line.Direction();
7087 if ( dot > maxDot ) maxDot = dot;
7088 if ( dot < minDot ) minDot = dot;
7090 touchingInt = ( minDot*maxDot < 0 );
7095 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7106 u_int1 = u_int2; // to next intersection
7108 } // loop on intersections with one line
7112 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7115 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7118 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7119 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7121 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7124 if ( (f<0) == (l<0) )
7127 if ( hasPositionInfo )
7128 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7130 } // loop on intersections of the tree lines - thorough analysis
7132 if ( !hasPositionInfo )
7134 // gather info on faces position - is face in the outer boundary or not
7135 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7136 findOuterBoundary( u2inters.begin()->second._face );
7139 } // two attempts - with and w/o faces position info in the mesh
7141 return TopAbs_UNKNOWN;
7144 //=======================================================================
7146 * \brief Return elements possibly intersecting the line
7148 //=======================================================================
7150 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7151 SMDSAbs_ElementType type,
7152 vector< const SMDS_MeshElement* >& foundElems)
7154 if ( !_ebbTree || _elementType != type )
7156 if ( _ebbTree ) delete _ebbTree;
7157 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7159 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7160 _ebbTree->getElementsNearLine( line, suspectFaces );
7161 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7164 //=======================================================================
7166 * \brief Return SMESH_ElementSearcher
7168 //=======================================================================
7170 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7172 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7175 //=======================================================================
7177 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7179 //=======================================================================
7181 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7183 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7186 //=======================================================================
7188 * \brief Return true if the point is IN or ON of the element
7190 //=======================================================================
7192 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7194 if ( element->GetType() == SMDSAbs_Volume)
7196 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7199 // get ordered nodes
7201 vector< gp_XYZ > xyz;
7202 vector<const SMDS_MeshNode*> nodeList;
7204 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7205 if ( element->IsQuadratic() ) {
7206 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7207 nodeIt = f->interlacedNodesElemIterator();
7208 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7209 nodeIt = e->interlacedNodesElemIterator();
7211 while ( nodeIt->more() )
7213 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7214 xyz.push_back( SMESH_TNodeXYZ(node) );
7215 nodeList.push_back(node);
7218 int i, nbNodes = element->NbNodes();
7220 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7222 // compute face normal
7223 gp_Vec faceNorm(0,0,0);
7224 xyz.push_back( xyz.front() );
7225 nodeList.push_back( nodeList.front() );
7226 for ( i = 0; i < nbNodes; ++i )
7228 gp_Vec edge1( xyz[i+1], xyz[i]);
7229 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7230 faceNorm += edge1 ^ edge2;
7232 double normSize = faceNorm.Magnitude();
7233 if ( normSize <= tol )
7235 // degenerated face: point is out if it is out of all face edges
7236 for ( i = 0; i < nbNodes; ++i )
7238 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7239 if ( !IsOut( &edge, point, tol ))
7244 faceNorm /= normSize;
7246 // check if the point lays on face plane
7247 gp_Vec n2p( xyz[0], point );
7248 if ( fabs( n2p * faceNorm ) > tol )
7249 return true; // not on face plane
7251 // check if point is out of face boundary:
7252 // define it by closest transition of a ray point->infinity through face boundary
7253 // on the face plane.
7254 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7255 // to find intersections of the ray with the boundary.
7257 gp_Vec plnNorm = ray ^ faceNorm;
7258 normSize = plnNorm.Magnitude();
7259 if ( normSize <= tol ) return false; // point coincides with the first node
7260 plnNorm /= normSize;
7261 // for each node of the face, compute its signed distance to the plane
7262 vector<double> dist( nbNodes + 1);
7263 for ( i = 0; i < nbNodes; ++i )
7265 gp_Vec n2p( xyz[i], point );
7266 dist[i] = n2p * plnNorm;
7268 dist.back() = dist.front();
7269 // find the closest intersection
7271 double rClosest, distClosest = 1e100;;
7273 for ( i = 0; i < nbNodes; ++i )
7276 if ( fabs( dist[i]) < tol )
7278 else if ( fabs( dist[i+1]) < tol )
7280 else if ( dist[i] * dist[i+1] < 0 )
7281 r = dist[i] / ( dist[i] - dist[i+1] );
7283 continue; // no intersection
7284 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7285 gp_Vec p2int ( point, pInt);
7286 if ( p2int * ray > -tol ) // right half-space
7288 double intDist = p2int.SquareMagnitude();
7289 if ( intDist < distClosest )
7294 distClosest = intDist;
7299 return true; // no intesections - out
7301 // analyse transition
7302 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7303 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7304 gp_Vec p2int ( point, pClosest );
7305 bool out = (edgeNorm * p2int) < -tol;
7306 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7309 // ray pass through a face node; analyze transition through an adjacent edge
7310 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7311 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7312 gp_Vec edgeAdjacent( p1, p2 );
7313 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7314 bool out2 = (edgeNorm2 * p2int) < -tol;
7316 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7317 return covexCorner ? (out || out2) : (out && out2);
7319 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7321 // point is out of edge if it is NOT ON any straight part of edge
7322 // (we consider quadratic edge as being composed of two straight parts)
7323 for ( i = 1; i < nbNodes; ++i )
7325 gp_Vec edge( xyz[i-1], xyz[i]);
7326 gp_Vec n1p ( xyz[i-1], point);
7327 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7330 gp_Vec n2p( xyz[i], point );
7331 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7333 return false; // point is ON this part
7337 // Node or 0D element -------------------------------------------------------------------------
7339 gp_Vec n2p ( xyz[0], point );
7340 return n2p.Magnitude() <= tol;
7345 //=======================================================================
7349 // Position of a point relative to a segment
7353 // VERTEX 1 o----ON-----> VERTEX 2
7357 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7358 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7362 int _index; // index of vertex or segment
7364 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7365 bool operator < (const PointPos& other ) const
7367 if ( _name == other._name )
7368 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7369 return _name < other._name;
7373 //================================================================================
7375 * \brief Return of a point relative to a segment
7376 * \param point2D - the point to analyze position of
7377 * \param xyVec - end points of segments
7378 * \param index0 - 0-based index of the first point of segment
7379 * \param posToFindOut - flags of positions to detect
7380 * \retval PointPos - point position
7382 //================================================================================
7384 PointPos getPointPosition( const gp_XY& point2D,
7385 const gp_XY* segEnds,
7386 const int index0 = 0,
7387 const int posToFindOut = POS_ALL)
7389 const gp_XY& p1 = segEnds[ index0 ];
7390 const gp_XY& p2 = segEnds[ index0+1 ];
7391 const gp_XY grad = p2 - p1;
7393 if ( posToFindOut & POS_VERTEX )
7395 // check if the point2D is at "vertex 1" zone
7396 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7397 p1.Y() + grad.X() ) };
7398 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7399 return PointPos( POS_VERTEX, index0 );
7401 // check if the point2D is at "vertex 2" zone
7402 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7403 p2.Y() + grad.X() ) };
7404 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7405 return PointPos( POS_VERTEX, index0 + 1);
7407 double edgeEquation =
7408 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7409 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7413 //=======================================================================
7415 * \brief Return minimal distance from a point to a face
7417 * Currently we ignore non-planarity and 2nd order of face
7419 //=======================================================================
7421 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7422 const gp_Pnt& point )
7424 double badDistance = -1;
7425 if ( !face ) return badDistance;
7427 // coordinates of nodes (medium nodes, if any, ignored)
7428 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7429 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7430 xyz.resize( face->NbCornerNodes()+1 );
7432 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7433 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7435 gp_Vec OZ ( xyz[0], xyz[1] );
7436 gp_Vec OX ( xyz[0], xyz[2] );
7437 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7439 if ( xyz.size() < 4 ) return badDistance;
7440 OZ = gp_Vec ( xyz[0], xyz[2] );
7441 OX = gp_Vec ( xyz[0], xyz[3] );
7445 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7447 catch ( Standard_Failure ) {
7450 trsf.SetTransformation( tgtCS );
7452 // move all the nodes to 2D
7453 vector<gp_XY> xy( xyz.size() );
7454 for ( size_t i = 0;i < xyz.size()-1; ++i )
7456 gp_XYZ p3d = xyz[i];
7457 trsf.Transforms( p3d );
7458 xy[i].SetCoord( p3d.X(), p3d.Z() );
7460 xyz.back() = xyz.front();
7461 xy.back() = xy.front();
7463 // // move the point in 2D
7464 gp_XYZ tmpPnt = point.XYZ();
7465 trsf.Transforms( tmpPnt );
7466 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7468 // loop on segments of the face to analyze point position ralative to the face
7469 set< PointPos > pntPosSet;
7470 for ( size_t i = 1; i < xy.size(); ++i )
7472 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7473 pntPosSet.insert( pos );
7477 PointPos pos = *pntPosSet.begin();
7478 // cout << "Face " << face->GetID() << " DIST: ";
7479 switch ( pos._name )
7482 // point is most close to a segment
7483 gp_Vec p0p1( point, xyz[ pos._index ] );
7484 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7486 double projDist = p0p1 * p1p2; // distance projected to the segment
7487 gp_Vec projVec = p1p2 * projDist;
7488 gp_Vec distVec = p0p1 - projVec;
7489 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7490 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7491 return distVec.Magnitude();
7494 // point is inside the face
7495 double distToFacePlane = tmpPnt.Y();
7496 // cout << distToFacePlane << ", INSIDE " << endl;
7497 return Abs( distToFacePlane );
7500 // point is most close to a node
7501 gp_Vec distVec( point, xyz[ pos._index ]);
7502 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7503 return distVec.Magnitude();
7509 //=======================================================================
7510 //function : SimplifyFace
7512 //=======================================================================
7513 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7514 vector<const SMDS_MeshNode *>& poly_nodes,
7515 vector<int>& quantities) const
7517 int nbNodes = faceNodes.size();
7522 set<const SMDS_MeshNode*> nodeSet;
7524 // get simple seq of nodes
7525 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7526 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7527 int iSimple = 0, nbUnique = 0;
7529 simpleNodes[iSimple++] = faceNodes[0];
7531 for (int iCur = 1; iCur < nbNodes; iCur++) {
7532 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7533 simpleNodes[iSimple++] = faceNodes[iCur];
7534 if (nodeSet.insert( faceNodes[iCur] ).second)
7538 int nbSimple = iSimple;
7539 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7549 bool foundLoop = (nbSimple > nbUnique);
7552 set<const SMDS_MeshNode*> loopSet;
7553 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7554 const SMDS_MeshNode* n = simpleNodes[iSimple];
7555 if (!loopSet.insert( n ).second) {
7559 int iC = 0, curLast = iSimple;
7560 for (; iC < curLast; iC++) {
7561 if (simpleNodes[iC] == n) break;
7563 int loopLen = curLast - iC;
7565 // create sub-element
7567 quantities.push_back(loopLen);
7568 for (; iC < curLast; iC++) {
7569 poly_nodes.push_back(simpleNodes[iC]);
7572 // shift the rest nodes (place from the first loop position)
7573 for (iC = curLast + 1; iC < nbSimple; iC++) {
7574 simpleNodes[iC - loopLen] = simpleNodes[iC];
7576 nbSimple -= loopLen;
7579 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7580 } // while (foundLoop)
7584 quantities.push_back(iSimple);
7585 for (int i = 0; i < iSimple; i++)
7586 poly_nodes.push_back(simpleNodes[i]);
7592 //=======================================================================
7593 //function : MergeNodes
7594 //purpose : In each group, the cdr of nodes are substituted by the first one
7596 //=======================================================================
7598 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7600 MESSAGE("MergeNodes");
7601 myLastCreatedElems.Clear();
7602 myLastCreatedNodes.Clear();
7604 SMESHDS_Mesh* aMesh = GetMeshDS();
7606 TNodeNodeMap nodeNodeMap; // node to replace - new node
7607 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7608 list< int > rmElemIds, rmNodeIds;
7610 // Fill nodeNodeMap and elems
7612 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7613 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7614 list<const SMDS_MeshNode*>& nodes = *grIt;
7615 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7616 const SMDS_MeshNode* nToKeep = *nIt;
7617 //MESSAGE("node to keep " << nToKeep->GetID());
7618 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7619 const SMDS_MeshNode* nToRemove = *nIt;
7620 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7621 if ( nToRemove != nToKeep ) {
7622 //MESSAGE(" node to remove " << nToRemove->GetID());
7623 rmNodeIds.push_back( nToRemove->GetID() );
7624 AddToSameGroups( nToKeep, nToRemove, aMesh );
7627 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7628 while ( invElemIt->more() ) {
7629 const SMDS_MeshElement* elem = invElemIt->next();
7634 // Change element nodes or remove an element
7636 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7637 for ( ; eIt != elems.end(); eIt++ ) {
7638 const SMDS_MeshElement* elem = *eIt;
7639 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7640 int nbNodes = elem->NbNodes();
7641 int aShapeId = FindShape( elem );
7643 set<const SMDS_MeshNode*> nodeSet;
7644 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7645 int iUnique = 0, iCur = 0, nbRepl = 0;
7646 vector<int> iRepl( nbNodes );
7648 // get new seq of nodes
7649 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7650 while ( itN->more() ) {
7651 const SMDS_MeshNode* n =
7652 static_cast<const SMDS_MeshNode*>( itN->next() );
7654 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7655 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7657 // BUG 0020185: begin
7659 bool stopRecur = false;
7660 set<const SMDS_MeshNode*> nodesRecur;
7661 nodesRecur.insert(n);
7662 while (!stopRecur) {
7663 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7664 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7665 n = (*nnIt_i).second;
7666 if (!nodesRecur.insert(n).second) {
7667 // error: recursive dependancy
7677 curNodes[ iCur ] = n;
7678 bool isUnique = nodeSet.insert( n ).second;
7680 uniqueNodes[ iUnique++ ] = n;
7682 iRepl[ nbRepl++ ] = iCur;
7686 // Analyse element topology after replacement
7689 int nbUniqueNodes = nodeSet.size();
7690 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7691 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7692 // Polygons and Polyhedral volumes
7693 if (elem->IsPoly()) {
7695 if (elem->GetType() == SMDSAbs_Face) {
7697 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7699 for (; inode < nbNodes; inode++) {
7700 face_nodes[inode] = curNodes[inode];
7703 vector<const SMDS_MeshNode *> polygons_nodes;
7704 vector<int> quantities;
7705 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7708 for (int iface = 0; iface < nbNew; iface++) {
7709 int nbNodes = quantities[iface];
7710 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7711 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7712 poly_nodes[ii] = polygons_nodes[inode];
7714 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7715 myLastCreatedElems.Append(newElem);
7717 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7720 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7721 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7722 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7724 if (nbNew > 0) quid = nbNew - 1;
7725 vector<int> newquant(quantities.begin()+quid, quantities.end());
7726 const SMDS_MeshElement* newElem = 0;
7727 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7728 myLastCreatedElems.Append(newElem);
7729 if ( aShapeId && newElem )
7730 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7731 rmElemIds.push_back(elem->GetID());
7734 rmElemIds.push_back(elem->GetID());
7738 else if (elem->GetType() == SMDSAbs_Volume) {
7739 // Polyhedral volume
7740 if (nbUniqueNodes < 4) {
7741 rmElemIds.push_back(elem->GetID());
7744 // each face has to be analyzed in order to check volume validity
7745 const SMDS_VtkVolume* aPolyedre =
7746 dynamic_cast<const SMDS_VtkVolume*>( elem );
7748 int nbFaces = aPolyedre->NbFaces();
7750 vector<const SMDS_MeshNode *> poly_nodes;
7751 vector<int> quantities;
7753 for (int iface = 1; iface <= nbFaces; iface++) {
7754 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7755 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7757 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7758 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7759 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7760 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7761 faceNode = (*nnIt).second;
7763 faceNodes[inode - 1] = faceNode;
7766 SimplifyFace(faceNodes, poly_nodes, quantities);
7769 if (quantities.size() > 3) {
7770 // to be done: remove coincident faces
7773 if (quantities.size() > 3)
7775 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7776 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7777 const SMDS_MeshElement* newElem = 0;
7778 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7779 myLastCreatedElems.Append(newElem);
7780 if ( aShapeId && newElem )
7781 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7782 rmElemIds.push_back(elem->GetID());
7786 rmElemIds.push_back(elem->GetID());
7797 // TODO not all the possible cases are solved. Find something more generic?
7798 switch ( nbNodes ) {
7799 case 2: ///////////////////////////////////// EDGE
7800 isOk = false; break;
7801 case 3: ///////////////////////////////////// TRIANGLE
7802 isOk = false; break;
7804 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7806 else { //////////////////////////////////// QUADRANGLE
7807 if ( nbUniqueNodes < 3 )
7809 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7810 isOk = false; // opposite nodes stick
7811 //MESSAGE("isOk " << isOk);
7814 case 6: ///////////////////////////////////// PENTAHEDRON
7815 if ( nbUniqueNodes == 4 ) {
7816 // ---------------------------------> tetrahedron
7818 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7819 // all top nodes stick: reverse a bottom
7820 uniqueNodes[ 0 ] = curNodes [ 1 ];
7821 uniqueNodes[ 1 ] = curNodes [ 0 ];
7823 else if (nbRepl == 3 &&
7824 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7825 // all bottom nodes stick: set a top before
7826 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7827 uniqueNodes[ 0 ] = curNodes [ 3 ];
7828 uniqueNodes[ 1 ] = curNodes [ 4 ];
7829 uniqueNodes[ 2 ] = curNodes [ 5 ];
7831 else if (nbRepl == 4 &&
7832 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7833 // a lateral face turns into a line: reverse a bottom
7834 uniqueNodes[ 0 ] = curNodes [ 1 ];
7835 uniqueNodes[ 1 ] = curNodes [ 0 ];
7840 else if ( nbUniqueNodes == 5 ) {
7841 // PENTAHEDRON --------------------> 2 tetrahedrons
7842 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7843 // a bottom node sticks with a linked top one
7845 SMDS_MeshElement* newElem =
7846 aMesh->AddVolume(curNodes[ 3 ],
7849 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7850 myLastCreatedElems.Append(newElem);
7852 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7853 // 2. : reverse a bottom
7854 uniqueNodes[ 0 ] = curNodes [ 1 ];
7855 uniqueNodes[ 1 ] = curNodes [ 0 ];
7865 if(elem->IsQuadratic()) { // Quadratic quadrangle
7877 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7880 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7882 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7883 uniqueNodes[0] = curNodes[0];
7884 uniqueNodes[1] = curNodes[2];
7885 uniqueNodes[2] = curNodes[3];
7886 uniqueNodes[3] = curNodes[5];
7887 uniqueNodes[4] = curNodes[6];
7888 uniqueNodes[5] = curNodes[7];
7891 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7892 uniqueNodes[0] = curNodes[0];
7893 uniqueNodes[1] = curNodes[1];
7894 uniqueNodes[2] = curNodes[2];
7895 uniqueNodes[3] = curNodes[4];
7896 uniqueNodes[4] = curNodes[5];
7897 uniqueNodes[5] = curNodes[6];
7900 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7901 uniqueNodes[0] = curNodes[1];
7902 uniqueNodes[1] = curNodes[2];
7903 uniqueNodes[2] = curNodes[3];
7904 uniqueNodes[3] = curNodes[5];
7905 uniqueNodes[4] = curNodes[6];
7906 uniqueNodes[5] = curNodes[0];
7909 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7910 uniqueNodes[0] = curNodes[0];
7911 uniqueNodes[1] = curNodes[1];
7912 uniqueNodes[2] = curNodes[3];
7913 uniqueNodes[3] = curNodes[4];
7914 uniqueNodes[4] = curNodes[6];
7915 uniqueNodes[5] = curNodes[7];
7918 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7919 uniqueNodes[0] = curNodes[0];
7920 uniqueNodes[1] = curNodes[2];
7921 uniqueNodes[2] = curNodes[3];
7922 uniqueNodes[3] = curNodes[1];
7923 uniqueNodes[4] = curNodes[6];
7924 uniqueNodes[5] = curNodes[7];
7927 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7928 uniqueNodes[0] = curNodes[0];
7929 uniqueNodes[1] = curNodes[1];
7930 uniqueNodes[2] = curNodes[2];
7931 uniqueNodes[3] = curNodes[4];
7932 uniqueNodes[4] = curNodes[5];
7933 uniqueNodes[5] = curNodes[7];
7936 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7937 uniqueNodes[0] = curNodes[0];
7938 uniqueNodes[1] = curNodes[1];
7939 uniqueNodes[2] = curNodes[3];
7940 uniqueNodes[3] = curNodes[4];
7941 uniqueNodes[4] = curNodes[2];
7942 uniqueNodes[5] = curNodes[7];
7945 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7946 uniqueNodes[0] = curNodes[0];
7947 uniqueNodes[1] = curNodes[1];
7948 uniqueNodes[2] = curNodes[2];
7949 uniqueNodes[3] = curNodes[4];
7950 uniqueNodes[4] = curNodes[5];
7951 uniqueNodes[5] = curNodes[3];
7956 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7959 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7963 //////////////////////////////////// HEXAHEDRON
7965 SMDS_VolumeTool hexa (elem);
7966 hexa.SetExternalNormal();
7967 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7968 //////////////////////// HEX ---> 1 tetrahedron
7969 for ( int iFace = 0; iFace < 6; iFace++ ) {
7970 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7971 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7972 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7973 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7974 // one face turns into a point ...
7975 int iOppFace = hexa.GetOppFaceIndex( iFace );
7976 ind = hexa.GetFaceNodesIndices( iOppFace );
7978 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7979 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7982 if ( nbStick == 1 ) {
7983 // ... and the opposite one - into a triangle.
7985 ind = hexa.GetFaceNodesIndices( iFace );
7986 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7993 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7994 //////////////////////// HEX ---> 1 prism
7995 int nbTria = 0, iTria[3];
7996 const int *ind; // indices of face nodes
7997 // look for triangular faces
7998 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7999 ind = hexa.GetFaceNodesIndices( iFace );
8000 TIDSortedNodeSet faceNodes;
8001 for ( iCur = 0; iCur < 4; iCur++ )
8002 faceNodes.insert( curNodes[ind[iCur]] );
8003 if ( faceNodes.size() == 3 )
8004 iTria[ nbTria++ ] = iFace;
8006 // check if triangles are opposite
8007 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8010 // set nodes of the bottom triangle
8011 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8013 for ( iCur = 0; iCur < 4; iCur++ )
8014 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8015 indB.push_back( ind[iCur] );
8016 if ( !hexa.IsForward() )
8017 std::swap( indB[0], indB[2] );
8018 for ( iCur = 0; iCur < 3; iCur++ )
8019 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8020 // set nodes of the top triangle
8021 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8022 for ( iCur = 0; iCur < 3; ++iCur )
8023 for ( int j = 0; j < 4; ++j )
8024 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8026 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8032 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8033 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8034 for ( int iFace = 0; iFace < 6; iFace++ ) {
8035 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8036 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8037 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8038 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8039 // one face turns into a point ...
8040 int iOppFace = hexa.GetOppFaceIndex( iFace );
8041 ind = hexa.GetFaceNodesIndices( iOppFace );
8043 iUnique = 2; // reverse a tetrahedron 1 bottom
8044 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8045 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8047 else if ( iUnique >= 0 )
8048 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8050 if ( nbStick == 0 ) {
8051 // ... and the opposite one is a quadrangle
8053 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8054 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8057 SMDS_MeshElement* newElem =
8058 aMesh->AddVolume(curNodes[ind[ 0 ]],
8061 curNodes[indTop[ 0 ]]);
8062 myLastCreatedElems.Append(newElem);
8064 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8071 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8072 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8073 // find indices of quad and tri faces
8074 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8075 for ( iFace = 0; iFace < 6; iFace++ ) {
8076 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8078 for ( iCur = 0; iCur < 4; iCur++ )
8079 nodeSet.insert( curNodes[ind[ iCur ]] );
8080 nbUniqueNodes = nodeSet.size();
8081 if ( nbUniqueNodes == 3 )
8082 iTriFace[ nbTri++ ] = iFace;
8083 else if ( nbUniqueNodes == 4 )
8084 iQuadFace[ nbQuad++ ] = iFace;
8086 if (nbQuad == 2 && nbTri == 4 &&
8087 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8088 // 2 opposite quadrangles stuck with a diagonal;
8089 // sample groups of merged indices: (0-4)(2-6)
8090 // --------------------------------------------> 2 tetrahedrons
8091 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8092 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8093 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8094 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8095 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8096 // stuck with 0-2 diagonal
8104 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8105 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8106 // stuck with 1-3 diagonal
8118 uniqueNodes[ 0 ] = curNodes [ i0 ];
8119 uniqueNodes[ 1 ] = curNodes [ i1d ];
8120 uniqueNodes[ 2 ] = curNodes [ i3d ];
8121 uniqueNodes[ 3 ] = curNodes [ i0t ];
8124 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8128 myLastCreatedElems.Append(newElem);
8130 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8133 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8134 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8135 // --------------------------------------------> prism
8136 // find 2 opposite triangles
8138 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8139 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8140 // find indices of kept and replaced nodes
8141 // and fill unique nodes of 2 opposite triangles
8142 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8143 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8144 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8145 // fill unique nodes
8148 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8149 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8150 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8152 // iCur of a linked node of the opposite face (make normals co-directed):
8153 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8154 // check that correspondent corners of triangles are linked
8155 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8158 uniqueNodes[ iUnique ] = n;
8159 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8168 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8171 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8178 } // switch ( nbNodes )
8180 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8182 if ( isOk ) { // the elem remains valid after sticking nodes
8183 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8185 // Change nodes of polyedre
8186 const SMDS_VtkVolume* aPolyedre =
8187 dynamic_cast<const SMDS_VtkVolume*>( elem );
8189 int nbFaces = aPolyedre->NbFaces();
8191 vector<const SMDS_MeshNode *> poly_nodes;
8192 vector<int> quantities (nbFaces);
8194 for (int iface = 1; iface <= nbFaces; iface++) {
8195 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8196 quantities[iface - 1] = nbFaceNodes;
8198 for (inode = 1; inode <= nbFaceNodes; inode++) {
8199 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8201 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8202 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8203 curNode = (*nnIt).second;
8205 poly_nodes.push_back(curNode);
8208 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8211 else // replace non-polyhedron elements
8213 const SMDSAbs_ElementType etyp = elem->GetType();
8214 const int elemId = elem->GetID();
8215 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8216 uniqueNodes.resize(nbUniqueNodes);
8218 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8220 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8221 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8222 if ( sm && newElem )
8223 sm->AddElement( newElem );
8224 if ( elem != newElem )
8225 ReplaceElemInGroups( elem, newElem, aMesh );
8229 // Remove invalid regular element or invalid polygon
8230 rmElemIds.push_back( elem->GetID() );
8233 } // loop on elements
8235 // Remove bad elements, then equal nodes (order important)
8237 Remove( rmElemIds, false );
8238 Remove( rmNodeIds, true );
8243 // ========================================================
8244 // class : SortableElement
8245 // purpose : allow sorting elements basing on their nodes
8246 // ========================================================
8247 class SortableElement : public set <const SMDS_MeshElement*>
8251 SortableElement( const SMDS_MeshElement* theElem )
8254 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8255 while ( nodeIt->more() )
8256 this->insert( nodeIt->next() );
8259 const SMDS_MeshElement* Get() const
8262 void Set(const SMDS_MeshElement* e) const
8267 mutable const SMDS_MeshElement* myElem;
8270 //=======================================================================
8271 //function : FindEqualElements
8272 //purpose : Return list of group of elements built on the same nodes.
8273 // Search among theElements or in the whole mesh if theElements is empty
8274 //=======================================================================
8276 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8277 TListOfListOfElementsID & theGroupsOfElementsID)
8279 myLastCreatedElems.Clear();
8280 myLastCreatedNodes.Clear();
8282 typedef map< SortableElement, int > TMapOfNodeSet;
8283 typedef list<int> TGroupOfElems;
8285 if ( theElements.empty() )
8286 { // get all elements in the mesh
8287 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8288 while ( eIt->more() )
8289 theElements.insert( theElements.end(), eIt->next());
8292 vector< TGroupOfElems > arrayOfGroups;
8293 TGroupOfElems groupOfElems;
8294 TMapOfNodeSet mapOfNodeSet;
8296 TIDSortedElemSet::iterator elemIt = theElements.begin();
8297 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8298 const SMDS_MeshElement* curElem = *elemIt;
8299 SortableElement SE(curElem);
8302 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8303 if( !(pp.second) ) {
8304 TMapOfNodeSet::iterator& itSE = pp.first;
8305 ind = (*itSE).second;
8306 arrayOfGroups[ind].push_back(curElem->GetID());
8309 groupOfElems.clear();
8310 groupOfElems.push_back(curElem->GetID());
8311 arrayOfGroups.push_back(groupOfElems);
8316 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8317 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8318 groupOfElems = *groupIt;
8319 if ( groupOfElems.size() > 1 ) {
8320 groupOfElems.sort();
8321 theGroupsOfElementsID.push_back(groupOfElems);
8326 //=======================================================================
8327 //function : MergeElements
8328 //purpose : In each given group, substitute all elements by the first one.
8329 //=======================================================================
8331 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8333 myLastCreatedElems.Clear();
8334 myLastCreatedNodes.Clear();
8336 typedef list<int> TListOfIDs;
8337 TListOfIDs rmElemIds; // IDs of elems to remove
8339 SMESHDS_Mesh* aMesh = GetMeshDS();
8341 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8342 while ( groupsIt != theGroupsOfElementsID.end() ) {
8343 TListOfIDs& aGroupOfElemID = *groupsIt;
8344 aGroupOfElemID.sort();
8345 int elemIDToKeep = aGroupOfElemID.front();
8346 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8347 aGroupOfElemID.pop_front();
8348 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8349 while ( idIt != aGroupOfElemID.end() ) {
8350 int elemIDToRemove = *idIt;
8351 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8352 // add the kept element in groups of removed one (PAL15188)
8353 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8354 rmElemIds.push_back( elemIDToRemove );
8360 Remove( rmElemIds, false );
8363 //=======================================================================
8364 //function : MergeEqualElements
8365 //purpose : Remove all but one of elements built on the same nodes.
8366 //=======================================================================
8368 void SMESH_MeshEditor::MergeEqualElements()
8370 TIDSortedElemSet aMeshElements; /* empty input ==
8371 to merge equal elements in the whole mesh */
8372 TListOfListOfElementsID aGroupsOfElementsID;
8373 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8374 MergeElements(aGroupsOfElementsID);
8377 //=======================================================================
8378 //function : FindFaceInSet
8379 //purpose : Return a face having linked nodes n1 and n2 and which is
8380 // - not in avoidSet,
8381 // - in elemSet provided that !elemSet.empty()
8382 // i1 and i2 optionally returns indices of n1 and n2
8383 //=======================================================================
8385 const SMDS_MeshElement*
8386 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8387 const SMDS_MeshNode* n2,
8388 const TIDSortedElemSet& elemSet,
8389 const TIDSortedElemSet& avoidSet,
8395 const SMDS_MeshElement* face = 0;
8397 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8398 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8399 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8401 //MESSAGE("in while ( invElemIt->more() && !face )");
8402 const SMDS_MeshElement* elem = invElemIt->next();
8403 if (avoidSet.count( elem ))
8405 if ( !elemSet.empty() && !elemSet.count( elem ))
8408 i1 = elem->GetNodeIndex( n1 );
8409 // find a n2 linked to n1
8410 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8411 for ( int di = -1; di < 2 && !face; di += 2 )
8413 i2 = (i1+di+nbN) % nbN;
8414 if ( elem->GetNode( i2 ) == n2 )
8417 if ( !face && elem->IsQuadratic())
8419 // analysis for quadratic elements using all nodes
8420 const SMDS_VtkFace* F =
8421 dynamic_cast<const SMDS_VtkFace*>(elem);
8422 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8423 // use special nodes iterator
8424 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8425 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8426 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8428 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8429 if ( n1 == prevN && n2 == n )
8433 else if ( n2 == prevN && n1 == n )
8435 face = elem; swap( i1, i2 );
8441 if ( n1ind ) *n1ind = i1;
8442 if ( n2ind ) *n2ind = i2;
8446 //=======================================================================
8447 //function : findAdjacentFace
8449 //=======================================================================
8451 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8452 const SMDS_MeshNode* n2,
8453 const SMDS_MeshElement* elem)
8455 TIDSortedElemSet elemSet, avoidSet;
8457 avoidSet.insert ( elem );
8458 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8461 //=======================================================================
8462 //function : FindFreeBorder
8464 //=======================================================================
8466 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8468 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8469 const SMDS_MeshNode* theSecondNode,
8470 const SMDS_MeshNode* theLastNode,
8471 list< const SMDS_MeshNode* > & theNodes,
8472 list< const SMDS_MeshElement* >& theFaces)
8474 if ( !theFirstNode || !theSecondNode )
8476 // find border face between theFirstNode and theSecondNode
8477 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8481 theFaces.push_back( curElem );
8482 theNodes.push_back( theFirstNode );
8483 theNodes.push_back( theSecondNode );
8485 //vector<const SMDS_MeshNode*> nodes;
8486 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8487 TIDSortedElemSet foundElems;
8488 bool needTheLast = ( theLastNode != 0 );
8490 while ( nStart != theLastNode ) {
8491 if ( nStart == theFirstNode )
8492 return !needTheLast;
8494 // find all free border faces sharing form nStart
8496 list< const SMDS_MeshElement* > curElemList;
8497 list< const SMDS_MeshNode* > nStartList;
8498 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8499 while ( invElemIt->more() ) {
8500 const SMDS_MeshElement* e = invElemIt->next();
8501 if ( e == curElem || foundElems.insert( e ).second ) {
8503 int iNode = 0, nbNodes = e->NbNodes();
8504 //const SMDS_MeshNode* nodes[nbNodes+1];
8505 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8507 if(e->IsQuadratic()) {
8508 const SMDS_VtkFace* F =
8509 dynamic_cast<const SMDS_VtkFace*>(e);
8510 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8511 // use special nodes iterator
8512 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8513 while( anIter->more() ) {
8514 nodes[ iNode++ ] = cast2Node(anIter->next());
8518 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8519 while ( nIt->more() )
8520 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8522 nodes[ iNode ] = nodes[ 0 ];
8524 for ( iNode = 0; iNode < nbNodes; iNode++ )
8525 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8526 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8527 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8529 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8530 curElemList.push_back( e );
8534 // analyse the found
8536 int nbNewBorders = curElemList.size();
8537 if ( nbNewBorders == 0 ) {
8538 // no free border furthermore
8539 return !needTheLast;
8541 else if ( nbNewBorders == 1 ) {
8542 // one more element found
8544 nStart = nStartList.front();
8545 curElem = curElemList.front();
8546 theFaces.push_back( curElem );
8547 theNodes.push_back( nStart );
8550 // several continuations found
8551 list< const SMDS_MeshElement* >::iterator curElemIt;
8552 list< const SMDS_MeshNode* >::iterator nStartIt;
8553 // check if one of them reached the last node
8554 if ( needTheLast ) {
8555 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8556 curElemIt!= curElemList.end();
8557 curElemIt++, nStartIt++ )
8558 if ( *nStartIt == theLastNode ) {
8559 theFaces.push_back( *curElemIt );
8560 theNodes.push_back( *nStartIt );
8564 // find the best free border by the continuations
8565 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8566 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8567 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8568 curElemIt!= curElemList.end();
8569 curElemIt++, nStartIt++ )
8571 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8572 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8573 // find one more free border
8574 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8578 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8579 // choice: clear a worse one
8580 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8581 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8582 contNodes[ iWorse ].clear();
8583 contFaces[ iWorse ].clear();
8586 if ( contNodes[0].empty() && contNodes[1].empty() )
8589 // append the best free border
8590 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8591 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8592 theNodes.pop_back(); // remove nIgnore
8593 theNodes.pop_back(); // remove nStart
8594 theFaces.pop_back(); // remove curElem
8595 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8596 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8597 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8598 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8601 } // several continuations found
8602 } // while ( nStart != theLastNode )
8607 //=======================================================================
8608 //function : CheckFreeBorderNodes
8609 //purpose : Return true if the tree nodes are on a free border
8610 //=======================================================================
8612 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8613 const SMDS_MeshNode* theNode2,
8614 const SMDS_MeshNode* theNode3)
8616 list< const SMDS_MeshNode* > nodes;
8617 list< const SMDS_MeshElement* > faces;
8618 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8621 //=======================================================================
8622 //function : SewFreeBorder
8624 //=======================================================================
8626 SMESH_MeshEditor::Sew_Error
8627 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8628 const SMDS_MeshNode* theBordSecondNode,
8629 const SMDS_MeshNode* theBordLastNode,
8630 const SMDS_MeshNode* theSideFirstNode,
8631 const SMDS_MeshNode* theSideSecondNode,
8632 const SMDS_MeshNode* theSideThirdNode,
8633 const bool theSideIsFreeBorder,
8634 const bool toCreatePolygons,
8635 const bool toCreatePolyedrs)
8637 myLastCreatedElems.Clear();
8638 myLastCreatedNodes.Clear();
8640 MESSAGE("::SewFreeBorder()");
8641 Sew_Error aResult = SEW_OK;
8643 // ====================================
8644 // find side nodes and elements
8645 // ====================================
8647 list< const SMDS_MeshNode* > nSide[ 2 ];
8648 list< const SMDS_MeshElement* > eSide[ 2 ];
8649 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8650 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8654 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8655 nSide[0], eSide[0])) {
8656 MESSAGE(" Free Border 1 not found " );
8657 aResult = SEW_BORDER1_NOT_FOUND;
8659 if (theSideIsFreeBorder) {
8662 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8663 nSide[1], eSide[1])) {
8664 MESSAGE(" Free Border 2 not found " );
8665 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8668 if ( aResult != SEW_OK )
8671 if (!theSideIsFreeBorder) {
8675 // -------------------------------------------------------------------------
8677 // 1. If nodes to merge are not coincident, move nodes of the free border
8678 // from the coord sys defined by the direction from the first to last
8679 // nodes of the border to the correspondent sys of the side 2
8680 // 2. On the side 2, find the links most co-directed with the correspondent
8681 // links of the free border
8682 // -------------------------------------------------------------------------
8684 // 1. Since sewing may break if there are volumes to split on the side 2,
8685 // we wont move nodes but just compute new coordinates for them
8686 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8687 TNodeXYZMap nBordXYZ;
8688 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8689 list< const SMDS_MeshNode* >::iterator nBordIt;
8691 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8692 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8693 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8694 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8695 double tol2 = 1.e-8;
8696 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8697 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8698 // Need node movement.
8700 // find X and Z axes to create trsf
8701 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8703 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8705 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8708 gp_Ax3 toBordAx( Pb1, Zb, X );
8709 gp_Ax3 fromSideAx( Ps1, Zs, X );
8710 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8712 gp_Trsf toBordSys, fromSide2Sys;
8713 toBordSys.SetTransformation( toBordAx );
8714 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8715 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8718 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8719 const SMDS_MeshNode* n = *nBordIt;
8720 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8721 toBordSys.Transforms( xyz );
8722 fromSide2Sys.Transforms( xyz );
8723 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8727 // just insert nodes XYZ in the nBordXYZ map
8728 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8729 const SMDS_MeshNode* n = *nBordIt;
8730 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8734 // 2. On the side 2, find the links most co-directed with the correspondent
8735 // links of the free border
8737 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8738 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8739 sideNodes.push_back( theSideFirstNode );
8741 bool hasVolumes = false;
8742 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8743 set<long> foundSideLinkIDs, checkedLinkIDs;
8744 SMDS_VolumeTool volume;
8745 //const SMDS_MeshNode* faceNodes[ 4 ];
8747 const SMDS_MeshNode* sideNode;
8748 const SMDS_MeshElement* sideElem;
8749 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8750 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8751 nBordIt = bordNodes.begin();
8753 // border node position and border link direction to compare with
8754 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8755 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8756 // choose next side node by link direction or by closeness to
8757 // the current border node:
8758 bool searchByDir = ( *nBordIt != theBordLastNode );
8760 // find the next node on the Side 2
8762 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8764 checkedLinkIDs.clear();
8765 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8767 // loop on inverse elements of current node (prevSideNode) on the Side 2
8768 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8769 while ( invElemIt->more() )
8771 const SMDS_MeshElement* elem = invElemIt->next();
8772 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8773 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8774 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8775 bool isVolume = volume.Set( elem );
8776 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8777 if ( isVolume ) // --volume
8779 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8780 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8781 if(elem->IsQuadratic()) {
8782 const SMDS_VtkFace* F =
8783 dynamic_cast<const SMDS_VtkFace*>(elem);
8784 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8785 // use special nodes iterator
8786 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8787 while( anIter->more() ) {
8788 nodes[ iNode ] = cast2Node(anIter->next());
8789 if ( nodes[ iNode++ ] == prevSideNode )
8790 iPrevNode = iNode - 1;
8794 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8795 while ( nIt->more() ) {
8796 nodes[ iNode ] = cast2Node( nIt->next() );
8797 if ( nodes[ iNode++ ] == prevSideNode )
8798 iPrevNode = iNode - 1;
8801 // there are 2 links to check
8806 // loop on links, to be precise, on the second node of links
8807 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8808 const SMDS_MeshNode* n = nodes[ iNode ];
8810 if ( !volume.IsLinked( n, prevSideNode ))
8814 if ( iNode ) // a node before prevSideNode
8815 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8816 else // a node after prevSideNode
8817 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8819 // check if this link was already used
8820 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8821 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8822 if (!isJustChecked &&
8823 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8825 // test a link geometrically
8826 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8827 bool linkIsBetter = false;
8828 double dot = 0.0, dist = 0.0;
8829 if ( searchByDir ) { // choose most co-directed link
8830 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8831 linkIsBetter = ( dot > maxDot );
8833 else { // choose link with the node closest to bordPos
8834 dist = ( nextXYZ - bordPos ).SquareModulus();
8835 linkIsBetter = ( dist < minDist );
8837 if ( linkIsBetter ) {
8846 } // loop on inverse elements of prevSideNode
8849 MESSAGE(" Cant find path by links of the Side 2 ");
8850 return SEW_BAD_SIDE_NODES;
8852 sideNodes.push_back( sideNode );
8853 sideElems.push_back( sideElem );
8854 foundSideLinkIDs.insert ( linkID );
8855 prevSideNode = sideNode;
8857 if ( *nBordIt == theBordLastNode )
8858 searchByDir = false;
8860 // find the next border link to compare with
8861 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8862 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8863 // move to next border node if sideNode is before forward border node (bordPos)
8864 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8865 prevBordNode = *nBordIt;
8867 bordPos = nBordXYZ[ *nBordIt ];
8868 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8869 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8873 while ( sideNode != theSideSecondNode );
8875 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8876 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8877 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8879 } // end nodes search on the side 2
8881 // ============================
8882 // sew the border to the side 2
8883 // ============================
8885 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8886 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8888 TListOfListOfNodes nodeGroupsToMerge;
8889 if ( nbNodes[0] == nbNodes[1] ||
8890 ( theSideIsFreeBorder && !theSideThirdNode)) {
8892 // all nodes are to be merged
8894 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8895 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8896 nIt[0]++, nIt[1]++ )
8898 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8899 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8900 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8905 // insert new nodes into the border and the side to get equal nb of segments
8907 // get normalized parameters of nodes on the borders
8908 //double param[ 2 ][ maxNbNodes ];
8910 param[0] = new double [ maxNbNodes ];
8911 param[1] = new double [ maxNbNodes ];
8913 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8914 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8915 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8916 const SMDS_MeshNode* nPrev = *nIt;
8917 double bordLength = 0;
8918 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8919 const SMDS_MeshNode* nCur = *nIt;
8920 gp_XYZ segment (nCur->X() - nPrev->X(),
8921 nCur->Y() - nPrev->Y(),
8922 nCur->Z() - nPrev->Z());
8923 double segmentLen = segment.Modulus();
8924 bordLength += segmentLen;
8925 param[ iBord ][ iNode ] = bordLength;
8928 // normalize within [0,1]
8929 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8930 param[ iBord ][ iNode ] /= bordLength;
8934 // loop on border segments
8935 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8936 int i[ 2 ] = { 0, 0 };
8937 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8938 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8940 TElemOfNodeListMap insertMap;
8941 TElemOfNodeListMap::iterator insertMapIt;
8943 // key: elem to insert nodes into
8944 // value: 2 nodes to insert between + nodes to be inserted
8946 bool next[ 2 ] = { false, false };
8948 // find min adjacent segment length after sewing
8949 double nextParam = 10., prevParam = 0;
8950 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8951 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8952 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8953 if ( i[ iBord ] > 0 )
8954 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8956 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8957 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8958 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8960 // choose to insert or to merge nodes
8961 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8962 if ( Abs( du ) <= minSegLen * 0.2 ) {
8965 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8966 const SMDS_MeshNode* n0 = *nIt[0];
8967 const SMDS_MeshNode* n1 = *nIt[1];
8968 nodeGroupsToMerge.back().push_back( n1 );
8969 nodeGroupsToMerge.back().push_back( n0 );
8970 // position of node of the border changes due to merge
8971 param[ 0 ][ i[0] ] += du;
8972 // move n1 for the sake of elem shape evaluation during insertion.
8973 // n1 will be removed by MergeNodes() anyway
8974 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8975 next[0] = next[1] = true;
8980 int intoBord = ( du < 0 ) ? 0 : 1;
8981 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8982 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8983 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8984 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8985 if ( intoBord == 1 ) {
8986 // move node of the border to be on a link of elem of the side
8987 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8988 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8989 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8990 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8991 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8993 insertMapIt = insertMap.find( elem );
8994 bool notFound = ( insertMapIt == insertMap.end() );
8995 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8997 // insert into another link of the same element:
8998 // 1. perform insertion into the other link of the elem
8999 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9000 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9001 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9002 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9003 // 2. perform insertion into the link of adjacent faces
9005 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9007 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9011 if (toCreatePolyedrs) {
9012 // perform insertion into the links of adjacent volumes
9013 UpdateVolumes(n12, n22, nodeList);
9015 // 3. find an element appeared on n1 and n2 after the insertion
9016 insertMap.erase( elem );
9017 elem = findAdjacentFace( n1, n2, 0 );
9019 if ( notFound || otherLink ) {
9020 // add element and nodes of the side into the insertMap
9021 insertMapIt = insertMap.insert
9022 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9023 (*insertMapIt).second.push_back( n1 );
9024 (*insertMapIt).second.push_back( n2 );
9026 // add node to be inserted into elem
9027 (*insertMapIt).second.push_back( nIns );
9028 next[ 1 - intoBord ] = true;
9031 // go to the next segment
9032 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9033 if ( next[ iBord ] ) {
9034 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9036 nPrev[ iBord ] = *nIt[ iBord ];
9037 nIt[ iBord ]++; i[ iBord ]++;
9041 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9043 // perform insertion of nodes into elements
9045 for (insertMapIt = insertMap.begin();
9046 insertMapIt != insertMap.end();
9049 const SMDS_MeshElement* elem = (*insertMapIt).first;
9050 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9051 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9052 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9054 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9056 if ( !theSideIsFreeBorder ) {
9057 // look for and insert nodes into the faces adjacent to elem
9059 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9061 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9066 if (toCreatePolyedrs) {
9067 // perform insertion into the links of adjacent volumes
9068 UpdateVolumes(n1, n2, nodeList);
9074 } // end: insert new nodes
9076 MergeNodes ( nodeGroupsToMerge );
9081 //=======================================================================
9082 //function : InsertNodesIntoLink
9083 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9084 // and theBetweenNode2 and split theElement
9085 //=======================================================================
9087 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9088 const SMDS_MeshNode* theBetweenNode1,
9089 const SMDS_MeshNode* theBetweenNode2,
9090 list<const SMDS_MeshNode*>& theNodesToInsert,
9091 const bool toCreatePoly)
9093 if ( theFace->GetType() != SMDSAbs_Face ) return;
9095 // find indices of 2 link nodes and of the rest nodes
9096 int iNode = 0, il1, il2, i3, i4;
9097 il1 = il2 = i3 = i4 = -1;
9098 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9099 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9101 if(theFace->IsQuadratic()) {
9102 const SMDS_VtkFace* F =
9103 dynamic_cast<const SMDS_VtkFace*>(theFace);
9104 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9105 // use special nodes iterator
9106 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9107 while( anIter->more() ) {
9108 const SMDS_MeshNode* n = cast2Node(anIter->next());
9109 if ( n == theBetweenNode1 )
9111 else if ( n == theBetweenNode2 )
9117 nodes[ iNode++ ] = n;
9121 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9122 while ( nodeIt->more() ) {
9123 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9124 if ( n == theBetweenNode1 )
9126 else if ( n == theBetweenNode2 )
9132 nodes[ iNode++ ] = n;
9135 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9138 // arrange link nodes to go one after another regarding the face orientation
9139 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9140 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9145 aNodesToInsert.reverse();
9147 // check that not link nodes of a quadrangles are in good order
9148 int nbFaceNodes = theFace->NbNodes();
9149 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9155 if (toCreatePoly || theFace->IsPoly()) {
9158 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9160 // add nodes of face up to first node of link
9163 if(theFace->IsQuadratic()) {
9164 const SMDS_VtkFace* F =
9165 dynamic_cast<const SMDS_VtkFace*>(theFace);
9166 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9167 // use special nodes iterator
9168 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9169 while( anIter->more() && !isFLN ) {
9170 const SMDS_MeshNode* n = cast2Node(anIter->next());
9171 poly_nodes[iNode++] = n;
9172 if (n == nodes[il1]) {
9176 // add nodes to insert
9177 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9178 for (; nIt != aNodesToInsert.end(); nIt++) {
9179 poly_nodes[iNode++] = *nIt;
9181 // add nodes of face starting from last node of link
9182 while ( anIter->more() ) {
9183 poly_nodes[iNode++] = cast2Node(anIter->next());
9187 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9188 while ( nodeIt->more() && !isFLN ) {
9189 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9190 poly_nodes[iNode++] = n;
9191 if (n == nodes[il1]) {
9195 // add nodes to insert
9196 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9197 for (; nIt != aNodesToInsert.end(); nIt++) {
9198 poly_nodes[iNode++] = *nIt;
9200 // add nodes of face starting from last node of link
9201 while ( nodeIt->more() ) {
9202 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9203 poly_nodes[iNode++] = n;
9207 // edit or replace the face
9208 SMESHDS_Mesh *aMesh = GetMeshDS();
9210 if (theFace->IsPoly()) {
9211 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9214 int aShapeId = FindShape( theFace );
9216 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9217 myLastCreatedElems.Append(newElem);
9218 if ( aShapeId && newElem )
9219 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9221 aMesh->RemoveElement(theFace);
9226 SMESHDS_Mesh *aMesh = GetMeshDS();
9227 if( !theFace->IsQuadratic() ) {
9229 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9230 int nbLinkNodes = 2 + aNodesToInsert.size();
9231 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9232 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9233 linkNodes[ 0 ] = nodes[ il1 ];
9234 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9235 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9236 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9237 linkNodes[ iNode++ ] = *nIt;
9239 // decide how to split a quadrangle: compare possible variants
9240 // and choose which of splits to be a quadrangle
9241 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9242 if ( nbFaceNodes == 3 ) {
9243 iBestQuad = nbSplits;
9246 else if ( nbFaceNodes == 4 ) {
9247 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9248 double aBestRate = DBL_MAX;
9249 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9251 double aBadRate = 0;
9252 // evaluate elements quality
9253 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9254 if ( iSplit == iQuad ) {
9255 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9259 aBadRate += getBadRate( &quad, aCrit );
9262 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9264 nodes[ iSplit < iQuad ? i4 : i3 ]);
9265 aBadRate += getBadRate( &tria, aCrit );
9269 if ( aBadRate < aBestRate ) {
9271 aBestRate = aBadRate;
9276 // create new elements
9277 int aShapeId = FindShape( theFace );
9280 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9281 SMDS_MeshElement* newElem = 0;
9282 if ( iSplit == iBestQuad )
9283 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9288 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9290 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9291 myLastCreatedElems.Append(newElem);
9292 if ( aShapeId && newElem )
9293 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9296 // change nodes of theFace
9297 const SMDS_MeshNode* newNodes[ 4 ];
9298 newNodes[ 0 ] = linkNodes[ i1 ];
9299 newNodes[ 1 ] = linkNodes[ i2 ];
9300 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9301 newNodes[ 3 ] = nodes[ i4 ];
9302 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9303 const SMDS_MeshElement* newElem = 0;
9304 if (iSplit == iBestQuad)
9305 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9307 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9308 myLastCreatedElems.Append(newElem);
9309 if ( aShapeId && newElem )
9310 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9311 } // end if(!theFace->IsQuadratic())
9312 else { // theFace is quadratic
9313 // we have to split theFace on simple triangles and one simple quadrangle
9315 int nbshift = tmp*2;
9316 // shift nodes in nodes[] by nbshift
9318 for(i=0; i<nbshift; i++) {
9319 const SMDS_MeshNode* n = nodes[0];
9320 for(j=0; j<nbFaceNodes-1; j++) {
9321 nodes[j] = nodes[j+1];
9323 nodes[nbFaceNodes-1] = n;
9325 il1 = il1 - nbshift;
9326 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9327 // n0 n1 n2 n0 n1 n2
9328 // +-----+-----+ +-----+-----+
9337 // create new elements
9338 int aShapeId = FindShape( theFace );
9341 if(nbFaceNodes==6) { // quadratic triangle
9342 SMDS_MeshElement* newElem =
9343 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9344 myLastCreatedElems.Append(newElem);
9345 if ( aShapeId && newElem )
9346 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9347 if(theFace->IsMediumNode(nodes[il1])) {
9348 // create quadrangle
9349 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9350 myLastCreatedElems.Append(newElem);
9351 if ( aShapeId && newElem )
9352 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9358 // create quadrangle
9359 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9360 myLastCreatedElems.Append(newElem);
9361 if ( aShapeId && newElem )
9362 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9368 else { // nbFaceNodes==8 - quadratic quadrangle
9369 SMDS_MeshElement* newElem =
9370 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9371 myLastCreatedElems.Append(newElem);
9372 if ( aShapeId && newElem )
9373 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9374 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9375 myLastCreatedElems.Append(newElem);
9376 if ( aShapeId && newElem )
9377 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9378 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9379 myLastCreatedElems.Append(newElem);
9380 if ( aShapeId && newElem )
9381 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9382 if(theFace->IsMediumNode(nodes[il1])) {
9383 // create quadrangle
9384 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9385 myLastCreatedElems.Append(newElem);
9386 if ( aShapeId && newElem )
9387 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9393 // create quadrangle
9394 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9395 myLastCreatedElems.Append(newElem);
9396 if ( aShapeId && newElem )
9397 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9403 // create needed triangles using n1,n2,n3 and inserted nodes
9404 int nbn = 2 + aNodesToInsert.size();
9405 //const SMDS_MeshNode* aNodes[nbn];
9406 vector<const SMDS_MeshNode*> aNodes(nbn);
9407 aNodes[0] = nodes[n1];
9408 aNodes[nbn-1] = nodes[n2];
9409 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9410 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9411 aNodes[iNode++] = *nIt;
9413 for(i=1; i<nbn; i++) {
9414 SMDS_MeshElement* newElem =
9415 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9416 myLastCreatedElems.Append(newElem);
9417 if ( aShapeId && newElem )
9418 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9422 aMesh->RemoveElement(theFace);
9425 //=======================================================================
9426 //function : UpdateVolumes
9428 //=======================================================================
9429 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9430 const SMDS_MeshNode* theBetweenNode2,
9431 list<const SMDS_MeshNode*>& theNodesToInsert)
9433 myLastCreatedElems.Clear();
9434 myLastCreatedNodes.Clear();
9436 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9437 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9438 const SMDS_MeshElement* elem = invElemIt->next();
9440 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9441 SMDS_VolumeTool aVolume (elem);
9442 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9445 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9446 int iface, nbFaces = aVolume.NbFaces();
9447 vector<const SMDS_MeshNode *> poly_nodes;
9448 vector<int> quantities (nbFaces);
9450 for (iface = 0; iface < nbFaces; iface++) {
9451 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9452 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9453 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9455 for (int inode = 0; inode < nbFaceNodes; inode++) {
9456 poly_nodes.push_back(faceNodes[inode]);
9458 if (nbInserted == 0) {
9459 if (faceNodes[inode] == theBetweenNode1) {
9460 if (faceNodes[inode + 1] == theBetweenNode2) {
9461 nbInserted = theNodesToInsert.size();
9463 // add nodes to insert
9464 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9465 for (; nIt != theNodesToInsert.end(); nIt++) {
9466 poly_nodes.push_back(*nIt);
9470 else if (faceNodes[inode] == theBetweenNode2) {
9471 if (faceNodes[inode + 1] == theBetweenNode1) {
9472 nbInserted = theNodesToInsert.size();
9474 // add nodes to insert in reversed order
9475 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9477 for (; nIt != theNodesToInsert.begin(); nIt--) {
9478 poly_nodes.push_back(*nIt);
9480 poly_nodes.push_back(*nIt);
9487 quantities[iface] = nbFaceNodes + nbInserted;
9490 // Replace or update the volume
9491 SMESHDS_Mesh *aMesh = GetMeshDS();
9493 if (elem->IsPoly()) {
9494 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9498 int aShapeId = FindShape( elem );
9500 SMDS_MeshElement* newElem =
9501 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9502 myLastCreatedElems.Append(newElem);
9503 if (aShapeId && newElem)
9504 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9506 aMesh->RemoveElement(elem);
9513 //================================================================================
9515 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9517 //================================================================================
9519 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9520 vector<const SMDS_MeshNode *> & nodes,
9521 vector<int> & nbNodeInFaces )
9524 nbNodeInFaces.clear();
9525 SMDS_VolumeTool vTool ( elem );
9526 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9528 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9529 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9530 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9535 //=======================================================================
9537 * \brief Convert elements contained in a submesh to quadratic
9538 * \return int - nb of checked elements
9540 //=======================================================================
9542 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9543 SMESH_MesherHelper& theHelper,
9544 const bool theForce3d)
9547 if( !theSm ) return nbElem;
9549 vector<int> nbNodeInFaces;
9550 vector<const SMDS_MeshNode *> nodes;
9551 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9552 while(ElemItr->more())
9555 const SMDS_MeshElement* elem = ElemItr->next();
9556 if( !elem || elem->IsQuadratic() ) continue;
9558 // get elem data needed to re-create it
9560 const int id = elem->GetID();
9561 const int nbNodes = elem->NbNodes();
9562 const SMDSAbs_ElementType aType = elem->GetType();
9563 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9564 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9565 if ( aGeomType == SMDSEntity_Polyhedra )
9566 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9567 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9568 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9570 // remove a linear element
9571 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9573 const SMDS_MeshElement* NewElem = 0;
9579 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9587 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9590 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9593 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9598 case SMDSAbs_Volume :
9602 case SMDSEntity_Tetra:
9603 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9605 case SMDSEntity_Pyramid:
9606 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9608 case SMDSEntity_Penta:
9609 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9611 case SMDSEntity_Hexa:
9612 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9613 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9615 case SMDSEntity_Hexagonal_Prism:
9617 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9624 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9626 theSm->AddElement( NewElem );
9631 //=======================================================================
9632 //function : ConvertToQuadratic
9634 //=======================================================================
9636 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9638 SMESHDS_Mesh* meshDS = GetMeshDS();
9640 SMESH_MesherHelper aHelper(*myMesh);
9641 aHelper.SetIsQuadratic( true );
9643 int nbCheckedElems = 0;
9644 if ( myMesh->HasShapeToMesh() )
9646 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9648 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9649 while ( smIt->more() ) {
9650 SMESH_subMesh* sm = smIt->next();
9651 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9652 aHelper.SetSubShape( sm->GetSubShape() );
9653 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9658 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9659 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9661 SMESHDS_SubMesh *smDS = 0;
9662 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9663 while(aEdgeItr->more())
9665 const SMDS_MeshEdge* edge = aEdgeItr->next();
9666 if(edge && !edge->IsQuadratic())
9668 int id = edge->GetID();
9669 //MESSAGE("edge->GetID() " << id);
9670 const SMDS_MeshNode* n1 = edge->GetNode(0);
9671 const SMDS_MeshNode* n2 = edge->GetNode(1);
9673 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9675 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9676 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9679 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9680 while(aFaceItr->more())
9682 const SMDS_MeshFace* face = aFaceItr->next();
9683 if(!face || face->IsQuadratic() ) continue;
9685 const int id = face->GetID();
9686 const SMDSAbs_EntityType type = face->GetEntityType();
9687 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9689 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9691 SMDS_MeshFace * NewFace = 0;
9694 case SMDSEntity_Triangle:
9695 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9697 case SMDSEntity_Quadrangle:
9698 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9701 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9703 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9705 vector<int> nbNodeInFaces;
9706 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9707 while(aVolumeItr->more())
9709 const SMDS_MeshVolume* volume = aVolumeItr->next();
9710 if(!volume || volume->IsQuadratic() ) continue;
9712 const int id = volume->GetID();
9713 const SMDSAbs_EntityType type = volume->GetEntityType();
9714 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9715 if ( type == SMDSEntity_Polyhedra )
9716 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9717 else if ( type == SMDSEntity_Hexagonal_Prism )
9718 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9720 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9722 SMDS_MeshVolume * NewVolume = 0;
9725 case SMDSEntity_Tetra:
9726 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9728 case SMDSEntity_Hexa:
9729 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9730 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9732 case SMDSEntity_Pyramid:
9733 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9734 nodes[3], nodes[4], id, theForce3d);
9736 case SMDSEntity_Penta:
9737 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9738 nodes[3], nodes[4], nodes[5], id, theForce3d);
9740 case SMDSEntity_Hexagonal_Prism:
9742 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9744 ReplaceElemInGroups(volume, NewVolume, meshDS);
9749 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9750 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9751 aHelper.FixQuadraticElements(myError);
9755 //================================================================================
9757 * \brief Makes given elements quadratic
9758 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9759 * \param theElements - elements to make quadratic
9761 //================================================================================
9763 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9764 TIDSortedElemSet& theElements)
9766 if ( theElements.empty() ) return;
9768 // we believe that all theElements are of the same type
9769 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9771 // get all nodes shared by theElements
9772 TIDSortedNodeSet allNodes;
9773 TIDSortedElemSet::iterator eIt = theElements.begin();
9774 for ( ; eIt != theElements.end(); ++eIt )
9775 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9777 // complete theElements with elements of lower dim whose all nodes are in allNodes
9779 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9780 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9781 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9782 for ( ; nIt != allNodes.end(); ++nIt )
9784 const SMDS_MeshNode* n = *nIt;
9785 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9786 while ( invIt->more() )
9788 const SMDS_MeshElement* e = invIt->next();
9789 if ( e->IsQuadratic() )
9791 quadAdjacentElems[ e->GetType() ].insert( e );
9794 if ( e->GetType() >= elemType )
9796 continue; // same type of more complex linear element
9799 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9800 continue; // e is already checked
9804 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9805 while ( nodeIt->more() && allIn )
9806 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9808 theElements.insert(e );
9812 SMESH_MesherHelper helper(*myMesh);
9813 helper.SetIsQuadratic( true );
9815 // add links of quadratic adjacent elements to the helper
9817 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9818 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9819 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9821 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9823 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9824 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9825 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9827 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9829 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9830 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9831 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9833 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9836 // make quadratic elements instead of linear ones
9838 SMESHDS_Mesh* meshDS = GetMeshDS();
9839 SMESHDS_SubMesh* smDS = 0;
9840 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9842 const SMDS_MeshElement* elem = *eIt;
9843 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9846 const int id = elem->GetID();
9847 const SMDSAbs_ElementType type = elem->GetType();
9848 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9850 if ( !smDS || !smDS->Contains( elem ))
9851 smDS = meshDS->MeshElements( elem->getshapeId() );
9852 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9854 SMDS_MeshElement * newElem = 0;
9855 switch( nodes.size() )
9857 case 4: // cases for most frequently used element types go first (for optimization)
9858 if ( type == SMDSAbs_Volume )
9859 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9861 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9864 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9865 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9868 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9871 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9874 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9875 nodes[4], id, theForce3d);
9878 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9879 nodes[4], nodes[5], id, theForce3d);
9883 ReplaceElemInGroups( elem, newElem, meshDS);
9884 if( newElem && smDS )
9885 smDS->AddElement( newElem );
9888 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9889 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9890 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9891 helper.FixQuadraticElements( myError );
9895 //=======================================================================
9897 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9898 * \return int - nb of checked elements
9900 //=======================================================================
9902 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9903 SMDS_ElemIteratorPtr theItr,
9904 const int theShapeID)
9907 SMESHDS_Mesh* meshDS = GetMeshDS();
9909 while( theItr->more() )
9911 const SMDS_MeshElement* elem = theItr->next();
9913 if( elem && elem->IsQuadratic())
9915 int id = elem->GetID();
9916 int nbCornerNodes = elem->NbCornerNodes();
9917 SMDSAbs_ElementType aType = elem->GetType();
9919 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9921 //remove a quadratic element
9922 if ( !theSm || !theSm->Contains( elem ))
9923 theSm = meshDS->MeshElements( elem->getshapeId() );
9924 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9926 // remove medium nodes
9927 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9928 if ( nodes[i]->NbInverseElements() == 0 )
9929 meshDS->RemoveFreeNode( nodes[i], theSm );
9931 // add a linear element
9932 nodes.resize( nbCornerNodes );
9933 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9934 ReplaceElemInGroups(elem, newElem, meshDS);
9935 if( theSm && newElem )
9936 theSm->AddElement( newElem );
9942 //=======================================================================
9943 //function : ConvertFromQuadratic
9945 //=======================================================================
9947 bool SMESH_MeshEditor::ConvertFromQuadratic()
9949 int nbCheckedElems = 0;
9950 if ( myMesh->HasShapeToMesh() )
9952 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9954 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9955 while ( smIt->more() ) {
9956 SMESH_subMesh* sm = smIt->next();
9957 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9958 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9964 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9965 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9967 SMESHDS_SubMesh *aSM = 0;
9968 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9976 //================================================================================
9978 * \brief Return true if all medium nodes of the element are in the node set
9980 //================================================================================
9982 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9984 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9985 if ( !nodeSet.count( elem->GetNode(i) ))
9991 //================================================================================
9993 * \brief Makes given elements linear
9995 //================================================================================
9997 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9999 if ( theElements.empty() ) return;
10001 // collect IDs of medium nodes of theElements; some of these nodes will be removed
10002 set<int> mediumNodeIDs;
10003 TIDSortedElemSet::iterator eIt = theElements.begin();
10004 for ( ; eIt != theElements.end(); ++eIt )
10006 const SMDS_MeshElement* e = *eIt;
10007 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10008 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10011 // replace given elements by linear ones
10012 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10013 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10014 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10016 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10017 // except those elements sharing medium nodes of quadratic element whose medium nodes
10018 // are not all in mediumNodeIDs
10020 // get remaining medium nodes
10021 TIDSortedNodeSet mediumNodes;
10022 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10023 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10024 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10025 mediumNodes.insert( mediumNodes.end(), n );
10027 // find more quadratic elements to convert
10028 TIDSortedElemSet moreElemsToConvert;
10029 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10030 for ( ; nIt != mediumNodes.end(); ++nIt )
10032 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10033 while ( invIt->more() )
10035 const SMDS_MeshElement* e = invIt->next();
10036 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10038 // find a more complex element including e and
10039 // whose medium nodes are not in mediumNodes
10040 bool complexFound = false;
10041 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10043 SMDS_ElemIteratorPtr invIt2 =
10044 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10045 while ( invIt2->more() )
10047 const SMDS_MeshElement* eComplex = invIt2->next();
10048 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10050 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10051 if ( nbCommonNodes == e->NbNodes())
10053 complexFound = true;
10054 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10060 if ( !complexFound )
10061 moreElemsToConvert.insert( e );
10065 elemIt = SMDS_ElemIteratorPtr
10066 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10067 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10070 //=======================================================================
10071 //function : SewSideElements
10073 //=======================================================================
10075 SMESH_MeshEditor::Sew_Error
10076 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10077 TIDSortedElemSet& theSide2,
10078 const SMDS_MeshNode* theFirstNode1,
10079 const SMDS_MeshNode* theFirstNode2,
10080 const SMDS_MeshNode* theSecondNode1,
10081 const SMDS_MeshNode* theSecondNode2)
10083 myLastCreatedElems.Clear();
10084 myLastCreatedNodes.Clear();
10086 MESSAGE ("::::SewSideElements()");
10087 if ( theSide1.size() != theSide2.size() )
10088 return SEW_DIFF_NB_OF_ELEMENTS;
10090 Sew_Error aResult = SEW_OK;
10092 // 1. Build set of faces representing each side
10093 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10094 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10096 // =======================================================================
10097 // 1. Build set of faces representing each side:
10098 // =======================================================================
10099 // a. build set of nodes belonging to faces
10100 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10101 // c. create temporary faces representing side of volumes if correspondent
10102 // face does not exist
10104 SMESHDS_Mesh* aMesh = GetMeshDS();
10105 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10106 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10107 TIDSortedElemSet faceSet1, faceSet2;
10108 set<const SMDS_MeshElement*> volSet1, volSet2;
10109 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10110 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10111 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10112 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10113 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10114 int iSide, iFace, iNode;
10116 list<const SMDS_MeshElement* > tempFaceList;
10117 for ( iSide = 0; iSide < 2; iSide++ ) {
10118 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10119 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10120 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10121 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10122 set<const SMDS_MeshElement*>::iterator vIt;
10123 TIDSortedElemSet::iterator eIt;
10124 set<const SMDS_MeshNode*>::iterator nIt;
10126 // check that given nodes belong to given elements
10127 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10128 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10129 int firstIndex = -1, secondIndex = -1;
10130 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10131 const SMDS_MeshElement* elem = *eIt;
10132 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10133 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10134 if ( firstIndex > -1 && secondIndex > -1 ) break;
10136 if ( firstIndex < 0 || secondIndex < 0 ) {
10137 // we can simply return until temporary faces created
10138 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10141 // -----------------------------------------------------------
10142 // 1a. Collect nodes of existing faces
10143 // and build set of face nodes in order to detect missing
10144 // faces corresponding to sides of volumes
10145 // -----------------------------------------------------------
10147 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10149 // loop on the given element of a side
10150 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10151 //const SMDS_MeshElement* elem = *eIt;
10152 const SMDS_MeshElement* elem = *eIt;
10153 if ( elem->GetType() == SMDSAbs_Face ) {
10154 faceSet->insert( elem );
10155 set <const SMDS_MeshNode*> faceNodeSet;
10156 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10157 while ( nodeIt->more() ) {
10158 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10159 nodeSet->insert( n );
10160 faceNodeSet.insert( n );
10162 setOfFaceNodeSet.insert( faceNodeSet );
10164 else if ( elem->GetType() == SMDSAbs_Volume )
10165 volSet->insert( elem );
10167 // ------------------------------------------------------------------------------
10168 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10169 // ------------------------------------------------------------------------------
10171 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10172 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10173 while ( fIt->more() ) { // loop on faces sharing a node
10174 const SMDS_MeshElement* f = fIt->next();
10175 if ( faceSet->find( f ) == faceSet->end() ) {
10176 // check if all nodes are in nodeSet and
10177 // complete setOfFaceNodeSet if they are
10178 set <const SMDS_MeshNode*> faceNodeSet;
10179 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10180 bool allInSet = true;
10181 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10182 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10183 if ( nodeSet->find( n ) == nodeSet->end() )
10186 faceNodeSet.insert( n );
10189 faceSet->insert( f );
10190 setOfFaceNodeSet.insert( faceNodeSet );
10196 // -------------------------------------------------------------------------
10197 // 1c. Create temporary faces representing sides of volumes if correspondent
10198 // face does not exist
10199 // -------------------------------------------------------------------------
10201 if ( !volSet->empty() ) {
10202 //int nodeSetSize = nodeSet->size();
10204 // loop on given volumes
10205 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10206 SMDS_VolumeTool vol (*vIt);
10207 // loop on volume faces: find free faces
10208 // --------------------------------------
10209 list<const SMDS_MeshElement* > freeFaceList;
10210 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10211 if ( !vol.IsFreeFace( iFace ))
10213 // check if there is already a face with same nodes in a face set
10214 const SMDS_MeshElement* aFreeFace = 0;
10215 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10216 int nbNodes = vol.NbFaceNodes( iFace );
10217 set <const SMDS_MeshNode*> faceNodeSet;
10218 vol.GetFaceNodes( iFace, faceNodeSet );
10219 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10221 // no such a face is given but it still can exist, check it
10222 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10223 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10225 if ( !aFreeFace ) {
10226 // create a temporary face
10227 if ( nbNodes == 3 ) {
10228 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10229 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10231 else if ( nbNodes == 4 ) {
10232 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10233 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10236 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10237 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10238 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10241 tempFaceList.push_back( aFreeFace );
10245 freeFaceList.push_back( aFreeFace );
10247 } // loop on faces of a volume
10249 // choose one of several free faces of a volume
10250 // --------------------------------------------
10251 if ( freeFaceList.size() > 1 ) {
10252 // choose a face having max nb of nodes shared by other elems of a side
10253 int maxNbNodes = -1;
10254 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10255 while ( fIt != freeFaceList.end() ) { // loop on free faces
10256 int nbSharedNodes = 0;
10257 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10258 while ( nodeIt->more() ) { // loop on free face nodes
10259 const SMDS_MeshNode* n =
10260 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10261 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10262 while ( invElemIt->more() ) {
10263 const SMDS_MeshElement* e = invElemIt->next();
10264 nbSharedNodes += faceSet->count( e );
10265 nbSharedNodes += elemSet->count( e );
10268 if ( nbSharedNodes > maxNbNodes ) {
10269 maxNbNodes = nbSharedNodes;
10270 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10272 else if ( nbSharedNodes == maxNbNodes ) {
10276 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10279 if ( freeFaceList.size() > 1 )
10281 // could not choose one face, use another way
10282 // choose a face most close to the bary center of the opposite side
10283 gp_XYZ aBC( 0., 0., 0. );
10284 set <const SMDS_MeshNode*> addedNodes;
10285 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10286 eIt = elemSet2->begin();
10287 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10288 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10289 while ( nodeIt->more() ) { // loop on free face nodes
10290 const SMDS_MeshNode* n =
10291 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10292 if ( addedNodes.insert( n ).second )
10293 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10296 aBC /= addedNodes.size();
10297 double minDist = DBL_MAX;
10298 fIt = freeFaceList.begin();
10299 while ( fIt != freeFaceList.end() ) { // loop on free faces
10301 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10302 while ( nodeIt->more() ) { // loop on free face nodes
10303 const SMDS_MeshNode* n =
10304 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10305 gp_XYZ p( n->X(),n->Y(),n->Z() );
10306 dist += ( aBC - p ).SquareModulus();
10308 if ( dist < minDist ) {
10310 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10313 fIt = freeFaceList.erase( fIt++ );
10316 } // choose one of several free faces of a volume
10318 if ( freeFaceList.size() == 1 ) {
10319 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10320 faceSet->insert( aFreeFace );
10321 // complete a node set with nodes of a found free face
10322 // for ( iNode = 0; iNode < ; iNode++ )
10323 // nodeSet->insert( fNodes[ iNode ] );
10326 } // loop on volumes of a side
10328 // // complete a set of faces if new nodes in a nodeSet appeared
10329 // // ----------------------------------------------------------
10330 // if ( nodeSetSize != nodeSet->size() ) {
10331 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10332 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10333 // while ( fIt->more() ) { // loop on faces sharing a node
10334 // const SMDS_MeshElement* f = fIt->next();
10335 // if ( faceSet->find( f ) == faceSet->end() ) {
10336 // // check if all nodes are in nodeSet and
10337 // // complete setOfFaceNodeSet if they are
10338 // set <const SMDS_MeshNode*> faceNodeSet;
10339 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10340 // bool allInSet = true;
10341 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10342 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10343 // if ( nodeSet->find( n ) == nodeSet->end() )
10344 // allInSet = false;
10346 // faceNodeSet.insert( n );
10348 // if ( allInSet ) {
10349 // faceSet->insert( f );
10350 // setOfFaceNodeSet.insert( faceNodeSet );
10356 } // Create temporary faces, if there are volumes given
10359 if ( faceSet1.size() != faceSet2.size() ) {
10360 // delete temporary faces: they are in reverseElements of actual nodes
10361 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10362 // while ( tmpFaceIt->more() )
10363 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10364 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10365 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10366 // aMesh->RemoveElement(*tmpFaceIt);
10367 MESSAGE("Diff nb of faces");
10368 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10371 // ============================================================
10372 // 2. Find nodes to merge:
10373 // bind a node to remove to a node to put instead
10374 // ============================================================
10376 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10377 if ( theFirstNode1 != theFirstNode2 )
10378 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10379 if ( theSecondNode1 != theSecondNode2 )
10380 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10382 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10383 set< long > linkIdSet; // links to process
10384 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10386 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10387 list< NLink > linkList[2];
10388 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10389 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10390 // loop on links in linkList; find faces by links and append links
10391 // of the found faces to linkList
10392 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10393 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10395 NLink link[] = { *linkIt[0], *linkIt[1] };
10396 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10397 if ( !linkIdSet.count( linkID ) )
10400 // by links, find faces in the face sets,
10401 // and find indices of link nodes in the found faces;
10402 // in a face set, there is only one or no face sharing a link
10403 // ---------------------------------------------------------------
10405 const SMDS_MeshElement* face[] = { 0, 0 };
10406 vector<const SMDS_MeshNode*> fnodes[2];
10407 int iLinkNode[2][2];
10408 TIDSortedElemSet avoidSet;
10409 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10410 const SMDS_MeshNode* n1 = link[iSide].first;
10411 const SMDS_MeshNode* n2 = link[iSide].second;
10412 //cout << "Side " << iSide << " ";
10413 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10414 // find a face by two link nodes
10415 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10416 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10417 if ( face[ iSide ])
10419 //cout << " F " << face[ iSide]->GetID() <<endl;
10420 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10421 // put face nodes to fnodes
10422 if ( face[ iSide ]->IsQuadratic() )
10424 // use interlaced nodes iterator
10425 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10426 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10427 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10428 while ( nIter->more() )
10429 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10433 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10434 face[ iSide ]->end_nodes() );
10436 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10440 // check similarity of elements of the sides
10441 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10442 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10443 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10444 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10447 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10449 break; // do not return because it's necessary to remove tmp faces
10452 // set nodes to merge
10453 // -------------------
10455 if ( face[0] && face[1] ) {
10456 const int nbNodes = face[0]->NbNodes();
10457 if ( nbNodes != face[1]->NbNodes() ) {
10458 MESSAGE("Diff nb of face nodes");
10459 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10460 break; // do not return because it s necessary to remove tmp faces
10462 bool reverse[] = { false, false }; // order of nodes in the link
10463 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10464 // analyse link orientation in faces
10465 int i1 = iLinkNode[ iSide ][ 0 ];
10466 int i2 = iLinkNode[ iSide ][ 1 ];
10467 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10469 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10470 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10471 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10473 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10474 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10477 // add other links of the faces to linkList
10478 // -----------------------------------------
10480 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10481 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10482 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10483 if ( !iter_isnew.second ) { // already in a set: no need to process
10484 linkIdSet.erase( iter_isnew.first );
10486 else // new in set == encountered for the first time: add
10488 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10489 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10490 linkList[0].push_back ( NLink( n1, n2 ));
10491 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10496 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10499 } // loop on link lists
10501 if ( aResult == SEW_OK &&
10502 ( //linkIt[0] != linkList[0].end() ||
10503 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10504 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10505 " " << (faceSetPtr[1]->empty()));
10506 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10509 // ====================================================================
10510 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10511 // ====================================================================
10513 // delete temporary faces
10514 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10515 // while ( tmpFaceIt->more() )
10516 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10517 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10518 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10519 aMesh->RemoveElement(*tmpFaceIt);
10521 if ( aResult != SEW_OK)
10524 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10525 // loop on nodes replacement map
10526 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10527 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10528 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10529 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10530 nodeIDsToRemove.push_back( nToRemove->GetID() );
10531 // loop on elements sharing nToRemove
10532 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10533 while ( invElemIt->more() ) {
10534 const SMDS_MeshElement* e = invElemIt->next();
10535 // get a new suite of nodes: make replacement
10536 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10537 vector< const SMDS_MeshNode*> nodes( nbNodes );
10538 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10539 while ( nIt->more() ) {
10540 const SMDS_MeshNode* n =
10541 static_cast<const SMDS_MeshNode*>( nIt->next() );
10542 nnIt = nReplaceMap.find( n );
10543 if ( nnIt != nReplaceMap.end() ) {
10545 n = (*nnIt).second;
10549 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10550 // elemIDsToRemove.push_back( e->GetID() );
10554 SMDSAbs_ElementType etyp = e->GetType();
10555 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10558 myLastCreatedElems.Append(newElem);
10559 AddToSameGroups(newElem, e, aMesh);
10560 int aShapeId = e->getshapeId();
10563 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10566 aMesh->RemoveElement(e);
10571 Remove( nodeIDsToRemove, true );
10576 //================================================================================
10578 * \brief Find corresponding nodes in two sets of faces
10579 * \param theSide1 - first face set
10580 * \param theSide2 - second first face
10581 * \param theFirstNode1 - a boundary node of set 1
10582 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10583 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10584 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10585 * \param nReplaceMap - output map of corresponding nodes
10586 * \return bool - is a success or not
10588 //================================================================================
10591 //#define DEBUG_MATCHING_NODES
10594 SMESH_MeshEditor::Sew_Error
10595 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10596 set<const SMDS_MeshElement*>& theSide2,
10597 const SMDS_MeshNode* theFirstNode1,
10598 const SMDS_MeshNode* theFirstNode2,
10599 const SMDS_MeshNode* theSecondNode1,
10600 const SMDS_MeshNode* theSecondNode2,
10601 TNodeNodeMap & nReplaceMap)
10603 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10605 nReplaceMap.clear();
10606 if ( theFirstNode1 != theFirstNode2 )
10607 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10608 if ( theSecondNode1 != theSecondNode2 )
10609 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10611 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10612 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10614 list< NLink > linkList[2];
10615 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10616 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10618 // loop on links in linkList; find faces by links and append links
10619 // of the found faces to linkList
10620 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10621 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10622 NLink link[] = { *linkIt[0], *linkIt[1] };
10623 if ( linkSet.find( link[0] ) == linkSet.end() )
10626 // by links, find faces in the face sets,
10627 // and find indices of link nodes in the found faces;
10628 // in a face set, there is only one or no face sharing a link
10629 // ---------------------------------------------------------------
10631 const SMDS_MeshElement* face[] = { 0, 0 };
10632 list<const SMDS_MeshNode*> notLinkNodes[2];
10633 //bool reverse[] = { false, false }; // order of notLinkNodes
10635 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10637 const SMDS_MeshNode* n1 = link[iSide].first;
10638 const SMDS_MeshNode* n2 = link[iSide].second;
10639 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10640 set< const SMDS_MeshElement* > facesOfNode1;
10641 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10643 // during a loop of the first node, we find all faces around n1,
10644 // during a loop of the second node, we find one face sharing both n1 and n2
10645 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10646 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10647 while ( fIt->more() ) { // loop on faces sharing a node
10648 const SMDS_MeshElement* f = fIt->next();
10649 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10650 ! facesOfNode1.insert( f ).second ) // f encounters twice
10652 if ( face[ iSide ] ) {
10653 MESSAGE( "2 faces per link " );
10654 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10657 faceSet->erase( f );
10659 // get not link nodes
10660 int nbN = f->NbNodes();
10661 if ( f->IsQuadratic() )
10663 nbNodes[ iSide ] = nbN;
10664 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10665 int i1 = f->GetNodeIndex( n1 );
10666 int i2 = f->GetNodeIndex( n2 );
10667 int iEnd = nbN, iBeg = -1, iDelta = 1;
10668 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10670 std::swap( iEnd, iBeg ); iDelta = -1;
10675 if ( i == iEnd ) i = iBeg + iDelta;
10676 if ( i == i1 ) break;
10677 nodes.push_back ( f->GetNode( i ) );
10683 // check similarity of elements of the sides
10684 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10685 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10686 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10687 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10690 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10694 // set nodes to merge
10695 // -------------------
10697 if ( face[0] && face[1] ) {
10698 if ( nbNodes[0] != nbNodes[1] ) {
10699 MESSAGE("Diff nb of face nodes");
10700 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10702 #ifdef DEBUG_MATCHING_NODES
10703 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10704 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10705 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10707 int nbN = nbNodes[0];
10709 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10710 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10711 for ( int i = 0 ; i < nbN - 2; ++i ) {
10712 #ifdef DEBUG_MATCHING_NODES
10713 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10715 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10719 // add other links of the face 1 to linkList
10720 // -----------------------------------------
10722 const SMDS_MeshElement* f0 = face[0];
10723 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10724 for ( int i = 0; i < nbN; i++ )
10726 const SMDS_MeshNode* n2 = f0->GetNode( i );
10727 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10728 linkSet.insert( SMESH_TLink( n1, n2 ));
10729 if ( !iter_isnew.second ) { // already in a set: no need to process
10730 linkSet.erase( iter_isnew.first );
10732 else // new in set == encountered for the first time: add
10734 #ifdef DEBUG_MATCHING_NODES
10735 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10736 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10738 linkList[0].push_back ( NLink( n1, n2 ));
10739 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10744 } // loop on link lists
10749 //================================================================================
10751 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10752 \param theElems - the list of elements (edges or faces) to be replicated
10753 The nodes for duplication could be found from these elements
10754 \param theNodesNot - list of nodes to NOT replicate
10755 \param theAffectedElems - the list of elements (cells and edges) to which the
10756 replicated nodes should be associated to.
10757 \return TRUE if operation has been completed successfully, FALSE otherwise
10759 //================================================================================
10761 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10762 const TIDSortedElemSet& theNodesNot,
10763 const TIDSortedElemSet& theAffectedElems )
10765 myLastCreatedElems.Clear();
10766 myLastCreatedNodes.Clear();
10768 if ( theElems.size() == 0 )
10771 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10776 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10777 // duplicate elements and nodes
10778 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10779 // replce nodes by duplications
10780 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10784 //================================================================================
10786 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10787 \param theMeshDS - mesh instance
10788 \param theElems - the elements replicated or modified (nodes should be changed)
10789 \param theNodesNot - nodes to NOT replicate
10790 \param theNodeNodeMap - relation of old node to new created node
10791 \param theIsDoubleElem - flag os to replicate element or modify
10792 \return TRUE if operation has been completed successfully, FALSE otherwise
10794 //================================================================================
10796 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10797 const TIDSortedElemSet& theElems,
10798 const TIDSortedElemSet& theNodesNot,
10799 std::map< const SMDS_MeshNode*,
10800 const SMDS_MeshNode* >& theNodeNodeMap,
10801 const bool theIsDoubleElem )
10803 MESSAGE("doubleNodes");
10804 // iterate on through element and duplicate them (by nodes duplication)
10806 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10807 for ( ; elemItr != theElems.end(); ++elemItr )
10809 const SMDS_MeshElement* anElem = *elemItr;
10813 bool isDuplicate = false;
10814 // duplicate nodes to duplicate element
10815 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10816 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10818 while ( anIter->more() )
10821 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10822 SMDS_MeshNode* aNewNode = aCurrNode;
10823 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10824 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10825 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10828 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10829 theNodeNodeMap[ aCurrNode ] = aNewNode;
10830 myLastCreatedNodes.Append( aNewNode );
10832 isDuplicate |= (aCurrNode != aNewNode);
10833 newNodes[ ind++ ] = aNewNode;
10835 if ( !isDuplicate )
10838 if ( theIsDoubleElem )
10839 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10842 MESSAGE("ChangeElementNodes");
10843 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10850 //================================================================================
10852 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10853 \param theNodes - identifiers of nodes to be doubled
10854 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10855 nodes. If list of element identifiers is empty then nodes are doubled but
10856 they not assigned to elements
10857 \return TRUE if operation has been completed successfully, FALSE otherwise
10859 //================================================================================
10861 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10862 const std::list< int >& theListOfModifiedElems )
10864 MESSAGE("DoubleNodes");
10865 myLastCreatedElems.Clear();
10866 myLastCreatedNodes.Clear();
10868 if ( theListOfNodes.size() == 0 )
10871 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10875 // iterate through nodes and duplicate them
10877 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10879 std::list< int >::const_iterator aNodeIter;
10880 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10882 int aCurr = *aNodeIter;
10883 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10889 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10892 anOldNodeToNewNode[ aNode ] = aNewNode;
10893 myLastCreatedNodes.Append( aNewNode );
10897 // Create map of new nodes for modified elements
10899 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10901 std::list< int >::const_iterator anElemIter;
10902 for ( anElemIter = theListOfModifiedElems.begin();
10903 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10905 int aCurr = *anElemIter;
10906 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10910 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10912 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10914 while ( anIter->more() )
10916 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10917 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10919 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10920 aNodeArr[ ind++ ] = aNewNode;
10923 aNodeArr[ ind++ ] = aCurrNode;
10925 anElemToNodes[ anElem ] = aNodeArr;
10928 // Change nodes of elements
10930 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10931 anElemToNodesIter = anElemToNodes.begin();
10932 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10934 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10935 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10938 MESSAGE("ChangeElementNodes");
10939 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10948 //================================================================================
10950 \brief Check if element located inside shape
10951 \return TRUE if IN or ON shape, FALSE otherwise
10953 //================================================================================
10955 template<class Classifier>
10956 bool isInside(const SMDS_MeshElement* theElem,
10957 Classifier& theClassifier,
10958 const double theTol)
10960 gp_XYZ centerXYZ (0, 0, 0);
10961 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10962 while (aNodeItr->more())
10963 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10965 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10966 theClassifier.Perform(aPnt, theTol);
10967 TopAbs_State aState = theClassifier.State();
10968 return (aState == TopAbs_IN || aState == TopAbs_ON );
10971 //================================================================================
10973 * \brief Classifier of the 3D point on the TopoDS_Face
10974 * with interaface suitable for isInside()
10976 //================================================================================
10978 struct _FaceClassifier
10980 Extrema_ExtPS _extremum;
10981 BRepAdaptor_Surface _surface;
10982 TopAbs_State _state;
10984 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10986 _extremum.Initialize( _surface,
10987 _surface.FirstUParameter(), _surface.LastUParameter(),
10988 _surface.FirstVParameter(), _surface.LastVParameter(),
10989 _surface.Tolerance(), _surface.Tolerance() );
10991 void Perform(const gp_Pnt& aPnt, double theTol)
10993 _state = TopAbs_OUT;
10994 _extremum.Perform(aPnt);
10995 if ( _extremum.IsDone() )
10996 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10997 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10998 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11000 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11003 TopAbs_State State() const
11010 //================================================================================
11012 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11013 This method is the first step of DoubleNodeElemGroupsInRegion.
11014 \param theElems - list of groups of elements (edges or faces) to be replicated
11015 \param theNodesNot - list of groups of nodes not to replicated
11016 \param theShape - shape to detect affected elements (element which geometric center
11017 located on or inside shape).
11018 The replicated nodes should be associated to affected elements.
11019 \return groups of affected elements
11020 \sa DoubleNodeElemGroupsInRegion()
11022 //================================================================================
11024 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11025 const TIDSortedElemSet& theNodesNot,
11026 const TopoDS_Shape& theShape,
11027 TIDSortedElemSet& theAffectedElems)
11029 if ( theShape.IsNull() )
11032 const double aTol = Precision::Confusion();
11033 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11034 auto_ptr<_FaceClassifier> aFaceClassifier;
11035 if ( theShape.ShapeType() == TopAbs_SOLID )
11037 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11038 bsc3d->PerformInfinitePoint(aTol);
11040 else if (theShape.ShapeType() == TopAbs_FACE )
11042 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11045 // iterates on indicated elements and get elements by back references from their nodes
11046 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11047 for ( ; elemItr != theElems.end(); ++elemItr )
11049 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11053 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11054 while ( nodeItr->more() )
11056 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11057 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11059 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11060 while ( backElemItr->more() )
11062 const SMDS_MeshElement* curElem = backElemItr->next();
11063 if ( curElem && theElems.find(curElem) == theElems.end() &&
11065 isInside( curElem, *bsc3d, aTol ) :
11066 isInside( curElem, *aFaceClassifier, aTol )))
11067 theAffectedElems.insert( curElem );
11074 //================================================================================
11076 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11077 \param theElems - group of of elements (edges or faces) to be replicated
11078 \param theNodesNot - group of nodes not to replicate
11079 \param theShape - shape to detect affected elements (element which geometric center
11080 located on or inside shape).
11081 The replicated nodes should be associated to affected elements.
11082 \return TRUE if operation has been completed successfully, FALSE otherwise
11084 //================================================================================
11086 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11087 const TIDSortedElemSet& theNodesNot,
11088 const TopoDS_Shape& theShape )
11090 if ( theShape.IsNull() )
11093 const double aTol = Precision::Confusion();
11094 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11095 auto_ptr<_FaceClassifier> aFaceClassifier;
11096 if ( theShape.ShapeType() == TopAbs_SOLID )
11098 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11099 bsc3d->PerformInfinitePoint(aTol);
11101 else if (theShape.ShapeType() == TopAbs_FACE )
11103 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11106 // iterates on indicated elements and get elements by back references from their nodes
11107 TIDSortedElemSet anAffected;
11108 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11109 for ( ; elemItr != theElems.end(); ++elemItr )
11111 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11115 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11116 while ( nodeItr->more() )
11118 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11119 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11121 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11122 while ( backElemItr->more() )
11124 const SMDS_MeshElement* curElem = backElemItr->next();
11125 if ( curElem && theElems.find(curElem) == theElems.end() &&
11127 isInside( curElem, *bsc3d, aTol ) :
11128 isInside( curElem, *aFaceClassifier, aTol )))
11129 anAffected.insert( curElem );
11133 return DoubleNodes( theElems, theNodesNot, anAffected );
11137 * \brief compute an oriented angle between two planes defined by four points.
11138 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11139 * @param p0 base of the rotation axe
11140 * @param p1 extremity of the rotation axe
11141 * @param g1 belongs to the first plane
11142 * @param g2 belongs to the second plane
11144 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11146 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11147 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11148 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11149 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11150 gp_Vec vref(p0, p1);
11153 gp_Vec n1 = vref.Crossed(v1);
11154 gp_Vec n2 = vref.Crossed(v2);
11155 return n2.AngleWithRef(n1, vref);
11159 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11160 * The list of groups must describe a partition of the mesh volumes.
11161 * The nodes of the internal faces at the boundaries of the groups are doubled.
11162 * In option, the internal faces are replaced by flat elements.
11163 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11164 * The flat elements are stored in groups of volumes.
11165 * @param theElems - list of groups of volumes, where a group of volume is a set of
11166 * SMDS_MeshElements sorted by Id.
11167 * @param createJointElems - if TRUE, create the elements
11168 * @return TRUE if operation has been completed successfully, FALSE otherwise
11170 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11171 bool createJointElems)
11173 MESSAGE("----------------------------------------------");
11174 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11175 MESSAGE("----------------------------------------------");
11177 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11178 meshDS->BuildDownWardConnectivity(true);
11180 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11182 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11183 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11184 // build the list of nodes shared by 2 or more domains, with their domain indexes
11186 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11187 std::map<int,int>celldom; // cell vtkId --> domain
11188 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11189 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11190 faceDomains.clear();
11192 cellDomains.clear();
11193 nodeDomains.clear();
11194 std::map<int,int> emptyMap;
11195 std::set<int> emptySet;
11198 for (int idom = 0; idom < theElems.size(); idom++)
11201 // --- build a map (face to duplicate --> volume to modify)
11202 // with all the faces shared by 2 domains (group of elements)
11203 // and corresponding volume of this domain, for each shared face.
11204 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11206 //MESSAGE("Domain " << idom);
11207 const TIDSortedElemSet& domain = theElems[idom];
11208 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11209 for (; elemItr != domain.end(); ++elemItr)
11211 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11214 int vtkId = anElem->getVtkId();
11215 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11216 int neighborsVtkIds[NBMAXNEIGHBORS];
11217 int downIds[NBMAXNEIGHBORS];
11218 unsigned char downTypes[NBMAXNEIGHBORS];
11219 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11220 for (int n = 0; n < nbNeighbors; n++)
11222 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11223 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11224 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11226 DownIdType face(downIds[n], downTypes[n]);
11227 if (!faceDomains.count(face))
11228 faceDomains[face] = emptyMap; // create an empty entry for face
11229 if (!faceDomains[face].count(idom))
11231 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11232 celldom[vtkId] = idom;
11233 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11240 //MESSAGE("Number of shared faces " << faceDomains.size());
11241 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11243 // --- explore the shared faces domain by domain,
11244 // explore the nodes of the face and see if they belong to a cell in the domain,
11245 // which has only a node or an edge on the border (not a shared face)
11247 for (int idomain = 0; idomain < theElems.size(); idomain++)
11249 //MESSAGE("Domain " << idomain);
11250 const TIDSortedElemSet& domain = theElems[idomain];
11251 itface = faceDomains.begin();
11252 for (; itface != faceDomains.end(); ++itface)
11254 std::map<int, int> domvol = itface->second;
11255 if (!domvol.count(idomain))
11257 DownIdType face = itface->first;
11258 //MESSAGE(" --- face " << face.cellId);
11259 std::set<int> oldNodes;
11261 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11262 std::set<int>::iterator itn = oldNodes.begin();
11263 for (; itn != oldNodes.end(); ++itn)
11266 //MESSAGE(" node " << oldId);
11267 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11268 for (int i=0; i<l.ncells; i++)
11270 int vtkId = l.cells[i];
11271 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11272 if (!domain.count(anElem))
11274 int vtkType = grid->GetCellType(vtkId);
11275 int downId = grid->CellIdToDownId(vtkId);
11278 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11279 continue; // not OK at this stage of the algorithm:
11280 //no cells created after BuildDownWardConnectivity
11282 DownIdType aCell(downId, vtkType);
11283 if (!cellDomains.count(aCell))
11284 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11285 cellDomains[aCell][idomain] = vtkId;
11286 celldom[vtkId] = idomain;
11287 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11293 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11294 // for each shared face, get the nodes
11295 // for each node, for each domain of the face, create a clone of the node
11297 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11298 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11299 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11301 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11302 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11303 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11305 for (int idomain = 0; idomain < theElems.size(); idomain++)
11307 itface = faceDomains.begin();
11308 for (; itface != faceDomains.end(); ++itface)
11310 std::map<int, int> domvol = itface->second;
11311 if (!domvol.count(idomain))
11313 DownIdType face = itface->first;
11314 //MESSAGE(" --- face " << face.cellId);
11315 std::set<int> oldNodes;
11317 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11318 std::set<int>::iterator itn = oldNodes.begin();
11319 for (; itn != oldNodes.end(); ++itn)
11322 //MESSAGE("-+-+-a node " << oldId);
11323 if (!nodeDomains.count(oldId))
11324 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11325 if (nodeDomains[oldId].empty())
11327 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11328 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11330 std::map<int, int>::iterator itdom = domvol.begin();
11331 for (; itdom != domvol.end(); ++itdom)
11333 int idom = itdom->first;
11334 //MESSAGE(" domain " << idom);
11335 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11337 if (nodeDomains[oldId].size() >= 2) // a multiple node
11339 vector<int> orderedDoms;
11340 //MESSAGE("multiple node " << oldId);
11341 if (mutipleNodes.count(oldId))
11342 orderedDoms = mutipleNodes[oldId];
11345 map<int,int>::iterator it = nodeDomains[oldId].begin();
11346 for (; it != nodeDomains[oldId].end(); ++it)
11347 orderedDoms.push_back(it->first);
11349 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11350 //stringstream txt;
11351 //for (int i=0; i<orderedDoms.size(); i++)
11352 // txt << orderedDoms[i] << " ";
11353 //MESSAGE("orderedDoms " << txt.str());
11354 mutipleNodes[oldId] = orderedDoms;
11356 double *coords = grid->GetPoint(oldId);
11357 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11358 int newId = newNode->getVtkId();
11359 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11360 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11367 for (int idomain = 0; idomain < theElems.size(); idomain++)
11369 itface = faceDomains.begin();
11370 for (; itface != faceDomains.end(); ++itface)
11372 std::map<int, int> domvol = itface->second;
11373 if (!domvol.count(idomain))
11375 DownIdType face = itface->first;
11376 //MESSAGE(" --- face " << face.cellId);
11377 std::set<int> oldNodes;
11379 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11380 int nbMultipleNodes = 0;
11381 std::set<int>::iterator itn = oldNodes.begin();
11382 for (; itn != oldNodes.end(); ++itn)
11385 if (mutipleNodes.count(oldId))
11388 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11390 //MESSAGE("multiple Nodes detected on a shared face");
11391 int downId = itface->first.cellId;
11392 unsigned char cellType = itface->first.cellType;
11393 // --- shared edge or shared face ?
11394 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11397 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11398 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11399 if (mutipleNodes.count(nodes[i]))
11400 if (!mutipleNodesToFace.count(nodes[i]))
11401 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11403 else // shared face (between two volumes)
11405 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11406 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11407 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11408 for (int ie =0; ie < nbEdges; ie++)
11411 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11412 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11414 vector<int> vn0 = mutipleNodes[nodes[0]];
11415 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11417 for (int i0 = 0; i0 < vn0.size(); i0++)
11418 for (int i1 = 0; i1 < vn1.size(); i1++)
11419 if (vn0[i0] == vn1[i1])
11420 doms.push_back(vn0[i0]);
11421 if (doms.size() >2)
11423 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11424 double *coords = grid->GetPoint(nodes[0]);
11425 gp_Pnt p0(coords[0], coords[1], coords[2]);
11426 coords = grid->GetPoint(nodes[nbNodes - 1]);
11427 gp_Pnt p1(coords[0], coords[1], coords[2]);
11429 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11430 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11431 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11432 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11433 for (int id=0; id < doms.size(); id++)
11435 int idom = doms[id];
11436 for (int ivol=0; ivol<nbvol; ivol++)
11438 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11439 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11440 if (theElems[idom].count(elem))
11442 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11443 domvol[idom] = svol;
11444 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11446 vtkIdType npts = 0;
11447 vtkIdType* pts = 0;
11448 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11449 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11452 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11453 angleDom[idom] = 0;
11457 gp_Pnt g(values[0], values[1], values[2]);
11458 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11459 //MESSAGE(" angle=" << angleDom[idom]);
11465 map<double, int> sortedDom; // sort domains by angle
11466 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11467 sortedDom[ia->second] = ia->first;
11468 vector<int> vnodes;
11470 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11472 vdom.push_back(ib->second);
11473 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11475 for (int ino = 0; ino < nbNodes; ino++)
11476 vnodes.push_back(nodes[ino]);
11477 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11486 // --- iterate on shared faces (volumes to modify, face to extrude)
11487 // get node id's of the face (id SMDS = id VTK)
11488 // create flat element with old and new nodes if requested
11490 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11491 // (domain1 X domain2) = domain1 + MAXINT*domain2
11493 std::map<int, std::map<long,int> > nodeQuadDomains;
11494 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11496 if (createJointElems)
11499 string joints2DName = "joints2D";
11500 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11501 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11502 string joints3DName = "joints3D";
11503 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11504 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11506 itface = faceDomains.begin();
11507 for (; itface != faceDomains.end(); ++itface)
11509 DownIdType face = itface->first;
11510 std::set<int> oldNodes;
11511 std::set<int>::iterator itn;
11513 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11515 std::map<int, int> domvol = itface->second;
11516 std::map<int, int>::iterator itdom = domvol.begin();
11517 int dom1 = itdom->first;
11518 int vtkVolId = itdom->second;
11520 int dom2 = itdom->first;
11521 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11523 stringstream grpname;
11526 grpname << dom1 << "_" << dom2;
11528 grpname << dom2 << "_" << dom1;
11529 string namegrp = grpname.str();
11530 if (!mapOfJunctionGroups.count(namegrp))
11531 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11532 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11534 sgrp->Add(vol->GetID());
11535 if (vol->GetType() == SMDSAbs_Volume)
11536 joints3DGrp->Add(vol->GetID());
11537 else if (vol->GetType() == SMDSAbs_Face)
11538 joints2DGrp->Add(vol->GetID());
11542 // --- create volumes on multiple domain intersection if requested
11543 // iterate on mutipleNodesToFace
11544 // iterate on edgesMultiDomains
11546 if (createJointElems)
11548 // --- iterate on mutipleNodesToFace
11550 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11551 for (; itn != mutipleNodesToFace.end(); ++itn)
11553 int node = itn->first;
11554 vector<int> orderDom = itn->second;
11555 vector<vtkIdType> orderedNodes;
11556 for (int idom = 0; idom <orderDom.size(); idom++)
11557 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11558 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11560 stringstream grpname;
11562 grpname << 0 << "_" << 0;
11564 string namegrp = grpname.str();
11565 if (!mapOfJunctionGroups.count(namegrp))
11566 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11567 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11569 sgrp->Add(face->GetID());
11572 // --- iterate on edgesMultiDomains
11574 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11575 for (; ite != edgesMultiDomains.end(); ++ite)
11577 vector<int> nodes = ite->first;
11578 vector<int> orderDom = ite->second;
11579 vector<vtkIdType> orderedNodes;
11580 if (nodes.size() == 2)
11582 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11583 for (int ino=0; ino < nodes.size(); ino++)
11584 if (orderDom.size() == 3)
11585 for (int idom = 0; idom <orderDom.size(); idom++)
11586 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11588 for (int idom = orderDom.size()-1; idom >=0; idom--)
11589 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11590 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11593 string namegrp = "jointsMultiples";
11594 if (!mapOfJunctionGroups.count(namegrp))
11595 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11596 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11598 sgrp->Add(vol->GetID());
11602 INFOS("Quadratic multiple joints not implemented");
11603 // TODO quadratic nodes
11608 // --- list the explicit faces and edges of the mesh that need to be modified,
11609 // i.e. faces and edges built with one or more duplicated nodes.
11610 // associate these faces or edges to their corresponding domain.
11611 // only the first domain found is kept when a face or edge is shared
11613 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11614 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11615 faceOrEdgeDom.clear();
11618 for (int idomain = 0; idomain < theElems.size(); idomain++)
11620 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11621 for (; itnod != nodeDomains.end(); ++itnod)
11623 int oldId = itnod->first;
11624 //MESSAGE(" node " << oldId);
11625 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11626 for (int i = 0; i < l.ncells; i++)
11628 int vtkId = l.cells[i];
11629 int vtkType = grid->GetCellType(vtkId);
11630 int downId = grid->CellIdToDownId(vtkId);
11632 continue; // new cells: not to be modified
11633 DownIdType aCell(downId, vtkType);
11634 int volParents[1000];
11635 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11636 for (int j = 0; j < nbvol; j++)
11637 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11638 if (!feDom.count(vtkId))
11640 feDom[vtkId] = idomain;
11641 faceOrEdgeDom[aCell] = emptyMap;
11642 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11643 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11644 // << " type " << vtkType << " downId " << downId);
11650 // --- iterate on shared faces (volumes to modify, face to extrude)
11651 // get node id's of the face
11652 // replace old nodes by new nodes in volumes, and update inverse connectivity
11654 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11655 for (int m=0; m<3; m++)
11657 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11658 itface = (*amap).begin();
11659 for (; itface != (*amap).end(); ++itface)
11661 DownIdType face = itface->first;
11662 std::set<int> oldNodes;
11663 std::set<int>::iterator itn;
11665 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11666 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11667 std::map<int, int> localClonedNodeIds;
11669 std::map<int, int> domvol = itface->second;
11670 std::map<int, int>::iterator itdom = domvol.begin();
11671 for (; itdom != domvol.end(); ++itdom)
11673 int idom = itdom->first;
11674 int vtkVolId = itdom->second;
11675 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11676 localClonedNodeIds.clear();
11677 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11680 if (nodeDomains[oldId].count(idom))
11682 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11683 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11686 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11691 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11692 grid->BuildLinks();
11700 * \brief Double nodes on some external faces and create flat elements.
11701 * Flat elements are mainly used by some types of mechanic calculations.
11703 * Each group of the list must be constituted of faces.
11704 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11705 * @param theElems - list of groups of faces, where a group of faces is a set of
11706 * SMDS_MeshElements sorted by Id.
11707 * @return TRUE if operation has been completed successfully, FALSE otherwise
11709 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11711 MESSAGE("-------------------------------------------------");
11712 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11713 MESSAGE("-------------------------------------------------");
11715 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11717 // --- For each group of faces
11718 // duplicate the nodes, create a flat element based on the face
11719 // replace the nodes of the faces by their clones
11721 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11722 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11723 clonedNodes.clear();
11724 intermediateNodes.clear();
11725 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11726 mapOfJunctionGroups.clear();
11728 for (int idom = 0; idom < theElems.size(); idom++)
11730 const TIDSortedElemSet& domain = theElems[idom];
11731 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11732 for (; elemItr != domain.end(); ++elemItr)
11734 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11735 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11738 // MESSAGE("aFace=" << aFace->GetID());
11739 bool isQuad = aFace->IsQuadratic();
11740 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11742 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11744 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11745 while (nodeIt->more())
11747 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11748 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11750 ln2.push_back(node);
11752 ln0.push_back(node);
11754 const SMDS_MeshNode* clone = 0;
11755 if (!clonedNodes.count(node))
11757 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11758 clonedNodes[node] = clone;
11761 clone = clonedNodes[node];
11764 ln3.push_back(clone);
11766 ln1.push_back(clone);
11768 const SMDS_MeshNode* inter = 0;
11769 if (isQuad && (!isMedium))
11771 if (!intermediateNodes.count(node))
11773 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11774 intermediateNodes[node] = inter;
11777 inter = intermediateNodes[node];
11778 ln4.push_back(inter);
11782 // --- extrude the face
11784 vector<const SMDS_MeshNode*> ln;
11785 SMDS_MeshVolume* vol = 0;
11786 vtkIdType aType = aFace->GetVtkType();
11790 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11791 // MESSAGE("vol prism " << vol->GetID());
11792 ln.push_back(ln1[0]);
11793 ln.push_back(ln1[1]);
11794 ln.push_back(ln1[2]);
11797 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11798 // MESSAGE("vol hexa " << vol->GetID());
11799 ln.push_back(ln1[0]);
11800 ln.push_back(ln1[1]);
11801 ln.push_back(ln1[2]);
11802 ln.push_back(ln1[3]);
11804 case VTK_QUADRATIC_TRIANGLE:
11805 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11806 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11807 // MESSAGE("vol quad prism " << vol->GetID());
11808 ln.push_back(ln1[0]);
11809 ln.push_back(ln1[1]);
11810 ln.push_back(ln1[2]);
11811 ln.push_back(ln3[0]);
11812 ln.push_back(ln3[1]);
11813 ln.push_back(ln3[2]);
11815 case VTK_QUADRATIC_QUAD:
11816 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11817 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11818 // ln4[0], ln4[1], ln4[2], ln4[3]);
11819 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11820 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11821 ln4[0], ln4[1], ln4[2], ln4[3]);
11822 // MESSAGE("vol quad hexa " << vol->GetID());
11823 ln.push_back(ln1[0]);
11824 ln.push_back(ln1[1]);
11825 ln.push_back(ln1[2]);
11826 ln.push_back(ln1[3]);
11827 ln.push_back(ln3[0]);
11828 ln.push_back(ln3[1]);
11829 ln.push_back(ln3[2]);
11830 ln.push_back(ln3[3]);
11840 stringstream grpname;
11844 string namegrp = grpname.str();
11845 if (!mapOfJunctionGroups.count(namegrp))
11846 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11847 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11849 sgrp->Add(vol->GetID());
11852 // --- modify the face
11854 aFace->ChangeNodes(&ln[0], ln.size());
11861 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11862 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11863 * groups of faces to remove inside the object, (idem edges).
11864 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11866 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11867 const TopoDS_Shape& theShape,
11868 SMESH_NodeSearcher* theNodeSearcher,
11869 const char* groupName,
11870 std::vector<double>& nodesCoords,
11871 std::vector<std::vector<int> >& listOfListOfNodes)
11873 MESSAGE("--------------------------------");
11874 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11875 MESSAGE("--------------------------------");
11877 // --- zone of volumes to remove is given :
11878 // 1 either by a geom shape (one or more vertices) and a radius,
11879 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11880 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11881 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11882 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11883 // defined by it's name.
11885 SMESHDS_GroupBase* groupDS = 0;
11886 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11887 while ( groupIt->more() )
11890 SMESH_Group * group = groupIt->next();
11891 if ( !group ) continue;
11892 groupDS = group->GetGroupDS();
11893 if ( !groupDS || groupDS->IsEmpty() ) continue;
11894 std::string grpName = group->GetName();
11895 //MESSAGE("grpName=" << grpName);
11896 if (grpName == groupName)
11902 bool isNodeGroup = false;
11903 bool isNodeCoords = false;
11906 if (groupDS->GetType() != SMDSAbs_Node)
11908 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11911 if (nodesCoords.size() > 0)
11912 isNodeCoords = true; // a list o nodes given by their coordinates
11913 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11915 // --- define groups to build
11917 int idg; // --- group of SMDS volumes
11918 string grpvName = groupName;
11919 grpvName += "_vol";
11920 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11923 MESSAGE("group not created " << grpvName);
11926 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11928 int idgs; // --- group of SMDS faces on the skin
11929 string grpsName = groupName;
11930 grpsName += "_skin";
11931 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11934 MESSAGE("group not created " << grpsName);
11937 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11939 int idgi; // --- group of SMDS faces internal (several shapes)
11940 string grpiName = groupName;
11941 grpiName += "_internalFaces";
11942 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11945 MESSAGE("group not created " << grpiName);
11948 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11950 int idgei; // --- group of SMDS faces internal (several shapes)
11951 string grpeiName = groupName;
11952 grpeiName += "_internalEdges";
11953 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11956 MESSAGE("group not created " << grpeiName);
11959 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11961 // --- build downward connectivity
11963 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11964 meshDS->BuildDownWardConnectivity(true);
11965 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11967 // --- set of volumes detected inside
11969 std::set<int> setOfInsideVol;
11970 std::set<int> setOfVolToCheck;
11972 std::vector<gp_Pnt> gpnts;
11975 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11977 MESSAGE("group of nodes provided");
11978 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11979 while ( elemIt->more() )
11981 const SMDS_MeshElement* elem = elemIt->next();
11984 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11987 SMDS_MeshElement* vol = 0;
11988 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11989 while (volItr->more())
11991 vol = (SMDS_MeshElement*)volItr->next();
11992 setOfInsideVol.insert(vol->getVtkId());
11993 sgrp->Add(vol->GetID());
11997 else if (isNodeCoords)
11999 MESSAGE("list of nodes coordinates provided");
12002 while (i < nodesCoords.size()-2)
12004 double x = nodesCoords[i++];
12005 double y = nodesCoords[i++];
12006 double z = nodesCoords[i++];
12007 gp_Pnt p = gp_Pnt(x, y ,z);
12008 gpnts.push_back(p);
12009 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12012 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12014 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12015 TopTools_IndexedMapOfShape vertexMap;
12016 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12017 gp_Pnt p = gp_Pnt(0,0,0);
12018 if (vertexMap.Extent() < 1)
12021 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12023 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12024 p = BRep_Tool::Pnt(vertex);
12025 gpnts.push_back(p);
12026 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12030 if (gpnts.size() > 0)
12033 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12035 nodeId = startNode->GetID();
12036 MESSAGE("nodeId " << nodeId);
12038 double radius2 = radius*radius;
12039 MESSAGE("radius2 " << radius2);
12041 // --- volumes on start node
12043 setOfVolToCheck.clear();
12044 SMDS_MeshElement* startVol = 0;
12045 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12046 while (volItr->more())
12048 startVol = (SMDS_MeshElement*)volItr->next();
12049 setOfVolToCheck.insert(startVol->getVtkId());
12051 if (setOfVolToCheck.empty())
12053 MESSAGE("No volumes found");
12057 // --- starting with central volumes then their neighbors, check if they are inside
12058 // or outside the domain, until no more new neighbor volume is inside.
12059 // Fill the group of inside volumes
12061 std::map<int, double> mapOfNodeDistance2;
12062 mapOfNodeDistance2.clear();
12063 std::set<int> setOfOutsideVol;
12064 while (!setOfVolToCheck.empty())
12066 std::set<int>::iterator it = setOfVolToCheck.begin();
12068 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12069 bool volInside = false;
12070 vtkIdType npts = 0;
12071 vtkIdType* pts = 0;
12072 grid->GetCellPoints(vtkId, npts, pts);
12073 for (int i=0; i<npts; i++)
12075 double distance2 = 0;
12076 if (mapOfNodeDistance2.count(pts[i]))
12078 distance2 = mapOfNodeDistance2[pts[i]];
12079 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12083 double *coords = grid->GetPoint(pts[i]);
12084 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12086 for (int j=0; j<gpnts.size(); j++)
12088 double d2 = aPoint.SquareDistance(gpnts[j]);
12089 if (d2 < distance2)
12092 if (distance2 < radius2)
12096 mapOfNodeDistance2[pts[i]] = distance2;
12097 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12099 if (distance2 < radius2)
12101 volInside = true; // one or more nodes inside the domain
12102 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12108 setOfInsideVol.insert(vtkId);
12109 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12110 int neighborsVtkIds[NBMAXNEIGHBORS];
12111 int downIds[NBMAXNEIGHBORS];
12112 unsigned char downTypes[NBMAXNEIGHBORS];
12113 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12114 for (int n = 0; n < nbNeighbors; n++)
12115 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12116 setOfVolToCheck.insert(neighborsVtkIds[n]);
12120 setOfOutsideVol.insert(vtkId);
12121 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12123 setOfVolToCheck.erase(vtkId);
12127 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12128 // If yes, add the volume to the inside set
12130 bool addedInside = true;
12131 std::set<int> setOfVolToReCheck;
12132 while (addedInside)
12134 MESSAGE(" --------------------------- re check");
12135 addedInside = false;
12136 std::set<int>::iterator itv = setOfInsideVol.begin();
12137 for (; itv != setOfInsideVol.end(); ++itv)
12140 int neighborsVtkIds[NBMAXNEIGHBORS];
12141 int downIds[NBMAXNEIGHBORS];
12142 unsigned char downTypes[NBMAXNEIGHBORS];
12143 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12144 for (int n = 0; n < nbNeighbors; n++)
12145 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12146 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12148 setOfVolToCheck = setOfVolToReCheck;
12149 setOfVolToReCheck.clear();
12150 while (!setOfVolToCheck.empty())
12152 std::set<int>::iterator it = setOfVolToCheck.begin();
12154 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12156 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12157 int countInside = 0;
12158 int neighborsVtkIds[NBMAXNEIGHBORS];
12159 int downIds[NBMAXNEIGHBORS];
12160 unsigned char downTypes[NBMAXNEIGHBORS];
12161 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12162 for (int n = 0; n < nbNeighbors; n++)
12163 if (setOfInsideVol.count(neighborsVtkIds[n]))
12165 MESSAGE("countInside " << countInside);
12166 if (countInside > 1)
12168 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12169 setOfInsideVol.insert(vtkId);
12170 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12171 addedInside = true;
12174 setOfVolToReCheck.insert(vtkId);
12176 setOfVolToCheck.erase(vtkId);
12180 // --- map of Downward faces at the boundary, inside the global volume
12181 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12182 // fill group of SMDS faces inside the volume (when several volume shapes)
12183 // fill group of SMDS faces on the skin of the global volume (if skin)
12185 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12186 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12187 std::set<int>::iterator it = setOfInsideVol.begin();
12188 for (; it != setOfInsideVol.end(); ++it)
12191 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12192 int neighborsVtkIds[NBMAXNEIGHBORS];
12193 int downIds[NBMAXNEIGHBORS];
12194 unsigned char downTypes[NBMAXNEIGHBORS];
12195 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12196 for (int n = 0; n < nbNeighbors; n++)
12198 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12199 if (neighborDim == 3)
12201 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12203 DownIdType face(downIds[n], downTypes[n]);
12204 boundaryFaces[face] = vtkId;
12206 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12207 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12208 if (vtkFaceId >= 0)
12210 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12211 // find also the smds edges on this face
12212 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12213 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12214 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12215 for (int i = 0; i < nbEdges; i++)
12217 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12218 if (vtkEdgeId >= 0)
12219 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12223 else if (neighborDim == 2) // skin of the volume
12225 DownIdType face(downIds[n], downTypes[n]);
12226 skinFaces[face] = vtkId;
12227 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12228 if (vtkFaceId >= 0)
12229 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12234 // --- identify the edges constituting the wire of each subshape on the skin
12235 // define polylines with the nodes of edges, equivalent to wires
12236 // project polylines on subshapes, and partition, to get geom faces
12238 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12239 std::set<int> emptySet;
12241 std::set<int> shapeIds;
12243 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12244 while (itelem->more())
12246 const SMDS_MeshElement *elem = itelem->next();
12247 int shapeId = elem->getshapeId();
12248 int vtkId = elem->getVtkId();
12249 if (!shapeIdToVtkIdSet.count(shapeId))
12251 shapeIdToVtkIdSet[shapeId] = emptySet;
12252 shapeIds.insert(shapeId);
12254 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12257 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12258 std::set<DownIdType, DownIdCompare> emptyEdges;
12259 emptyEdges.clear();
12261 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12262 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12264 int shapeId = itShape->first;
12265 MESSAGE(" --- Shape ID --- "<< shapeId);
12266 shapeIdToEdges[shapeId] = emptyEdges;
12268 std::vector<int> nodesEdges;
12270 std::set<int>::iterator its = itShape->second.begin();
12271 for (; its != itShape->second.end(); ++its)
12274 MESSAGE(" " << vtkId);
12275 int neighborsVtkIds[NBMAXNEIGHBORS];
12276 int downIds[NBMAXNEIGHBORS];
12277 unsigned char downTypes[NBMAXNEIGHBORS];
12278 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12279 for (int n = 0; n < nbNeighbors; n++)
12281 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12283 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12284 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12285 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12287 DownIdType edge(downIds[n], downTypes[n]);
12288 if (!shapeIdToEdges[shapeId].count(edge))
12290 shapeIdToEdges[shapeId].insert(edge);
12292 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12293 nodesEdges.push_back(vtkNodeId[0]);
12294 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12295 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12301 std::list<int> order;
12303 if (nodesEdges.size() > 0)
12305 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12306 nodesEdges[0] = -1;
12307 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12308 nodesEdges[1] = -1; // do not reuse this edge
12312 int nodeTofind = order.back(); // try first to push back
12314 for (i = 0; i<nodesEdges.size(); i++)
12315 if (nodesEdges[i] == nodeTofind)
12317 if (i == nodesEdges.size())
12318 found = false; // no follower found on back
12321 if (i%2) // odd ==> use the previous one
12322 if (nodesEdges[i-1] < 0)
12326 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12327 nodesEdges[i-1] = -1;
12329 else // even ==> use the next one
12330 if (nodesEdges[i+1] < 0)
12334 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12335 nodesEdges[i+1] = -1;
12340 // try to push front
12342 nodeTofind = order.front(); // try to push front
12343 for (i = 0; i<nodesEdges.size(); i++)
12344 if (nodesEdges[i] == nodeTofind)
12346 if (i == nodesEdges.size())
12348 found = false; // no predecessor found on front
12351 if (i%2) // odd ==> use the previous one
12352 if (nodesEdges[i-1] < 0)
12356 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12357 nodesEdges[i-1] = -1;
12359 else // even ==> use the next one
12360 if (nodesEdges[i+1] < 0)
12364 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12365 nodesEdges[i+1] = -1;
12371 std::vector<int> nodes;
12372 nodes.push_back(shapeId);
12373 std::list<int>::iterator itl = order.begin();
12374 for (; itl != order.end(); itl++)
12376 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12377 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12379 listOfListOfNodes.push_back(nodes);
12382 // partition geom faces with blocFissure
12383 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12384 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12390 //================================================================================
12392 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12393 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12394 * \return TRUE if operation has been completed successfully, FALSE otherwise
12396 //================================================================================
12398 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12400 // iterates on volume elements and detect all free faces on them
12401 SMESHDS_Mesh* aMesh = GetMeshDS();
12404 //bool res = false;
12405 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12406 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12409 const SMDS_MeshVolume* volume = vIt->next();
12410 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12411 vTool.SetExternalNormal();
12412 //const bool isPoly = volume->IsPoly();
12413 const int iQuad = volume->IsQuadratic();
12414 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12416 if (!vTool.IsFreeFace(iface))
12419 vector<const SMDS_MeshNode *> nodes;
12420 int nbFaceNodes = vTool.NbFaceNodes(iface);
12421 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12423 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12424 nodes.push_back(faceNodes[inode]);
12425 if (iQuad) { // add medium nodes
12426 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12427 nodes.push_back(faceNodes[inode]);
12428 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12429 nodes.push_back(faceNodes[8]);
12431 // add new face based on volume nodes
12432 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12434 continue; // face already exsist
12436 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12440 return ( nbFree==(nbExisted+nbCreated) );
12445 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12447 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12449 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12452 //================================================================================
12454 * \brief Creates missing boundary elements
12455 * \param elements - elements whose boundary is to be checked
12456 * \param dimension - defines type of boundary elements to create
12457 * \param group - a group to store created boundary elements in
12458 * \param targetMesh - a mesh to store created boundary elements in
12459 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12460 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12461 * boundary elements will be copied into the targetMesh
12462 * \param toAddExistingBondary - if true, not only new but also pre-existing
12463 * boundary elements will be added into the new group
12464 * \param aroundElements - if true, elements will be created on boundary of given
12465 * elements else, on boundary of the whole mesh.
12466 * \return nb of added boundary elements
12468 //================================================================================
12470 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12471 Bnd_Dimension dimension,
12472 SMESH_Group* group/*=0*/,
12473 SMESH_Mesh* targetMesh/*=0*/,
12474 bool toCopyElements/*=false*/,
12475 bool toCopyExistingBoundary/*=false*/,
12476 bool toAddExistingBondary/*= false*/,
12477 bool aroundElements/*= false*/)
12479 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12480 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12481 // hope that all elements are of the same type, do not check them all
12482 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12483 throw SALOME_Exception(LOCALIZED("wrong element type"));
12486 toCopyElements = toCopyExistingBoundary = false;
12488 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12489 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12490 int nbAddedBnd = 0;
12492 // editor adding present bnd elements and optionally holding elements to add to the group
12493 SMESH_MeshEditor* presentEditor;
12494 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12495 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12497 SMESH_MesherHelper helper( *myMesh );
12498 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12499 SMDS_VolumeTool vTool;
12500 TIDSortedElemSet avoidSet;
12501 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12504 typedef vector<const SMDS_MeshNode*> TConnectivity;
12506 SMDS_ElemIteratorPtr eIt;
12507 if (elements.empty())
12508 eIt = aMesh->elementsIterator(elemType);
12510 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12512 while (eIt->more())
12514 const SMDS_MeshElement* elem = eIt->next();
12515 const int iQuad = elem->IsQuadratic();
12517 // ------------------------------------------------------------------------------------
12518 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12519 // ------------------------------------------------------------------------------------
12520 vector<const SMDS_MeshElement*> presentBndElems;
12521 vector<TConnectivity> missingBndElems;
12522 TConnectivity nodes;
12523 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12525 vTool.SetExternalNormal();
12526 const SMDS_MeshElement* otherVol = 0;
12527 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12529 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12530 ( !aroundElements || elements.count( otherVol )))
12532 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12533 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12534 if ( missType == SMDSAbs_Edge ) // boundary edges
12536 nodes.resize( 2+iQuad );
12537 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12539 for ( int j = 0; j < nodes.size(); ++j )
12541 if ( const SMDS_MeshElement* edge =
12542 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12543 presentBndElems.push_back( edge );
12545 missingBndElems.push_back( nodes );
12548 else // boundary face
12551 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12552 nodes.push_back( nn[inode] );
12553 if (iQuad) // add medium nodes
12554 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12555 nodes.push_back( nn[inode] );
12556 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12558 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12560 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12561 SMDSAbs_Face, /*noMedium=*/false ))
12562 presentBndElems.push_back( f );
12564 missingBndElems.push_back( nodes );
12566 if ( targetMesh != myMesh )
12568 // add 1D elements on face boundary to be added to a new mesh
12569 const SMDS_MeshElement* edge;
12570 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12573 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12575 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12576 if ( edge && avoidSet.insert( edge ).second )
12577 presentBndElems.push_back( edge );
12583 else // elem is a face ------------------------------------------
12585 avoidSet.clear(), avoidSet.insert( elem );
12586 int nbNodes = elem->NbCornerNodes();
12587 nodes.resize( 2 /*+ iQuad*/);
12588 for ( int i = 0; i < nbNodes; i++ )
12590 nodes[0] = elem->GetNode(i);
12591 nodes[1] = elem->GetNode((i+1)%nbNodes);
12592 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12593 continue; // not free link
12596 //nodes[2] = elem->GetNode( i + nbNodes );
12597 if ( const SMDS_MeshElement* edge =
12598 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12599 presentBndElems.push_back( edge );
12601 missingBndElems.push_back( nodes );
12605 // ---------------------------------
12606 // 2. Add missing boundary elements
12607 // ---------------------------------
12608 if ( targetMesh != myMesh )
12609 // instead of making a map of nodes in this mesh and targetMesh,
12610 // we create nodes with same IDs.
12611 for ( int i = 0; i < missingBndElems.size(); ++i )
12613 TConnectivity& srcNodes = missingBndElems[i];
12614 TConnectivity nodes( srcNodes.size() );
12615 for ( inode = 0; inode < nodes.size(); ++inode )
12616 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12617 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12619 /*noMedium=*/false))
12621 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12625 for ( int i = 0; i < missingBndElems.size(); ++i )
12627 TConnectivity& nodes = missingBndElems[i];
12628 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12630 /*noMedium=*/false))
12632 SMDS_MeshElement* elem =
12633 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12636 // try to set a new element to a shape
12637 if ( myMesh->HasShapeToMesh() )
12640 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12641 const int nbN = nodes.size() / (iQuad+1 );
12642 for ( inode = 0; inode < nbN && ok; ++inode )
12644 pair<int, TopAbs_ShapeEnum> i_stype =
12645 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12646 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12647 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12649 if ( ok && mediumShapes.size() > 1 )
12651 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12652 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12653 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12655 if (( ok = ( stype_i->first != stype_i_0.first )))
12656 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12657 aMesh->IndexToShape( stype_i_0.second ));
12660 if ( ok && mediumShapes.begin()->first == missShapeType )
12661 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12665 // ----------------------------------
12666 // 3. Copy present boundary elements
12667 // ----------------------------------
12668 if ( toCopyExistingBoundary )
12669 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12671 const SMDS_MeshElement* e = presentBndElems[i];
12672 TConnectivity nodes( e->NbNodes() );
12673 for ( inode = 0; inode < nodes.size(); ++inode )
12674 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12675 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12677 else // store present elements to add them to a group
12678 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12680 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12683 } // loop on given elements
12685 // ---------------------------------------------
12686 // 4. Fill group with boundary elements
12687 // ---------------------------------------------
12690 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12691 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12692 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12694 tgtEditor.myLastCreatedElems.Clear();
12695 tgtEditor2.myLastCreatedElems.Clear();
12697 // -----------------------
12698 // 5. Copy given elements
12699 // -----------------------
12700 if ( toCopyElements && targetMesh != myMesh )
12702 if (elements.empty())
12703 eIt = aMesh->elementsIterator(elemType);
12705 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12706 while (eIt->more())
12708 const SMDS_MeshElement* elem = eIt->next();
12709 TConnectivity nodes( elem->NbNodes() );
12710 for ( inode = 0; inode < nodes.size(); ++inode )
12711 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12712 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12714 tgtEditor.myLastCreatedElems.Clear();