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;
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 ( startFace != startFaces.end() )
1164 theFace = *startFace;
1165 const int nbNodes = theFace->NbCornerNodes();
1168 avoidSet.insert(theFace);
1170 NLink link( theFace->GetNode( 0 ), 0 );
1171 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1173 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1174 linkIt_isNew = checkedLinks.insert( link );
1175 if ( !linkIt_isNew.second )
1177 // link has already been checked and won't be encountered more
1178 // if the group (theFaces) is manifold
1179 checkedLinks.erase( linkIt_isNew.first );
1183 facesNearLink.clear();
1184 nodeIndsOfFace.clear();
1185 while (( otherFace = FindFaceInSet( link.first, link.second,
1186 theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1187 if ( otherFace != theFace)
1189 facesNearLink.push_back( otherFace );
1190 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1191 avoidSet.insert( otherFace );
1193 if ( facesNearLink.size() > 1 )
1195 // select a face most co-directed with theFace,
1196 // other faces won't be visited this time
1198 SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1199 double proj, maxProj = 0;
1200 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1201 SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1202 if (( proj = Abs( NF * NOF )) > maxProj ) {
1204 otherFace = facesNearLink[i];
1205 nodeInd1 = nodeIndsOfFace[i].first;
1206 nodeInd2 = nodeIndsOfFace[i].second;
1210 else if ( facesNearLink.size() == 1 )
1212 otherFace = facesNearLink[0];
1214 if ( otherFace && otherFace != theFace)
1216 // link must be reverse in otherFace if orientation ot otherFace
1217 // is same as that of theFace
1218 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1220 // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1221 // << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1222 nbReori += Reorient( otherFace );
1224 startFaces.insert( otherFace );
1225 if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1226 theFaces.erase( otherFace );
1229 std::swap( link.first, link.second ); // reverse the link
1231 startFaces.erase( startFace );
1232 startFace = startFaces.begin();
1237 //=======================================================================
1238 //function : getBadRate
1240 //=======================================================================
1242 static double getBadRate (const SMDS_MeshElement* theElem,
1243 SMESH::Controls::NumericalFunctorPtr& theCrit)
1245 SMESH::Controls::TSequenceOfXYZ P;
1246 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1248 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1249 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1252 //=======================================================================
1253 //function : QuadToTri
1254 //purpose : Cut quadrangles into triangles.
1255 // theCrit is used to select a diagonal to cut
1256 //=======================================================================
1258 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1259 SMESH::Controls::NumericalFunctorPtr theCrit)
1261 myLastCreatedElems.Clear();
1262 myLastCreatedNodes.Clear();
1264 MESSAGE( "::QuadToTri()" );
1266 if ( !theCrit.get() )
1269 SMESHDS_Mesh * aMesh = GetMeshDS();
1271 Handle(Geom_Surface) surface;
1272 SMESH_MesherHelper helper( *GetMesh() );
1274 TIDSortedElemSet::iterator itElem;
1275 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1276 const SMDS_MeshElement* elem = *itElem;
1277 if ( !elem || elem->GetType() != SMDSAbs_Face )
1279 if ( elem->NbCornerNodes() != 4 )
1282 // retrieve element nodes
1283 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1285 // compare two sets of possible triangles
1286 double aBadRate1, aBadRate2; // to what extent a set is bad
1287 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1288 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1289 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1291 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1292 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1293 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1295 int aShapeId = FindShape( elem );
1296 const SMDS_MeshElement* newElem1 = 0;
1297 const SMDS_MeshElement* newElem2 = 0;
1299 if( !elem->IsQuadratic() ) {
1301 // split liner quadrangle
1302 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1303 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1304 if ( aBadRate1 <= aBadRate2 ) {
1305 // tr1 + tr2 is better
1306 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1307 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1310 // tr3 + tr4 is better
1311 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1312 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1317 // split quadratic quadrangle
1319 // get surface elem is on
1320 if ( aShapeId != helper.GetSubShapeID() ) {
1324 shape = aMesh->IndexToShape( aShapeId );
1325 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1326 TopoDS_Face face = TopoDS::Face( shape );
1327 surface = BRep_Tool::Surface( face );
1328 if ( !surface.IsNull() )
1329 helper.SetSubShape( shape );
1332 // find middle point for (0,1,2,3)
1333 // and create a node in this point;
1334 const SMDS_MeshNode* newN = 0;
1335 if ( aNodes.size() == 9 )
1337 // SMDSEntity_BiQuad_Quadrangle
1338 newN = aNodes.back();
1343 if ( surface.IsNull() )
1345 for ( int i = 0; i < 4; i++ )
1346 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1351 const SMDS_MeshNode* inFaceNode = 0;
1352 if ( helper.GetNodeUVneedInFaceNode() )
1353 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1354 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1355 inFaceNode = aNodes[ i ];
1357 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1359 for ( int i = 0; i < 4; i++ )
1360 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1362 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1364 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1365 myLastCreatedNodes.Append(newN);
1367 // create a new element
1368 if ( aBadRate1 <= aBadRate2 ) {
1369 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1370 aNodes[6], aNodes[7], newN );
1371 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1372 newN, aNodes[4], aNodes[5] );
1375 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1376 aNodes[7], aNodes[4], newN );
1377 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1378 newN, aNodes[5], aNodes[6] );
1382 // care of a new element
1384 myLastCreatedElems.Append(newElem1);
1385 myLastCreatedElems.Append(newElem2);
1386 AddToSameGroups( newElem1, elem, aMesh );
1387 AddToSameGroups( newElem2, elem, aMesh );
1389 // put a new triangle on the same shape
1392 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1393 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1395 aMesh->RemoveElement( elem );
1400 //=======================================================================
1401 //function : BestSplit
1402 //purpose : Find better diagonal for cutting.
1403 //=======================================================================
1405 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1406 SMESH::Controls::NumericalFunctorPtr theCrit)
1408 myLastCreatedElems.Clear();
1409 myLastCreatedNodes.Clear();
1414 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1417 if( theQuad->NbNodes()==4 ||
1418 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1420 // retrieve element nodes
1421 const SMDS_MeshNode* aNodes [4];
1422 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1424 //while (itN->more())
1426 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1428 // compare two sets of possible triangles
1429 double aBadRate1, aBadRate2; // to what extent a set is bad
1430 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1431 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1432 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1434 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1435 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1436 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1440 return 1; // diagonal 1-3
1442 return 2; // diagonal 2-4
1449 // Methods of splitting volumes into tetra
1451 const int theHexTo5_1[5*4+1] =
1453 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1455 const int theHexTo5_2[5*4+1] =
1457 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1459 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1461 const int theHexTo6_1[6*4+1] =
1463 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
1465 const int theHexTo6_2[6*4+1] =
1467 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
1469 const int theHexTo6_3[6*4+1] =
1471 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
1473 const int theHexTo6_4[6*4+1] =
1475 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
1477 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1479 const int thePyraTo2_1[2*4+1] =
1481 0, 1, 2, 4, 0, 2, 3, 4, -1
1483 const int thePyraTo2_2[2*4+1] =
1485 1, 2, 3, 4, 1, 3, 0, 4, -1
1487 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1489 const int thePentaTo3_1[3*4+1] =
1491 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1493 const int thePentaTo3_2[3*4+1] =
1495 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1497 const int thePentaTo3_3[3*4+1] =
1499 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1501 const int thePentaTo3_4[3*4+1] =
1503 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1505 const int thePentaTo3_5[3*4+1] =
1507 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1509 const int thePentaTo3_6[3*4+1] =
1511 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1513 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1514 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1516 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1519 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1520 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1521 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1526 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1527 bool _baryNode; //!< additional node is to be created at cell barycenter
1528 bool _ownConn; //!< to delete _connectivity in destructor
1529 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1531 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1532 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1533 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1534 bool hasFacet( const TTriangleFacet& facet ) const
1536 const int* tetConn = _connectivity;
1537 for ( ; tetConn[0] >= 0; tetConn += 4 )
1538 if (( facet.contains( tetConn[0] ) +
1539 facet.contains( tetConn[1] ) +
1540 facet.contains( tetConn[2] ) +
1541 facet.contains( tetConn[3] )) == 3 )
1547 //=======================================================================
1549 * \brief return TSplitMethod for the given element
1551 //=======================================================================
1553 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1555 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1557 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1558 // an edge and a face barycenter; tertaherdons are based on triangles and
1559 // a volume barycenter
1560 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1562 // Find out how adjacent volumes are split
1564 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1565 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1566 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1568 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1569 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1570 if ( nbNodes < 4 ) continue;
1572 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1573 const int* nInd = vol.GetFaceNodesIndices( iF );
1576 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1577 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1578 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1579 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1583 int iCom = 0; // common node of triangle faces to split into
1584 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1586 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1587 nInd[ iQ * ( (iCom+1)%nbNodes )],
1588 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1589 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1590 nInd[ iQ * ( (iCom+2)%nbNodes )],
1591 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1592 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1594 triaSplits.push_back( t012 );
1595 triaSplits.push_back( t023 );
1600 if ( !triaSplits.empty() )
1601 hasAdjacentSplits = true;
1604 // Among variants of split method select one compliant with adjacent volumes
1606 TSplitMethod method;
1607 if ( !vol.Element()->IsPoly() && !is24TetMode )
1609 int nbVariants = 2, nbTet = 0;
1610 const int** connVariants = 0;
1611 switch ( vol.Element()->GetEntityType() )
1613 case SMDSEntity_Hexa:
1614 case SMDSEntity_Quad_Hexa:
1615 case SMDSEntity_TriQuad_Hexa:
1616 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1617 connVariants = theHexTo5, nbTet = 5;
1619 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1621 case SMDSEntity_Pyramid:
1622 case SMDSEntity_Quad_Pyramid:
1623 connVariants = thePyraTo2; nbTet = 2;
1625 case SMDSEntity_Penta:
1626 case SMDSEntity_Quad_Penta:
1627 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1632 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1634 // check method compliancy with adjacent tetras,
1635 // all found splits must be among facets of tetras described by this method
1636 method = TSplitMethod( nbTet, connVariants[variant] );
1637 if ( hasAdjacentSplits && method._nbTetra > 0 )
1639 bool facetCreated = true;
1640 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1642 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1643 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1644 facetCreated = method.hasFacet( *facet );
1646 if ( !facetCreated )
1647 method = TSplitMethod(0); // incompatible method
1651 if ( method._nbTetra < 1 )
1653 // No standard method is applicable, use a generic solution:
1654 // each facet of a volume is split into triangles and
1655 // each of triangles and a volume barycenter form a tetrahedron.
1657 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1659 int* connectivity = new int[ maxTetConnSize + 1 ];
1660 method._connectivity = connectivity;
1661 method._ownConn = true;
1662 method._baryNode = !isHex27; // to create central node or not
1665 int baryCenInd = vol.NbNodes() - int( isHex27 );
1666 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1668 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1669 const int* nInd = vol.GetFaceNodesIndices( iF );
1670 // find common node of triangle facets of tetra to create
1671 int iCommon = 0; // index in linear numeration
1672 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1673 if ( !triaSplits.empty() )
1676 const TTriangleFacet* facet = &triaSplits.front();
1677 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1678 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1679 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1682 else if ( nbNodes > 3 && !is24TetMode )
1684 // find the best method of splitting into triangles by aspect ratio
1685 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1686 map< double, int > badness2iCommon;
1687 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1688 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1689 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1692 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1694 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1695 nodes[ iQ*((iLast-1)%nbNodes)],
1696 nodes[ iQ*((iLast )%nbNodes)]);
1697 badness += getBadRate( &tria, aspectRatio );
1699 badness2iCommon.insert( make_pair( badness, iCommon ));
1701 // use iCommon with lowest badness
1702 iCommon = badness2iCommon.begin()->second;
1704 if ( iCommon >= nbNodes )
1705 iCommon = 0; // something wrong
1707 // fill connectivity of tetrahedra based on a current face
1708 int nbTet = nbNodes - 2;
1709 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1714 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1715 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1719 method._faceBaryNode[ iF ] = 0;
1720 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1723 for ( int i = 0; i < nbTet; ++i )
1725 int i1 = i, i2 = (i+1) % nbNodes;
1726 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1727 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1728 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1729 connectivity[ connSize++ ] = faceBaryCenInd;
1730 connectivity[ connSize++ ] = baryCenInd;
1735 for ( int i = 0; i < nbTet; ++i )
1737 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1738 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1739 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1740 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1741 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1742 connectivity[ connSize++ ] = baryCenInd;
1745 method._nbTetra += nbTet;
1747 } // loop on volume faces
1749 connectivity[ connSize++ ] = -1;
1751 } // end of generic solution
1755 //================================================================================
1757 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1759 //================================================================================
1761 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1763 // find the tetrahedron including the three nodes of facet
1764 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1765 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1766 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1767 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1768 while ( volIt1->more() )
1770 const SMDS_MeshElement* v = volIt1->next();
1771 SMDSAbs_EntityType type = v->GetEntityType();
1772 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1774 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1775 continue; // medium node not allowed
1776 const int ind2 = v->GetNodeIndex( n2 );
1777 if ( ind2 < 0 || 3 < ind2 )
1779 const int ind3 = v->GetNodeIndex( n3 );
1780 if ( ind3 < 0 || 3 < ind3 )
1787 //=======================================================================
1789 * \brief A key of a face of volume
1791 //=======================================================================
1793 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1795 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1797 TIDSortedNodeSet sortedNodes;
1798 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1799 int nbNodes = vol.NbFaceNodes( iF );
1800 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1801 for ( int i = 0; i < nbNodes; i += iQ )
1802 sortedNodes.insert( fNodes[i] );
1803 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1804 first.first = (*(n++))->GetID();
1805 first.second = (*(n++))->GetID();
1806 second.first = (*(n++))->GetID();
1807 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1812 //=======================================================================
1813 //function : SplitVolumesIntoTetra
1814 //purpose : Split volume elements into tetrahedra.
1815 //=======================================================================
1817 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1818 const int theMethodFlags)
1820 // std-like iterator on coordinates of nodes of mesh element
1821 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1822 NXyzIterator xyzEnd;
1824 SMDS_VolumeTool volTool;
1825 SMESH_MesherHelper helper( *GetMesh());
1827 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1828 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1830 SMESH_SequenceOfElemPtr newNodes, newElems;
1832 // map face of volume to it's baricenrtic node
1833 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1836 TIDSortedElemSet::const_iterator elem = theElems.begin();
1837 for ( ; elem != theElems.end(); ++elem )
1839 if ( (*elem)->GetType() != SMDSAbs_Volume )
1841 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1842 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1845 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1847 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1848 if ( splitMethod._nbTetra < 1 ) continue;
1850 // find submesh to add new tetras to
1851 if ( !subMesh || !subMesh->Contains( *elem ))
1853 int shapeID = FindShape( *elem );
1854 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1855 subMesh = GetMeshDS()->MeshElements( shapeID );
1858 if ( (*elem)->IsQuadratic() )
1861 // add quadratic links to the helper
1862 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1864 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1865 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1866 for ( int iN = 0; iN < nbN; iN += iQ )
1867 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1869 helper.SetIsQuadratic( true );
1874 helper.SetIsQuadratic( false );
1876 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1877 helper.SetElementsOnShape( true );
1878 if ( splitMethod._baryNode )
1880 // make a node at barycenter
1881 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1882 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1883 nodes.push_back( gcNode );
1884 newNodes.Append( gcNode );
1886 if ( !splitMethod._faceBaryNode.empty() )
1888 // make or find baricentric nodes of faces
1889 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1890 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1892 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1893 volFace2BaryNode.insert
1894 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1897 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1898 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1900 nodes.push_back( iF_n->second = f_n->second );
1905 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1906 const int* tetConn = splitMethod._connectivity;
1907 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1908 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1909 nodes[ tetConn[1] ],
1910 nodes[ tetConn[2] ],
1911 nodes[ tetConn[3] ]));
1913 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1915 // Split faces on sides of the split volume
1917 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1918 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1920 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1921 if ( nbNodes < 4 ) continue;
1923 // find an existing face
1924 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1925 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1926 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1927 /*noMedium=*/false))
1930 helper.SetElementsOnShape( false );
1931 vector< const SMDS_MeshElement* > triangles;
1933 // find submesh to add new triangles in
1934 if ( !fSubMesh || !fSubMesh->Contains( face ))
1936 int shapeID = FindShape( face );
1937 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1939 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1940 if ( iF_n != splitMethod._faceBaryNode.end() )
1942 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1944 const SMDS_MeshNode* n1 = fNodes[iN];
1945 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1946 const SMDS_MeshNode *n3 = iF_n->second;
1947 if ( !volTool.IsFaceExternal( iF ))
1949 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1951 if ( fSubMesh && n3->getshapeId() < 1 )
1952 fSubMesh->AddNode( n3 );
1957 // among possible triangles create ones discribed by split method
1958 const int* nInd = volTool.GetFaceNodesIndices( iF );
1959 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1960 int iCom = 0; // common node of triangle faces to split into
1961 list< TTriangleFacet > facets;
1962 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1964 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1965 nInd[ iQ * ( (iCom+1)%nbNodes )],
1966 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1967 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1968 nInd[ iQ * ( (iCom+2)%nbNodes )],
1969 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1970 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1972 facets.push_back( t012 );
1973 facets.push_back( t023 );
1974 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1975 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1976 nInd[ iQ * ((iLast-1)%nbNodes )],
1977 nInd[ iQ * ((iLast )%nbNodes )]));
1981 list< TTriangleFacet >::iterator facet = facets.begin();
1982 for ( ; facet != facets.end(); ++facet )
1984 if ( !volTool.IsFaceExternal( iF ))
1985 swap( facet->_n2, facet->_n3 );
1986 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1987 volNodes[ facet->_n2 ],
1988 volNodes[ facet->_n3 ]));
1991 for ( int i = 0; i < triangles.size(); ++i )
1993 if ( !triangles[i] ) continue;
1995 fSubMesh->AddElement( triangles[i]);
1996 newElems.Append( triangles[i] );
1998 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1999 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2002 } // loop on volume faces to split them into triangles
2004 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2006 if ( geomType == SMDSEntity_TriQuad_Hexa )
2008 // remove medium nodes that could become free
2009 for ( int i = 20; i < volTool.NbNodes(); ++i )
2010 if ( volNodes[i]->NbInverseElements() == 0 )
2011 GetMeshDS()->RemoveNode( volNodes[i] );
2013 } // loop on volumes to split
2015 myLastCreatedNodes = newNodes;
2016 myLastCreatedElems = newElems;
2019 //=======================================================================
2020 //function : AddToSameGroups
2021 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2022 //=======================================================================
2024 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2025 const SMDS_MeshElement* elemInGroups,
2026 SMESHDS_Mesh * aMesh)
2028 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2029 if (!groups.empty()) {
2030 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2031 for ( ; grIt != groups.end(); grIt++ ) {
2032 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2033 if ( group && group->Contains( elemInGroups ))
2034 group->SMDSGroup().Add( elemToAdd );
2040 //=======================================================================
2041 //function : RemoveElemFromGroups
2042 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2043 //=======================================================================
2044 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2045 SMESHDS_Mesh * aMesh)
2047 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2048 if (!groups.empty())
2050 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2051 for (; GrIt != groups.end(); GrIt++)
2053 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2054 if (!grp || grp->IsEmpty()) continue;
2055 grp->SMDSGroup().Remove(removeelem);
2060 //================================================================================
2062 * \brief Replace elemToRm by elemToAdd in the all groups
2064 //================================================================================
2066 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2067 const SMDS_MeshElement* elemToAdd,
2068 SMESHDS_Mesh * aMesh)
2070 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2071 if (!groups.empty()) {
2072 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2073 for ( ; grIt != groups.end(); grIt++ ) {
2074 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2075 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2076 group->SMDSGroup().Add( elemToAdd );
2081 //================================================================================
2083 * \brief Replace elemToRm by elemToAdd in the all groups
2085 //================================================================================
2087 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2088 const vector<const SMDS_MeshElement*>& elemToAdd,
2089 SMESHDS_Mesh * aMesh)
2091 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2092 if (!groups.empty())
2094 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2095 for ( ; grIt != groups.end(); grIt++ ) {
2096 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2097 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2098 for ( int i = 0; i < elemToAdd.size(); ++i )
2099 group->SMDSGroup().Add( elemToAdd[ i ] );
2104 //=======================================================================
2105 //function : QuadToTri
2106 //purpose : Cut quadrangles into triangles.
2107 // theCrit is used to select a diagonal to cut
2108 //=======================================================================
2110 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2111 const bool the13Diag)
2113 myLastCreatedElems.Clear();
2114 myLastCreatedNodes.Clear();
2116 MESSAGE( "::QuadToTri()" );
2118 SMESHDS_Mesh * aMesh = GetMeshDS();
2120 Handle(Geom_Surface) surface;
2121 SMESH_MesherHelper helper( *GetMesh() );
2123 TIDSortedElemSet::iterator itElem;
2124 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2125 const SMDS_MeshElement* elem = *itElem;
2126 if ( !elem || elem->GetType() != SMDSAbs_Face )
2128 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2129 if(!isquad) continue;
2131 if(elem->NbNodes()==4) {
2132 // retrieve element nodes
2133 const SMDS_MeshNode* aNodes [4];
2134 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2136 while ( itN->more() )
2137 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2139 int aShapeId = FindShape( elem );
2140 const SMDS_MeshElement* newElem1 = 0;
2141 const SMDS_MeshElement* newElem2 = 0;
2143 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2144 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2147 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2148 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2150 myLastCreatedElems.Append(newElem1);
2151 myLastCreatedElems.Append(newElem2);
2152 // put a new triangle on the same shape and add to the same groups
2155 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2156 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2158 AddToSameGroups( newElem1, elem, aMesh );
2159 AddToSameGroups( newElem2, elem, aMesh );
2160 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2161 aMesh->RemoveElement( elem );
2164 // Quadratic quadrangle
2166 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2168 // get surface elem is on
2169 int aShapeId = FindShape( elem );
2170 if ( aShapeId != helper.GetSubShapeID() ) {
2174 shape = aMesh->IndexToShape( aShapeId );
2175 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2176 TopoDS_Face face = TopoDS::Face( shape );
2177 surface = BRep_Tool::Surface( face );
2178 if ( !surface.IsNull() )
2179 helper.SetSubShape( shape );
2183 const SMDS_MeshNode* aNodes [8];
2184 const SMDS_MeshNode* inFaceNode = 0;
2185 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2187 while ( itN->more() ) {
2188 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2189 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2190 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2192 inFaceNode = aNodes[ i-1 ];
2196 // find middle point for (0,1,2,3)
2197 // and create a node in this point;
2199 if ( surface.IsNull() ) {
2201 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2205 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2208 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2210 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2212 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2213 myLastCreatedNodes.Append(newN);
2215 // create a new element
2216 const SMDS_MeshElement* newElem1 = 0;
2217 const SMDS_MeshElement* newElem2 = 0;
2219 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2220 aNodes[6], aNodes[7], newN );
2221 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2222 newN, aNodes[4], aNodes[5] );
2225 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2226 aNodes[7], aNodes[4], newN );
2227 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2228 newN, aNodes[5], aNodes[6] );
2230 myLastCreatedElems.Append(newElem1);
2231 myLastCreatedElems.Append(newElem2);
2232 // put a new triangle on the same shape and add to the same groups
2235 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2236 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2238 AddToSameGroups( newElem1, elem, aMesh );
2239 AddToSameGroups( newElem2, elem, aMesh );
2240 aMesh->RemoveElement( elem );
2247 //=======================================================================
2248 //function : getAngle
2250 //=======================================================================
2252 double getAngle(const SMDS_MeshElement * tr1,
2253 const SMDS_MeshElement * tr2,
2254 const SMDS_MeshNode * n1,
2255 const SMDS_MeshNode * n2)
2257 double angle = 2. * M_PI; // bad angle
2260 SMESH::Controls::TSequenceOfXYZ P1, P2;
2261 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2262 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2265 if(!tr1->IsQuadratic())
2266 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2268 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2269 if ( N1.SquareMagnitude() <= gp::Resolution() )
2271 if(!tr2->IsQuadratic())
2272 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2274 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2275 if ( N2.SquareMagnitude() <= gp::Resolution() )
2278 // find the first diagonal node n1 in the triangles:
2279 // take in account a diagonal link orientation
2280 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2281 for ( int t = 0; t < 2; t++ ) {
2282 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2283 int i = 0, iDiag = -1;
2284 while ( it->more()) {
2285 const SMDS_MeshElement *n = it->next();
2286 if ( n == n1 || n == n2 ) {
2290 if ( i - iDiag == 1 )
2291 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2300 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2303 angle = N1.Angle( N2 );
2308 // =================================================
2309 // class generating a unique ID for a pair of nodes
2310 // and able to return nodes by that ID
2311 // =================================================
2315 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2316 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2319 long GetLinkID (const SMDS_MeshNode * n1,
2320 const SMDS_MeshNode * n2) const
2322 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2325 bool GetNodes (const long theLinkID,
2326 const SMDS_MeshNode* & theNode1,
2327 const SMDS_MeshNode* & theNode2) const
2329 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2330 if ( !theNode1 ) return false;
2331 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2332 if ( !theNode2 ) return false;
2338 const SMESHDS_Mesh* myMesh;
2343 //=======================================================================
2344 //function : TriToQuad
2345 //purpose : Fuse neighbour triangles into quadrangles.
2346 // theCrit is used to select a neighbour to fuse with.
2347 // theMaxAngle is a max angle between element normals at which
2348 // fusion is still performed.
2349 //=======================================================================
2351 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2352 SMESH::Controls::NumericalFunctorPtr theCrit,
2353 const double theMaxAngle)
2355 myLastCreatedElems.Clear();
2356 myLastCreatedNodes.Clear();
2358 MESSAGE( "::TriToQuad()" );
2360 if ( !theCrit.get() )
2363 SMESHDS_Mesh * aMesh = GetMeshDS();
2365 // Prepare data for algo: build
2366 // 1. map of elements with their linkIDs
2367 // 2. map of linkIDs with their elements
2369 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2370 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2371 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2372 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2374 TIDSortedElemSet::iterator itElem;
2375 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2376 const SMDS_MeshElement* elem = *itElem;
2377 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2378 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2379 if(!IsTria) continue;
2381 // retrieve element nodes
2382 const SMDS_MeshNode* aNodes [4];
2383 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2386 aNodes[ i++ ] = cast2Node( itN->next() );
2387 aNodes[ 3 ] = aNodes[ 0 ];
2390 for ( i = 0; i < 3; i++ ) {
2391 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2392 // check if elements sharing a link can be fused
2393 itLE = mapLi_listEl.find( link );
2394 if ( itLE != mapLi_listEl.end() ) {
2395 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2397 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2398 //if ( FindShape( elem ) != FindShape( elem2 ))
2399 // continue; // do not fuse triangles laying on different shapes
2400 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2401 continue; // avoid making badly shaped quads
2402 (*itLE).second.push_back( elem );
2405 mapLi_listEl[ link ].push_back( elem );
2407 mapEl_setLi [ elem ].insert( link );
2410 // Clean the maps from the links shared by a sole element, ie
2411 // links to which only one element is bound in mapLi_listEl
2413 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2414 int nbElems = (*itLE).second.size();
2415 if ( nbElems < 2 ) {
2416 const SMDS_MeshElement* elem = (*itLE).second.front();
2417 SMESH_TLink link = (*itLE).first;
2418 mapEl_setLi[ elem ].erase( link );
2419 if ( mapEl_setLi[ elem ].empty() )
2420 mapEl_setLi.erase( elem );
2424 // Algo: fuse triangles into quadrangles
2426 while ( ! mapEl_setLi.empty() ) {
2427 // Look for the start element:
2428 // the element having the least nb of shared links
2429 const SMDS_MeshElement* startElem = 0;
2431 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2432 int nbLinks = (*itEL).second.size();
2433 if ( nbLinks < minNbLinks ) {
2434 startElem = (*itEL).first;
2435 minNbLinks = nbLinks;
2436 if ( minNbLinks == 1 )
2441 // search elements to fuse starting from startElem or links of elements
2442 // fused earlyer - startLinks
2443 list< SMESH_TLink > startLinks;
2444 while ( startElem || !startLinks.empty() ) {
2445 while ( !startElem && !startLinks.empty() ) {
2446 // Get an element to start, by a link
2447 SMESH_TLink linkId = startLinks.front();
2448 startLinks.pop_front();
2449 itLE = mapLi_listEl.find( linkId );
2450 if ( itLE != mapLi_listEl.end() ) {
2451 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2452 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2453 for ( ; itE != listElem.end() ; itE++ )
2454 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2456 mapLi_listEl.erase( itLE );
2461 // Get candidates to be fused
2462 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2463 const SMESH_TLink *link12, *link13;
2465 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2466 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2467 ASSERT( !setLi.empty() );
2468 set< SMESH_TLink >::iterator itLi;
2469 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2471 const SMESH_TLink & link = (*itLi);
2472 itLE = mapLi_listEl.find( link );
2473 if ( itLE == mapLi_listEl.end() )
2476 const SMDS_MeshElement* elem = (*itLE).second.front();
2478 elem = (*itLE).second.back();
2479 mapLi_listEl.erase( itLE );
2480 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2491 // add other links of elem to list of links to re-start from
2492 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2493 set< SMESH_TLink >::iterator it;
2494 for ( it = links.begin(); it != links.end(); it++ ) {
2495 const SMESH_TLink& link2 = (*it);
2496 if ( link2 != link )
2497 startLinks.push_back( link2 );
2501 // Get nodes of possible quadrangles
2502 const SMDS_MeshNode *n12 [4], *n13 [4];
2503 bool Ok12 = false, Ok13 = false;
2504 const SMDS_MeshNode *linkNode1, *linkNode2;
2506 linkNode1 = link12->first;
2507 linkNode2 = link12->second;
2508 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2512 linkNode1 = link13->first;
2513 linkNode2 = link13->second;
2514 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2518 // Choose a pair to fuse
2519 if ( Ok12 && Ok13 ) {
2520 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2521 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2522 double aBadRate12 = getBadRate( &quad12, theCrit );
2523 double aBadRate13 = getBadRate( &quad13, theCrit );
2524 if ( aBadRate13 < aBadRate12 )
2531 // and remove fused elems and removed links from the maps
2532 mapEl_setLi.erase( tr1 );
2534 mapEl_setLi.erase( tr2 );
2535 mapLi_listEl.erase( *link12 );
2536 if(tr1->NbNodes()==3) {
2537 const SMDS_MeshElement* newElem = 0;
2538 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2539 myLastCreatedElems.Append(newElem);
2540 AddToSameGroups( newElem, tr1, aMesh );
2541 int aShapeId = tr1->getshapeId();
2544 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2546 aMesh->RemoveElement( tr1 );
2547 aMesh->RemoveElement( tr2 );
2550 const SMDS_MeshNode* N1 [6];
2551 const SMDS_MeshNode* N2 [6];
2552 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2553 // now we receive following N1 and N2 (using numeration as above image)
2554 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2555 // i.e. first nodes from both arrays determ new diagonal
2556 const SMDS_MeshNode* aNodes[8];
2565 const SMDS_MeshElement* newElem = 0;
2566 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2567 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2568 myLastCreatedElems.Append(newElem);
2569 AddToSameGroups( newElem, tr1, aMesh );
2570 int aShapeId = tr1->getshapeId();
2573 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2575 aMesh->RemoveElement( tr1 );
2576 aMesh->RemoveElement( tr2 );
2577 // remove middle node (9)
2578 GetMeshDS()->RemoveNode( N1[4] );
2582 mapEl_setLi.erase( tr3 );
2583 mapLi_listEl.erase( *link13 );
2584 if(tr1->NbNodes()==3) {
2585 const SMDS_MeshElement* newElem = 0;
2586 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2587 myLastCreatedElems.Append(newElem);
2588 AddToSameGroups( newElem, tr1, aMesh );
2589 int aShapeId = tr1->getshapeId();
2592 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2594 aMesh->RemoveElement( tr1 );
2595 aMesh->RemoveElement( tr3 );
2598 const SMDS_MeshNode* N1 [6];
2599 const SMDS_MeshNode* N2 [6];
2600 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2601 // now we receive following N1 and N2 (using numeration as above image)
2602 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2603 // i.e. first nodes from both arrays determ new diagonal
2604 const SMDS_MeshNode* aNodes[8];
2613 const SMDS_MeshElement* newElem = 0;
2614 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2615 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2616 myLastCreatedElems.Append(newElem);
2617 AddToSameGroups( newElem, tr1, aMesh );
2618 int aShapeId = tr1->getshapeId();
2621 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2623 aMesh->RemoveElement( tr1 );
2624 aMesh->RemoveElement( tr3 );
2625 // remove middle node (9)
2626 GetMeshDS()->RemoveNode( N1[4] );
2630 // Next element to fuse: the rejected one
2632 startElem = Ok12 ? tr3 : tr2;
2634 } // if ( startElem )
2635 } // while ( startElem || !startLinks.empty() )
2636 } // while ( ! mapEl_setLi.empty() )
2642 /*#define DUMPSO(txt) \
2643 // cout << txt << endl;
2644 //=============================================================================
2648 //=============================================================================
2649 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2653 int tmp = idNodes[ i1 ];
2654 idNodes[ i1 ] = idNodes[ i2 ];
2655 idNodes[ i2 ] = tmp;
2656 gp_Pnt Ptmp = P[ i1 ];
2659 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2662 //=======================================================================
2663 //function : SortQuadNodes
2664 //purpose : Set 4 nodes of a quadrangle face in a good order.
2665 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2667 //=======================================================================
2669 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2674 for ( i = 0; i < 4; i++ ) {
2675 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2677 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2680 gp_Vec V1(P[0], P[1]);
2681 gp_Vec V2(P[0], P[2]);
2682 gp_Vec V3(P[0], P[3]);
2684 gp_Vec Cross1 = V1 ^ V2;
2685 gp_Vec Cross2 = V2 ^ V3;
2688 if (Cross1.Dot(Cross2) < 0)
2693 if (Cross1.Dot(Cross2) < 0)
2697 swap ( i, i + 1, idNodes, P );
2699 // for ( int ii = 0; ii < 4; ii++ ) {
2700 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2701 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2707 //=======================================================================
2708 //function : SortHexaNodes
2709 //purpose : Set 8 nodes of a hexahedron in a good order.
2710 // Return success status
2711 //=======================================================================
2713 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2718 DUMPSO( "INPUT: ========================================");
2719 for ( i = 0; i < 8; i++ ) {
2720 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2721 if ( !n ) return false;
2722 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2723 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2725 DUMPSO( "========================================");
2728 set<int> faceNodes; // ids of bottom face nodes, to be found
2729 set<int> checkedId1; // ids of tried 2-nd nodes
2730 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2731 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2732 int iMin, iLoop1 = 0;
2734 // Loop to try the 2-nd nodes
2736 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2738 // Find not checked 2-nd node
2739 for ( i = 1; i < 8; i++ )
2740 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2741 int id1 = idNodes[i];
2742 swap ( 1, i, idNodes, P );
2743 checkedId1.insert ( id1 );
2747 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2748 // ie that all but meybe one (id3 which is on the same face) nodes
2749 // lay on the same side from the triangle plane.
2751 bool manyInPlane = false; // more than 4 nodes lay in plane
2753 while ( ++iLoop2 < 6 ) {
2755 // get 1-2-3 plane coeffs
2756 Standard_Real A, B, C, D;
2757 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2758 if ( N.SquareMagnitude() > gp::Resolution() )
2760 gp_Pln pln ( P[0], N );
2761 pln.Coefficients( A, B, C, D );
2763 // find the node (iMin) closest to pln
2764 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2766 for ( i = 3; i < 8; i++ ) {
2767 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2768 if ( fabs( dist[i] ) < minDist ) {
2769 minDist = fabs( dist[i] );
2772 if ( fabs( dist[i] ) <= tol )
2773 idInPln.insert( idNodes[i] );
2776 // there should not be more than 4 nodes in bottom plane
2777 if ( idInPln.size() > 1 )
2779 DUMPSO( "### idInPln.size() = " << idInPln.size());
2780 // idInPlane does not contain the first 3 nodes
2781 if ( manyInPlane || idInPln.size() == 5)
2782 return false; // all nodes in one plane
2785 // set the 1-st node to be not in plane
2786 for ( i = 3; i < 8; i++ ) {
2787 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2788 DUMPSO( "### Reset 0-th node");
2789 swap( 0, i, idNodes, P );
2794 // reset to re-check second nodes
2795 leastDist = DBL_MAX;
2799 break; // from iLoop2;
2802 // check that the other 4 nodes are on the same side
2803 bool sameSide = true;
2804 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2805 for ( i = 3; sameSide && i < 8; i++ ) {
2807 sameSide = ( isNeg == dist[i] <= 0.);
2810 // keep best solution
2811 if ( sameSide && minDist < leastDist ) {
2812 leastDist = minDist;
2814 faceNodes.insert( idNodes[ 1 ] );
2815 faceNodes.insert( idNodes[ 2 ] );
2816 faceNodes.insert( idNodes[ iMin ] );
2817 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2818 << " leastDist = " << leastDist);
2819 if ( leastDist <= DBL_MIN )
2824 // set next 3-d node to check
2825 int iNext = 2 + iLoop2;
2827 DUMPSO( "Try 2-nd");
2828 swap ( 2, iNext, idNodes, P );
2830 } // while ( iLoop2 < 6 )
2833 if ( faceNodes.empty() ) return false;
2835 // Put the faceNodes in proper places
2836 for ( i = 4; i < 8; i++ ) {
2837 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2838 // find a place to put
2840 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2842 DUMPSO( "Set faceNodes");
2843 swap ( iTo, i, idNodes, P );
2848 // Set nodes of the found bottom face in good order
2849 DUMPSO( " Found bottom face: ");
2850 i = SortQuadNodes( theMesh, idNodes );
2852 gp_Pnt Ptmp = P[ i ];
2857 // for ( int ii = 0; ii < 4; ii++ ) {
2858 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2859 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2862 // Gravity center of the top and bottom faces
2863 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2864 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2866 // Get direction from the bottom to the top face
2867 gp_Vec upDir ( aGCb, aGCt );
2868 Standard_Real upDirSize = upDir.Magnitude();
2869 if ( upDirSize <= gp::Resolution() ) return false;
2872 // Assure that the bottom face normal points up
2873 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2874 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2875 if ( Nb.Dot( upDir ) < 0 ) {
2876 DUMPSO( "Reverse bottom face");
2877 swap( 1, 3, idNodes, P );
2880 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2881 Standard_Real minDist = DBL_MAX;
2882 for ( i = 4; i < 8; i++ ) {
2883 // projection of P[i] to the plane defined by P[0] and upDir
2884 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2885 Standard_Real sqDist = P[0].SquareDistance( Pp );
2886 if ( sqDist < minDist ) {
2891 DUMPSO( "Set 4-th");
2892 swap ( 4, iMin, idNodes, P );
2894 // Set nodes of the top face in good order
2895 DUMPSO( "Sort top face");
2896 i = SortQuadNodes( theMesh, &idNodes[4] );
2899 gp_Pnt Ptmp = P[ i ];
2904 // Assure that direction of the top face normal is from the bottom face
2905 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2906 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2907 if ( Nt.Dot( upDir ) < 0 ) {
2908 DUMPSO( "Reverse top face");
2909 swap( 5, 7, idNodes, P );
2912 // DUMPSO( "OUTPUT: ========================================");
2913 // for ( i = 0; i < 8; i++ ) {
2914 // float *p = ugrid->GetPoint(idNodes[i]);
2915 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2921 //================================================================================
2923 * \brief Return nodes linked to the given one
2924 * \param theNode - the node
2925 * \param linkedNodes - the found nodes
2926 * \param type - the type of elements to check
2928 * Medium nodes are ignored
2930 //================================================================================
2932 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2933 TIDSortedElemSet & linkedNodes,
2934 SMDSAbs_ElementType type )
2936 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2937 while ( elemIt->more() )
2939 const SMDS_MeshElement* elem = elemIt->next();
2940 if(elem->GetType() == SMDSAbs_0DElement)
2943 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2944 if ( elem->GetType() == SMDSAbs_Volume )
2946 SMDS_VolumeTool vol( elem );
2947 while ( nodeIt->more() ) {
2948 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2949 if ( theNode != n && vol.IsLinked( theNode, n ))
2950 linkedNodes.insert( n );
2955 for ( int i = 0; nodeIt->more(); ++i ) {
2956 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2957 if ( n == theNode ) {
2958 int iBefore = i - 1;
2960 if ( elem->IsQuadratic() ) {
2961 int nb = elem->NbNodes() / 2;
2962 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2963 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2965 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2966 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2973 //=======================================================================
2974 //function : laplacianSmooth
2975 //purpose : pulls theNode toward the center of surrounding nodes directly
2976 // connected to that node along an element edge
2977 //=======================================================================
2979 void laplacianSmooth(const SMDS_MeshNode* theNode,
2980 const Handle(Geom_Surface)& theSurface,
2981 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2983 // find surrounding nodes
2985 TIDSortedElemSet nodeSet;
2986 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2988 // compute new coodrs
2990 double coord[] = { 0., 0., 0. };
2991 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2992 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2993 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2994 if ( theSurface.IsNull() ) { // smooth in 3D
2995 coord[0] += node->X();
2996 coord[1] += node->Y();
2997 coord[2] += node->Z();
2999 else { // smooth in 2D
3000 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3001 gp_XY* uv = theUVMap[ node ];
3002 coord[0] += uv->X();
3003 coord[1] += uv->Y();
3006 int nbNodes = nodeSet.size();
3009 coord[0] /= nbNodes;
3010 coord[1] /= nbNodes;
3012 if ( !theSurface.IsNull() ) {
3013 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3014 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3015 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3021 coord[2] /= nbNodes;
3025 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3028 //=======================================================================
3029 //function : centroidalSmooth
3030 //purpose : pulls theNode toward the element-area-weighted centroid of the
3031 // surrounding elements
3032 //=======================================================================
3034 void centroidalSmooth(const SMDS_MeshNode* theNode,
3035 const Handle(Geom_Surface)& theSurface,
3036 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3038 gp_XYZ aNewXYZ(0.,0.,0.);
3039 SMESH::Controls::Area anAreaFunc;
3040 double totalArea = 0.;
3045 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3046 while ( elemIt->more() )
3048 const SMDS_MeshElement* elem = elemIt->next();
3051 gp_XYZ elemCenter(0.,0.,0.);
3052 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3053 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3054 int nn = elem->NbNodes();
3055 if(elem->IsQuadratic()) nn = nn/2;
3057 //while ( itN->more() ) {
3059 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3061 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3062 aNodePoints.push_back( aP );
3063 if ( !theSurface.IsNull() ) { // smooth in 2D
3064 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3065 gp_XY* uv = theUVMap[ aNode ];
3066 aP.SetCoord( uv->X(), uv->Y(), 0. );
3070 double elemArea = anAreaFunc.GetValue( aNodePoints );
3071 totalArea += elemArea;
3073 aNewXYZ += elemCenter * elemArea;
3075 aNewXYZ /= totalArea;
3076 if ( !theSurface.IsNull() ) {
3077 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3078 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3083 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3086 //=======================================================================
3087 //function : getClosestUV
3088 //purpose : return UV of closest projection
3089 //=======================================================================
3091 static bool getClosestUV (Extrema_GenExtPS& projector,
3092 const gp_Pnt& point,
3095 projector.Perform( point );
3096 if ( projector.IsDone() ) {
3097 double u, v, minVal = DBL_MAX;
3098 for ( int i = projector.NbExt(); i > 0; i-- )
3099 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3100 if ( projector.SquareDistance( i ) < minVal ) {
3101 minVal = projector.SquareDistance( i );
3103 if ( projector.Value( i ) < minVal ) {
3104 minVal = projector.Value( i );
3106 projector.Point( i ).Parameter( u, v );
3108 result.SetCoord( u, v );
3114 //=======================================================================
3116 //purpose : Smooth theElements during theNbIterations or until a worst
3117 // element has aspect ratio <= theTgtAspectRatio.
3118 // Aspect Ratio varies in range [1.0, inf].
3119 // If theElements is empty, the whole mesh is smoothed.
3120 // theFixedNodes contains additionally fixed nodes. Nodes built
3121 // on edges and boundary nodes are always fixed.
3122 //=======================================================================
3124 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3125 set<const SMDS_MeshNode*> & theFixedNodes,
3126 const SmoothMethod theSmoothMethod,
3127 const int theNbIterations,
3128 double theTgtAspectRatio,
3131 myLastCreatedElems.Clear();
3132 myLastCreatedNodes.Clear();
3134 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3136 if ( theTgtAspectRatio < 1.0 )
3137 theTgtAspectRatio = 1.0;
3139 const double disttol = 1.e-16;
3141 SMESH::Controls::AspectRatio aQualityFunc;
3143 SMESHDS_Mesh* aMesh = GetMeshDS();
3145 if ( theElems.empty() ) {
3146 // add all faces to theElems
3147 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3148 while ( fIt->more() ) {
3149 const SMDS_MeshElement* face = fIt->next();
3150 theElems.insert( theElems.end(), face );
3153 // get all face ids theElems are on
3154 set< int > faceIdSet;
3155 TIDSortedElemSet::iterator itElem;
3157 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3158 int fId = FindShape( *itElem );
3159 // check that corresponding submesh exists and a shape is face
3161 faceIdSet.find( fId ) == faceIdSet.end() &&
3162 aMesh->MeshElements( fId )) {
3163 TopoDS_Shape F = aMesh->IndexToShape( fId );
3164 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3165 faceIdSet.insert( fId );
3168 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3170 // ===============================================
3171 // smooth elements on each TopoDS_Face separately
3172 // ===============================================
3174 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3175 for ( ; fId != faceIdSet.rend(); ++fId ) {
3176 // get face surface and submesh
3177 Handle(Geom_Surface) surface;
3178 SMESHDS_SubMesh* faceSubMesh = 0;
3180 double fToler2 = 0, f,l;
3181 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3182 bool isUPeriodic = false, isVPeriodic = false;
3184 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3185 surface = BRep_Tool::Surface( face );
3186 faceSubMesh = aMesh->MeshElements( *fId );
3187 fToler2 = BRep_Tool::Tolerance( face );
3188 fToler2 *= fToler2 * 10.;
3189 isUPeriodic = surface->IsUPeriodic();
3192 isVPeriodic = surface->IsVPeriodic();
3195 surface->Bounds( u1, u2, v1, v2 );
3197 // ---------------------------------------------------------
3198 // for elements on a face, find movable and fixed nodes and
3199 // compute UV for them
3200 // ---------------------------------------------------------
3201 bool checkBoundaryNodes = false;
3202 bool isQuadratic = false;
3203 set<const SMDS_MeshNode*> setMovableNodes;
3204 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3205 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3206 list< const SMDS_MeshElement* > elemsOnFace;
3208 Extrema_GenExtPS projector;
3209 GeomAdaptor_Surface surfAdaptor;
3210 if ( !surface.IsNull() ) {
3211 surfAdaptor.Load( surface );
3212 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3214 int nbElemOnFace = 0;
3215 itElem = theElems.begin();
3216 // loop on not yet smoothed elements: look for elems on a face
3217 while ( itElem != theElems.end() ) {
3218 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3219 break; // all elements found
3221 const SMDS_MeshElement* elem = *itElem;
3222 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3223 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3227 elemsOnFace.push_back( elem );
3228 theElems.erase( itElem++ );
3232 isQuadratic = elem->IsQuadratic();
3234 // get movable nodes of elem
3235 const SMDS_MeshNode* node;
3236 SMDS_TypeOfPosition posType;
3237 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3238 int nn = 0, nbn = elem->NbNodes();
3239 if(elem->IsQuadratic())
3241 while ( nn++ < nbn ) {
3242 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3243 const SMDS_PositionPtr& pos = node->GetPosition();
3244 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3245 if (posType != SMDS_TOP_EDGE &&
3246 posType != SMDS_TOP_VERTEX &&
3247 theFixedNodes.find( node ) == theFixedNodes.end())
3249 // check if all faces around the node are on faceSubMesh
3250 // because a node on edge may be bound to face
3251 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3253 if ( faceSubMesh ) {
3254 while ( eIt->more() && all ) {
3255 const SMDS_MeshElement* e = eIt->next();
3256 all = faceSubMesh->Contains( e );
3260 setMovableNodes.insert( node );
3262 checkBoundaryNodes = true;
3264 if ( posType == SMDS_TOP_3DSPACE )
3265 checkBoundaryNodes = true;
3268 if ( surface.IsNull() )
3271 // get nodes to check UV
3272 list< const SMDS_MeshNode* > uvCheckNodes;
3273 itN = elem->nodesIterator();
3274 nn = 0; nbn = elem->NbNodes();
3275 if(elem->IsQuadratic())
3277 while ( nn++ < nbn ) {
3278 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3279 if ( uvMap.find( node ) == uvMap.end() )
3280 uvCheckNodes.push_back( node );
3281 // add nodes of elems sharing node
3282 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3283 // while ( eIt->more() ) {
3284 // const SMDS_MeshElement* e = eIt->next();
3285 // if ( e != elem ) {
3286 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3287 // while ( nIt->more() ) {
3288 // const SMDS_MeshNode* n =
3289 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3290 // if ( uvMap.find( n ) == uvMap.end() )
3291 // uvCheckNodes.push_back( n );
3297 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3298 for ( ; n != uvCheckNodes.end(); ++n ) {
3301 const SMDS_PositionPtr& pos = node->GetPosition();
3302 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3304 switch ( posType ) {
3305 case SMDS_TOP_FACE: {
3306 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3307 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3310 case SMDS_TOP_EDGE: {
3311 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3312 Handle(Geom2d_Curve) pcurve;
3313 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3314 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3315 if ( !pcurve.IsNull() ) {
3316 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3317 uv = pcurve->Value( u ).XY();
3321 case SMDS_TOP_VERTEX: {
3322 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3323 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3324 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3329 // check existing UV
3330 bool project = true;
3331 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3332 double dist1 = DBL_MAX, dist2 = 0;
3333 if ( posType != SMDS_TOP_3DSPACE ) {
3334 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3335 project = dist1 > fToler2;
3337 if ( project ) { // compute new UV
3339 if ( !getClosestUV( projector, pNode, newUV )) {
3340 MESSAGE("Node Projection Failed " << node);
3344 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3346 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3348 if ( posType != SMDS_TOP_3DSPACE )
3349 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3350 if ( dist2 < dist1 )
3354 // store UV in the map
3355 listUV.push_back( uv );
3356 uvMap.insert( make_pair( node, &listUV.back() ));
3358 } // loop on not yet smoothed elements
3360 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3361 checkBoundaryNodes = true;
3363 // fix nodes on mesh boundary
3365 if ( checkBoundaryNodes ) {
3366 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3367 map< SMESH_TLink, int >::iterator link_nb;
3368 // put all elements links to linkNbMap
3369 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3370 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3371 const SMDS_MeshElement* elem = (*elemIt);
3372 int nbn = elem->NbCornerNodes();
3373 // loop on elem links: insert them in linkNbMap
3374 for ( int iN = 0; iN < nbn; ++iN ) {
3375 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3376 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3377 SMESH_TLink link( n1, n2 );
3378 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3382 // remove nodes that are in links encountered only once from setMovableNodes
3383 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3384 if ( link_nb->second == 1 ) {
3385 setMovableNodes.erase( link_nb->first.node1() );
3386 setMovableNodes.erase( link_nb->first.node2() );
3391 // -----------------------------------------------------
3392 // for nodes on seam edge, compute one more UV ( uvMap2 );
3393 // find movable nodes linked to nodes on seam and which
3394 // are to be smoothed using the second UV ( uvMap2 )
3395 // -----------------------------------------------------
3397 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3398 if ( !surface.IsNull() ) {
3399 TopExp_Explorer eExp( face, TopAbs_EDGE );
3400 for ( ; eExp.More(); eExp.Next() ) {
3401 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3402 if ( !BRep_Tool::IsClosed( edge, face ))
3404 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3405 if ( !sm ) continue;
3406 // find out which parameter varies for a node on seam
3409 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3410 if ( pcurve.IsNull() ) continue;
3411 uv1 = pcurve->Value( f );
3413 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3414 if ( pcurve.IsNull() ) continue;
3415 uv2 = pcurve->Value( f );
3416 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3418 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3419 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3421 // get nodes on seam and its vertices
3422 list< const SMDS_MeshNode* > seamNodes;
3423 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3424 while ( nSeamIt->more() ) {
3425 const SMDS_MeshNode* node = nSeamIt->next();
3426 if ( !isQuadratic || !IsMedium( node ))
3427 seamNodes.push_back( node );
3429 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3430 for ( ; vExp.More(); vExp.Next() ) {
3431 sm = aMesh->MeshElements( vExp.Current() );
3433 nSeamIt = sm->GetNodes();
3434 while ( nSeamIt->more() )
3435 seamNodes.push_back( nSeamIt->next() );
3438 // loop on nodes on seam
3439 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3440 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3441 const SMDS_MeshNode* nSeam = *noSeIt;
3442 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3443 if ( n_uv == uvMap.end() )
3446 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3447 // set the second UV
3448 listUV.push_back( *n_uv->second );
3449 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3450 if ( uvMap2.empty() )
3451 uvMap2 = uvMap; // copy the uvMap contents
3452 uvMap2[ nSeam ] = &listUV.back();
3454 // collect movable nodes linked to ones on seam in nodesNearSeam
3455 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3456 while ( eIt->more() ) {
3457 const SMDS_MeshElement* e = eIt->next();
3458 int nbUseMap1 = 0, nbUseMap2 = 0;
3459 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3460 int nn = 0, nbn = e->NbNodes();
3461 if(e->IsQuadratic()) nbn = nbn/2;
3462 while ( nn++ < nbn )
3464 const SMDS_MeshNode* n =
3465 static_cast<const SMDS_MeshNode*>( nIt->next() );
3467 setMovableNodes.find( n ) == setMovableNodes.end() )
3469 // add only nodes being closer to uv2 than to uv1
3470 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3471 0.5 * ( n->Y() + nSeam->Y() ),
3472 0.5 * ( n->Z() + nSeam->Z() ));
3474 getClosestUV( projector, pMid, uv );
3475 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3476 nodesNearSeam.insert( n );
3482 // for centroidalSmooth all element nodes must
3483 // be on one side of a seam
3484 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3485 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3487 while ( nn++ < nbn ) {
3488 const SMDS_MeshNode* n =
3489 static_cast<const SMDS_MeshNode*>( nIt->next() );
3490 setMovableNodes.erase( n );
3494 } // loop on nodes on seam
3495 } // loop on edge of a face
3496 } // if ( !face.IsNull() )
3498 if ( setMovableNodes.empty() ) {
3499 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3500 continue; // goto next face
3508 double maxRatio = -1., maxDisplacement = -1.;
3509 set<const SMDS_MeshNode*>::iterator nodeToMove;
3510 for ( it = 0; it < theNbIterations; it++ ) {
3511 maxDisplacement = 0.;
3512 nodeToMove = setMovableNodes.begin();
3513 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3514 const SMDS_MeshNode* node = (*nodeToMove);
3515 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3518 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3519 if ( theSmoothMethod == LAPLACIAN )
3520 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3522 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3524 // node displacement
3525 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3526 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3527 if ( aDispl > maxDisplacement )
3528 maxDisplacement = aDispl;
3530 // no node movement => exit
3531 //if ( maxDisplacement < 1.e-16 ) {
3532 if ( maxDisplacement < disttol ) {
3533 MESSAGE("-- no node movement --");
3537 // check elements quality
3539 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3540 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3541 const SMDS_MeshElement* elem = (*elemIt);
3542 if ( !elem || elem->GetType() != SMDSAbs_Face )
3544 SMESH::Controls::TSequenceOfXYZ aPoints;
3545 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3546 double aValue = aQualityFunc.GetValue( aPoints );
3547 if ( aValue > maxRatio )
3551 if ( maxRatio <= theTgtAspectRatio ) {
3552 MESSAGE("-- quality achived --");
3555 if (it+1 == theNbIterations) {
3556 MESSAGE("-- Iteration limit exceeded --");
3558 } // smoothing iterations
3560 MESSAGE(" Face id: " << *fId <<
3561 " Nb iterstions: " << it <<
3562 " Displacement: " << maxDisplacement <<
3563 " Aspect Ratio " << maxRatio);
3565 // ---------------------------------------
3566 // new nodes positions are computed,
3567 // record movement in DS and set new UV
3568 // ---------------------------------------
3569 nodeToMove = setMovableNodes.begin();
3570 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3571 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3572 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3573 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3574 if ( node_uv != uvMap.end() ) {
3575 gp_XY* uv = node_uv->second;
3577 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3581 // move medium nodes of quadratic elements
3584 SMESH_MesherHelper helper( *GetMesh() );
3585 if ( !face.IsNull() )
3586 helper.SetSubShape( face );
3587 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3588 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3589 const SMDS_VtkFace* QF =
3590 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3591 if(QF && QF->IsQuadratic()) {
3592 vector<const SMDS_MeshNode*> Ns;
3593 Ns.reserve(QF->NbNodes()+1);
3594 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3595 while ( anIter->more() )
3596 Ns.push_back( cast2Node(anIter->next()) );
3597 Ns.push_back( Ns[0] );
3599 for(int i=0; i<QF->NbNodes(); i=i+2) {
3600 if ( !surface.IsNull() ) {
3601 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3602 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3603 gp_XY uv = ( uv1 + uv2 ) / 2.;
3604 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3605 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3608 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3609 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3610 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3612 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3613 fabs( Ns[i+1]->Y() - y ) > disttol ||
3614 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3615 // we have to move i+1 node
3616 aMesh->MoveNode( Ns[i+1], x, y, z );
3623 } // loop on face ids
3627 //=======================================================================
3628 //function : isReverse
3629 //purpose : Return true if normal of prevNodes is not co-directied with
3630 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3631 // iNotSame is where prevNodes and nextNodes are different.
3632 // If result is true then future volume orientation is OK
3633 //=======================================================================
3635 static bool isReverse(const SMDS_MeshElement* face,
3636 const vector<const SMDS_MeshNode*>& prevNodes,
3637 const vector<const SMDS_MeshNode*>& nextNodes,
3641 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3642 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3643 gp_XYZ extrDir( pN - pP ), faceNorm;
3644 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3646 return faceNorm * extrDir < 0.0;
3649 //=======================================================================
3651 * \brief Create elements by sweeping an element
3652 * \param elem - element to sweep
3653 * \param newNodesItVec - nodes generated from each node of the element
3654 * \param newElems - generated elements
3655 * \param nbSteps - number of sweeping steps
3656 * \param srcElements - to append elem for each generated element
3658 //=======================================================================
3660 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3661 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3662 list<const SMDS_MeshElement*>& newElems,
3664 SMESH_SequenceOfElemPtr& srcElements)
3666 //MESSAGE("sweepElement " << nbSteps);
3667 SMESHDS_Mesh* aMesh = GetMeshDS();
3669 const int nbNodes = elem->NbNodes();
3670 const int nbCorners = elem->NbCornerNodes();
3671 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3672 polyhedron creation !!! */
3673 // Loop on elem nodes:
3674 // find new nodes and detect same nodes indices
3675 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3676 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3677 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3678 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3680 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3681 vector<int> sames(nbNodes);
3682 vector<bool> isSingleNode(nbNodes);
3684 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3685 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3686 const SMDS_MeshNode* node = nnIt->first;
3687 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3688 if ( listNewNodes.empty() )
3691 itNN [ iNode ] = listNewNodes.begin();
3692 prevNod[ iNode ] = node;
3693 nextNod[ iNode ] = listNewNodes.front();
3695 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3696 corner node of linear */
3697 if ( prevNod[ iNode ] != nextNod [ iNode ])
3698 nbDouble += !isSingleNode[iNode];
3700 if( iNode < nbCorners ) { // check corners only
3701 if ( prevNod[ iNode ] == nextNod [ iNode ])
3702 sames[nbSame++] = iNode;
3704 iNotSameNode = iNode;
3708 if ( nbSame == nbNodes || nbSame > 2) {
3709 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3713 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3715 // fix nodes order to have bottom normal external
3716 if ( baseType == SMDSEntity_Polygon )
3718 std::reverse( itNN.begin(), itNN.end() );
3719 std::reverse( prevNod.begin(), prevNod.end() );
3720 std::reverse( midlNod.begin(), midlNod.end() );
3721 std::reverse( nextNod.begin(), nextNod.end() );
3722 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3726 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3727 SMDS_MeshCell::applyInterlace( ind, itNN );
3728 SMDS_MeshCell::applyInterlace( ind, prevNod );
3729 SMDS_MeshCell::applyInterlace( ind, nextNod );
3730 SMDS_MeshCell::applyInterlace( ind, midlNod );
3731 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3734 sames[nbSame] = iNotSameNode;
3735 for ( int j = 0; j <= nbSame; ++j )
3736 for ( size_t i = 0; i < ind.size(); ++i )
3737 if ( ind[i] == sames[j] )
3742 iNotSameNode = sames[nbSame];
3747 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3749 iSameNode = sames[ nbSame-1 ];
3750 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3751 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3752 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3755 // make new elements
3756 for (int iStep = 0; iStep < nbSteps; iStep++ )
3759 for ( iNode = 0; iNode < nbNodes; iNode++ )
3761 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3762 nextNod[ iNode ] = *itNN[ iNode ]++;
3765 SMDS_MeshElement* aNewElem = 0;
3766 /*if(!elem->IsPoly())*/ {
3767 switch ( baseType ) {
3769 case SMDSEntity_Node: { // sweep NODE
3770 if ( nbSame == 0 ) {
3771 if ( isSingleNode[0] )
3772 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3774 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3780 case SMDSEntity_Edge: { // sweep EDGE
3781 if ( nbDouble == 0 )
3783 if ( nbSame == 0 ) // ---> quadrangle
3784 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3785 nextNod[ 1 ], nextNod[ 0 ] );
3786 else // ---> triangle
3787 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3788 nextNod[ iNotSameNode ] );
3790 else // ---> polygon
3792 vector<const SMDS_MeshNode*> poly_nodes;
3793 poly_nodes.push_back( prevNod[0] );
3794 poly_nodes.push_back( prevNod[1] );
3795 if ( prevNod[1] != nextNod[1] )
3797 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3798 poly_nodes.push_back( nextNod[1] );
3800 if ( prevNod[0] != nextNod[0] )
3802 poly_nodes.push_back( nextNod[0] );
3803 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3805 switch ( poly_nodes.size() ) {
3807 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3810 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3811 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3814 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3819 case SMDSEntity_Triangle: // TRIANGLE --->
3821 if ( nbDouble > 0 ) break;
3822 if ( nbSame == 0 ) // ---> pentahedron
3823 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3824 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3826 else if ( nbSame == 1 ) // ---> pyramid
3827 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3828 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3829 nextNod[ iSameNode ]);
3831 else // 2 same nodes: ---> tetrahedron
3832 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3833 nextNod[ iNotSameNode ]);
3836 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3840 if ( nbDouble+nbSame == 2 )
3842 if(nbSame==0) { // ---> quadratic quadrangle
3843 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3844 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3846 else { //(nbSame==1) // ---> quadratic triangle
3848 return; // medium node on axis
3850 else if(sames[0]==0)
3851 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3852 nextNod[2], midlNod[1], prevNod[2]);
3854 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3855 midlNod[0], nextNod[2], prevNod[2]);
3858 else if ( nbDouble == 3 )
3860 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3861 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3862 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3869 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3870 if ( nbDouble > 0 ) break;
3872 if ( nbSame == 0 ) // ---> hexahedron
3873 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3874 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3876 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3877 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3878 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3879 nextNod[ iSameNode ]);
3880 newElems.push_back( aNewElem );
3881 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3882 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3883 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3885 else if ( nbSame == 2 ) { // ---> pentahedron
3886 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3887 // iBeforeSame is same too
3888 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3889 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3890 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3892 // iAfterSame is same too
3893 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3894 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3895 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3899 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3900 if ( nbDouble+nbSame != 3 ) break;
3902 // ---> pentahedron with 15 nodes
3903 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3904 nextNod[0], nextNod[1], nextNod[2],
3905 prevNod[3], prevNod[4], prevNod[5],
3906 nextNod[3], nextNod[4], nextNod[5],
3907 midlNod[0], midlNod[1], midlNod[2]);
3909 else if(nbSame==1) {
3910 // ---> 2d order pyramid of 13 nodes
3911 int apex = iSameNode;
3912 int i0 = ( apex + 1 ) % nbCorners;
3913 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3917 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3918 nextNod[i0], nextNod[i1], prevNod[apex],
3919 prevNod[i01], midlNod[i0],
3920 nextNod[i01], midlNod[i1],
3921 prevNod[i1a], prevNod[i0a],
3922 nextNod[i0a], nextNod[i1a]);
3924 else if(nbSame==2) {
3925 // ---> 2d order tetrahedron of 10 nodes
3926 int n1 = iNotSameNode;
3927 int n2 = ( n1 + 1 ) % nbCorners;
3928 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3932 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3933 prevNod[n12], prevNod[n23], prevNod[n31],
3934 midlNod[n1], nextNod[n12], nextNod[n31]);
3938 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3940 if ( nbDouble != 4 ) break;
3941 // ---> hexahedron with 20 nodes
3942 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3943 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3944 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3945 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3946 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3948 else if(nbSame==1) {
3949 // ---> pyramid + pentahedron - can not be created since it is needed
3950 // additional middle node at the center of face
3951 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3954 else if( nbSame == 2 ) {
3955 if ( nbDouble != 2 ) break;
3956 // ---> 2d order Pentahedron with 15 nodes
3958 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3959 // iBeforeSame is same too
3966 // iAfterSame is same too
3976 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3977 prevNod[n4], prevNod[n5], nextNod[n5],
3978 prevNod[n12], midlNod[n2], nextNod[n12],
3979 prevNod[n45], midlNod[n5], nextNod[n45],
3980 prevNod[n14], prevNod[n25], nextNod[n25]);
3984 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3986 if( nbSame == 0 && nbDouble == 9 ) {
3987 // ---> tri-quadratic hexahedron with 27 nodes
3988 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3989 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3990 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3991 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3992 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3993 prevNod[8], // bottom center
3994 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3995 nextNod[8], // top center
3996 midlNod[8]);// elem center
4004 case SMDSEntity_Polygon: { // sweep POLYGON
4006 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4007 // ---> hexagonal prism
4008 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4009 prevNod[3], prevNod[4], prevNod[5],
4010 nextNod[0], nextNod[1], nextNod[2],
4011 nextNod[3], nextNod[4], nextNod[5]);
4015 case SMDSEntity_Ball:
4023 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4025 if ( baseType != SMDSEntity_Polygon )
4027 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4028 SMDS_MeshCell::applyInterlace( ind, prevNod );
4029 SMDS_MeshCell::applyInterlace( ind, nextNod );
4030 SMDS_MeshCell::applyInterlace( ind, midlNod );
4031 SMDS_MeshCell::applyInterlace( ind, itNN );
4032 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4033 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4035 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4036 vector<int> quantities (nbNodes + 2);
4037 polyedre_nodes.clear();
4041 for (int inode = 0; inode < nbNodes; inode++)
4042 polyedre_nodes.push_back( prevNod[inode] );
4043 quantities.push_back( nbNodes );
4046 polyedre_nodes.push_back( nextNod[0] );
4047 for (int inode = nbNodes; inode-1; --inode )
4048 polyedre_nodes.push_back( nextNod[inode-1] );
4049 quantities.push_back( nbNodes );
4052 for (int iface = 0; iface < nbNodes; iface++)
4054 const int prevNbNodes = polyedre_nodes.size();
4055 int inextface = (iface+1) % nbNodes;
4056 polyedre_nodes.push_back( prevNod[inextface] );
4057 polyedre_nodes.push_back( prevNod[iface] );
4058 if ( prevNod[iface] != nextNod[iface] )
4060 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4061 polyedre_nodes.push_back( nextNod[iface] );
4063 if ( prevNod[inextface] != nextNod[inextface] )
4065 polyedre_nodes.push_back( nextNod[inextface] );
4066 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4068 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4069 if ( nbFaceNodes > 2 )
4070 quantities.push_back( nbFaceNodes );
4071 else // degenerated face
4072 polyedre_nodes.resize( prevNbNodes );
4074 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4078 newElems.push_back( aNewElem );
4079 myLastCreatedElems.Append(aNewElem);
4080 srcElements.Append( elem );
4083 // set new prev nodes
4084 for ( iNode = 0; iNode < nbNodes; iNode++ )
4085 prevNod[ iNode ] = nextNod[ iNode ];
4090 //=======================================================================
4092 * \brief Create 1D and 2D elements around swept elements
4093 * \param mapNewNodes - source nodes and ones generated from them
4094 * \param newElemsMap - source elements and ones generated from them
4095 * \param elemNewNodesMap - nodes generated from each node of each element
4096 * \param elemSet - all swept elements
4097 * \param nbSteps - number of sweeping steps
4098 * \param srcElements - to append elem for each generated element
4100 //=======================================================================
4102 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4103 TElemOfElemListMap & newElemsMap,
4104 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4105 TIDSortedElemSet& elemSet,
4107 SMESH_SequenceOfElemPtr& srcElements)
4109 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4110 SMESHDS_Mesh* aMesh = GetMeshDS();
4112 // Find nodes belonging to only one initial element - sweep them to get edges.
4114 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4115 for ( ; nList != mapNewNodes.end(); nList++ )
4117 const SMDS_MeshNode* node =
4118 static_cast<const SMDS_MeshNode*>( nList->first );
4119 if ( newElemsMap.count( node ))
4120 continue; // node was extruded into edge
4121 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4122 int nbInitElems = 0;
4123 const SMDS_MeshElement* el = 0;
4124 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4125 while ( eIt->more() && nbInitElems < 2 ) {
4127 SMDSAbs_ElementType type = el->GetType();
4128 if ( type == SMDSAbs_Volume || type < highType ) continue;
4129 if ( type > highType ) {
4133 nbInitElems += elemSet.count(el);
4135 if ( nbInitElems < 2 ) {
4136 bool NotCreateEdge = el && el->IsMediumNode(node);
4137 if(!NotCreateEdge) {
4138 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4139 list<const SMDS_MeshElement*> newEdges;
4140 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4145 // Make a ceiling for each element ie an equal element of last new nodes.
4146 // Find free links of faces - make edges and sweep them into faces.
4148 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4149 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4150 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4152 const SMDS_MeshElement* elem = itElem->first;
4153 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4155 if(itElem->second.size()==0) continue;
4157 const bool isQuadratic = elem->IsQuadratic();
4159 if ( elem->GetType() == SMDSAbs_Edge ) {
4160 // create a ceiling edge
4161 if ( !isQuadratic ) {
4162 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4163 vecNewNodes[ 1 ]->second.back())) {
4164 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4165 vecNewNodes[ 1 ]->second.back()));
4166 srcElements.Append( myLastCreatedElems.Last() );
4170 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4171 vecNewNodes[ 1 ]->second.back(),
4172 vecNewNodes[ 2 ]->second.back())) {
4173 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4174 vecNewNodes[ 1 ]->second.back(),
4175 vecNewNodes[ 2 ]->second.back()));
4176 srcElements.Append( myLastCreatedElems.Last() );
4180 if ( elem->GetType() != SMDSAbs_Face )
4183 bool hasFreeLinks = false;
4185 TIDSortedElemSet avoidSet;
4186 avoidSet.insert( elem );
4188 set<const SMDS_MeshNode*> aFaceLastNodes;
4189 int iNode, nbNodes = vecNewNodes.size();
4190 if ( !isQuadratic ) {
4191 // loop on the face nodes
4192 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4193 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4194 // look for free links of the face
4195 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4196 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4197 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4198 // check if a link is free
4199 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4200 hasFreeLinks = true;
4201 // make an edge and a ceiling for a new edge
4202 if ( !aMesh->FindEdge( n1, n2 )) {
4203 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4204 srcElements.Append( myLastCreatedElems.Last() );
4206 n1 = vecNewNodes[ iNode ]->second.back();
4207 n2 = vecNewNodes[ iNext ]->second.back();
4208 if ( !aMesh->FindEdge( n1, n2 )) {
4209 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4210 srcElements.Append( myLastCreatedElems.Last() );
4215 else { // elem is quadratic face
4216 int nbn = nbNodes/2;
4217 for ( iNode = 0; iNode < nbn; iNode++ ) {
4218 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4219 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4220 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4221 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4222 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4223 // check if a link is free
4224 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4225 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4226 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4227 hasFreeLinks = true;
4228 // make an edge and a ceiling for a new edge
4230 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4231 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4232 srcElements.Append( myLastCreatedElems.Last() );
4234 n1 = vecNewNodes[ iNode ]->second.back();
4235 n2 = vecNewNodes[ iNext ]->second.back();
4236 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4237 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4238 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4239 srcElements.Append( myLastCreatedElems.Last() );
4243 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4244 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4248 // sweep free links into faces
4250 if ( hasFreeLinks ) {
4251 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4252 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4254 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4255 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4256 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4257 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4259 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4260 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4261 std::advance( v, volNb );
4262 // find indices of free faces of a volume and their source edges
4263 list< int > freeInd;
4264 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4265 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4266 int iF, nbF = vTool.NbFaces();
4267 for ( iF = 0; iF < nbF; iF ++ ) {
4268 if (vTool.IsFreeFace( iF ) &&
4269 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4270 initNodeSet != faceNodeSet) // except an initial face
4272 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4274 freeInd.push_back( iF );
4275 // find source edge of a free face iF
4276 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4277 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4278 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4279 initNodeSet.begin(), initNodeSet.end(),
4280 commonNodes.begin());
4281 if ( (*v)->IsQuadratic() )
4282 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4284 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4286 if ( !srcEdges.back() )
4288 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4289 << iF << " of volume #" << vTool.ID() << endl;
4294 if ( freeInd.empty() )
4297 // create faces for all steps;
4298 // if such a face has been already created by sweep of edge,
4299 // assure that its orientation is OK
4300 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4301 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4302 vTool.SetExternalNormal();
4303 const int nextShift = vTool.IsForward() ? +1 : -1;
4304 list< int >::iterator ind = freeInd.begin();
4305 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4306 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4308 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4309 int nbn = vTool.NbFaceNodes( *ind );
4310 const SMDS_MeshElement * f = 0;
4311 if ( nbn == 3 ) ///// triangle
4313 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4315 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4317 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4319 nodes[ 1 + nextShift ] };
4321 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4323 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4327 else if ( nbn == 4 ) ///// quadrangle
4329 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4331 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4333 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4334 nodes[ 2 ], nodes[ 2+nextShift ] };
4336 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4338 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4339 newOrder[ 2 ], newOrder[ 3 ]));
4342 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4344 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4346 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4348 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4350 nodes[2 + 2*nextShift],
4351 nodes[3 - 2*nextShift],
4353 nodes[3 + 2*nextShift]};
4355 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4357 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4365 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4367 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4368 nodes[1], nodes[3], nodes[5], nodes[7] );
4370 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4372 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4373 nodes[4 - 2*nextShift],
4375 nodes[4 + 2*nextShift],
4377 nodes[5 - 2*nextShift],
4379 nodes[5 + 2*nextShift] };
4381 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4383 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4384 newOrder[ 2 ], newOrder[ 3 ],
4385 newOrder[ 4 ], newOrder[ 5 ],
4386 newOrder[ 6 ], newOrder[ 7 ]));
4389 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4391 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4392 SMDSAbs_Face, /*noMedium=*/false);
4394 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4396 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4397 nodes[4 - 2*nextShift],
4399 nodes[4 + 2*nextShift],
4401 nodes[5 - 2*nextShift],
4403 nodes[5 + 2*nextShift],
4406 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4408 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4409 newOrder[ 2 ], newOrder[ 3 ],
4410 newOrder[ 4 ], newOrder[ 5 ],
4411 newOrder[ 6 ], newOrder[ 7 ],
4415 else //////// polygon
4417 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4418 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4420 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4422 if ( !vTool.IsForward() )
4423 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4425 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4427 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4431 while ( srcElements.Length() < myLastCreatedElems.Length() )
4432 srcElements.Append( *srcEdge );
4434 } // loop on free faces
4436 // go to the next volume
4438 while ( iVol++ < nbVolumesByStep ) v++;
4441 } // loop on volumes of one step
4442 } // sweep free links into faces
4444 // Make a ceiling face with a normal external to a volume
4446 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4448 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4450 lastVol.SetExternalNormal();
4451 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4452 int nbn = lastVol.NbFaceNodes( iF );
4454 if (!hasFreeLinks ||
4455 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4456 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4458 else if ( nbn == 4 )
4460 if (!hasFreeLinks ||
4461 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4462 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4464 else if ( nbn == 6 && isQuadratic )
4466 if (!hasFreeLinks ||
4467 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4468 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4469 nodes[1], nodes[3], nodes[5]));
4471 else if ( nbn == 8 && isQuadratic )
4473 if (!hasFreeLinks ||
4474 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4475 nodes[1], nodes[3], nodes[5], nodes[7]) )
4476 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4477 nodes[1], nodes[3], nodes[5], nodes[7]));
4479 else if ( nbn == 9 && isQuadratic )
4481 if (!hasFreeLinks ||
4482 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4483 SMDSAbs_Face, /*noMedium=*/false) )
4484 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4485 nodes[1], nodes[3], nodes[5], nodes[7],
4489 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4490 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4491 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4494 while ( srcElements.Length() < myLastCreatedElems.Length() )
4495 srcElements.Append( myLastCreatedElems.Last() );
4497 } // loop on swept elements
4500 //=======================================================================
4501 //function : RotationSweep
4503 //=======================================================================
4505 SMESH_MeshEditor::PGroupIDs
4506 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4507 const gp_Ax1& theAxis,
4508 const double theAngle,
4509 const int theNbSteps,
4510 const double theTol,
4511 const bool theMakeGroups,
4512 const bool theMakeWalls)
4514 myLastCreatedElems.Clear();
4515 myLastCreatedNodes.Clear();
4517 // source elements for each generated one
4518 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4520 MESSAGE( "RotationSweep()");
4522 aTrsf.SetRotation( theAxis, theAngle );
4524 aTrsf2.SetRotation( theAxis, theAngle/2. );
4526 gp_Lin aLine( theAxis );
4527 double aSqTol = theTol * theTol;
4529 SMESHDS_Mesh* aMesh = GetMeshDS();
4531 TNodeOfNodeListMap mapNewNodes;
4532 TElemOfVecOfNnlmiMap mapElemNewNodes;
4533 TElemOfElemListMap newElemsMap;
4535 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4536 myMesh->NbFaces(ORDER_QUADRATIC) +
4537 myMesh->NbVolumes(ORDER_QUADRATIC) );
4539 TIDSortedElemSet::iterator itElem;
4540 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4541 const SMDS_MeshElement* elem = *itElem;
4542 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4544 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4545 newNodesItVec.reserve( elem->NbNodes() );
4547 // loop on elem nodes
4548 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4549 while ( itN->more() )
4551 // check if a node has been already sweeped
4552 const SMDS_MeshNode* node = cast2Node( itN->next() );
4554 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4556 aXYZ.Coord( coord[0], coord[1], coord[2] );
4557 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4559 TNodeOfNodeListMapItr nIt =
4560 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4561 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4562 if ( listNewNodes.empty() )
4564 // check if we are to create medium nodes between corner ones
4565 bool needMediumNodes = false;
4566 if ( isQuadraticMesh )
4568 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4569 while (it->more() && !needMediumNodes )
4571 const SMDS_MeshElement* invElem = it->next();
4572 if ( invElem != elem && !theElems.count( invElem )) continue;
4573 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4574 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4575 needMediumNodes = true;
4580 const SMDS_MeshNode * newNode = node;
4581 for ( int i = 0; i < theNbSteps; i++ ) {
4583 if ( needMediumNodes ) // create a medium node
4585 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4586 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4587 myLastCreatedNodes.Append(newNode);
4588 srcNodes.Append( node );
4589 listNewNodes.push_back( newNode );
4590 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4593 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4595 // create a corner node
4596 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4597 myLastCreatedNodes.Append(newNode);
4598 srcNodes.Append( node );
4599 listNewNodes.push_back( newNode );
4602 listNewNodes.push_back( newNode );
4603 // if ( needMediumNodes )
4604 // listNewNodes.push_back( newNode );
4608 newNodesItVec.push_back( nIt );
4610 // make new elements
4611 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4615 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4617 PGroupIDs newGroupIDs;
4618 if ( theMakeGroups )
4619 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4625 //=======================================================================
4626 //function : CreateNode
4628 //=======================================================================
4629 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4632 const double tolnode,
4633 SMESH_SequenceOfNode& aNodes)
4635 // myLastCreatedElems.Clear();
4636 // myLastCreatedNodes.Clear();
4639 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4641 // try to search in sequence of existing nodes
4642 // if aNodes.Length()>0 we 'nave to use given sequence
4643 // else - use all nodes of mesh
4644 if(aNodes.Length()>0) {
4646 for(i=1; i<=aNodes.Length(); i++) {
4647 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4648 if(P1.Distance(P2)<tolnode)
4649 return aNodes.Value(i);
4653 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4654 while(itn->more()) {
4655 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4656 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4657 if(P1.Distance(P2)<tolnode)
4662 // create new node and return it
4663 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4664 //myLastCreatedNodes.Append(NewNode);
4669 //=======================================================================
4670 //function : ExtrusionSweep
4672 //=======================================================================
4674 SMESH_MeshEditor::PGroupIDs
4675 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4676 const gp_Vec& theStep,
4677 const int theNbSteps,
4678 TElemOfElemListMap& newElemsMap,
4679 const bool theMakeGroups,
4681 const double theTolerance)
4683 ExtrusParam aParams;
4684 aParams.myDir = gp_Dir(theStep);
4685 aParams.myNodes.Clear();
4686 aParams.mySteps = new TColStd_HSequenceOfReal;
4688 for(i=1; i<=theNbSteps; i++)
4689 aParams.mySteps->Append(theStep.Magnitude());
4692 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4696 //=======================================================================
4697 //function : ExtrusionSweep
4699 //=======================================================================
4701 SMESH_MeshEditor::PGroupIDs
4702 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4703 ExtrusParam& theParams,
4704 TElemOfElemListMap& newElemsMap,
4705 const bool theMakeGroups,
4707 const double theTolerance)
4709 myLastCreatedElems.Clear();
4710 myLastCreatedNodes.Clear();
4712 // source elements for each generated one
4713 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4715 SMESHDS_Mesh* aMesh = GetMeshDS();
4717 int nbsteps = theParams.mySteps->Length();
4719 TNodeOfNodeListMap mapNewNodes;
4720 //TNodeOfNodeVecMap mapNewNodes;
4721 TElemOfVecOfNnlmiMap mapElemNewNodes;
4722 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4724 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4725 myMesh->NbFaces(ORDER_QUADRATIC) +
4726 myMesh->NbVolumes(ORDER_QUADRATIC) );
4728 TIDSortedElemSet::iterator itElem;
4729 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4730 // check element type
4731 const SMDS_MeshElement* elem = *itElem;
4732 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4735 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4736 newNodesItVec.reserve( elem->NbNodes() );
4738 // loop on elem nodes
4739 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4740 while ( itN->more() )
4742 // check if a node has been already sweeped
4743 const SMDS_MeshNode* node = cast2Node( itN->next() );
4744 TNodeOfNodeListMap::iterator nIt =
4745 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4746 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4747 if ( listNewNodes.empty() )
4751 // check if we are to create medium nodes between corner ones
4752 bool needMediumNodes = false;
4753 if ( isQuadraticMesh )
4755 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4756 while (it->more() && !needMediumNodes )
4758 const SMDS_MeshElement* invElem = it->next();
4759 if ( invElem != elem && !theElems.count( invElem )) continue;
4760 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4761 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4762 needMediumNodes = true;
4766 double coord[] = { node->X(), node->Y(), node->Z() };
4767 for ( int i = 0; i < nbsteps; i++ )
4769 if ( needMediumNodes ) // create a medium node
4771 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4772 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4773 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4774 if( theFlags & EXTRUSION_FLAG_SEW ) {
4775 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4776 theTolerance, theParams.myNodes);
4777 listNewNodes.push_back( newNode );
4780 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4781 myLastCreatedNodes.Append(newNode);
4782 srcNodes.Append( node );
4783 listNewNodes.push_back( newNode );
4786 // create a corner node
4787 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4788 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4789 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4790 if( theFlags & EXTRUSION_FLAG_SEW ) {
4791 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4792 theTolerance, theParams.myNodes);
4793 listNewNodes.push_back( newNode );
4796 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4797 myLastCreatedNodes.Append(newNode);
4798 srcNodes.Append( node );
4799 listNewNodes.push_back( newNode );
4803 newNodesItVec.push_back( nIt );
4805 // make new elements
4806 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4809 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4810 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4812 PGroupIDs newGroupIDs;
4813 if ( theMakeGroups )
4814 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4819 //=======================================================================
4820 //function : ExtrusionAlongTrack
4822 //=======================================================================
4823 SMESH_MeshEditor::Extrusion_Error
4824 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4825 SMESH_subMesh* theTrack,
4826 const SMDS_MeshNode* theN1,
4827 const bool theHasAngles,
4828 list<double>& theAngles,
4829 const bool theLinearVariation,
4830 const bool theHasRefPoint,
4831 const gp_Pnt& theRefPoint,
4832 const bool theMakeGroups)
4834 MESSAGE("ExtrusionAlongTrack");
4835 myLastCreatedElems.Clear();
4836 myLastCreatedNodes.Clear();
4839 std::list<double> aPrms;
4840 TIDSortedElemSet::iterator itElem;
4843 TopoDS_Edge aTrackEdge;
4844 TopoDS_Vertex aV1, aV2;
4846 SMDS_ElemIteratorPtr aItE;
4847 SMDS_NodeIteratorPtr aItN;
4848 SMDSAbs_ElementType aTypeE;
4850 TNodeOfNodeListMap mapNewNodes;
4853 aNbE = theElements.size();
4856 return EXTR_NO_ELEMENTS;
4858 // 1.1 Track Pattern
4861 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4863 aItE = pSubMeshDS->GetElements();
4864 while ( aItE->more() ) {
4865 const SMDS_MeshElement* pE = aItE->next();
4866 aTypeE = pE->GetType();
4867 // Pattern must contain links only
4868 if ( aTypeE != SMDSAbs_Edge )
4869 return EXTR_PATH_NOT_EDGE;
4872 list<SMESH_MeshEditor_PathPoint> fullList;
4874 const TopoDS_Shape& aS = theTrack->GetSubShape();
4875 // Sub-shape for the Pattern must be an Edge or Wire
4876 if( aS.ShapeType() == TopAbs_EDGE ) {
4877 aTrackEdge = TopoDS::Edge( aS );
4878 // the Edge must not be degenerated
4879 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4880 return EXTR_BAD_PATH_SHAPE;
4881 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4882 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4883 const SMDS_MeshNode* aN1 = aItN->next();
4884 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4885 const SMDS_MeshNode* aN2 = aItN->next();
4886 // starting node must be aN1 or aN2
4887 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4888 return EXTR_BAD_STARTING_NODE;
4889 aItN = pSubMeshDS->GetNodes();
4890 while ( aItN->more() ) {
4891 const SMDS_MeshNode* pNode = aItN->next();
4892 const SMDS_EdgePosition* pEPos =
4893 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4894 double aT = pEPos->GetUParameter();
4895 aPrms.push_back( aT );
4897 //Extrusion_Error err =
4898 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4899 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4900 list< SMESH_subMesh* > LSM;
4901 TopTools_SequenceOfShape Edges;
4902 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4903 while(itSM->more()) {
4904 SMESH_subMesh* SM = itSM->next();
4906 const TopoDS_Shape& aS = SM->GetSubShape();
4909 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4910 int startNid = theN1->GetID();
4911 TColStd_MapOfInteger UsedNums;
4913 int NbEdges = Edges.Length();
4915 for(; i<=NbEdges; i++) {
4917 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4918 for(; itLSM!=LSM.end(); itLSM++) {
4920 if(UsedNums.Contains(k)) continue;
4921 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4922 SMESH_subMesh* locTrack = *itLSM;
4923 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4924 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4925 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4926 const SMDS_MeshNode* aN1 = aItN->next();
4927 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4928 const SMDS_MeshNode* aN2 = aItN->next();
4929 // starting node must be aN1 or aN2
4930 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4931 // 2. Collect parameters on the track edge
4933 aItN = locMeshDS->GetNodes();
4934 while ( aItN->more() ) {
4935 const SMDS_MeshNode* pNode = aItN->next();
4936 const SMDS_EdgePosition* pEPos =
4937 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4938 double aT = pEPos->GetUParameter();
4939 aPrms.push_back( aT );
4941 list<SMESH_MeshEditor_PathPoint> LPP;
4942 //Extrusion_Error err =
4943 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4944 LLPPs.push_back(LPP);
4946 // update startN for search following egde
4947 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4948 else startNid = aN1->GetID();
4952 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4953 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4954 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4955 for(; itPP!=firstList.end(); itPP++) {
4956 fullList.push_back( *itPP );
4958 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4959 fullList.pop_back();
4961 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4962 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4963 itPP = currList.begin();
4964 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4965 gp_Dir D1 = PP1.Tangent();
4966 gp_Dir D2 = PP2.Tangent();
4967 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4968 (D1.Z()+D2.Z())/2 ) );
4969 PP1.SetTangent(Dnew);
4970 fullList.push_back(PP1);
4972 for(; itPP!=firstList.end(); itPP++) {
4973 fullList.push_back( *itPP );
4975 PP1 = fullList.back();
4976 fullList.pop_back();
4978 // if wire not closed
4979 fullList.push_back(PP1);
4983 return EXTR_BAD_PATH_SHAPE;
4986 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4987 theHasRefPoint, theRefPoint, theMakeGroups);
4991 //=======================================================================
4992 //function : ExtrusionAlongTrack
4994 //=======================================================================
4995 SMESH_MeshEditor::Extrusion_Error
4996 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4997 SMESH_Mesh* theTrack,
4998 const SMDS_MeshNode* theN1,
4999 const bool theHasAngles,
5000 list<double>& theAngles,
5001 const bool theLinearVariation,
5002 const bool theHasRefPoint,
5003 const gp_Pnt& theRefPoint,
5004 const bool theMakeGroups)
5006 myLastCreatedElems.Clear();
5007 myLastCreatedNodes.Clear();
5010 std::list<double> aPrms;
5011 TIDSortedElemSet::iterator itElem;
5014 TopoDS_Edge aTrackEdge;
5015 TopoDS_Vertex aV1, aV2;
5017 SMDS_ElemIteratorPtr aItE;
5018 SMDS_NodeIteratorPtr aItN;
5019 SMDSAbs_ElementType aTypeE;
5021 TNodeOfNodeListMap mapNewNodes;
5024 aNbE = theElements.size();
5027 return EXTR_NO_ELEMENTS;
5029 // 1.1 Track Pattern
5032 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5034 aItE = pMeshDS->elementsIterator();
5035 while ( aItE->more() ) {
5036 const SMDS_MeshElement* pE = aItE->next();
5037 aTypeE = pE->GetType();
5038 // Pattern must contain links only
5039 if ( aTypeE != SMDSAbs_Edge )
5040 return EXTR_PATH_NOT_EDGE;
5043 list<SMESH_MeshEditor_PathPoint> fullList;
5045 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5047 if( aS == SMESH_Mesh::PseudoShape() ) {
5048 //Mesh without shape
5049 const SMDS_MeshNode* currentNode = NULL;
5050 const SMDS_MeshNode* prevNode = theN1;
5051 std::vector<const SMDS_MeshNode*> aNodesList;
5052 aNodesList.push_back(theN1);
5053 int nbEdges = 0, conn=0;
5054 const SMDS_MeshElement* prevElem = NULL;
5055 const SMDS_MeshElement* currentElem = NULL;
5056 int totalNbEdges = theTrack->NbEdges();
5057 SMDS_ElemIteratorPtr nIt;
5060 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5061 return EXTR_BAD_STARTING_NODE;
5064 conn = nbEdgeConnectivity(theN1);
5066 return EXTR_PATH_NOT_EDGE;
5068 aItE = theN1->GetInverseElementIterator();
5069 prevElem = aItE->next();
5070 currentElem = prevElem;
5072 if(totalNbEdges == 1 ) {
5073 nIt = currentElem->nodesIterator();
5074 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5075 if(currentNode == prevNode)
5076 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5077 aNodesList.push_back(currentNode);
5079 nIt = currentElem->nodesIterator();
5080 while( nIt->more() ) {
5081 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5082 if(currentNode == prevNode)
5083 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5084 aNodesList.push_back(currentNode);
5086 //case of the closed mesh
5087 if(currentNode == theN1) {
5092 conn = nbEdgeConnectivity(currentNode);
5094 return EXTR_PATH_NOT_EDGE;
5095 }else if( conn == 1 && nbEdges > 0 ) {
5100 prevNode = currentNode;
5101 aItE = currentNode->GetInverseElementIterator();
5102 currentElem = aItE->next();
5103 if( currentElem == prevElem)
5104 currentElem = aItE->next();
5105 nIt = currentElem->nodesIterator();
5106 prevElem = currentElem;
5112 if(nbEdges != totalNbEdges)
5113 return EXTR_PATH_NOT_EDGE;
5115 TopTools_SequenceOfShape Edges;
5116 double x1,x2,y1,y2,z1,z2;
5117 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5118 int startNid = theN1->GetID();
5119 for(int i = 1; i < aNodesList.size(); i++) {
5120 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5121 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5122 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5123 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5124 list<SMESH_MeshEditor_PathPoint> LPP;
5126 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5127 LLPPs.push_back(LPP);
5128 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5129 else startNid = aNodesList[i-1]->GetID();
5133 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5134 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5135 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5136 for(; itPP!=firstList.end(); itPP++) {
5137 fullList.push_back( *itPP );
5140 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5141 SMESH_MeshEditor_PathPoint PP2;
5142 fullList.pop_back();
5144 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5145 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5146 itPP = currList.begin();
5147 PP2 = currList.front();
5148 gp_Dir D1 = PP1.Tangent();
5149 gp_Dir D2 = PP2.Tangent();
5150 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5151 (D1.Z()+D2.Z())/2 ) );
5152 PP1.SetTangent(Dnew);
5153 fullList.push_back(PP1);
5155 for(; itPP!=currList.end(); itPP++) {
5156 fullList.push_back( *itPP );
5158 PP1 = fullList.back();
5159 fullList.pop_back();
5161 fullList.push_back(PP1);
5163 } // Sub-shape for the Pattern must be an Edge or Wire
5164 else if( aS.ShapeType() == TopAbs_EDGE ) {
5165 aTrackEdge = TopoDS::Edge( aS );
5166 // the Edge must not be degenerated
5167 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5168 return EXTR_BAD_PATH_SHAPE;
5169 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5170 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5171 const SMDS_MeshNode* aN1 = aItN->next();
5172 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5173 const SMDS_MeshNode* aN2 = aItN->next();
5174 // starting node must be aN1 or aN2
5175 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5176 return EXTR_BAD_STARTING_NODE;
5177 aItN = pMeshDS->nodesIterator();
5178 while ( aItN->more() ) {
5179 const SMDS_MeshNode* pNode = aItN->next();
5180 if( pNode==aN1 || pNode==aN2 ) continue;
5181 const SMDS_EdgePosition* pEPos =
5182 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5183 double aT = pEPos->GetUParameter();
5184 aPrms.push_back( aT );
5186 //Extrusion_Error err =
5187 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5189 else if( aS.ShapeType() == TopAbs_WIRE ) {
5190 list< SMESH_subMesh* > LSM;
5191 TopTools_SequenceOfShape Edges;
5192 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5193 for(; eExp.More(); eExp.Next()) {
5194 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5195 if( BRep_Tool::Degenerated(E) ) continue;
5196 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5202 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5203 int startNid = theN1->GetID();
5204 TColStd_MapOfInteger UsedNums;
5205 int NbEdges = Edges.Length();
5207 for(; i<=NbEdges; i++) {
5209 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5210 for(; itLSM!=LSM.end(); itLSM++) {
5212 if(UsedNums.Contains(k)) continue;
5213 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5214 SMESH_subMesh* locTrack = *itLSM;
5215 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5216 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5217 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5218 const SMDS_MeshNode* aN1 = aItN->next();
5219 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5220 const SMDS_MeshNode* aN2 = aItN->next();
5221 // starting node must be aN1 or aN2
5222 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5223 // 2. Collect parameters on the track edge
5225 aItN = locMeshDS->GetNodes();
5226 while ( aItN->more() ) {
5227 const SMDS_MeshNode* pNode = aItN->next();
5228 const SMDS_EdgePosition* pEPos =
5229 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5230 double aT = pEPos->GetUParameter();
5231 aPrms.push_back( aT );
5233 list<SMESH_MeshEditor_PathPoint> LPP;
5234 //Extrusion_Error err =
5235 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5236 LLPPs.push_back(LPP);
5238 // update startN for search following egde
5239 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5240 else startNid = aN1->GetID();
5244 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5245 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5246 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5247 for(; itPP!=firstList.end(); itPP++) {
5248 fullList.push_back( *itPP );
5250 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5251 fullList.pop_back();
5253 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5254 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5255 itPP = currList.begin();
5256 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5257 gp_Dir D1 = PP1.Tangent();
5258 gp_Dir D2 = PP2.Tangent();
5259 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5260 (D1.Z()+D2.Z())/2 ) );
5261 PP1.SetTangent(Dnew);
5262 fullList.push_back(PP1);
5264 for(; itPP!=currList.end(); itPP++) {
5265 fullList.push_back( *itPP );
5267 PP1 = fullList.back();
5268 fullList.pop_back();
5270 // if wire not closed
5271 fullList.push_back(PP1);
5275 return EXTR_BAD_PATH_SHAPE;
5278 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5279 theHasRefPoint, theRefPoint, theMakeGroups);
5283 //=======================================================================
5284 //function : MakeEdgePathPoints
5285 //purpose : auxilary for ExtrusionAlongTrack
5286 //=======================================================================
5287 SMESH_MeshEditor::Extrusion_Error
5288 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5289 const TopoDS_Edge& aTrackEdge,
5291 list<SMESH_MeshEditor_PathPoint>& LPP)
5293 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5295 aTolVec2=aTolVec*aTolVec;
5297 TopoDS_Vertex aV1, aV2;
5298 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5299 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5300 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5301 // 2. Collect parameters on the track edge
5302 aPrms.push_front( aT1 );
5303 aPrms.push_back( aT2 );
5306 if( FirstIsStart ) {
5317 SMESH_MeshEditor_PathPoint aPP;
5318 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5319 std::list<double>::iterator aItD = aPrms.begin();
5320 for(; aItD != aPrms.end(); ++aItD) {
5324 aC3D->D1( aT, aP3D, aVec );
5325 aL2 = aVec.SquareMagnitude();
5326 if ( aL2 < aTolVec2 )
5327 return EXTR_CANT_GET_TANGENT;
5328 gp_Dir aTgt( aVec );
5330 aPP.SetTangent( aTgt );
5331 aPP.SetParameter( aT );
5338 //=======================================================================
5339 //function : MakeExtrElements
5340 //purpose : auxilary for ExtrusionAlongTrack
5341 //=======================================================================
5342 SMESH_MeshEditor::Extrusion_Error
5343 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5344 list<SMESH_MeshEditor_PathPoint>& fullList,
5345 const bool theHasAngles,
5346 list<double>& theAngles,
5347 const bool theLinearVariation,
5348 const bool theHasRefPoint,
5349 const gp_Pnt& theRefPoint,
5350 const bool theMakeGroups)
5352 MESSAGE("MakeExtrElements");
5353 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5354 int aNbTP = fullList.size();
5355 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5357 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5358 LinearAngleVariation(aNbTP-1, theAngles);
5360 vector<double> aAngles( aNbTP );
5362 for(; j<aNbTP; ++j) {
5365 if ( theHasAngles ) {
5367 std::list<double>::iterator aItD = theAngles.begin();
5368 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5370 aAngles[j] = anAngle;
5373 // fill vector of path points with angles
5374 //aPPs.resize(fullList.size());
5376 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5377 for(; itPP!=fullList.end(); itPP++) {
5379 SMESH_MeshEditor_PathPoint PP = *itPP;
5380 PP.SetAngle(aAngles[j]);
5384 TNodeOfNodeListMap mapNewNodes;
5385 TElemOfVecOfNnlmiMap mapElemNewNodes;
5386 TElemOfElemListMap newElemsMap;
5387 TIDSortedElemSet::iterator itElem;
5390 SMDSAbs_ElementType aTypeE;
5391 // source elements for each generated one
5392 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5394 // 3. Center of rotation aV0
5395 gp_Pnt aV0 = theRefPoint;
5397 if ( !theHasRefPoint ) {
5399 aGC.SetCoord( 0.,0.,0. );
5401 itElem = theElements.begin();
5402 for ( ; itElem != theElements.end(); itElem++ ) {
5403 const SMDS_MeshElement* elem = *itElem;
5405 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5406 while ( itN->more() ) {
5407 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5412 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5413 list<const SMDS_MeshNode*> aLNx;
5414 mapNewNodes[node] = aLNx;
5416 gp_XYZ aXYZ( aX, aY, aZ );
5424 } // if (!theHasRefPoint) {
5425 mapNewNodes.clear();
5427 // 4. Processing the elements
5428 SMESHDS_Mesh* aMesh = GetMeshDS();
5430 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5431 // check element type
5432 const SMDS_MeshElement* elem = *itElem;
5433 aTypeE = elem->GetType();
5434 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5437 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5438 newNodesItVec.reserve( elem->NbNodes() );
5440 // loop on elem nodes
5442 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5443 while ( itN->more() )
5446 // check if a node has been already processed
5447 const SMDS_MeshNode* node =
5448 static_cast<const SMDS_MeshNode*>( itN->next() );
5449 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5450 if ( nIt == mapNewNodes.end() ) {
5451 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5452 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5455 aX = node->X(); aY = node->Y(); aZ = node->Z();
5457 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5458 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5459 gp_Ax1 anAx1, anAxT1T0;
5460 gp_Dir aDT1x, aDT0x, aDT1T0;
5465 aPN0.SetCoord(aX, aY, aZ);
5467 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5469 aDT0x= aPP0.Tangent();
5470 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5472 for ( j = 1; j < aNbTP; ++j ) {
5473 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5475 aDT1x = aPP1.Tangent();
5476 aAngle1x = aPP1.Angle();
5478 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5480 gp_Vec aV01x( aP0x, aP1x );
5481 aTrsf.SetTranslation( aV01x );
5484 aV1x = aV0x.Transformed( aTrsf );
5485 aPN1 = aPN0.Transformed( aTrsf );
5487 // rotation 1 [ T1,T0 ]
5488 aAngleT1T0=-aDT1x.Angle( aDT0x );
5489 if (fabs(aAngleT1T0) > aTolAng) {
5491 anAxT1T0.SetLocation( aV1x );
5492 anAxT1T0.SetDirection( aDT1T0 );
5493 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5495 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5499 if ( theHasAngles ) {
5500 anAx1.SetLocation( aV1x );
5501 anAx1.SetDirection( aDT1x );
5502 aTrsfRot.SetRotation( anAx1, aAngle1x );
5504 aPN1 = aPN1.Transformed( aTrsfRot );
5508 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5509 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5510 // create additional node
5511 double x = ( aPN1.X() + aPN0.X() )/2.;
5512 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5513 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5514 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5515 myLastCreatedNodes.Append(newNode);
5516 srcNodes.Append( node );
5517 listNewNodes.push_back( newNode );
5522 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5523 myLastCreatedNodes.Append(newNode);
5524 srcNodes.Append( node );
5525 listNewNodes.push_back( newNode );
5535 // if current elem is quadratic and current node is not medium
5536 // we have to check - may be it is needed to insert additional nodes
5537 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5538 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5539 if(listNewNodes.size()==aNbTP-1) {
5540 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5541 gp_XYZ P(node->X(), node->Y(), node->Z());
5542 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5544 for(i=0; i<aNbTP-1; i++) {
5545 const SMDS_MeshNode* N = *it;
5546 double x = ( N->X() + P.X() )/2.;
5547 double y = ( N->Y() + P.Y() )/2.;
5548 double z = ( N->Z() + P.Z() )/2.;
5549 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5550 srcNodes.Append( node );
5551 myLastCreatedNodes.Append(newN);
5554 P = gp_XYZ(N->X(),N->Y(),N->Z());
5556 listNewNodes.clear();
5557 for(i=0; i<2*(aNbTP-1); i++) {
5558 listNewNodes.push_back(aNodes[i]);
5564 newNodesItVec.push_back( nIt );
5566 // make new elements
5567 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5568 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5569 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5572 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5574 if ( theMakeGroups )
5575 generateGroups( srcNodes, srcElems, "extruded");
5581 //=======================================================================
5582 //function : LinearAngleVariation
5583 //purpose : auxilary for ExtrusionAlongTrack
5584 //=======================================================================
5585 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5586 list<double>& Angles)
5588 int nbAngles = Angles.size();
5589 if( nbSteps > nbAngles ) {
5590 vector<double> theAngles(nbAngles);
5591 list<double>::iterator it = Angles.begin();
5593 for(; it!=Angles.end(); it++) {
5595 theAngles[i] = (*it);
5598 double rAn2St = double( nbAngles ) / double( nbSteps );
5599 double angPrev = 0, angle;
5600 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5601 double angCur = rAn2St * ( iSt+1 );
5602 double angCurFloor = floor( angCur );
5603 double angPrevFloor = floor( angPrev );
5604 if ( angPrevFloor == angCurFloor )
5605 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5607 int iP = int( angPrevFloor );
5608 double angPrevCeil = ceil(angPrev);
5609 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5611 int iC = int( angCurFloor );
5612 if ( iC < nbAngles )
5613 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5615 iP = int( angPrevCeil );
5617 angle += theAngles[ iC ];
5619 res.push_back(angle);
5624 for(; it!=res.end(); it++)
5625 Angles.push_back( *it );
5630 //================================================================================
5632 * \brief Move or copy theElements applying theTrsf to their nodes
5633 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5634 * \param theTrsf - transformation to apply
5635 * \param theCopy - if true, create translated copies of theElems
5636 * \param theMakeGroups - if true and theCopy, create translated groups
5637 * \param theTargetMesh - mesh to copy translated elements into
5638 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5640 //================================================================================
5642 SMESH_MeshEditor::PGroupIDs
5643 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5644 const gp_Trsf& theTrsf,
5646 const bool theMakeGroups,
5647 SMESH_Mesh* theTargetMesh)
5649 myLastCreatedElems.Clear();
5650 myLastCreatedNodes.Clear();
5652 bool needReverse = false;
5653 string groupPostfix;
5654 switch ( theTrsf.Form() ) {
5656 MESSAGE("gp_PntMirror");
5658 groupPostfix = "mirrored";
5661 MESSAGE("gp_Ax1Mirror");
5662 groupPostfix = "mirrored";
5665 MESSAGE("gp_Ax2Mirror");
5667 groupPostfix = "mirrored";
5670 MESSAGE("gp_Rotation");
5671 groupPostfix = "rotated";
5673 case gp_Translation:
5674 MESSAGE("gp_Translation");
5675 groupPostfix = "translated";
5678 MESSAGE("gp_Scale");
5679 groupPostfix = "scaled";
5681 case gp_CompoundTrsf: // different scale by axis
5682 MESSAGE("gp_CompoundTrsf");
5683 groupPostfix = "scaled";
5687 needReverse = false;
5688 groupPostfix = "transformed";
5691 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5692 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5693 SMESHDS_Mesh* aMesh = GetMeshDS();
5696 // map old node to new one
5697 TNodeNodeMap nodeMap;
5699 // elements sharing moved nodes; those of them which have all
5700 // nodes mirrored but are not in theElems are to be reversed
5701 TIDSortedElemSet inverseElemSet;
5703 // source elements for each generated one
5704 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5706 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5707 TIDSortedElemSet orphanNode;
5709 if ( theElems.empty() ) // transform the whole mesh
5712 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5713 while ( eIt->more() ) theElems.insert( eIt->next() );
5715 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5716 while ( nIt->more() )
5718 const SMDS_MeshNode* node = nIt->next();
5719 if ( node->NbInverseElements() == 0)
5720 orphanNode.insert( node );
5724 // loop on elements to transform nodes : first orphan nodes then elems
5725 TIDSortedElemSet::iterator itElem;
5726 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5727 for (int i=0; i<2; i++)
5728 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5729 const SMDS_MeshElement* elem = *itElem;
5733 // loop on elem nodes
5734 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5735 while ( itN->more() ) {
5737 const SMDS_MeshNode* node = cast2Node( itN->next() );
5738 // check if a node has been already transformed
5739 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5740 nodeMap.insert( make_pair ( node, node ));
5741 if ( !n2n_isnew.second )
5745 coord[0] = node->X();
5746 coord[1] = node->Y();
5747 coord[2] = node->Z();
5748 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5749 if ( theTargetMesh ) {
5750 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5751 n2n_isnew.first->second = newNode;
5752 myLastCreatedNodes.Append(newNode);
5753 srcNodes.Append( node );
5755 else if ( theCopy ) {
5756 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5757 n2n_isnew.first->second = newNode;
5758 myLastCreatedNodes.Append(newNode);
5759 srcNodes.Append( node );
5762 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5763 // node position on shape becomes invalid
5764 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5765 ( SMDS_SpacePosition::originSpacePosition() );
5768 // keep inverse elements
5769 if ( !theCopy && !theTargetMesh && needReverse ) {
5770 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5771 while ( invElemIt->more() ) {
5772 const SMDS_MeshElement* iel = invElemIt->next();
5773 inverseElemSet.insert( iel );
5779 // either create new elements or reverse mirrored ones
5780 if ( !theCopy && !needReverse && !theTargetMesh )
5783 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5784 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5785 theElems.insert( *invElemIt );
5787 // Replicate or reverse elements
5789 std::vector<int> iForw;
5790 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5792 const SMDS_MeshElement* elem = *itElem;
5793 if ( !elem ) continue;
5795 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5796 int nbNodes = elem->NbNodes();
5797 if ( geomType == SMDSGeom_NONE ) continue; // node
5799 switch ( geomType ) {
5801 case SMDSGeom_POLYGON: // ---------------------- polygon
5803 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5805 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5806 while (itN->more()) {
5807 const SMDS_MeshNode* node =
5808 static_cast<const SMDS_MeshNode*>(itN->next());
5809 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5810 if (nodeMapIt == nodeMap.end())
5811 break; // not all nodes transformed
5813 // reverse mirrored faces and volumes
5814 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5816 poly_nodes[iNode] = (*nodeMapIt).second;
5820 if ( iNode != nbNodes )
5821 continue; // not all nodes transformed
5823 if ( theTargetMesh ) {
5824 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5825 srcElems.Append( elem );
5827 else if ( theCopy ) {
5828 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5829 srcElems.Append( elem );
5832 aMesh->ChangePolygonNodes(elem, poly_nodes);
5837 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5839 const SMDS_VtkVolume* aPolyedre =
5840 dynamic_cast<const SMDS_VtkVolume*>( elem );
5842 MESSAGE("Warning: bad volumic element");
5846 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5847 vector<int> quantities; quantities.reserve( nbNodes );
5849 bool allTransformed = true;
5850 int nbFaces = aPolyedre->NbFaces();
5851 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5852 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5853 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5854 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5855 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5856 if (nodeMapIt == nodeMap.end()) {
5857 allTransformed = false; // not all nodes transformed
5859 poly_nodes.push_back((*nodeMapIt).second);
5861 if ( needReverse && allTransformed )
5862 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5864 quantities.push_back(nbFaceNodes);
5866 if ( !allTransformed )
5867 continue; // not all nodes transformed
5869 if ( theTargetMesh ) {
5870 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5871 srcElems.Append( elem );
5873 else if ( theCopy ) {
5874 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5875 srcElems.Append( elem );
5878 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5883 case SMDSGeom_BALL: // -------------------- Ball
5885 if ( !theCopy && !theTargetMesh ) continue;
5887 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5888 if (nodeMapIt == nodeMap.end())
5889 continue; // not all nodes transformed
5891 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5892 if ( theTargetMesh ) {
5893 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5894 srcElems.Append( elem );
5897 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5898 srcElems.Append( elem );
5903 default: // ----------------------- Regular elements
5905 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5906 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5907 const std::vector<int>& i = needReverse ? iRev : iForw;
5909 // find transformed nodes
5910 vector<const SMDS_MeshNode*> nodes(nbNodes);
5912 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5913 while ( itN->more() ) {
5914 const SMDS_MeshNode* node =
5915 static_cast<const SMDS_MeshNode*>( itN->next() );
5916 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5917 if ( nodeMapIt == nodeMap.end() )
5918 break; // not all nodes transformed
5919 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5921 if ( iNode != nbNodes )
5922 continue; // not all nodes transformed
5924 if ( theTargetMesh ) {
5925 if ( SMDS_MeshElement* copy =
5926 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5927 myLastCreatedElems.Append( copy );
5928 srcElems.Append( elem );
5931 else if ( theCopy ) {
5932 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5933 srcElems.Append( elem );
5936 // reverse element as it was reversed by transformation
5938 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5940 } // switch ( geomType )
5942 } // loop on elements
5944 PGroupIDs newGroupIDs;
5946 if ( ( theMakeGroups && theCopy ) ||
5947 ( theMakeGroups && theTargetMesh ) )
5948 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5953 //=======================================================================
5955 * \brief Create groups of elements made during transformation
5956 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5957 * \param elemGens - elements making corresponding myLastCreatedElems
5958 * \param postfix - to append to names of new groups
5960 //=======================================================================
5962 SMESH_MeshEditor::PGroupIDs
5963 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5964 const SMESH_SequenceOfElemPtr& elemGens,
5965 const std::string& postfix,
5966 SMESH_Mesh* targetMesh)
5968 PGroupIDs newGroupIDs( new list<int> );
5969 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5971 // Sort existing groups by types and collect their names
5973 // to store an old group and a generated new one
5974 typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5975 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5976 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5978 set< string > groupNames;
5980 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5981 if ( !groupIt->more() ) return newGroupIDs;
5983 int newGroupID = mesh->GetGroupIds().back()+1;
5984 while ( groupIt->more() )
5986 SMESH_Group * group = groupIt->next();
5987 if ( !group ) continue;
5988 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5989 if ( !groupDS || groupDS->IsEmpty() ) continue;
5990 groupNames.insert( group->GetName() );
5991 groupDS->SetStoreName( group->GetName() );
5992 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5993 groupDS->GetType() );
5994 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5995 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5998 // Loop on nodes and elements to add them in new groups
6000 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6002 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6003 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6004 if ( gens.Length() != elems.Length() )
6005 throw SALOME_Exception(LOCALIZED("invalid args"));
6007 // loop on created elements
6008 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6010 const SMDS_MeshElement* sourceElem = gens( iElem );
6011 if ( !sourceElem ) {
6012 MESSAGE("generateGroups(): NULL source element");
6015 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6016 if ( groupsOldNew.empty() ) { // no groups of this type at all
6017 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6018 ++iElem; // skip all elements made by sourceElem
6021 // collect all elements made by sourceElem
6022 list< const SMDS_MeshElement* > resultElems;
6023 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6024 if ( resElem != sourceElem )
6025 resultElems.push_back( resElem );
6026 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6027 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6028 if ( resElem != sourceElem )
6029 resultElems.push_back( resElem );
6031 // add resultElems to groups made by ones the sourceElem belongs to
6032 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6033 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6035 SMESHDS_GroupBase* oldGroup = gOldNew->first;
6036 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6038 // fill in a new group
6039 SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6040 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6041 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6042 newGroup.Add( *resElemIt );
6045 } // loop on created elements
6046 }// loop on nodes and elements
6048 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6050 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6052 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6053 SMESHDS_Group* newGroupDS = orderedOldNewGroups[i]->second;
6054 if ( newGroupDS->IsEmpty() )
6056 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6061 string name = oldGroupDS->GetStoreName();
6062 if ( !targetMesh ) {
6066 while ( !groupNames.insert( name ).second ) // name exists
6067 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6069 newGroupDS->SetStoreName( name.c_str() );
6071 // make a SMESH_Groups
6072 mesh->AddGroup( newGroupDS );
6073 newGroupIDs->push_back( newGroupDS->GetID() );
6076 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6083 //================================================================================
6085 * \brief Return list of group of nodes close to each other within theTolerance
6086 * Search among theNodes or in the whole mesh if theNodes is empty using
6087 * an Octree algorithm
6089 //================================================================================
6091 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6092 const double theTolerance,
6093 TListOfListOfNodes & theGroupsOfNodes)
6095 myLastCreatedElems.Clear();
6096 myLastCreatedNodes.Clear();
6098 if ( theNodes.empty() )
6099 { // get all nodes in the mesh
6100 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6101 while ( nIt->more() )
6102 theNodes.insert( theNodes.end(),nIt->next());
6105 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6109 //=======================================================================
6111 * \brief Implementation of search for the node closest to point
6113 //=======================================================================
6115 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6117 //---------------------------------------------------------------------
6119 * \brief Constructor
6121 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6123 myMesh = ( SMESHDS_Mesh* ) theMesh;
6125 TIDSortedNodeSet nodes;
6127 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6128 while ( nIt->more() )
6129 nodes.insert( nodes.end(), nIt->next() );
6131 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6133 // get max size of a leaf box
6134 SMESH_OctreeNode* tree = myOctreeNode;
6135 while ( !tree->isLeaf() )
6137 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6141 myHalfLeafSize = tree->maxSize() / 2.;
6144 //---------------------------------------------------------------------
6146 * \brief Move node and update myOctreeNode accordingly
6148 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6150 myOctreeNode->UpdateByMoveNode( node, toPnt );
6151 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6154 //---------------------------------------------------------------------
6156 * \brief Do it's job
6158 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6160 map<double, const SMDS_MeshNode*> dist2Nodes;
6161 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6162 if ( !dist2Nodes.empty() )
6163 return dist2Nodes.begin()->second;
6164 list<const SMDS_MeshNode*> nodes;
6165 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6167 double minSqDist = DBL_MAX;
6168 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6170 // sort leafs by their distance from thePnt
6171 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6172 TDistTreeMap treeMap;
6173 list< SMESH_OctreeNode* > treeList;
6174 list< SMESH_OctreeNode* >::iterator trIt;
6175 treeList.push_back( myOctreeNode );
6177 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6178 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6179 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6181 SMESH_OctreeNode* tree = *trIt;
6182 if ( !tree->isLeaf() ) // put children to the queue
6184 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6185 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6186 while ( cIt->more() )
6187 treeList.push_back( cIt->next() );
6189 else if ( tree->NbNodes() ) // put a tree to the treeMap
6191 const Bnd_B3d& box = *tree->getBox();
6192 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6193 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6194 if ( !it_in.second ) // not unique distance to box center
6195 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6198 // find distance after which there is no sense to check tree's
6199 double sqLimit = DBL_MAX;
6200 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6201 if ( treeMap.size() > 5 ) {
6202 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6203 const Bnd_B3d& box = *closestTree->getBox();
6204 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6205 sqLimit = limit * limit;
6207 // get all nodes from trees
6208 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6209 if ( sqDist_tree->first > sqLimit )
6211 SMESH_OctreeNode* tree = sqDist_tree->second;
6212 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6215 // find closest among nodes
6216 minSqDist = DBL_MAX;
6217 const SMDS_MeshNode* closestNode = 0;
6218 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6219 for ( ; nIt != nodes.end(); ++nIt ) {
6220 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6221 if ( minSqDist > sqDist ) {
6229 //---------------------------------------------------------------------
6233 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6235 //---------------------------------------------------------------------
6237 * \brief Return the node tree
6239 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6242 SMESH_OctreeNode* myOctreeNode;
6243 SMESHDS_Mesh* myMesh;
6244 double myHalfLeafSize; // max size of a leaf box
6247 //=======================================================================
6249 * \brief Return SMESH_NodeSearcher
6251 //=======================================================================
6253 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6255 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6258 // ========================================================================
6259 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6261 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6262 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6263 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6265 //=======================================================================
6267 * \brief Octal tree of bounding boxes of elements
6269 //=======================================================================
6271 class ElementBndBoxTree : public SMESH_Octree
6275 ElementBndBoxTree(const SMDS_Mesh& mesh,
6276 SMDSAbs_ElementType elemType,
6277 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6278 double tolerance = NodeRadius );
6279 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6280 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6281 void getElementsInSphere ( const gp_XYZ& center,
6282 const double radius, TIDSortedElemSet& foundElems);
6283 size_t getSize() { return std::max( _size, _elements.size() ); }
6284 ~ElementBndBoxTree();
6287 ElementBndBoxTree():_size(0) {}
6288 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6289 void buildChildrenData();
6290 Bnd_B3d* buildRootBox();
6292 //!< Bounding box of element
6293 struct ElementBox : public Bnd_B3d
6295 const SMDS_MeshElement* _element;
6296 int _refCount; // an ElementBox can be included in several tree branches
6297 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6299 vector< ElementBox* > _elements;
6303 //================================================================================
6305 * \brief ElementBndBoxTree creation
6307 //================================================================================
6309 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6310 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6312 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6313 _elements.reserve( nbElems );
6315 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6316 while ( elemIt->more() )
6317 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6322 //================================================================================
6326 //================================================================================
6328 ElementBndBoxTree::~ElementBndBoxTree()
6330 for ( int i = 0; i < _elements.size(); ++i )
6331 if ( --_elements[i]->_refCount <= 0 )
6332 delete _elements[i];
6335 //================================================================================
6337 * \brief Return the maximal box
6339 //================================================================================
6341 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6343 Bnd_B3d* box = new Bnd_B3d;
6344 for ( int i = 0; i < _elements.size(); ++i )
6345 box->Add( *_elements[i] );
6349 //================================================================================
6351 * \brief Redistrubute element boxes among children
6353 //================================================================================
6355 void ElementBndBoxTree::buildChildrenData()
6357 for ( int i = 0; i < _elements.size(); ++i )
6359 for (int j = 0; j < 8; j++)
6361 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6363 _elements[i]->_refCount++;
6364 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6367 _elements[i]->_refCount--;
6369 _size = _elements.size();
6370 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6372 for (int j = 0; j < 8; j++)
6374 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6375 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6376 child->myIsLeaf = true;
6378 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6379 SMESHUtils::CompactVector( child->_elements );
6383 //================================================================================
6385 * \brief Return elements which can include the point
6387 //================================================================================
6389 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6390 TIDSortedElemSet& foundElems)
6392 if ( getBox()->IsOut( point.XYZ() ))
6397 for ( int i = 0; i < _elements.size(); ++i )
6398 if ( !_elements[i]->IsOut( point.XYZ() ))
6399 foundElems.insert( _elements[i]->_element );
6403 for (int i = 0; i < 8; i++)
6404 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6408 //================================================================================
6410 * \brief Return elements which can be intersected by the line
6412 //================================================================================
6414 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6415 TIDSortedElemSet& foundElems)
6417 if ( getBox()->IsOut( line ))
6422 for ( int i = 0; i < _elements.size(); ++i )
6423 if ( !_elements[i]->IsOut( line ))
6424 foundElems.insert( _elements[i]->_element );
6428 for (int i = 0; i < 8; i++)
6429 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6433 //================================================================================
6435 * \brief Return elements from leaves intersecting the sphere
6437 //================================================================================
6439 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6440 const double radius,
6441 TIDSortedElemSet& foundElems)
6443 if ( getBox()->IsOut( center, radius ))
6448 for ( int i = 0; i < _elements.size(); ++i )
6449 if ( !_elements[i]->IsOut( center, radius ))
6450 foundElems.insert( _elements[i]->_element );
6454 for (int i = 0; i < 8; i++)
6455 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6459 //================================================================================
6461 * \brief Construct the element box
6463 //================================================================================
6465 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6469 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6470 while ( nIt->more() )
6471 Add( SMESH_TNodeXYZ( nIt->next() ));
6472 Enlarge( tolerance );
6477 //=======================================================================
6479 * \brief Implementation of search for the elements by point and
6480 * of classification of point in 2D mesh
6482 //=======================================================================
6484 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6486 SMESHDS_Mesh* _mesh;
6487 SMDS_ElemIteratorPtr _meshPartIt;
6488 ElementBndBoxTree* _ebbTree;
6489 SMESH_NodeSearcherImpl* _nodeSearcher;
6490 SMDSAbs_ElementType _elementType;
6492 bool _outerFacesFound;
6493 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6495 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6496 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6497 ~SMESH_ElementSearcherImpl()
6499 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6500 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6502 virtual int FindElementsByPoint(const gp_Pnt& point,
6503 SMDSAbs_ElementType type,
6504 vector< const SMDS_MeshElement* >& foundElements);
6505 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6506 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6507 SMDSAbs_ElementType type );
6509 void GetElementsNearLine( const gp_Ax1& line,
6510 SMDSAbs_ElementType type,
6511 vector< const SMDS_MeshElement* >& foundElems);
6512 double getTolerance();
6513 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6514 const double tolerance, double & param);
6515 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6516 bool isOuterBoundary(const SMDS_MeshElement* face) const
6518 return _outerFaces.empty() || _outerFaces.count(face);
6520 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6522 const SMDS_MeshElement* _face;
6524 bool _coincides; //!< the line lays in face plane
6525 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6526 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6528 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6531 TIDSortedElemSet _faces;
6532 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6533 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6537 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6539 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6540 << ", _coincides="<<i._coincides << ")";
6543 //=======================================================================
6545 * \brief define tolerance for search
6547 //=======================================================================
6549 double SMESH_ElementSearcherImpl::getTolerance()
6551 if ( _tolerance < 0 )
6553 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6556 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6558 double boxSize = _nodeSearcher->getTree()->maxSize();
6559 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6561 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6563 double boxSize = _ebbTree->maxSize();
6564 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6566 if ( _tolerance == 0 )
6568 // define tolerance by size of a most complex element
6569 int complexType = SMDSAbs_Volume;
6570 while ( complexType > SMDSAbs_All &&
6571 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6573 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6575 if ( complexType == int( SMDSAbs_Node ))
6577 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6579 if ( meshInfo.NbNodes() > 2 )
6580 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6584 SMDS_ElemIteratorPtr elemIt =
6585 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6586 const SMDS_MeshElement* elem = elemIt->next();
6587 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6588 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6590 while ( nodeIt->more() )
6592 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6593 elemSize = max( dist, elemSize );
6596 _tolerance = 1e-4 * elemSize;
6602 //================================================================================
6604 * \brief Find intersection of the line and an edge of face and return parameter on line
6606 //================================================================================
6608 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6609 const SMDS_MeshElement* face,
6616 GeomAPI_ExtremaCurveCurve anExtCC;
6617 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6619 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6620 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6622 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6623 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6624 anExtCC.Init( lineCurve, edge);
6625 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6627 Quantity_Parameter pl, pe;
6628 anExtCC.LowerDistanceParameters( pl, pe );
6630 if ( ++nbInts == 2 )
6634 if ( nbInts > 0 ) param /= nbInts;
6637 //================================================================================
6639 * \brief Find all faces belonging to the outer boundary of mesh
6641 //================================================================================
6643 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6645 if ( _outerFacesFound ) return;
6647 // Collect all outer faces by passing from one outer face to another via their links
6648 // and BTW find out if there are internal faces at all.
6650 // checked links and links where outer boundary meets internal one
6651 set< SMESH_TLink > visitedLinks, seamLinks;
6653 // links to treat with already visited faces sharing them
6654 list < TFaceLink > startLinks;
6656 // load startLinks with the first outerFace
6657 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6658 _outerFaces.insert( outerFace );
6660 TIDSortedElemSet emptySet;
6661 while ( !startLinks.empty() )
6663 const SMESH_TLink& link = startLinks.front()._link;
6664 TIDSortedElemSet& faces = startLinks.front()._faces;
6666 outerFace = *faces.begin();
6667 // find other faces sharing the link
6668 const SMDS_MeshElement* f;
6669 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6672 // select another outer face among the found
6673 const SMDS_MeshElement* outerFace2 = 0;
6674 if ( faces.size() == 2 )
6676 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6678 else if ( faces.size() > 2 )
6680 seamLinks.insert( link );
6682 // link direction within the outerFace
6683 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6684 SMESH_TNodeXYZ( link.node2()));
6685 int i1 = outerFace->GetNodeIndex( link.node1() );
6686 int i2 = outerFace->GetNodeIndex( link.node2() );
6687 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6688 if ( rev ) n1n2.Reverse();
6690 gp_XYZ ofNorm, fNorm;
6691 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6693 // direction from the link inside outerFace
6694 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6695 // sort all other faces by angle with the dirInOF
6696 map< double, const SMDS_MeshElement* > angle2Face;
6697 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6698 for ( ; face != faces.end(); ++face )
6700 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6702 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6703 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6704 if ( angle < 0 ) angle += 2. * M_PI;
6705 angle2Face.insert( make_pair( angle, *face ));
6707 if ( !angle2Face.empty() )
6708 outerFace2 = angle2Face.begin()->second;
6711 // store the found outer face and add its links to continue seaching from
6714 _outerFaces.insert( outerFace );
6715 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6716 for ( int i = 0; i < nbNodes; ++i )
6718 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6719 if ( visitedLinks.insert( link2 ).second )
6720 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6723 startLinks.pop_front();
6725 _outerFacesFound = true;
6727 if ( !seamLinks.empty() )
6729 // There are internal boundaries touching the outher one,
6730 // find all faces of internal boundaries in order to find
6731 // faces of boundaries of holes, if any.
6736 _outerFaces.clear();
6740 //=======================================================================
6742 * \brief Find elements of given type where the given point is IN or ON.
6743 * Returns nb of found elements and elements them-selves.
6745 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6747 //=======================================================================
6749 int SMESH_ElementSearcherImpl::
6750 FindElementsByPoint(const gp_Pnt& point,
6751 SMDSAbs_ElementType type,
6752 vector< const SMDS_MeshElement* >& foundElements)
6754 foundElements.clear();
6756 double tolerance = getTolerance();
6758 // =================================================================================
6759 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6761 if ( !_nodeSearcher )
6762 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6764 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6765 if ( !closeNode ) return foundElements.size();
6767 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6768 return foundElements.size(); // to far from any node
6770 if ( type == SMDSAbs_Node )
6772 foundElements.push_back( closeNode );
6776 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6777 while ( elemIt->more() )
6778 foundElements.push_back( elemIt->next() );
6781 // =================================================================================
6782 else // elements more complex than 0D
6784 if ( !_ebbTree || _elementType != type )
6786 if ( _ebbTree ) delete _ebbTree;
6787 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6789 TIDSortedElemSet suspectElems;
6790 _ebbTree->getElementsNearPoint( point, suspectElems );
6791 TIDSortedElemSet::iterator elem = suspectElems.begin();
6792 for ( ; elem != suspectElems.end(); ++elem )
6793 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6794 foundElements.push_back( *elem );
6796 return foundElements.size();
6799 //=======================================================================
6801 * \brief Find an element of given type most close to the given point
6803 * WARNING: Only face search is implemeneted so far
6805 //=======================================================================
6807 const SMDS_MeshElement*
6808 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6809 SMDSAbs_ElementType type )
6811 const SMDS_MeshElement* closestElem = 0;
6813 if ( type == SMDSAbs_Face )
6815 if ( !_ebbTree || _elementType != type )
6817 if ( _ebbTree ) delete _ebbTree;
6818 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6820 TIDSortedElemSet suspectElems;
6821 _ebbTree->getElementsNearPoint( point, suspectElems );
6823 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6825 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6826 _ebbTree->getBox()->CornerMax() );
6828 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6829 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6831 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6832 while ( suspectElems.empty() )
6834 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6838 double minDist = std::numeric_limits<double>::max();
6839 multimap< double, const SMDS_MeshElement* > dist2face;
6840 TIDSortedElemSet::iterator elem = suspectElems.begin();
6841 for ( ; elem != suspectElems.end(); ++elem )
6843 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6845 if ( dist < minDist + 1e-10)
6848 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6851 if ( !dist2face.empty() )
6853 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6854 closestElem = d2f->second;
6855 // if there are several elements at the same distance, select one
6856 // with GC closest to the point
6857 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6858 double minDistToGC = 0;
6859 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6861 if ( minDistToGC == 0 )
6864 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6865 TXyzIterator(), gc ) / closestElem->NbNodes();
6866 minDistToGC = point.SquareDistance( gc );
6869 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6870 TXyzIterator(), gc ) / d2f->second->NbNodes();
6871 double d = point.SquareDistance( gc );
6872 if ( d < minDistToGC )
6875 closestElem = d2f->second;
6878 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6879 // <<closestElem->GetID() << " DIST " << minDist << endl;
6884 // NOT IMPLEMENTED SO FAR
6890 //================================================================================
6892 * \brief Classify the given point in the closed 2D mesh
6894 //================================================================================
6896 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6898 double tolerance = getTolerance();
6899 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6901 if ( _ebbTree ) delete _ebbTree;
6902 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6904 // Algo: analyse transition of a line starting at the point through mesh boundary;
6905 // try three lines parallel to axis of the coordinate system and perform rough
6906 // analysis. If solution is not clear perform thorough analysis.
6908 const int nbAxes = 3;
6909 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6910 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6911 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6912 multimap< int, int > nbInt2Axis; // to find the simplest case
6913 for ( int axis = 0; axis < nbAxes; ++axis )
6915 gp_Ax1 lineAxis( point, axisDir[axis]);
6916 gp_Lin line ( lineAxis );
6918 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6919 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6921 // Intersect faces with the line
6923 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6924 TIDSortedElemSet::iterator face = suspectFaces.begin();
6925 for ( ; face != suspectFaces.end(); ++face )
6929 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6930 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6932 // perform intersection
6933 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6934 if ( !intersection.IsDone() )
6936 if ( intersection.IsInQuadric() )
6938 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6940 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6942 gp_Pnt intersectionPoint = intersection.Point(1);
6943 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6944 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6947 // Analyse intersections roughly
6949 int nbInter = u2inters.size();
6953 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6954 if ( nbInter == 1 ) // not closed mesh
6955 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6957 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6960 if ( (f<0) == (l<0) )
6963 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6964 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6965 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6968 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6970 if ( _outerFacesFound ) break; // pass to thorough analysis
6972 } // three attempts - loop on CS axes
6974 // Analyse intersections thoroughly.
6975 // We make two loops maximum, on the first one we only exclude touching intersections,
6976 // on the second, if situation is still unclear, we gather and use information on
6977 // position of faces (internal or outer). If faces position is already gathered,
6978 // we make the second loop right away.
6980 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6982 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6983 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6985 int axis = nb_axis->second;
6986 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6988 gp_Ax1 lineAxis( point, axisDir[axis]);
6989 gp_Lin line ( lineAxis );
6991 // add tangent intersections to u2inters
6993 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6994 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6995 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6996 u2inters.insert(make_pair( param, *tgtInt ));
6997 tangentInters[ axis ].clear();
6999 // Count intersections before and after the point excluding touching ones.
7000 // If hasPositionInfo we count intersections of outer boundary only
7002 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7003 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7004 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7005 bool ok = ! u_int1->second._coincides;
7006 while ( ok && u_int1 != u2inters.end() )
7008 double u = u_int1->first;
7009 bool touchingInt = false;
7010 if ( ++u_int2 != u2inters.end() )
7012 // skip intersections at the same point (if the line passes through edge or node)
7014 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7020 // skip tangent intersections
7022 const SMDS_MeshElement* prevFace = u_int1->second._face;
7023 while ( ok && u_int2->second._coincides )
7025 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7031 ok = ( u_int2 != u2inters.end() );
7036 // skip intersections at the same point after tangent intersections
7039 double u2 = u_int2->first;
7041 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7047 // decide if we skipped a touching intersection
7048 if ( nbSamePnt + nbTgt > 0 )
7050 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7051 map< double, TInters >::iterator u_int = u_int1;
7052 for ( ; u_int != u_int2; ++u_int )
7054 if ( u_int->second._coincides ) continue;
7055 double dot = u_int->second._faceNorm * line.Direction();
7056 if ( dot > maxDot ) maxDot = dot;
7057 if ( dot < minDot ) minDot = dot;
7059 touchingInt = ( minDot*maxDot < 0 );
7064 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7075 u_int1 = u_int2; // to next intersection
7077 } // loop on intersections with one line
7081 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7084 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7087 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7088 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7090 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7093 if ( (f<0) == (l<0) )
7096 if ( hasPositionInfo )
7097 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7099 } // loop on intersections of the tree lines - thorough analysis
7101 if ( !hasPositionInfo )
7103 // gather info on faces position - is face in the outer boundary or not
7104 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7105 findOuterBoundary( u2inters.begin()->second._face );
7108 } // two attempts - with and w/o faces position info in the mesh
7110 return TopAbs_UNKNOWN;
7113 //=======================================================================
7115 * \brief Return elements possibly intersecting the line
7117 //=======================================================================
7119 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7120 SMDSAbs_ElementType type,
7121 vector< const SMDS_MeshElement* >& foundElems)
7123 if ( !_ebbTree || _elementType != type )
7125 if ( _ebbTree ) delete _ebbTree;
7126 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7128 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7129 _ebbTree->getElementsNearLine( line, suspectFaces );
7130 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7133 //=======================================================================
7135 * \brief Return SMESH_ElementSearcher
7137 //=======================================================================
7139 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7141 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7144 //=======================================================================
7146 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7148 //=======================================================================
7150 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7152 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7155 //=======================================================================
7157 * \brief Return true if the point is IN or ON of the element
7159 //=======================================================================
7161 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7163 if ( element->GetType() == SMDSAbs_Volume)
7165 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7168 // get ordered nodes
7170 vector< gp_XYZ > xyz;
7171 vector<const SMDS_MeshNode*> nodeList;
7173 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7174 if ( element->IsQuadratic() ) {
7175 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7176 nodeIt = f->interlacedNodesElemIterator();
7177 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7178 nodeIt = e->interlacedNodesElemIterator();
7180 while ( nodeIt->more() )
7182 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7183 xyz.push_back( SMESH_TNodeXYZ(node) );
7184 nodeList.push_back(node);
7187 int i, nbNodes = element->NbNodes();
7189 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7191 // compute face normal
7192 gp_Vec faceNorm(0,0,0);
7193 xyz.push_back( xyz.front() );
7194 nodeList.push_back( nodeList.front() );
7195 for ( i = 0; i < nbNodes; ++i )
7197 gp_Vec edge1( xyz[i+1], xyz[i]);
7198 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7199 faceNorm += edge1 ^ edge2;
7201 double normSize = faceNorm.Magnitude();
7202 if ( normSize <= tol )
7204 // degenerated face: point is out if it is out of all face edges
7205 for ( i = 0; i < nbNodes; ++i )
7207 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7208 if ( !IsOut( &edge, point, tol ))
7213 faceNorm /= normSize;
7215 // check if the point lays on face plane
7216 gp_Vec n2p( xyz[0], point );
7217 if ( fabs( n2p * faceNorm ) > tol )
7218 return true; // not on face plane
7220 // check if point is out of face boundary:
7221 // define it by closest transition of a ray point->infinity through face boundary
7222 // on the face plane.
7223 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7224 // to find intersections of the ray with the boundary.
7226 gp_Vec plnNorm = ray ^ faceNorm;
7227 normSize = plnNorm.Magnitude();
7228 if ( normSize <= tol ) return false; // point coincides with the first node
7229 plnNorm /= normSize;
7230 // for each node of the face, compute its signed distance to the plane
7231 vector<double> dist( nbNodes + 1);
7232 for ( i = 0; i < nbNodes; ++i )
7234 gp_Vec n2p( xyz[i], point );
7235 dist[i] = n2p * plnNorm;
7237 dist.back() = dist.front();
7238 // find the closest intersection
7240 double rClosest, distClosest = 1e100;;
7242 for ( i = 0; i < nbNodes; ++i )
7245 if ( fabs( dist[i]) < tol )
7247 else if ( fabs( dist[i+1]) < tol )
7249 else if ( dist[i] * dist[i+1] < 0 )
7250 r = dist[i] / ( dist[i] - dist[i+1] );
7252 continue; // no intersection
7253 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7254 gp_Vec p2int ( point, pInt);
7255 if ( p2int * ray > -tol ) // right half-space
7257 double intDist = p2int.SquareMagnitude();
7258 if ( intDist < distClosest )
7263 distClosest = intDist;
7268 return true; // no intesections - out
7270 // analyse transition
7271 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7272 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7273 gp_Vec p2int ( point, pClosest );
7274 bool out = (edgeNorm * p2int) < -tol;
7275 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7278 // ray pass through a face node; analyze transition through an adjacent edge
7279 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7280 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7281 gp_Vec edgeAdjacent( p1, p2 );
7282 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7283 bool out2 = (edgeNorm2 * p2int) < -tol;
7285 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7286 return covexCorner ? (out || out2) : (out && out2);
7288 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7290 // point is out of edge if it is NOT ON any straight part of edge
7291 // (we consider quadratic edge as being composed of two straight parts)
7292 for ( i = 1; i < nbNodes; ++i )
7294 gp_Vec edge( xyz[i-1], xyz[i]);
7295 gp_Vec n1p ( xyz[i-1], point);
7296 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7299 gp_Vec n2p( xyz[i], point );
7300 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7302 return false; // point is ON this part
7306 // Node or 0D element -------------------------------------------------------------------------
7308 gp_Vec n2p ( xyz[0], point );
7309 return n2p.Magnitude() <= tol;
7314 //=======================================================================
7318 // Position of a point relative to a segment
7322 // VERTEX 1 o----ON-----> VERTEX 2
7326 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7327 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7331 int _index; // index of vertex or segment
7333 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7334 bool operator < (const PointPos& other ) const
7336 if ( _name == other._name )
7337 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7338 return _name < other._name;
7342 //================================================================================
7344 * \brief Return of a point relative to a segment
7345 * \param point2D - the point to analyze position of
7346 * \param xyVec - end points of segments
7347 * \param index0 - 0-based index of the first point of segment
7348 * \param posToFindOut - flags of positions to detect
7349 * \retval PointPos - point position
7351 //================================================================================
7353 PointPos getPointPosition( const gp_XY& point2D,
7354 const gp_XY* segEnds,
7355 const int index0 = 0,
7356 const int posToFindOut = POS_ALL)
7358 const gp_XY& p1 = segEnds[ index0 ];
7359 const gp_XY& p2 = segEnds[ index0+1 ];
7360 const gp_XY grad = p2 - p1;
7362 if ( posToFindOut & POS_VERTEX )
7364 // check if the point2D is at "vertex 1" zone
7365 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7366 p1.Y() + grad.X() ) };
7367 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7368 return PointPos( POS_VERTEX, index0 );
7370 // check if the point2D is at "vertex 2" zone
7371 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7372 p2.Y() + grad.X() ) };
7373 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7374 return PointPos( POS_VERTEX, index0 + 1);
7376 double edgeEquation =
7377 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7378 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7382 //=======================================================================
7384 * \brief Return minimal distance from a point to a face
7386 * Currently we ignore non-planarity and 2nd order of face
7388 //=======================================================================
7390 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7391 const gp_Pnt& point )
7393 double badDistance = -1;
7394 if ( !face ) return badDistance;
7396 // coordinates of nodes (medium nodes, if any, ignored)
7397 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7398 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7399 xyz.resize( face->NbCornerNodes()+1 );
7401 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7402 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7404 gp_Vec OZ ( xyz[0], xyz[1] );
7405 gp_Vec OX ( xyz[0], xyz[2] );
7406 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7408 if ( xyz.size() < 4 ) return badDistance;
7409 OZ = gp_Vec ( xyz[0], xyz[2] );
7410 OX = gp_Vec ( xyz[0], xyz[3] );
7414 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7416 catch ( Standard_Failure ) {
7419 trsf.SetTransformation( tgtCS );
7421 // move all the nodes to 2D
7422 vector<gp_XY> xy( xyz.size() );
7423 for ( size_t i = 0;i < xyz.size()-1; ++i )
7425 gp_XYZ p3d = xyz[i];
7426 trsf.Transforms( p3d );
7427 xy[i].SetCoord( p3d.X(), p3d.Z() );
7429 xyz.back() = xyz.front();
7430 xy.back() = xy.front();
7432 // // move the point in 2D
7433 gp_XYZ tmpPnt = point.XYZ();
7434 trsf.Transforms( tmpPnt );
7435 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7437 // loop on segments of the face to analyze point position ralative to the face
7438 set< PointPos > pntPosSet;
7439 for ( size_t i = 1; i < xy.size(); ++i )
7441 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7442 pntPosSet.insert( pos );
7446 PointPos pos = *pntPosSet.begin();
7447 // cout << "Face " << face->GetID() << " DIST: ";
7448 switch ( pos._name )
7451 // point is most close to a segment
7452 gp_Vec p0p1( point, xyz[ pos._index ] );
7453 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7455 double projDist = p0p1 * p1p2; // distance projected to the segment
7456 gp_Vec projVec = p1p2 * projDist;
7457 gp_Vec distVec = p0p1 - projVec;
7458 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7459 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7460 return distVec.Magnitude();
7463 // point is inside the face
7464 double distToFacePlane = tmpPnt.Y();
7465 // cout << distToFacePlane << ", INSIDE " << endl;
7466 return Abs( distToFacePlane );
7469 // point is most close to a node
7470 gp_Vec distVec( point, xyz[ pos._index ]);
7471 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7472 return distVec.Magnitude();
7478 //=======================================================================
7479 //function : SimplifyFace
7481 //=======================================================================
7482 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7483 vector<const SMDS_MeshNode *>& poly_nodes,
7484 vector<int>& quantities) const
7486 int nbNodes = faceNodes.size();
7491 set<const SMDS_MeshNode*> nodeSet;
7493 // get simple seq of nodes
7494 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7495 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7496 int iSimple = 0, nbUnique = 0;
7498 simpleNodes[iSimple++] = faceNodes[0];
7500 for (int iCur = 1; iCur < nbNodes; iCur++) {
7501 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7502 simpleNodes[iSimple++] = faceNodes[iCur];
7503 if (nodeSet.insert( faceNodes[iCur] ).second)
7507 int nbSimple = iSimple;
7508 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7518 bool foundLoop = (nbSimple > nbUnique);
7521 set<const SMDS_MeshNode*> loopSet;
7522 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7523 const SMDS_MeshNode* n = simpleNodes[iSimple];
7524 if (!loopSet.insert( n ).second) {
7528 int iC = 0, curLast = iSimple;
7529 for (; iC < curLast; iC++) {
7530 if (simpleNodes[iC] == n) break;
7532 int loopLen = curLast - iC;
7534 // create sub-element
7536 quantities.push_back(loopLen);
7537 for (; iC < curLast; iC++) {
7538 poly_nodes.push_back(simpleNodes[iC]);
7541 // shift the rest nodes (place from the first loop position)
7542 for (iC = curLast + 1; iC < nbSimple; iC++) {
7543 simpleNodes[iC - loopLen] = simpleNodes[iC];
7545 nbSimple -= loopLen;
7548 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7549 } // while (foundLoop)
7553 quantities.push_back(iSimple);
7554 for (int i = 0; i < iSimple; i++)
7555 poly_nodes.push_back(simpleNodes[i]);
7561 //=======================================================================
7562 //function : MergeNodes
7563 //purpose : In each group, the cdr of nodes are substituted by the first one
7565 //=======================================================================
7567 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7569 MESSAGE("MergeNodes");
7570 myLastCreatedElems.Clear();
7571 myLastCreatedNodes.Clear();
7573 SMESHDS_Mesh* aMesh = GetMeshDS();
7575 TNodeNodeMap nodeNodeMap; // node to replace - new node
7576 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7577 list< int > rmElemIds, rmNodeIds;
7579 // Fill nodeNodeMap and elems
7581 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7582 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7583 list<const SMDS_MeshNode*>& nodes = *grIt;
7584 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7585 const SMDS_MeshNode* nToKeep = *nIt;
7586 //MESSAGE("node to keep " << nToKeep->GetID());
7587 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7588 const SMDS_MeshNode* nToRemove = *nIt;
7589 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7590 if ( nToRemove != nToKeep ) {
7591 //MESSAGE(" node to remove " << nToRemove->GetID());
7592 rmNodeIds.push_back( nToRemove->GetID() );
7593 AddToSameGroups( nToKeep, nToRemove, aMesh );
7596 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7597 while ( invElemIt->more() ) {
7598 const SMDS_MeshElement* elem = invElemIt->next();
7603 // Change element nodes or remove an element
7605 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7606 for ( ; eIt != elems.end(); eIt++ ) {
7607 const SMDS_MeshElement* elem = *eIt;
7608 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7609 int nbNodes = elem->NbNodes();
7610 int aShapeId = FindShape( elem );
7612 set<const SMDS_MeshNode*> nodeSet;
7613 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7614 int iUnique = 0, iCur = 0, nbRepl = 0;
7615 vector<int> iRepl( nbNodes );
7617 // get new seq of nodes
7618 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7619 while ( itN->more() ) {
7620 const SMDS_MeshNode* n =
7621 static_cast<const SMDS_MeshNode*>( itN->next() );
7623 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7624 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7626 // BUG 0020185: begin
7628 bool stopRecur = false;
7629 set<const SMDS_MeshNode*> nodesRecur;
7630 nodesRecur.insert(n);
7631 while (!stopRecur) {
7632 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7633 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7634 n = (*nnIt_i).second;
7635 if (!nodesRecur.insert(n).second) {
7636 // error: recursive dependancy
7646 curNodes[ iCur ] = n;
7647 bool isUnique = nodeSet.insert( n ).second;
7649 uniqueNodes[ iUnique++ ] = n;
7651 iRepl[ nbRepl++ ] = iCur;
7655 // Analyse element topology after replacement
7658 int nbUniqueNodes = nodeSet.size();
7659 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7660 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7661 // Polygons and Polyhedral volumes
7662 if (elem->IsPoly()) {
7664 if (elem->GetType() == SMDSAbs_Face) {
7666 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7668 for (; inode < nbNodes; inode++) {
7669 face_nodes[inode] = curNodes[inode];
7672 vector<const SMDS_MeshNode *> polygons_nodes;
7673 vector<int> quantities;
7674 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7677 for (int iface = 0; iface < nbNew; iface++) {
7678 int nbNodes = quantities[iface];
7679 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7680 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7681 poly_nodes[ii] = polygons_nodes[inode];
7683 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7684 myLastCreatedElems.Append(newElem);
7686 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7689 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7690 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7691 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7693 if (nbNew > 0) quid = nbNew - 1;
7694 vector<int> newquant(quantities.begin()+quid, quantities.end());
7695 const SMDS_MeshElement* newElem = 0;
7696 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7697 myLastCreatedElems.Append(newElem);
7698 if ( aShapeId && newElem )
7699 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7700 rmElemIds.push_back(elem->GetID());
7703 rmElemIds.push_back(elem->GetID());
7707 else if (elem->GetType() == SMDSAbs_Volume) {
7708 // Polyhedral volume
7709 if (nbUniqueNodes < 4) {
7710 rmElemIds.push_back(elem->GetID());
7713 // each face has to be analyzed in order to check volume validity
7714 const SMDS_VtkVolume* aPolyedre =
7715 dynamic_cast<const SMDS_VtkVolume*>( elem );
7717 int nbFaces = aPolyedre->NbFaces();
7719 vector<const SMDS_MeshNode *> poly_nodes;
7720 vector<int> quantities;
7722 for (int iface = 1; iface <= nbFaces; iface++) {
7723 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7724 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7726 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7727 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7728 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7729 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7730 faceNode = (*nnIt).second;
7732 faceNodes[inode - 1] = faceNode;
7735 SimplifyFace(faceNodes, poly_nodes, quantities);
7738 if (quantities.size() > 3) {
7739 // to be done: remove coincident faces
7742 if (quantities.size() > 3)
7744 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7745 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7746 const SMDS_MeshElement* newElem = 0;
7747 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7748 myLastCreatedElems.Append(newElem);
7749 if ( aShapeId && newElem )
7750 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7751 rmElemIds.push_back(elem->GetID());
7755 rmElemIds.push_back(elem->GetID());
7766 // TODO not all the possible cases are solved. Find something more generic?
7767 switch ( nbNodes ) {
7768 case 2: ///////////////////////////////////// EDGE
7769 isOk = false; break;
7770 case 3: ///////////////////////////////////// TRIANGLE
7771 isOk = false; break;
7773 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7775 else { //////////////////////////////////// QUADRANGLE
7776 if ( nbUniqueNodes < 3 )
7778 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7779 isOk = false; // opposite nodes stick
7780 //MESSAGE("isOk " << isOk);
7783 case 6: ///////////////////////////////////// PENTAHEDRON
7784 if ( nbUniqueNodes == 4 ) {
7785 // ---------------------------------> tetrahedron
7787 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7788 // all top nodes stick: reverse a bottom
7789 uniqueNodes[ 0 ] = curNodes [ 1 ];
7790 uniqueNodes[ 1 ] = curNodes [ 0 ];
7792 else if (nbRepl == 3 &&
7793 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7794 // all bottom nodes stick: set a top before
7795 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7796 uniqueNodes[ 0 ] = curNodes [ 3 ];
7797 uniqueNodes[ 1 ] = curNodes [ 4 ];
7798 uniqueNodes[ 2 ] = curNodes [ 5 ];
7800 else if (nbRepl == 4 &&
7801 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7802 // a lateral face turns into a line: reverse a bottom
7803 uniqueNodes[ 0 ] = curNodes [ 1 ];
7804 uniqueNodes[ 1 ] = curNodes [ 0 ];
7809 else if ( nbUniqueNodes == 5 ) {
7810 // PENTAHEDRON --------------------> 2 tetrahedrons
7811 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7812 // a bottom node sticks with a linked top one
7814 SMDS_MeshElement* newElem =
7815 aMesh->AddVolume(curNodes[ 3 ],
7818 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7819 myLastCreatedElems.Append(newElem);
7821 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7822 // 2. : reverse a bottom
7823 uniqueNodes[ 0 ] = curNodes [ 1 ];
7824 uniqueNodes[ 1 ] = curNodes [ 0 ];
7834 if(elem->IsQuadratic()) { // Quadratic quadrangle
7846 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7849 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7851 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7852 uniqueNodes[0] = curNodes[0];
7853 uniqueNodes[1] = curNodes[2];
7854 uniqueNodes[2] = curNodes[3];
7855 uniqueNodes[3] = curNodes[5];
7856 uniqueNodes[4] = curNodes[6];
7857 uniqueNodes[5] = curNodes[7];
7860 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7861 uniqueNodes[0] = curNodes[0];
7862 uniqueNodes[1] = curNodes[1];
7863 uniqueNodes[2] = curNodes[2];
7864 uniqueNodes[3] = curNodes[4];
7865 uniqueNodes[4] = curNodes[5];
7866 uniqueNodes[5] = curNodes[6];
7869 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7870 uniqueNodes[0] = curNodes[1];
7871 uniqueNodes[1] = curNodes[2];
7872 uniqueNodes[2] = curNodes[3];
7873 uniqueNodes[3] = curNodes[5];
7874 uniqueNodes[4] = curNodes[6];
7875 uniqueNodes[5] = curNodes[0];
7878 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7879 uniqueNodes[0] = curNodes[0];
7880 uniqueNodes[1] = curNodes[1];
7881 uniqueNodes[2] = curNodes[3];
7882 uniqueNodes[3] = curNodes[4];
7883 uniqueNodes[4] = curNodes[6];
7884 uniqueNodes[5] = curNodes[7];
7887 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7888 uniqueNodes[0] = curNodes[0];
7889 uniqueNodes[1] = curNodes[2];
7890 uniqueNodes[2] = curNodes[3];
7891 uniqueNodes[3] = curNodes[1];
7892 uniqueNodes[4] = curNodes[6];
7893 uniqueNodes[5] = curNodes[7];
7896 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7897 uniqueNodes[0] = curNodes[0];
7898 uniqueNodes[1] = curNodes[1];
7899 uniqueNodes[2] = curNodes[2];
7900 uniqueNodes[3] = curNodes[4];
7901 uniqueNodes[4] = curNodes[5];
7902 uniqueNodes[5] = curNodes[7];
7905 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7906 uniqueNodes[0] = curNodes[0];
7907 uniqueNodes[1] = curNodes[1];
7908 uniqueNodes[2] = curNodes[3];
7909 uniqueNodes[3] = curNodes[4];
7910 uniqueNodes[4] = curNodes[2];
7911 uniqueNodes[5] = curNodes[7];
7914 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7915 uniqueNodes[0] = curNodes[0];
7916 uniqueNodes[1] = curNodes[1];
7917 uniqueNodes[2] = curNodes[2];
7918 uniqueNodes[3] = curNodes[4];
7919 uniqueNodes[4] = curNodes[5];
7920 uniqueNodes[5] = curNodes[3];
7925 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7928 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7932 //////////////////////////////////// HEXAHEDRON
7934 SMDS_VolumeTool hexa (elem);
7935 hexa.SetExternalNormal();
7936 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7937 //////////////////////// HEX ---> 1 tetrahedron
7938 for ( int iFace = 0; iFace < 6; iFace++ ) {
7939 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7940 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7941 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7942 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7943 // one face turns into a point ...
7944 int iOppFace = hexa.GetOppFaceIndex( iFace );
7945 ind = hexa.GetFaceNodesIndices( iOppFace );
7947 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7948 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7951 if ( nbStick == 1 ) {
7952 // ... and the opposite one - into a triangle.
7954 ind = hexa.GetFaceNodesIndices( iFace );
7955 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7962 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7963 //////////////////////// HEX ---> 1 prism
7964 int nbTria = 0, iTria[3];
7965 const int *ind; // indices of face nodes
7966 // look for triangular faces
7967 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7968 ind = hexa.GetFaceNodesIndices( iFace );
7969 TIDSortedNodeSet faceNodes;
7970 for ( iCur = 0; iCur < 4; iCur++ )
7971 faceNodes.insert( curNodes[ind[iCur]] );
7972 if ( faceNodes.size() == 3 )
7973 iTria[ nbTria++ ] = iFace;
7975 // check if triangles are opposite
7976 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7979 // set nodes of the bottom triangle
7980 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7982 for ( iCur = 0; iCur < 4; iCur++ )
7983 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7984 indB.push_back( ind[iCur] );
7985 if ( !hexa.IsForward() )
7986 std::swap( indB[0], indB[2] );
7987 for ( iCur = 0; iCur < 3; iCur++ )
7988 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7989 // set nodes of the top triangle
7990 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7991 for ( iCur = 0; iCur < 3; ++iCur )
7992 for ( int j = 0; j < 4; ++j )
7993 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7995 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8001 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8002 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8003 for ( int iFace = 0; iFace < 6; iFace++ ) {
8004 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8005 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8006 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8007 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8008 // one face turns into a point ...
8009 int iOppFace = hexa.GetOppFaceIndex( iFace );
8010 ind = hexa.GetFaceNodesIndices( iOppFace );
8012 iUnique = 2; // reverse a tetrahedron 1 bottom
8013 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8014 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8016 else if ( iUnique >= 0 )
8017 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8019 if ( nbStick == 0 ) {
8020 // ... and the opposite one is a quadrangle
8022 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8023 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8026 SMDS_MeshElement* newElem =
8027 aMesh->AddVolume(curNodes[ind[ 0 ]],
8030 curNodes[indTop[ 0 ]]);
8031 myLastCreatedElems.Append(newElem);
8033 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8040 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8041 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8042 // find indices of quad and tri faces
8043 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8044 for ( iFace = 0; iFace < 6; iFace++ ) {
8045 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8047 for ( iCur = 0; iCur < 4; iCur++ )
8048 nodeSet.insert( curNodes[ind[ iCur ]] );
8049 nbUniqueNodes = nodeSet.size();
8050 if ( nbUniqueNodes == 3 )
8051 iTriFace[ nbTri++ ] = iFace;
8052 else if ( nbUniqueNodes == 4 )
8053 iQuadFace[ nbQuad++ ] = iFace;
8055 if (nbQuad == 2 && nbTri == 4 &&
8056 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8057 // 2 opposite quadrangles stuck with a diagonal;
8058 // sample groups of merged indices: (0-4)(2-6)
8059 // --------------------------------------------> 2 tetrahedrons
8060 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8061 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8062 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8063 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8064 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8065 // stuck with 0-2 diagonal
8073 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8074 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8075 // stuck with 1-3 diagonal
8087 uniqueNodes[ 0 ] = curNodes [ i0 ];
8088 uniqueNodes[ 1 ] = curNodes [ i1d ];
8089 uniqueNodes[ 2 ] = curNodes [ i3d ];
8090 uniqueNodes[ 3 ] = curNodes [ i0t ];
8093 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8097 myLastCreatedElems.Append(newElem);
8099 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8102 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8103 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8104 // --------------------------------------------> prism
8105 // find 2 opposite triangles
8107 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8108 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8109 // find indices of kept and replaced nodes
8110 // and fill unique nodes of 2 opposite triangles
8111 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8112 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8113 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8114 // fill unique nodes
8117 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8118 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8119 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8121 // iCur of a linked node of the opposite face (make normals co-directed):
8122 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8123 // check that correspondent corners of triangles are linked
8124 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8127 uniqueNodes[ iUnique ] = n;
8128 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8137 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8140 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8147 } // switch ( nbNodes )
8149 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8151 if ( isOk ) { // the elem remains valid after sticking nodes
8152 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8154 // Change nodes of polyedre
8155 const SMDS_VtkVolume* aPolyedre =
8156 dynamic_cast<const SMDS_VtkVolume*>( elem );
8158 int nbFaces = aPolyedre->NbFaces();
8160 vector<const SMDS_MeshNode *> poly_nodes;
8161 vector<int> quantities (nbFaces);
8163 for (int iface = 1; iface <= nbFaces; iface++) {
8164 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8165 quantities[iface - 1] = nbFaceNodes;
8167 for (inode = 1; inode <= nbFaceNodes; inode++) {
8168 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8170 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8171 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8172 curNode = (*nnIt).second;
8174 poly_nodes.push_back(curNode);
8177 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8180 else // replace non-polyhedron elements
8182 const SMDSAbs_ElementType etyp = elem->GetType();
8183 const int elemId = elem->GetID();
8184 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8185 uniqueNodes.resize(nbUniqueNodes);
8187 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8189 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8190 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8191 if ( sm && newElem )
8192 sm->AddElement( newElem );
8193 if ( elem != newElem )
8194 ReplaceElemInGroups( elem, newElem, aMesh );
8198 // Remove invalid regular element or invalid polygon
8199 rmElemIds.push_back( elem->GetID() );
8202 } // loop on elements
8204 // Remove bad elements, then equal nodes (order important)
8206 Remove( rmElemIds, false );
8207 Remove( rmNodeIds, true );
8212 // ========================================================
8213 // class : SortableElement
8214 // purpose : allow sorting elements basing on their nodes
8215 // ========================================================
8216 class SortableElement : public set <const SMDS_MeshElement*>
8220 SortableElement( const SMDS_MeshElement* theElem )
8223 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8224 while ( nodeIt->more() )
8225 this->insert( nodeIt->next() );
8228 const SMDS_MeshElement* Get() const
8231 void Set(const SMDS_MeshElement* e) const
8236 mutable const SMDS_MeshElement* myElem;
8239 //=======================================================================
8240 //function : FindEqualElements
8241 //purpose : Return list of group of elements built on the same nodes.
8242 // Search among theElements or in the whole mesh if theElements is empty
8243 //=======================================================================
8245 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8246 TListOfListOfElementsID & theGroupsOfElementsID)
8248 myLastCreatedElems.Clear();
8249 myLastCreatedNodes.Clear();
8251 typedef map< SortableElement, int > TMapOfNodeSet;
8252 typedef list<int> TGroupOfElems;
8254 if ( theElements.empty() )
8255 { // get all elements in the mesh
8256 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8257 while ( eIt->more() )
8258 theElements.insert( theElements.end(), eIt->next());
8261 vector< TGroupOfElems > arrayOfGroups;
8262 TGroupOfElems groupOfElems;
8263 TMapOfNodeSet mapOfNodeSet;
8265 TIDSortedElemSet::iterator elemIt = theElements.begin();
8266 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8267 const SMDS_MeshElement* curElem = *elemIt;
8268 SortableElement SE(curElem);
8271 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8272 if( !(pp.second) ) {
8273 TMapOfNodeSet::iterator& itSE = pp.first;
8274 ind = (*itSE).second;
8275 arrayOfGroups[ind].push_back(curElem->GetID());
8278 groupOfElems.clear();
8279 groupOfElems.push_back(curElem->GetID());
8280 arrayOfGroups.push_back(groupOfElems);
8285 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8286 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8287 groupOfElems = *groupIt;
8288 if ( groupOfElems.size() > 1 ) {
8289 groupOfElems.sort();
8290 theGroupsOfElementsID.push_back(groupOfElems);
8295 //=======================================================================
8296 //function : MergeElements
8297 //purpose : In each given group, substitute all elements by the first one.
8298 //=======================================================================
8300 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8302 myLastCreatedElems.Clear();
8303 myLastCreatedNodes.Clear();
8305 typedef list<int> TListOfIDs;
8306 TListOfIDs rmElemIds; // IDs of elems to remove
8308 SMESHDS_Mesh* aMesh = GetMeshDS();
8310 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8311 while ( groupsIt != theGroupsOfElementsID.end() ) {
8312 TListOfIDs& aGroupOfElemID = *groupsIt;
8313 aGroupOfElemID.sort();
8314 int elemIDToKeep = aGroupOfElemID.front();
8315 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8316 aGroupOfElemID.pop_front();
8317 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8318 while ( idIt != aGroupOfElemID.end() ) {
8319 int elemIDToRemove = *idIt;
8320 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8321 // add the kept element in groups of removed one (PAL15188)
8322 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8323 rmElemIds.push_back( elemIDToRemove );
8329 Remove( rmElemIds, false );
8332 //=======================================================================
8333 //function : MergeEqualElements
8334 //purpose : Remove all but one of elements built on the same nodes.
8335 //=======================================================================
8337 void SMESH_MeshEditor::MergeEqualElements()
8339 TIDSortedElemSet aMeshElements; /* empty input ==
8340 to merge equal elements in the whole mesh */
8341 TListOfListOfElementsID aGroupsOfElementsID;
8342 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8343 MergeElements(aGroupsOfElementsID);
8346 //=======================================================================
8347 //function : FindFaceInSet
8348 //purpose : Return a face having linked nodes n1 and n2 and which is
8349 // - not in avoidSet,
8350 // - in elemSet provided that !elemSet.empty()
8351 // i1 and i2 optionally returns indices of n1 and n2
8352 //=======================================================================
8354 const SMDS_MeshElement*
8355 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8356 const SMDS_MeshNode* n2,
8357 const TIDSortedElemSet& elemSet,
8358 const TIDSortedElemSet& avoidSet,
8364 const SMDS_MeshElement* face = 0;
8366 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8367 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8368 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8370 //MESSAGE("in while ( invElemIt->more() && !face )");
8371 const SMDS_MeshElement* elem = invElemIt->next();
8372 if (avoidSet.count( elem ))
8374 if ( !elemSet.empty() && !elemSet.count( elem ))
8377 i1 = elem->GetNodeIndex( n1 );
8378 // find a n2 linked to n1
8379 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8380 for ( int di = -1; di < 2 && !face; di += 2 )
8382 i2 = (i1+di+nbN) % nbN;
8383 if ( elem->GetNode( i2 ) == n2 )
8386 if ( !face && elem->IsQuadratic())
8388 // analysis for quadratic elements using all nodes
8389 const SMDS_VtkFace* F =
8390 dynamic_cast<const SMDS_VtkFace*>(elem);
8391 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8392 // use special nodes iterator
8393 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8394 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8395 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8397 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8398 if ( n1 == prevN && n2 == n )
8402 else if ( n2 == prevN && n1 == n )
8404 face = elem; swap( i1, i2 );
8410 if ( n1ind ) *n1ind = i1;
8411 if ( n2ind ) *n2ind = i2;
8415 //=======================================================================
8416 //function : findAdjacentFace
8418 //=======================================================================
8420 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8421 const SMDS_MeshNode* n2,
8422 const SMDS_MeshElement* elem)
8424 TIDSortedElemSet elemSet, avoidSet;
8426 avoidSet.insert ( elem );
8427 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8430 //=======================================================================
8431 //function : FindFreeBorder
8433 //=======================================================================
8435 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8437 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8438 const SMDS_MeshNode* theSecondNode,
8439 const SMDS_MeshNode* theLastNode,
8440 list< const SMDS_MeshNode* > & theNodes,
8441 list< const SMDS_MeshElement* >& theFaces)
8443 if ( !theFirstNode || !theSecondNode )
8445 // find border face between theFirstNode and theSecondNode
8446 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8450 theFaces.push_back( curElem );
8451 theNodes.push_back( theFirstNode );
8452 theNodes.push_back( theSecondNode );
8454 //vector<const SMDS_MeshNode*> nodes;
8455 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8456 TIDSortedElemSet foundElems;
8457 bool needTheLast = ( theLastNode != 0 );
8459 while ( nStart != theLastNode ) {
8460 if ( nStart == theFirstNode )
8461 return !needTheLast;
8463 // find all free border faces sharing form nStart
8465 list< const SMDS_MeshElement* > curElemList;
8466 list< const SMDS_MeshNode* > nStartList;
8467 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8468 while ( invElemIt->more() ) {
8469 const SMDS_MeshElement* e = invElemIt->next();
8470 if ( e == curElem || foundElems.insert( e ).second ) {
8472 int iNode = 0, nbNodes = e->NbNodes();
8473 //const SMDS_MeshNode* nodes[nbNodes+1];
8474 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8476 if(e->IsQuadratic()) {
8477 const SMDS_VtkFace* F =
8478 dynamic_cast<const SMDS_VtkFace*>(e);
8479 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8480 // use special nodes iterator
8481 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8482 while( anIter->more() ) {
8483 nodes[ iNode++ ] = cast2Node(anIter->next());
8487 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8488 while ( nIt->more() )
8489 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8491 nodes[ iNode ] = nodes[ 0 ];
8493 for ( iNode = 0; iNode < nbNodes; iNode++ )
8494 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8495 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8496 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8498 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8499 curElemList.push_back( e );
8503 // analyse the found
8505 int nbNewBorders = curElemList.size();
8506 if ( nbNewBorders == 0 ) {
8507 // no free border furthermore
8508 return !needTheLast;
8510 else if ( nbNewBorders == 1 ) {
8511 // one more element found
8513 nStart = nStartList.front();
8514 curElem = curElemList.front();
8515 theFaces.push_back( curElem );
8516 theNodes.push_back( nStart );
8519 // several continuations found
8520 list< const SMDS_MeshElement* >::iterator curElemIt;
8521 list< const SMDS_MeshNode* >::iterator nStartIt;
8522 // check if one of them reached the last node
8523 if ( needTheLast ) {
8524 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8525 curElemIt!= curElemList.end();
8526 curElemIt++, nStartIt++ )
8527 if ( *nStartIt == theLastNode ) {
8528 theFaces.push_back( *curElemIt );
8529 theNodes.push_back( *nStartIt );
8533 // find the best free border by the continuations
8534 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8535 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8536 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8537 curElemIt!= curElemList.end();
8538 curElemIt++, nStartIt++ )
8540 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8541 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8542 // find one more free border
8543 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8547 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8548 // choice: clear a worse one
8549 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8550 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8551 contNodes[ iWorse ].clear();
8552 contFaces[ iWorse ].clear();
8555 if ( contNodes[0].empty() && contNodes[1].empty() )
8558 // append the best free border
8559 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8560 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8561 theNodes.pop_back(); // remove nIgnore
8562 theNodes.pop_back(); // remove nStart
8563 theFaces.pop_back(); // remove curElem
8564 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8565 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8566 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8567 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8570 } // several continuations found
8571 } // while ( nStart != theLastNode )
8576 //=======================================================================
8577 //function : CheckFreeBorderNodes
8578 //purpose : Return true if the tree nodes are on a free border
8579 //=======================================================================
8581 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8582 const SMDS_MeshNode* theNode2,
8583 const SMDS_MeshNode* theNode3)
8585 list< const SMDS_MeshNode* > nodes;
8586 list< const SMDS_MeshElement* > faces;
8587 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8590 //=======================================================================
8591 //function : SewFreeBorder
8593 //=======================================================================
8595 SMESH_MeshEditor::Sew_Error
8596 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8597 const SMDS_MeshNode* theBordSecondNode,
8598 const SMDS_MeshNode* theBordLastNode,
8599 const SMDS_MeshNode* theSideFirstNode,
8600 const SMDS_MeshNode* theSideSecondNode,
8601 const SMDS_MeshNode* theSideThirdNode,
8602 const bool theSideIsFreeBorder,
8603 const bool toCreatePolygons,
8604 const bool toCreatePolyedrs)
8606 myLastCreatedElems.Clear();
8607 myLastCreatedNodes.Clear();
8609 MESSAGE("::SewFreeBorder()");
8610 Sew_Error aResult = SEW_OK;
8612 // ====================================
8613 // find side nodes and elements
8614 // ====================================
8616 list< const SMDS_MeshNode* > nSide[ 2 ];
8617 list< const SMDS_MeshElement* > eSide[ 2 ];
8618 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8619 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8623 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8624 nSide[0], eSide[0])) {
8625 MESSAGE(" Free Border 1 not found " );
8626 aResult = SEW_BORDER1_NOT_FOUND;
8628 if (theSideIsFreeBorder) {
8631 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8632 nSide[1], eSide[1])) {
8633 MESSAGE(" Free Border 2 not found " );
8634 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8637 if ( aResult != SEW_OK )
8640 if (!theSideIsFreeBorder) {
8644 // -------------------------------------------------------------------------
8646 // 1. If nodes to merge are not coincident, move nodes of the free border
8647 // from the coord sys defined by the direction from the first to last
8648 // nodes of the border to the correspondent sys of the side 2
8649 // 2. On the side 2, find the links most co-directed with the correspondent
8650 // links of the free border
8651 // -------------------------------------------------------------------------
8653 // 1. Since sewing may break if there are volumes to split on the side 2,
8654 // we wont move nodes but just compute new coordinates for them
8655 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8656 TNodeXYZMap nBordXYZ;
8657 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8658 list< const SMDS_MeshNode* >::iterator nBordIt;
8660 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8661 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8662 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8663 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8664 double tol2 = 1.e-8;
8665 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8666 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8667 // Need node movement.
8669 // find X and Z axes to create trsf
8670 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8672 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8674 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8677 gp_Ax3 toBordAx( Pb1, Zb, X );
8678 gp_Ax3 fromSideAx( Ps1, Zs, X );
8679 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8681 gp_Trsf toBordSys, fromSide2Sys;
8682 toBordSys.SetTransformation( toBordAx );
8683 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8684 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8687 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8688 const SMDS_MeshNode* n = *nBordIt;
8689 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8690 toBordSys.Transforms( xyz );
8691 fromSide2Sys.Transforms( xyz );
8692 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8696 // just insert nodes XYZ in the nBordXYZ map
8697 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8698 const SMDS_MeshNode* n = *nBordIt;
8699 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8703 // 2. On the side 2, find the links most co-directed with the correspondent
8704 // links of the free border
8706 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8707 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8708 sideNodes.push_back( theSideFirstNode );
8710 bool hasVolumes = false;
8711 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8712 set<long> foundSideLinkIDs, checkedLinkIDs;
8713 SMDS_VolumeTool volume;
8714 //const SMDS_MeshNode* faceNodes[ 4 ];
8716 const SMDS_MeshNode* sideNode;
8717 const SMDS_MeshElement* sideElem;
8718 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8719 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8720 nBordIt = bordNodes.begin();
8722 // border node position and border link direction to compare with
8723 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8724 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8725 // choose next side node by link direction or by closeness to
8726 // the current border node:
8727 bool searchByDir = ( *nBordIt != theBordLastNode );
8729 // find the next node on the Side 2
8731 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8733 checkedLinkIDs.clear();
8734 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8736 // loop on inverse elements of current node (prevSideNode) on the Side 2
8737 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8738 while ( invElemIt->more() )
8740 const SMDS_MeshElement* elem = invElemIt->next();
8741 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8742 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8743 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8744 bool isVolume = volume.Set( elem );
8745 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8746 if ( isVolume ) // --volume
8748 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8749 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8750 if(elem->IsQuadratic()) {
8751 const SMDS_VtkFace* F =
8752 dynamic_cast<const SMDS_VtkFace*>(elem);
8753 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8754 // use special nodes iterator
8755 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8756 while( anIter->more() ) {
8757 nodes[ iNode ] = cast2Node(anIter->next());
8758 if ( nodes[ iNode++ ] == prevSideNode )
8759 iPrevNode = iNode - 1;
8763 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8764 while ( nIt->more() ) {
8765 nodes[ iNode ] = cast2Node( nIt->next() );
8766 if ( nodes[ iNode++ ] == prevSideNode )
8767 iPrevNode = iNode - 1;
8770 // there are 2 links to check
8775 // loop on links, to be precise, on the second node of links
8776 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8777 const SMDS_MeshNode* n = nodes[ iNode ];
8779 if ( !volume.IsLinked( n, prevSideNode ))
8783 if ( iNode ) // a node before prevSideNode
8784 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8785 else // a node after prevSideNode
8786 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8788 // check if this link was already used
8789 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8790 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8791 if (!isJustChecked &&
8792 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8794 // test a link geometrically
8795 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8796 bool linkIsBetter = false;
8797 double dot = 0.0, dist = 0.0;
8798 if ( searchByDir ) { // choose most co-directed link
8799 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8800 linkIsBetter = ( dot > maxDot );
8802 else { // choose link with the node closest to bordPos
8803 dist = ( nextXYZ - bordPos ).SquareModulus();
8804 linkIsBetter = ( dist < minDist );
8806 if ( linkIsBetter ) {
8815 } // loop on inverse elements of prevSideNode
8818 MESSAGE(" Cant find path by links of the Side 2 ");
8819 return SEW_BAD_SIDE_NODES;
8821 sideNodes.push_back( sideNode );
8822 sideElems.push_back( sideElem );
8823 foundSideLinkIDs.insert ( linkID );
8824 prevSideNode = sideNode;
8826 if ( *nBordIt == theBordLastNode )
8827 searchByDir = false;
8829 // find the next border link to compare with
8830 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8831 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8832 // move to next border node if sideNode is before forward border node (bordPos)
8833 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8834 prevBordNode = *nBordIt;
8836 bordPos = nBordXYZ[ *nBordIt ];
8837 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8838 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8842 while ( sideNode != theSideSecondNode );
8844 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8845 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8846 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8848 } // end nodes search on the side 2
8850 // ============================
8851 // sew the border to the side 2
8852 // ============================
8854 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8855 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8857 TListOfListOfNodes nodeGroupsToMerge;
8858 if ( nbNodes[0] == nbNodes[1] ||
8859 ( theSideIsFreeBorder && !theSideThirdNode)) {
8861 // all nodes are to be merged
8863 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8864 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8865 nIt[0]++, nIt[1]++ )
8867 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8868 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8869 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8874 // insert new nodes into the border and the side to get equal nb of segments
8876 // get normalized parameters of nodes on the borders
8877 //double param[ 2 ][ maxNbNodes ];
8879 param[0] = new double [ maxNbNodes ];
8880 param[1] = new double [ maxNbNodes ];
8882 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8883 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8884 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8885 const SMDS_MeshNode* nPrev = *nIt;
8886 double bordLength = 0;
8887 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8888 const SMDS_MeshNode* nCur = *nIt;
8889 gp_XYZ segment (nCur->X() - nPrev->X(),
8890 nCur->Y() - nPrev->Y(),
8891 nCur->Z() - nPrev->Z());
8892 double segmentLen = segment.Modulus();
8893 bordLength += segmentLen;
8894 param[ iBord ][ iNode ] = bordLength;
8897 // normalize within [0,1]
8898 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8899 param[ iBord ][ iNode ] /= bordLength;
8903 // loop on border segments
8904 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8905 int i[ 2 ] = { 0, 0 };
8906 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8907 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8909 TElemOfNodeListMap insertMap;
8910 TElemOfNodeListMap::iterator insertMapIt;
8912 // key: elem to insert nodes into
8913 // value: 2 nodes to insert between + nodes to be inserted
8915 bool next[ 2 ] = { false, false };
8917 // find min adjacent segment length after sewing
8918 double nextParam = 10., prevParam = 0;
8919 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8920 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8921 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8922 if ( i[ iBord ] > 0 )
8923 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8925 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8926 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8927 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8929 // choose to insert or to merge nodes
8930 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8931 if ( Abs( du ) <= minSegLen * 0.2 ) {
8934 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8935 const SMDS_MeshNode* n0 = *nIt[0];
8936 const SMDS_MeshNode* n1 = *nIt[1];
8937 nodeGroupsToMerge.back().push_back( n1 );
8938 nodeGroupsToMerge.back().push_back( n0 );
8939 // position of node of the border changes due to merge
8940 param[ 0 ][ i[0] ] += du;
8941 // move n1 for the sake of elem shape evaluation during insertion.
8942 // n1 will be removed by MergeNodes() anyway
8943 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8944 next[0] = next[1] = true;
8949 int intoBord = ( du < 0 ) ? 0 : 1;
8950 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8951 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8952 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8953 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8954 if ( intoBord == 1 ) {
8955 // move node of the border to be on a link of elem of the side
8956 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8957 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8958 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8959 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8960 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8962 insertMapIt = insertMap.find( elem );
8963 bool notFound = ( insertMapIt == insertMap.end() );
8964 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8966 // insert into another link of the same element:
8967 // 1. perform insertion into the other link of the elem
8968 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8969 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8970 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8971 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8972 // 2. perform insertion into the link of adjacent faces
8974 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8976 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8980 if (toCreatePolyedrs) {
8981 // perform insertion into the links of adjacent volumes
8982 UpdateVolumes(n12, n22, nodeList);
8984 // 3. find an element appeared on n1 and n2 after the insertion
8985 insertMap.erase( elem );
8986 elem = findAdjacentFace( n1, n2, 0 );
8988 if ( notFound || otherLink ) {
8989 // add element and nodes of the side into the insertMap
8990 insertMapIt = insertMap.insert
8991 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8992 (*insertMapIt).second.push_back( n1 );
8993 (*insertMapIt).second.push_back( n2 );
8995 // add node to be inserted into elem
8996 (*insertMapIt).second.push_back( nIns );
8997 next[ 1 - intoBord ] = true;
9000 // go to the next segment
9001 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9002 if ( next[ iBord ] ) {
9003 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9005 nPrev[ iBord ] = *nIt[ iBord ];
9006 nIt[ iBord ]++; i[ iBord ]++;
9010 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9012 // perform insertion of nodes into elements
9014 for (insertMapIt = insertMap.begin();
9015 insertMapIt != insertMap.end();
9018 const SMDS_MeshElement* elem = (*insertMapIt).first;
9019 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9020 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9021 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9023 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9025 if ( !theSideIsFreeBorder ) {
9026 // look for and insert nodes into the faces adjacent to elem
9028 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9030 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9035 if (toCreatePolyedrs) {
9036 // perform insertion into the links of adjacent volumes
9037 UpdateVolumes(n1, n2, nodeList);
9043 } // end: insert new nodes
9045 MergeNodes ( nodeGroupsToMerge );
9050 //=======================================================================
9051 //function : InsertNodesIntoLink
9052 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9053 // and theBetweenNode2 and split theElement
9054 //=======================================================================
9056 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9057 const SMDS_MeshNode* theBetweenNode1,
9058 const SMDS_MeshNode* theBetweenNode2,
9059 list<const SMDS_MeshNode*>& theNodesToInsert,
9060 const bool toCreatePoly)
9062 if ( theFace->GetType() != SMDSAbs_Face ) return;
9064 // find indices of 2 link nodes and of the rest nodes
9065 int iNode = 0, il1, il2, i3, i4;
9066 il1 = il2 = i3 = i4 = -1;
9067 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9068 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9070 if(theFace->IsQuadratic()) {
9071 const SMDS_VtkFace* F =
9072 dynamic_cast<const SMDS_VtkFace*>(theFace);
9073 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9074 // use special nodes iterator
9075 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9076 while( anIter->more() ) {
9077 const SMDS_MeshNode* n = cast2Node(anIter->next());
9078 if ( n == theBetweenNode1 )
9080 else if ( n == theBetweenNode2 )
9086 nodes[ iNode++ ] = n;
9090 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9091 while ( nodeIt->more() ) {
9092 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9093 if ( n == theBetweenNode1 )
9095 else if ( n == theBetweenNode2 )
9101 nodes[ iNode++ ] = n;
9104 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9107 // arrange link nodes to go one after another regarding the face orientation
9108 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9109 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9114 aNodesToInsert.reverse();
9116 // check that not link nodes of a quadrangles are in good order
9117 int nbFaceNodes = theFace->NbNodes();
9118 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9124 if (toCreatePoly || theFace->IsPoly()) {
9127 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9129 // add nodes of face up to first node of link
9132 if(theFace->IsQuadratic()) {
9133 const SMDS_VtkFace* F =
9134 dynamic_cast<const SMDS_VtkFace*>(theFace);
9135 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9136 // use special nodes iterator
9137 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9138 while( anIter->more() && !isFLN ) {
9139 const SMDS_MeshNode* n = cast2Node(anIter->next());
9140 poly_nodes[iNode++] = n;
9141 if (n == nodes[il1]) {
9145 // add nodes to insert
9146 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9147 for (; nIt != aNodesToInsert.end(); nIt++) {
9148 poly_nodes[iNode++] = *nIt;
9150 // add nodes of face starting from last node of link
9151 while ( anIter->more() ) {
9152 poly_nodes[iNode++] = cast2Node(anIter->next());
9156 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9157 while ( nodeIt->more() && !isFLN ) {
9158 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9159 poly_nodes[iNode++] = n;
9160 if (n == nodes[il1]) {
9164 // add nodes to insert
9165 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9166 for (; nIt != aNodesToInsert.end(); nIt++) {
9167 poly_nodes[iNode++] = *nIt;
9169 // add nodes of face starting from last node of link
9170 while ( nodeIt->more() ) {
9171 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9172 poly_nodes[iNode++] = n;
9176 // edit or replace the face
9177 SMESHDS_Mesh *aMesh = GetMeshDS();
9179 if (theFace->IsPoly()) {
9180 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9183 int aShapeId = FindShape( theFace );
9185 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9186 myLastCreatedElems.Append(newElem);
9187 if ( aShapeId && newElem )
9188 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9190 aMesh->RemoveElement(theFace);
9195 SMESHDS_Mesh *aMesh = GetMeshDS();
9196 if( !theFace->IsQuadratic() ) {
9198 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9199 int nbLinkNodes = 2 + aNodesToInsert.size();
9200 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9201 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9202 linkNodes[ 0 ] = nodes[ il1 ];
9203 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9204 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9205 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9206 linkNodes[ iNode++ ] = *nIt;
9208 // decide how to split a quadrangle: compare possible variants
9209 // and choose which of splits to be a quadrangle
9210 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9211 if ( nbFaceNodes == 3 ) {
9212 iBestQuad = nbSplits;
9215 else if ( nbFaceNodes == 4 ) {
9216 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9217 double aBestRate = DBL_MAX;
9218 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9220 double aBadRate = 0;
9221 // evaluate elements quality
9222 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9223 if ( iSplit == iQuad ) {
9224 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9228 aBadRate += getBadRate( &quad, aCrit );
9231 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9233 nodes[ iSplit < iQuad ? i4 : i3 ]);
9234 aBadRate += getBadRate( &tria, aCrit );
9238 if ( aBadRate < aBestRate ) {
9240 aBestRate = aBadRate;
9245 // create new elements
9246 int aShapeId = FindShape( theFace );
9249 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9250 SMDS_MeshElement* newElem = 0;
9251 if ( iSplit == iBestQuad )
9252 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9257 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9259 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9260 myLastCreatedElems.Append(newElem);
9261 if ( aShapeId && newElem )
9262 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9265 // change nodes of theFace
9266 const SMDS_MeshNode* newNodes[ 4 ];
9267 newNodes[ 0 ] = linkNodes[ i1 ];
9268 newNodes[ 1 ] = linkNodes[ i2 ];
9269 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9270 newNodes[ 3 ] = nodes[ i4 ];
9271 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9272 const SMDS_MeshElement* newElem = 0;
9273 if (iSplit == iBestQuad)
9274 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9276 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9277 myLastCreatedElems.Append(newElem);
9278 if ( aShapeId && newElem )
9279 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9280 } // end if(!theFace->IsQuadratic())
9281 else { // theFace is quadratic
9282 // we have to split theFace on simple triangles and one simple quadrangle
9284 int nbshift = tmp*2;
9285 // shift nodes in nodes[] by nbshift
9287 for(i=0; i<nbshift; i++) {
9288 const SMDS_MeshNode* n = nodes[0];
9289 for(j=0; j<nbFaceNodes-1; j++) {
9290 nodes[j] = nodes[j+1];
9292 nodes[nbFaceNodes-1] = n;
9294 il1 = il1 - nbshift;
9295 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9296 // n0 n1 n2 n0 n1 n2
9297 // +-----+-----+ +-----+-----+
9306 // create new elements
9307 int aShapeId = FindShape( theFace );
9310 if(nbFaceNodes==6) { // quadratic triangle
9311 SMDS_MeshElement* newElem =
9312 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9313 myLastCreatedElems.Append(newElem);
9314 if ( aShapeId && newElem )
9315 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9316 if(theFace->IsMediumNode(nodes[il1])) {
9317 // create quadrangle
9318 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9319 myLastCreatedElems.Append(newElem);
9320 if ( aShapeId && newElem )
9321 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9327 // create quadrangle
9328 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9329 myLastCreatedElems.Append(newElem);
9330 if ( aShapeId && newElem )
9331 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9337 else { // nbFaceNodes==8 - quadratic quadrangle
9338 SMDS_MeshElement* newElem =
9339 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9340 myLastCreatedElems.Append(newElem);
9341 if ( aShapeId && newElem )
9342 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9343 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9344 myLastCreatedElems.Append(newElem);
9345 if ( aShapeId && newElem )
9346 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9347 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9348 myLastCreatedElems.Append(newElem);
9349 if ( aShapeId && newElem )
9350 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9351 if(theFace->IsMediumNode(nodes[il1])) {
9352 // create quadrangle
9353 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9354 myLastCreatedElems.Append(newElem);
9355 if ( aShapeId && newElem )
9356 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9362 // create quadrangle
9363 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9364 myLastCreatedElems.Append(newElem);
9365 if ( aShapeId && newElem )
9366 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9372 // create needed triangles using n1,n2,n3 and inserted nodes
9373 int nbn = 2 + aNodesToInsert.size();
9374 //const SMDS_MeshNode* aNodes[nbn];
9375 vector<const SMDS_MeshNode*> aNodes(nbn);
9376 aNodes[0] = nodes[n1];
9377 aNodes[nbn-1] = nodes[n2];
9378 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9379 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9380 aNodes[iNode++] = *nIt;
9382 for(i=1; i<nbn; i++) {
9383 SMDS_MeshElement* newElem =
9384 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9385 myLastCreatedElems.Append(newElem);
9386 if ( aShapeId && newElem )
9387 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9391 aMesh->RemoveElement(theFace);
9394 //=======================================================================
9395 //function : UpdateVolumes
9397 //=======================================================================
9398 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9399 const SMDS_MeshNode* theBetweenNode2,
9400 list<const SMDS_MeshNode*>& theNodesToInsert)
9402 myLastCreatedElems.Clear();
9403 myLastCreatedNodes.Clear();
9405 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9406 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9407 const SMDS_MeshElement* elem = invElemIt->next();
9409 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9410 SMDS_VolumeTool aVolume (elem);
9411 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9414 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9415 int iface, nbFaces = aVolume.NbFaces();
9416 vector<const SMDS_MeshNode *> poly_nodes;
9417 vector<int> quantities (nbFaces);
9419 for (iface = 0; iface < nbFaces; iface++) {
9420 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9421 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9422 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9424 for (int inode = 0; inode < nbFaceNodes; inode++) {
9425 poly_nodes.push_back(faceNodes[inode]);
9427 if (nbInserted == 0) {
9428 if (faceNodes[inode] == theBetweenNode1) {
9429 if (faceNodes[inode + 1] == theBetweenNode2) {
9430 nbInserted = theNodesToInsert.size();
9432 // add nodes to insert
9433 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9434 for (; nIt != theNodesToInsert.end(); nIt++) {
9435 poly_nodes.push_back(*nIt);
9439 else if (faceNodes[inode] == theBetweenNode2) {
9440 if (faceNodes[inode + 1] == theBetweenNode1) {
9441 nbInserted = theNodesToInsert.size();
9443 // add nodes to insert in reversed order
9444 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9446 for (; nIt != theNodesToInsert.begin(); nIt--) {
9447 poly_nodes.push_back(*nIt);
9449 poly_nodes.push_back(*nIt);
9456 quantities[iface] = nbFaceNodes + nbInserted;
9459 // Replace or update the volume
9460 SMESHDS_Mesh *aMesh = GetMeshDS();
9462 if (elem->IsPoly()) {
9463 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9467 int aShapeId = FindShape( elem );
9469 SMDS_MeshElement* newElem =
9470 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9471 myLastCreatedElems.Append(newElem);
9472 if (aShapeId && newElem)
9473 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9475 aMesh->RemoveElement(elem);
9482 //================================================================================
9484 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9486 //================================================================================
9488 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9489 vector<const SMDS_MeshNode *> & nodes,
9490 vector<int> & nbNodeInFaces )
9493 nbNodeInFaces.clear();
9494 SMDS_VolumeTool vTool ( elem );
9495 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9497 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9498 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9499 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9504 //=======================================================================
9506 * \brief Convert elements contained in a submesh to quadratic
9507 * \return int - nb of checked elements
9509 //=======================================================================
9511 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9512 SMESH_MesherHelper& theHelper,
9513 const bool theForce3d)
9516 if( !theSm ) return nbElem;
9518 vector<int> nbNodeInFaces;
9519 vector<const SMDS_MeshNode *> nodes;
9520 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9521 while(ElemItr->more())
9524 const SMDS_MeshElement* elem = ElemItr->next();
9525 if( !elem || elem->IsQuadratic() ) continue;
9527 // get elem data needed to re-create it
9529 const int id = elem->GetID();
9530 const int nbNodes = elem->NbNodes();
9531 const SMDSAbs_ElementType aType = elem->GetType();
9532 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9533 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9534 if ( aGeomType == SMDSEntity_Polyhedra )
9535 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9536 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9537 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9539 // remove a linear element
9540 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9542 const SMDS_MeshElement* NewElem = 0;
9548 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9556 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9559 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9562 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9567 case SMDSAbs_Volume :
9571 case SMDSEntity_Tetra:
9572 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9574 case SMDSEntity_Pyramid:
9575 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9577 case SMDSEntity_Penta:
9578 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9580 case SMDSEntity_Hexa:
9581 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9582 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9584 case SMDSEntity_Hexagonal_Prism:
9586 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9593 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9595 theSm->AddElement( NewElem );
9600 //=======================================================================
9601 //function : ConvertToQuadratic
9603 //=======================================================================
9605 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9607 SMESHDS_Mesh* meshDS = GetMeshDS();
9609 SMESH_MesherHelper aHelper(*myMesh);
9610 aHelper.SetIsQuadratic( true );
9612 int nbCheckedElems = 0;
9613 if ( myMesh->HasShapeToMesh() )
9615 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9617 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9618 while ( smIt->more() ) {
9619 SMESH_subMesh* sm = smIt->next();
9620 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9621 aHelper.SetSubShape( sm->GetSubShape() );
9622 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9627 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9628 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9630 SMESHDS_SubMesh *smDS = 0;
9631 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9632 while(aEdgeItr->more())
9634 const SMDS_MeshEdge* edge = aEdgeItr->next();
9635 if(edge && !edge->IsQuadratic())
9637 int id = edge->GetID();
9638 //MESSAGE("edge->GetID() " << id);
9639 const SMDS_MeshNode* n1 = edge->GetNode(0);
9640 const SMDS_MeshNode* n2 = edge->GetNode(1);
9642 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9644 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9645 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9648 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9649 while(aFaceItr->more())
9651 const SMDS_MeshFace* face = aFaceItr->next();
9652 if(!face || face->IsQuadratic() ) continue;
9654 const int id = face->GetID();
9655 const SMDSAbs_EntityType type = face->GetEntityType();
9656 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9658 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9660 SMDS_MeshFace * NewFace = 0;
9663 case SMDSEntity_Triangle:
9664 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9666 case SMDSEntity_Quadrangle:
9667 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9670 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9672 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9674 vector<int> nbNodeInFaces;
9675 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9676 while(aVolumeItr->more())
9678 const SMDS_MeshVolume* volume = aVolumeItr->next();
9679 if(!volume || volume->IsQuadratic() ) continue;
9681 const int id = volume->GetID();
9682 const SMDSAbs_EntityType type = volume->GetEntityType();
9683 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9684 if ( type == SMDSEntity_Polyhedra )
9685 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9686 else if ( type == SMDSEntity_Hexagonal_Prism )
9687 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9689 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9691 SMDS_MeshVolume * NewVolume = 0;
9694 case SMDSEntity_Tetra:
9695 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9697 case SMDSEntity_Hexa:
9698 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9699 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9701 case SMDSEntity_Pyramid:
9702 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9703 nodes[3], nodes[4], id, theForce3d);
9705 case SMDSEntity_Penta:
9706 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9707 nodes[3], nodes[4], nodes[5], id, theForce3d);
9709 case SMDSEntity_Hexagonal_Prism:
9711 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9713 ReplaceElemInGroups(volume, NewVolume, meshDS);
9718 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9719 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9720 aHelper.FixQuadraticElements(myError);
9724 //================================================================================
9726 * \brief Makes given elements quadratic
9727 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9728 * \param theElements - elements to make quadratic
9730 //================================================================================
9732 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9733 TIDSortedElemSet& theElements)
9735 if ( theElements.empty() ) return;
9737 // we believe that all theElements are of the same type
9738 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9740 // get all nodes shared by theElements
9741 TIDSortedNodeSet allNodes;
9742 TIDSortedElemSet::iterator eIt = theElements.begin();
9743 for ( ; eIt != theElements.end(); ++eIt )
9744 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9746 // complete theElements with elements of lower dim whose all nodes are in allNodes
9748 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9749 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9750 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9751 for ( ; nIt != allNodes.end(); ++nIt )
9753 const SMDS_MeshNode* n = *nIt;
9754 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9755 while ( invIt->more() )
9757 const SMDS_MeshElement* e = invIt->next();
9758 if ( e->IsQuadratic() )
9760 quadAdjacentElems[ e->GetType() ].insert( e );
9763 if ( e->GetType() >= elemType )
9765 continue; // same type of more complex linear element
9768 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9769 continue; // e is already checked
9773 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9774 while ( nodeIt->more() && allIn )
9775 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9777 theElements.insert(e );
9781 SMESH_MesherHelper helper(*myMesh);
9782 helper.SetIsQuadratic( true );
9784 // add links of quadratic adjacent elements to the helper
9786 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9787 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9788 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9790 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9792 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9793 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9794 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9796 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9798 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9799 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9800 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9802 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9805 // make quadratic elements instead of linear ones
9807 SMESHDS_Mesh* meshDS = GetMeshDS();
9808 SMESHDS_SubMesh* smDS = 0;
9809 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9811 const SMDS_MeshElement* elem = *eIt;
9812 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9815 const int id = elem->GetID();
9816 const SMDSAbs_ElementType type = elem->GetType();
9817 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9819 if ( !smDS || !smDS->Contains( elem ))
9820 smDS = meshDS->MeshElements( elem->getshapeId() );
9821 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9823 SMDS_MeshElement * newElem = 0;
9824 switch( nodes.size() )
9826 case 4: // cases for most frequently used element types go first (for optimization)
9827 if ( type == SMDSAbs_Volume )
9828 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9830 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9833 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9834 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9837 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9840 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9843 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9844 nodes[4], id, theForce3d);
9847 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9848 nodes[4], nodes[5], id, theForce3d);
9852 ReplaceElemInGroups( elem, newElem, meshDS);
9853 if( newElem && smDS )
9854 smDS->AddElement( newElem );
9857 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9858 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9859 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9860 helper.FixQuadraticElements( myError );
9864 //=======================================================================
9866 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9867 * \return int - nb of checked elements
9869 //=======================================================================
9871 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9872 SMDS_ElemIteratorPtr theItr,
9873 const int theShapeID)
9876 SMESHDS_Mesh* meshDS = GetMeshDS();
9878 while( theItr->more() )
9880 const SMDS_MeshElement* elem = theItr->next();
9882 if( elem && elem->IsQuadratic())
9884 int id = elem->GetID();
9885 int nbCornerNodes = elem->NbCornerNodes();
9886 SMDSAbs_ElementType aType = elem->GetType();
9888 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9890 //remove a quadratic element
9891 if ( !theSm || !theSm->Contains( elem ))
9892 theSm = meshDS->MeshElements( elem->getshapeId() );
9893 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9895 // remove medium nodes
9896 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9897 if ( nodes[i]->NbInverseElements() == 0 )
9898 meshDS->RemoveFreeNode( nodes[i], theSm );
9900 // add a linear element
9901 nodes.resize( nbCornerNodes );
9902 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9903 ReplaceElemInGroups(elem, newElem, meshDS);
9904 if( theSm && newElem )
9905 theSm->AddElement( newElem );
9911 //=======================================================================
9912 //function : ConvertFromQuadratic
9914 //=======================================================================
9916 bool SMESH_MeshEditor::ConvertFromQuadratic()
9918 int nbCheckedElems = 0;
9919 if ( myMesh->HasShapeToMesh() )
9921 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9923 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9924 while ( smIt->more() ) {
9925 SMESH_subMesh* sm = smIt->next();
9926 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9927 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9933 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9934 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9936 SMESHDS_SubMesh *aSM = 0;
9937 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9945 //================================================================================
9947 * \brief Return true if all medium nodes of the element are in the node set
9949 //================================================================================
9951 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9953 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9954 if ( !nodeSet.count( elem->GetNode(i) ))
9960 //================================================================================
9962 * \brief Makes given elements linear
9964 //================================================================================
9966 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9968 if ( theElements.empty() ) return;
9970 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9971 set<int> mediumNodeIDs;
9972 TIDSortedElemSet::iterator eIt = theElements.begin();
9973 for ( ; eIt != theElements.end(); ++eIt )
9975 const SMDS_MeshElement* e = *eIt;
9976 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9977 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9980 // replace given elements by linear ones
9981 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9982 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9983 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9985 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9986 // except those elements sharing medium nodes of quadratic element whose medium nodes
9987 // are not all in mediumNodeIDs
9989 // get remaining medium nodes
9990 TIDSortedNodeSet mediumNodes;
9991 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9992 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9993 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9994 mediumNodes.insert( mediumNodes.end(), n );
9996 // find more quadratic elements to convert
9997 TIDSortedElemSet moreElemsToConvert;
9998 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9999 for ( ; nIt != mediumNodes.end(); ++nIt )
10001 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10002 while ( invIt->more() )
10004 const SMDS_MeshElement* e = invIt->next();
10005 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10007 // find a more complex element including e and
10008 // whose medium nodes are not in mediumNodes
10009 bool complexFound = false;
10010 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10012 SMDS_ElemIteratorPtr invIt2 =
10013 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10014 while ( invIt2->more() )
10016 const SMDS_MeshElement* eComplex = invIt2->next();
10017 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10019 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10020 if ( nbCommonNodes == e->NbNodes())
10022 complexFound = true;
10023 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10029 if ( !complexFound )
10030 moreElemsToConvert.insert( e );
10034 elemIt = SMDS_ElemIteratorPtr
10035 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10036 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10039 //=======================================================================
10040 //function : SewSideElements
10042 //=======================================================================
10044 SMESH_MeshEditor::Sew_Error
10045 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10046 TIDSortedElemSet& theSide2,
10047 const SMDS_MeshNode* theFirstNode1,
10048 const SMDS_MeshNode* theFirstNode2,
10049 const SMDS_MeshNode* theSecondNode1,
10050 const SMDS_MeshNode* theSecondNode2)
10052 myLastCreatedElems.Clear();
10053 myLastCreatedNodes.Clear();
10055 MESSAGE ("::::SewSideElements()");
10056 if ( theSide1.size() != theSide2.size() )
10057 return SEW_DIFF_NB_OF_ELEMENTS;
10059 Sew_Error aResult = SEW_OK;
10061 // 1. Build set of faces representing each side
10062 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10063 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10065 // =======================================================================
10066 // 1. Build set of faces representing each side:
10067 // =======================================================================
10068 // a. build set of nodes belonging to faces
10069 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10070 // c. create temporary faces representing side of volumes if correspondent
10071 // face does not exist
10073 SMESHDS_Mesh* aMesh = GetMeshDS();
10074 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10075 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10076 TIDSortedElemSet faceSet1, faceSet2;
10077 set<const SMDS_MeshElement*> volSet1, volSet2;
10078 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10079 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10080 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10081 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10082 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10083 int iSide, iFace, iNode;
10085 list<const SMDS_MeshElement* > tempFaceList;
10086 for ( iSide = 0; iSide < 2; iSide++ ) {
10087 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10088 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10089 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10090 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10091 set<const SMDS_MeshElement*>::iterator vIt;
10092 TIDSortedElemSet::iterator eIt;
10093 set<const SMDS_MeshNode*>::iterator nIt;
10095 // check that given nodes belong to given elements
10096 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10097 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10098 int firstIndex = -1, secondIndex = -1;
10099 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10100 const SMDS_MeshElement* elem = *eIt;
10101 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10102 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10103 if ( firstIndex > -1 && secondIndex > -1 ) break;
10105 if ( firstIndex < 0 || secondIndex < 0 ) {
10106 // we can simply return until temporary faces created
10107 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10110 // -----------------------------------------------------------
10111 // 1a. Collect nodes of existing faces
10112 // and build set of face nodes in order to detect missing
10113 // faces corresponding to sides of volumes
10114 // -----------------------------------------------------------
10116 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10118 // loop on the given element of a side
10119 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10120 //const SMDS_MeshElement* elem = *eIt;
10121 const SMDS_MeshElement* elem = *eIt;
10122 if ( elem->GetType() == SMDSAbs_Face ) {
10123 faceSet->insert( elem );
10124 set <const SMDS_MeshNode*> faceNodeSet;
10125 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10126 while ( nodeIt->more() ) {
10127 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10128 nodeSet->insert( n );
10129 faceNodeSet.insert( n );
10131 setOfFaceNodeSet.insert( faceNodeSet );
10133 else if ( elem->GetType() == SMDSAbs_Volume )
10134 volSet->insert( elem );
10136 // ------------------------------------------------------------------------------
10137 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10138 // ------------------------------------------------------------------------------
10140 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10141 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10142 while ( fIt->more() ) { // loop on faces sharing a node
10143 const SMDS_MeshElement* f = fIt->next();
10144 if ( faceSet->find( f ) == faceSet->end() ) {
10145 // check if all nodes are in nodeSet and
10146 // complete setOfFaceNodeSet if they are
10147 set <const SMDS_MeshNode*> faceNodeSet;
10148 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10149 bool allInSet = true;
10150 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10151 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10152 if ( nodeSet->find( n ) == nodeSet->end() )
10155 faceNodeSet.insert( n );
10158 faceSet->insert( f );
10159 setOfFaceNodeSet.insert( faceNodeSet );
10165 // -------------------------------------------------------------------------
10166 // 1c. Create temporary faces representing sides of volumes if correspondent
10167 // face does not exist
10168 // -------------------------------------------------------------------------
10170 if ( !volSet->empty() ) {
10171 //int nodeSetSize = nodeSet->size();
10173 // loop on given volumes
10174 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10175 SMDS_VolumeTool vol (*vIt);
10176 // loop on volume faces: find free faces
10177 // --------------------------------------
10178 list<const SMDS_MeshElement* > freeFaceList;
10179 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10180 if ( !vol.IsFreeFace( iFace ))
10182 // check if there is already a face with same nodes in a face set
10183 const SMDS_MeshElement* aFreeFace = 0;
10184 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10185 int nbNodes = vol.NbFaceNodes( iFace );
10186 set <const SMDS_MeshNode*> faceNodeSet;
10187 vol.GetFaceNodes( iFace, faceNodeSet );
10188 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10190 // no such a face is given but it still can exist, check it
10191 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10192 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10194 if ( !aFreeFace ) {
10195 // create a temporary face
10196 if ( nbNodes == 3 ) {
10197 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10198 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10200 else if ( nbNodes == 4 ) {
10201 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10202 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10205 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10206 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10207 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10210 tempFaceList.push_back( aFreeFace );
10214 freeFaceList.push_back( aFreeFace );
10216 } // loop on faces of a volume
10218 // choose one of several free faces of a volume
10219 // --------------------------------------------
10220 if ( freeFaceList.size() > 1 ) {
10221 // choose a face having max nb of nodes shared by other elems of a side
10222 int maxNbNodes = -1;
10223 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10224 while ( fIt != freeFaceList.end() ) { // loop on free faces
10225 int nbSharedNodes = 0;
10226 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10227 while ( nodeIt->more() ) { // loop on free face nodes
10228 const SMDS_MeshNode* n =
10229 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10230 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10231 while ( invElemIt->more() ) {
10232 const SMDS_MeshElement* e = invElemIt->next();
10233 nbSharedNodes += faceSet->count( e );
10234 nbSharedNodes += elemSet->count( e );
10237 if ( nbSharedNodes > maxNbNodes ) {
10238 maxNbNodes = nbSharedNodes;
10239 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10241 else if ( nbSharedNodes == maxNbNodes ) {
10245 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10248 if ( freeFaceList.size() > 1 )
10250 // could not choose one face, use another way
10251 // choose a face most close to the bary center of the opposite side
10252 gp_XYZ aBC( 0., 0., 0. );
10253 set <const SMDS_MeshNode*> addedNodes;
10254 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10255 eIt = elemSet2->begin();
10256 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10257 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10258 while ( nodeIt->more() ) { // loop on free face nodes
10259 const SMDS_MeshNode* n =
10260 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10261 if ( addedNodes.insert( n ).second )
10262 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10265 aBC /= addedNodes.size();
10266 double minDist = DBL_MAX;
10267 fIt = freeFaceList.begin();
10268 while ( fIt != freeFaceList.end() ) { // loop on free faces
10270 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10271 while ( nodeIt->more() ) { // loop on free face nodes
10272 const SMDS_MeshNode* n =
10273 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10274 gp_XYZ p( n->X(),n->Y(),n->Z() );
10275 dist += ( aBC - p ).SquareModulus();
10277 if ( dist < minDist ) {
10279 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10282 fIt = freeFaceList.erase( fIt++ );
10285 } // choose one of several free faces of a volume
10287 if ( freeFaceList.size() == 1 ) {
10288 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10289 faceSet->insert( aFreeFace );
10290 // complete a node set with nodes of a found free face
10291 // for ( iNode = 0; iNode < ; iNode++ )
10292 // nodeSet->insert( fNodes[ iNode ] );
10295 } // loop on volumes of a side
10297 // // complete a set of faces if new nodes in a nodeSet appeared
10298 // // ----------------------------------------------------------
10299 // if ( nodeSetSize != nodeSet->size() ) {
10300 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10301 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10302 // while ( fIt->more() ) { // loop on faces sharing a node
10303 // const SMDS_MeshElement* f = fIt->next();
10304 // if ( faceSet->find( f ) == faceSet->end() ) {
10305 // // check if all nodes are in nodeSet and
10306 // // complete setOfFaceNodeSet if they are
10307 // set <const SMDS_MeshNode*> faceNodeSet;
10308 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10309 // bool allInSet = true;
10310 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10311 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10312 // if ( nodeSet->find( n ) == nodeSet->end() )
10313 // allInSet = false;
10315 // faceNodeSet.insert( n );
10317 // if ( allInSet ) {
10318 // faceSet->insert( f );
10319 // setOfFaceNodeSet.insert( faceNodeSet );
10325 } // Create temporary faces, if there are volumes given
10328 if ( faceSet1.size() != faceSet2.size() ) {
10329 // delete temporary faces: they are in reverseElements of actual nodes
10330 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10331 // while ( tmpFaceIt->more() )
10332 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10333 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10334 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10335 // aMesh->RemoveElement(*tmpFaceIt);
10336 MESSAGE("Diff nb of faces");
10337 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10340 // ============================================================
10341 // 2. Find nodes to merge:
10342 // bind a node to remove to a node to put instead
10343 // ============================================================
10345 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10346 if ( theFirstNode1 != theFirstNode2 )
10347 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10348 if ( theSecondNode1 != theSecondNode2 )
10349 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10351 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10352 set< long > linkIdSet; // links to process
10353 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10355 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10356 list< NLink > linkList[2];
10357 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10358 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10359 // loop on links in linkList; find faces by links and append links
10360 // of the found faces to linkList
10361 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10362 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10364 NLink link[] = { *linkIt[0], *linkIt[1] };
10365 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10366 if ( !linkIdSet.count( linkID ) )
10369 // by links, find faces in the face sets,
10370 // and find indices of link nodes in the found faces;
10371 // in a face set, there is only one or no face sharing a link
10372 // ---------------------------------------------------------------
10374 const SMDS_MeshElement* face[] = { 0, 0 };
10375 vector<const SMDS_MeshNode*> fnodes[2];
10376 int iLinkNode[2][2];
10377 TIDSortedElemSet avoidSet;
10378 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10379 const SMDS_MeshNode* n1 = link[iSide].first;
10380 const SMDS_MeshNode* n2 = link[iSide].second;
10381 //cout << "Side " << iSide << " ";
10382 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10383 // find a face by two link nodes
10384 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10385 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10386 if ( face[ iSide ])
10388 //cout << " F " << face[ iSide]->GetID() <<endl;
10389 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10390 // put face nodes to fnodes
10391 if ( face[ iSide ]->IsQuadratic() )
10393 // use interlaced nodes iterator
10394 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10395 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10396 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10397 while ( nIter->more() )
10398 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10402 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10403 face[ iSide ]->end_nodes() );
10405 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10409 // check similarity of elements of the sides
10410 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10411 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10412 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10413 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10416 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10418 break; // do not return because it's necessary to remove tmp faces
10421 // set nodes to merge
10422 // -------------------
10424 if ( face[0] && face[1] ) {
10425 const int nbNodes = face[0]->NbNodes();
10426 if ( nbNodes != face[1]->NbNodes() ) {
10427 MESSAGE("Diff nb of face nodes");
10428 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10429 break; // do not return because it s necessary to remove tmp faces
10431 bool reverse[] = { false, false }; // order of nodes in the link
10432 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10433 // analyse link orientation in faces
10434 int i1 = iLinkNode[ iSide ][ 0 ];
10435 int i2 = iLinkNode[ iSide ][ 1 ];
10436 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10438 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10439 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10440 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10442 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10443 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10446 // add other links of the faces to linkList
10447 // -----------------------------------------
10449 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10450 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10451 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10452 if ( !iter_isnew.second ) { // already in a set: no need to process
10453 linkIdSet.erase( iter_isnew.first );
10455 else // new in set == encountered for the first time: add
10457 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10458 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10459 linkList[0].push_back ( NLink( n1, n2 ));
10460 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10465 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10468 } // loop on link lists
10470 if ( aResult == SEW_OK &&
10471 ( //linkIt[0] != linkList[0].end() ||
10472 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10473 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10474 " " << (faceSetPtr[1]->empty()));
10475 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10478 // ====================================================================
10479 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10480 // ====================================================================
10482 // delete temporary faces
10483 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10484 // while ( tmpFaceIt->more() )
10485 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10486 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10487 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10488 aMesh->RemoveElement(*tmpFaceIt);
10490 if ( aResult != SEW_OK)
10493 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10494 // loop on nodes replacement map
10495 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10496 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10497 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10498 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10499 nodeIDsToRemove.push_back( nToRemove->GetID() );
10500 // loop on elements sharing nToRemove
10501 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10502 while ( invElemIt->more() ) {
10503 const SMDS_MeshElement* e = invElemIt->next();
10504 // get a new suite of nodes: make replacement
10505 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10506 vector< const SMDS_MeshNode*> nodes( nbNodes );
10507 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10508 while ( nIt->more() ) {
10509 const SMDS_MeshNode* n =
10510 static_cast<const SMDS_MeshNode*>( nIt->next() );
10511 nnIt = nReplaceMap.find( n );
10512 if ( nnIt != nReplaceMap.end() ) {
10514 n = (*nnIt).second;
10518 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10519 // elemIDsToRemove.push_back( e->GetID() );
10523 SMDSAbs_ElementType etyp = e->GetType();
10524 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10527 myLastCreatedElems.Append(newElem);
10528 AddToSameGroups(newElem, e, aMesh);
10529 int aShapeId = e->getshapeId();
10532 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10535 aMesh->RemoveElement(e);
10540 Remove( nodeIDsToRemove, true );
10545 //================================================================================
10547 * \brief Find corresponding nodes in two sets of faces
10548 * \param theSide1 - first face set
10549 * \param theSide2 - second first face
10550 * \param theFirstNode1 - a boundary node of set 1
10551 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10552 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10553 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10554 * \param nReplaceMap - output map of corresponding nodes
10555 * \return bool - is a success or not
10557 //================================================================================
10560 //#define DEBUG_MATCHING_NODES
10563 SMESH_MeshEditor::Sew_Error
10564 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10565 set<const SMDS_MeshElement*>& theSide2,
10566 const SMDS_MeshNode* theFirstNode1,
10567 const SMDS_MeshNode* theFirstNode2,
10568 const SMDS_MeshNode* theSecondNode1,
10569 const SMDS_MeshNode* theSecondNode2,
10570 TNodeNodeMap & nReplaceMap)
10572 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10574 nReplaceMap.clear();
10575 if ( theFirstNode1 != theFirstNode2 )
10576 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10577 if ( theSecondNode1 != theSecondNode2 )
10578 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10580 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10581 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10583 list< NLink > linkList[2];
10584 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10585 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10587 // loop on links in linkList; find faces by links and append links
10588 // of the found faces to linkList
10589 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10590 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10591 NLink link[] = { *linkIt[0], *linkIt[1] };
10592 if ( linkSet.find( link[0] ) == linkSet.end() )
10595 // by links, find faces in the face sets,
10596 // and find indices of link nodes in the found faces;
10597 // in a face set, there is only one or no face sharing a link
10598 // ---------------------------------------------------------------
10600 const SMDS_MeshElement* face[] = { 0, 0 };
10601 list<const SMDS_MeshNode*> notLinkNodes[2];
10602 //bool reverse[] = { false, false }; // order of notLinkNodes
10604 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10606 const SMDS_MeshNode* n1 = link[iSide].first;
10607 const SMDS_MeshNode* n2 = link[iSide].second;
10608 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10609 set< const SMDS_MeshElement* > facesOfNode1;
10610 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10612 // during a loop of the first node, we find all faces around n1,
10613 // during a loop of the second node, we find one face sharing both n1 and n2
10614 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10615 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10616 while ( fIt->more() ) { // loop on faces sharing a node
10617 const SMDS_MeshElement* f = fIt->next();
10618 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10619 ! facesOfNode1.insert( f ).second ) // f encounters twice
10621 if ( face[ iSide ] ) {
10622 MESSAGE( "2 faces per link " );
10623 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10626 faceSet->erase( f );
10628 // get not link nodes
10629 int nbN = f->NbNodes();
10630 if ( f->IsQuadratic() )
10632 nbNodes[ iSide ] = nbN;
10633 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10634 int i1 = f->GetNodeIndex( n1 );
10635 int i2 = f->GetNodeIndex( n2 );
10636 int iEnd = nbN, iBeg = -1, iDelta = 1;
10637 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10639 std::swap( iEnd, iBeg ); iDelta = -1;
10644 if ( i == iEnd ) i = iBeg + iDelta;
10645 if ( i == i1 ) break;
10646 nodes.push_back ( f->GetNode( i ) );
10652 // check similarity of elements of the sides
10653 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10654 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10655 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10656 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10659 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10663 // set nodes to merge
10664 // -------------------
10666 if ( face[0] && face[1] ) {
10667 if ( nbNodes[0] != nbNodes[1] ) {
10668 MESSAGE("Diff nb of face nodes");
10669 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10671 #ifdef DEBUG_MATCHING_NODES
10672 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10673 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10674 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10676 int nbN = nbNodes[0];
10678 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10679 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10680 for ( int i = 0 ; i < nbN - 2; ++i ) {
10681 #ifdef DEBUG_MATCHING_NODES
10682 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10684 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10688 // add other links of the face 1 to linkList
10689 // -----------------------------------------
10691 const SMDS_MeshElement* f0 = face[0];
10692 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10693 for ( int i = 0; i < nbN; i++ )
10695 const SMDS_MeshNode* n2 = f0->GetNode( i );
10696 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10697 linkSet.insert( SMESH_TLink( n1, n2 ));
10698 if ( !iter_isnew.second ) { // already in a set: no need to process
10699 linkSet.erase( iter_isnew.first );
10701 else // new in set == encountered for the first time: add
10703 #ifdef DEBUG_MATCHING_NODES
10704 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10705 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10707 linkList[0].push_back ( NLink( n1, n2 ));
10708 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10713 } // loop on link lists
10718 //================================================================================
10720 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10721 \param theElems - the list of elements (edges or faces) to be replicated
10722 The nodes for duplication could be found from these elements
10723 \param theNodesNot - list of nodes to NOT replicate
10724 \param theAffectedElems - the list of elements (cells and edges) to which the
10725 replicated nodes should be associated to.
10726 \return TRUE if operation has been completed successfully, FALSE otherwise
10728 //================================================================================
10730 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10731 const TIDSortedElemSet& theNodesNot,
10732 const TIDSortedElemSet& theAffectedElems )
10734 myLastCreatedElems.Clear();
10735 myLastCreatedNodes.Clear();
10737 if ( theElems.size() == 0 )
10740 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10745 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10746 // duplicate elements and nodes
10747 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10748 // replce nodes by duplications
10749 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10753 //================================================================================
10755 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10756 \param theMeshDS - mesh instance
10757 \param theElems - the elements replicated or modified (nodes should be changed)
10758 \param theNodesNot - nodes to NOT replicate
10759 \param theNodeNodeMap - relation of old node to new created node
10760 \param theIsDoubleElem - flag os to replicate element or modify
10761 \return TRUE if operation has been completed successfully, FALSE otherwise
10763 //================================================================================
10765 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10766 const TIDSortedElemSet& theElems,
10767 const TIDSortedElemSet& theNodesNot,
10768 std::map< const SMDS_MeshNode*,
10769 const SMDS_MeshNode* >& theNodeNodeMap,
10770 const bool theIsDoubleElem )
10772 MESSAGE("doubleNodes");
10773 // iterate on through element and duplicate them (by nodes duplication)
10775 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10776 for ( ; elemItr != theElems.end(); ++elemItr )
10778 const SMDS_MeshElement* anElem = *elemItr;
10782 bool isDuplicate = false;
10783 // duplicate nodes to duplicate element
10784 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10785 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10787 while ( anIter->more() )
10790 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10791 SMDS_MeshNode* aNewNode = aCurrNode;
10792 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10793 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10794 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10797 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10798 theNodeNodeMap[ aCurrNode ] = aNewNode;
10799 myLastCreatedNodes.Append( aNewNode );
10801 isDuplicate |= (aCurrNode != aNewNode);
10802 newNodes[ ind++ ] = aNewNode;
10804 if ( !isDuplicate )
10807 if ( theIsDoubleElem )
10808 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10811 MESSAGE("ChangeElementNodes");
10812 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10819 //================================================================================
10821 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10822 \param theNodes - identifiers of nodes to be doubled
10823 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10824 nodes. If list of element identifiers is empty then nodes are doubled but
10825 they not assigned to elements
10826 \return TRUE if operation has been completed successfully, FALSE otherwise
10828 //================================================================================
10830 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10831 const std::list< int >& theListOfModifiedElems )
10833 MESSAGE("DoubleNodes");
10834 myLastCreatedElems.Clear();
10835 myLastCreatedNodes.Clear();
10837 if ( theListOfNodes.size() == 0 )
10840 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10844 // iterate through nodes and duplicate them
10846 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10848 std::list< int >::const_iterator aNodeIter;
10849 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10851 int aCurr = *aNodeIter;
10852 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10858 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10861 anOldNodeToNewNode[ aNode ] = aNewNode;
10862 myLastCreatedNodes.Append( aNewNode );
10866 // Create map of new nodes for modified elements
10868 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10870 std::list< int >::const_iterator anElemIter;
10871 for ( anElemIter = theListOfModifiedElems.begin();
10872 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10874 int aCurr = *anElemIter;
10875 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10879 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10881 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10883 while ( anIter->more() )
10885 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10886 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10888 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10889 aNodeArr[ ind++ ] = aNewNode;
10892 aNodeArr[ ind++ ] = aCurrNode;
10894 anElemToNodes[ anElem ] = aNodeArr;
10897 // Change nodes of elements
10899 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10900 anElemToNodesIter = anElemToNodes.begin();
10901 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10903 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10904 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10907 MESSAGE("ChangeElementNodes");
10908 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10917 //================================================================================
10919 \brief Check if element located inside shape
10920 \return TRUE if IN or ON shape, FALSE otherwise
10922 //================================================================================
10924 template<class Classifier>
10925 bool isInside(const SMDS_MeshElement* theElem,
10926 Classifier& theClassifier,
10927 const double theTol)
10929 gp_XYZ centerXYZ (0, 0, 0);
10930 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10931 while (aNodeItr->more())
10932 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10934 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10935 theClassifier.Perform(aPnt, theTol);
10936 TopAbs_State aState = theClassifier.State();
10937 return (aState == TopAbs_IN || aState == TopAbs_ON );
10940 //================================================================================
10942 * \brief Classifier of the 3D point on the TopoDS_Face
10943 * with interaface suitable for isInside()
10945 //================================================================================
10947 struct _FaceClassifier
10949 Extrema_ExtPS _extremum;
10950 BRepAdaptor_Surface _surface;
10951 TopAbs_State _state;
10953 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10955 _extremum.Initialize( _surface,
10956 _surface.FirstUParameter(), _surface.LastUParameter(),
10957 _surface.FirstVParameter(), _surface.LastVParameter(),
10958 _surface.Tolerance(), _surface.Tolerance() );
10960 void Perform(const gp_Pnt& aPnt, double theTol)
10962 _state = TopAbs_OUT;
10963 _extremum.Perform(aPnt);
10964 if ( _extremum.IsDone() )
10965 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10966 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10967 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10969 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10972 TopAbs_State State() const
10979 //================================================================================
10981 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10982 This method is the first step of DoubleNodeElemGroupsInRegion.
10983 \param theElems - list of groups of elements (edges or faces) to be replicated
10984 \param theNodesNot - list of groups of nodes not to replicated
10985 \param theShape - shape to detect affected elements (element which geometric center
10986 located on or inside shape).
10987 The replicated nodes should be associated to affected elements.
10988 \return groups of affected elements
10989 \sa DoubleNodeElemGroupsInRegion()
10991 //================================================================================
10993 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10994 const TIDSortedElemSet& theNodesNot,
10995 const TopoDS_Shape& theShape,
10996 TIDSortedElemSet& theAffectedElems)
10998 if ( theShape.IsNull() )
11001 const double aTol = Precision::Confusion();
11002 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11003 auto_ptr<_FaceClassifier> aFaceClassifier;
11004 if ( theShape.ShapeType() == TopAbs_SOLID )
11006 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11007 bsc3d->PerformInfinitePoint(aTol);
11009 else if (theShape.ShapeType() == TopAbs_FACE )
11011 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11014 // iterates on indicated elements and get elements by back references from their nodes
11015 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11016 for ( ; elemItr != theElems.end(); ++elemItr )
11018 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11022 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11023 while ( nodeItr->more() )
11025 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11026 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11028 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11029 while ( backElemItr->more() )
11031 const SMDS_MeshElement* curElem = backElemItr->next();
11032 if ( curElem && theElems.find(curElem) == theElems.end() &&
11034 isInside( curElem, *bsc3d, aTol ) :
11035 isInside( curElem, *aFaceClassifier, aTol )))
11036 theAffectedElems.insert( curElem );
11043 //================================================================================
11045 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11046 \param theElems - group of of elements (edges or faces) to be replicated
11047 \param theNodesNot - group of nodes not to replicate
11048 \param theShape - shape to detect affected elements (element which geometric center
11049 located on or inside shape).
11050 The replicated nodes should be associated to affected elements.
11051 \return TRUE if operation has been completed successfully, FALSE otherwise
11053 //================================================================================
11055 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11056 const TIDSortedElemSet& theNodesNot,
11057 const TopoDS_Shape& theShape )
11059 if ( theShape.IsNull() )
11062 const double aTol = Precision::Confusion();
11063 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11064 auto_ptr<_FaceClassifier> aFaceClassifier;
11065 if ( theShape.ShapeType() == TopAbs_SOLID )
11067 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11068 bsc3d->PerformInfinitePoint(aTol);
11070 else if (theShape.ShapeType() == TopAbs_FACE )
11072 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11075 // iterates on indicated elements and get elements by back references from their nodes
11076 TIDSortedElemSet anAffected;
11077 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11078 for ( ; elemItr != theElems.end(); ++elemItr )
11080 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11084 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11085 while ( nodeItr->more() )
11087 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11088 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11090 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11091 while ( backElemItr->more() )
11093 const SMDS_MeshElement* curElem = backElemItr->next();
11094 if ( curElem && theElems.find(curElem) == theElems.end() &&
11096 isInside( curElem, *bsc3d, aTol ) :
11097 isInside( curElem, *aFaceClassifier, aTol )))
11098 anAffected.insert( curElem );
11102 return DoubleNodes( theElems, theNodesNot, anAffected );
11106 * \brief compute an oriented angle between two planes defined by four points.
11107 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11108 * @param p0 base of the rotation axe
11109 * @param p1 extremity of the rotation axe
11110 * @param g1 belongs to the first plane
11111 * @param g2 belongs to the second plane
11113 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11115 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11116 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11117 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11118 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11119 gp_Vec vref(p0, p1);
11122 gp_Vec n1 = vref.Crossed(v1);
11123 gp_Vec n2 = vref.Crossed(v2);
11124 return n2.AngleWithRef(n1, vref);
11128 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11129 * The list of groups must describe a partition of the mesh volumes.
11130 * The nodes of the internal faces at the boundaries of the groups are doubled.
11131 * In option, the internal faces are replaced by flat elements.
11132 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11133 * The flat elements are stored in groups of volumes.
11134 * @param theElems - list of groups of volumes, where a group of volume is a set of
11135 * SMDS_MeshElements sorted by Id.
11136 * @param createJointElems - if TRUE, create the elements
11137 * @return TRUE if operation has been completed successfully, FALSE otherwise
11139 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11140 bool createJointElems)
11142 MESSAGE("----------------------------------------------");
11143 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11144 MESSAGE("----------------------------------------------");
11146 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11147 meshDS->BuildDownWardConnectivity(true);
11149 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11151 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11152 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11153 // build the list of nodes shared by 2 or more domains, with their domain indexes
11155 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11156 std::map<int,int>celldom; // cell vtkId --> domain
11157 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11158 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11159 faceDomains.clear();
11161 cellDomains.clear();
11162 nodeDomains.clear();
11163 std::map<int,int> emptyMap;
11164 std::set<int> emptySet;
11167 for (int idom = 0; idom < theElems.size(); idom++)
11170 // --- build a map (face to duplicate --> volume to modify)
11171 // with all the faces shared by 2 domains (group of elements)
11172 // and corresponding volume of this domain, for each shared face.
11173 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11175 //MESSAGE("Domain " << idom);
11176 const TIDSortedElemSet& domain = theElems[idom];
11177 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11178 for (; elemItr != domain.end(); ++elemItr)
11180 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11183 int vtkId = anElem->getVtkId();
11184 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11185 int neighborsVtkIds[NBMAXNEIGHBORS];
11186 int downIds[NBMAXNEIGHBORS];
11187 unsigned char downTypes[NBMAXNEIGHBORS];
11188 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11189 for (int n = 0; n < nbNeighbors; n++)
11191 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11192 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11193 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11195 DownIdType face(downIds[n], downTypes[n]);
11196 if (!faceDomains.count(face))
11197 faceDomains[face] = emptyMap; // create an empty entry for face
11198 if (!faceDomains[face].count(idom))
11200 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11201 celldom[vtkId] = idom;
11202 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11209 //MESSAGE("Number of shared faces " << faceDomains.size());
11210 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11212 // --- explore the shared faces domain by domain,
11213 // explore the nodes of the face and see if they belong to a cell in the domain,
11214 // which has only a node or an edge on the border (not a shared face)
11216 for (int idomain = 0; idomain < theElems.size(); idomain++)
11218 //MESSAGE("Domain " << idomain);
11219 const TIDSortedElemSet& domain = theElems[idomain];
11220 itface = faceDomains.begin();
11221 for (; itface != faceDomains.end(); ++itface)
11223 std::map<int, int> domvol = itface->second;
11224 if (!domvol.count(idomain))
11226 DownIdType face = itface->first;
11227 //MESSAGE(" --- face " << face.cellId);
11228 std::set<int> oldNodes;
11230 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11231 std::set<int>::iterator itn = oldNodes.begin();
11232 for (; itn != oldNodes.end(); ++itn)
11235 //MESSAGE(" node " << oldId);
11236 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11237 for (int i=0; i<l.ncells; i++)
11239 int vtkId = l.cells[i];
11240 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11241 if (!domain.count(anElem))
11243 int vtkType = grid->GetCellType(vtkId);
11244 int downId = grid->CellIdToDownId(vtkId);
11247 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11248 continue; // not OK at this stage of the algorithm:
11249 //no cells created after BuildDownWardConnectivity
11251 DownIdType aCell(downId, vtkType);
11252 if (!cellDomains.count(aCell))
11253 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11254 cellDomains[aCell][idomain] = vtkId;
11255 celldom[vtkId] = idomain;
11256 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11262 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11263 // for each shared face, get the nodes
11264 // for each node, for each domain of the face, create a clone of the node
11266 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11267 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11268 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11270 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11271 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11272 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11274 for (int idomain = 0; idomain < theElems.size(); idomain++)
11276 itface = faceDomains.begin();
11277 for (; itface != faceDomains.end(); ++itface)
11279 std::map<int, int> domvol = itface->second;
11280 if (!domvol.count(idomain))
11282 DownIdType face = itface->first;
11283 //MESSAGE(" --- face " << face.cellId);
11284 std::set<int> oldNodes;
11286 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11287 std::set<int>::iterator itn = oldNodes.begin();
11288 for (; itn != oldNodes.end(); ++itn)
11291 //MESSAGE("-+-+-a node " << oldId);
11292 if (!nodeDomains.count(oldId))
11293 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11294 if (nodeDomains[oldId].empty())
11296 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11297 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11299 std::map<int, int>::iterator itdom = domvol.begin();
11300 for (; itdom != domvol.end(); ++itdom)
11302 int idom = itdom->first;
11303 //MESSAGE(" domain " << idom);
11304 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11306 if (nodeDomains[oldId].size() >= 2) // a multiple node
11308 vector<int> orderedDoms;
11309 //MESSAGE("multiple node " << oldId);
11310 if (mutipleNodes.count(oldId))
11311 orderedDoms = mutipleNodes[oldId];
11314 map<int,int>::iterator it = nodeDomains[oldId].begin();
11315 for (; it != nodeDomains[oldId].end(); ++it)
11316 orderedDoms.push_back(it->first);
11318 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11319 //stringstream txt;
11320 //for (int i=0; i<orderedDoms.size(); i++)
11321 // txt << orderedDoms[i] << " ";
11322 //MESSAGE("orderedDoms " << txt.str());
11323 mutipleNodes[oldId] = orderedDoms;
11325 double *coords = grid->GetPoint(oldId);
11326 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11327 int newId = newNode->getVtkId();
11328 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11329 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11336 for (int idomain = 0; idomain < theElems.size(); idomain++)
11338 itface = faceDomains.begin();
11339 for (; itface != faceDomains.end(); ++itface)
11341 std::map<int, int> domvol = itface->second;
11342 if (!domvol.count(idomain))
11344 DownIdType face = itface->first;
11345 //MESSAGE(" --- face " << face.cellId);
11346 std::set<int> oldNodes;
11348 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11349 int nbMultipleNodes = 0;
11350 std::set<int>::iterator itn = oldNodes.begin();
11351 for (; itn != oldNodes.end(); ++itn)
11354 if (mutipleNodes.count(oldId))
11357 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11359 //MESSAGE("multiple Nodes detected on a shared face");
11360 int downId = itface->first.cellId;
11361 unsigned char cellType = itface->first.cellType;
11362 // --- shared edge or shared face ?
11363 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11366 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11367 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11368 if (mutipleNodes.count(nodes[i]))
11369 if (!mutipleNodesToFace.count(nodes[i]))
11370 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11372 else // shared face (between two volumes)
11374 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11375 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11376 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11377 for (int ie =0; ie < nbEdges; ie++)
11380 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11381 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11383 vector<int> vn0 = mutipleNodes[nodes[0]];
11384 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11386 for (int i0 = 0; i0 < vn0.size(); i0++)
11387 for (int i1 = 0; i1 < vn1.size(); i1++)
11388 if (vn0[i0] == vn1[i1])
11389 doms.push_back(vn0[i0]);
11390 if (doms.size() >2)
11392 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11393 double *coords = grid->GetPoint(nodes[0]);
11394 gp_Pnt p0(coords[0], coords[1], coords[2]);
11395 coords = grid->GetPoint(nodes[nbNodes - 1]);
11396 gp_Pnt p1(coords[0], coords[1], coords[2]);
11398 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11399 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11400 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11401 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11402 for (int id=0; id < doms.size(); id++)
11404 int idom = doms[id];
11405 for (int ivol=0; ivol<nbvol; ivol++)
11407 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11408 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11409 if (theElems[idom].count(elem))
11411 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11412 domvol[idom] = svol;
11413 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11415 vtkIdType npts = 0;
11416 vtkIdType* pts = 0;
11417 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11418 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11421 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11422 angleDom[idom] = 0;
11426 gp_Pnt g(values[0], values[1], values[2]);
11427 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11428 //MESSAGE(" angle=" << angleDom[idom]);
11434 map<double, int> sortedDom; // sort domains by angle
11435 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11436 sortedDom[ia->second] = ia->first;
11437 vector<int> vnodes;
11439 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11441 vdom.push_back(ib->second);
11442 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11444 for (int ino = 0; ino < nbNodes; ino++)
11445 vnodes.push_back(nodes[ino]);
11446 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11455 // --- iterate on shared faces (volumes to modify, face to extrude)
11456 // get node id's of the face (id SMDS = id VTK)
11457 // create flat element with old and new nodes if requested
11459 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11460 // (domain1 X domain2) = domain1 + MAXINT*domain2
11462 std::map<int, std::map<long,int> > nodeQuadDomains;
11463 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11465 if (createJointElems)
11468 string joints2DName = "joints2D";
11469 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11470 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11471 string joints3DName = "joints3D";
11472 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11473 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11475 itface = faceDomains.begin();
11476 for (; itface != faceDomains.end(); ++itface)
11478 DownIdType face = itface->first;
11479 std::set<int> oldNodes;
11480 std::set<int>::iterator itn;
11482 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11484 std::map<int, int> domvol = itface->second;
11485 std::map<int, int>::iterator itdom = domvol.begin();
11486 int dom1 = itdom->first;
11487 int vtkVolId = itdom->second;
11489 int dom2 = itdom->first;
11490 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11492 stringstream grpname;
11495 grpname << dom1 << "_" << dom2;
11497 grpname << dom2 << "_" << dom1;
11498 string namegrp = grpname.str();
11499 if (!mapOfJunctionGroups.count(namegrp))
11500 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11501 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11503 sgrp->Add(vol->GetID());
11504 if (vol->GetType() == SMDSAbs_Volume)
11505 joints3DGrp->Add(vol->GetID());
11506 else if (vol->GetType() == SMDSAbs_Face)
11507 joints2DGrp->Add(vol->GetID());
11511 // --- create volumes on multiple domain intersection if requested
11512 // iterate on mutipleNodesToFace
11513 // iterate on edgesMultiDomains
11515 if (createJointElems)
11517 // --- iterate on mutipleNodesToFace
11519 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11520 for (; itn != mutipleNodesToFace.end(); ++itn)
11522 int node = itn->first;
11523 vector<int> orderDom = itn->second;
11524 vector<vtkIdType> orderedNodes;
11525 for (int idom = 0; idom <orderDom.size(); idom++)
11526 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11527 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11529 stringstream grpname;
11531 grpname << 0 << "_" << 0;
11533 string namegrp = grpname.str();
11534 if (!mapOfJunctionGroups.count(namegrp))
11535 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11536 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11538 sgrp->Add(face->GetID());
11541 // --- iterate on edgesMultiDomains
11543 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11544 for (; ite != edgesMultiDomains.end(); ++ite)
11546 vector<int> nodes = ite->first;
11547 vector<int> orderDom = ite->second;
11548 vector<vtkIdType> orderedNodes;
11549 if (nodes.size() == 2)
11551 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11552 for (int ino=0; ino < nodes.size(); ino++)
11553 if (orderDom.size() == 3)
11554 for (int idom = 0; idom <orderDom.size(); idom++)
11555 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11557 for (int idom = orderDom.size()-1; idom >=0; idom--)
11558 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11559 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11562 string namegrp = "jointsMultiples";
11563 if (!mapOfJunctionGroups.count(namegrp))
11564 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11565 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11567 sgrp->Add(vol->GetID());
11571 INFOS("Quadratic multiple joints not implemented");
11572 // TODO quadratic nodes
11577 // --- list the explicit faces and edges of the mesh that need to be modified,
11578 // i.e. faces and edges built with one or more duplicated nodes.
11579 // associate these faces or edges to their corresponding domain.
11580 // only the first domain found is kept when a face or edge is shared
11582 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11583 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11584 faceOrEdgeDom.clear();
11587 for (int idomain = 0; idomain < theElems.size(); idomain++)
11589 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11590 for (; itnod != nodeDomains.end(); ++itnod)
11592 int oldId = itnod->first;
11593 //MESSAGE(" node " << oldId);
11594 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11595 for (int i = 0; i < l.ncells; i++)
11597 int vtkId = l.cells[i];
11598 int vtkType = grid->GetCellType(vtkId);
11599 int downId = grid->CellIdToDownId(vtkId);
11601 continue; // new cells: not to be modified
11602 DownIdType aCell(downId, vtkType);
11603 int volParents[1000];
11604 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11605 for (int j = 0; j < nbvol; j++)
11606 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11607 if (!feDom.count(vtkId))
11609 feDom[vtkId] = idomain;
11610 faceOrEdgeDom[aCell] = emptyMap;
11611 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11612 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11613 // << " type " << vtkType << " downId " << downId);
11619 // --- iterate on shared faces (volumes to modify, face to extrude)
11620 // get node id's of the face
11621 // replace old nodes by new nodes in volumes, and update inverse connectivity
11623 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11624 for (int m=0; m<3; m++)
11626 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11627 itface = (*amap).begin();
11628 for (; itface != (*amap).end(); ++itface)
11630 DownIdType face = itface->first;
11631 std::set<int> oldNodes;
11632 std::set<int>::iterator itn;
11634 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11635 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11636 std::map<int, int> localClonedNodeIds;
11638 std::map<int, int> domvol = itface->second;
11639 std::map<int, int>::iterator itdom = domvol.begin();
11640 for (; itdom != domvol.end(); ++itdom)
11642 int idom = itdom->first;
11643 int vtkVolId = itdom->second;
11644 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11645 localClonedNodeIds.clear();
11646 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11649 if (nodeDomains[oldId].count(idom))
11651 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11652 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11655 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11660 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11661 grid->BuildLinks();
11669 * \brief Double nodes on some external faces and create flat elements.
11670 * Flat elements are mainly used by some types of mechanic calculations.
11672 * Each group of the list must be constituted of faces.
11673 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11674 * @param theElems - list of groups of faces, where a group of faces is a set of
11675 * SMDS_MeshElements sorted by Id.
11676 * @return TRUE if operation has been completed successfully, FALSE otherwise
11678 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11680 MESSAGE("-------------------------------------------------");
11681 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11682 MESSAGE("-------------------------------------------------");
11684 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11686 // --- For each group of faces
11687 // duplicate the nodes, create a flat element based on the face
11688 // replace the nodes of the faces by their clones
11690 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11691 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11692 clonedNodes.clear();
11693 intermediateNodes.clear();
11694 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11695 mapOfJunctionGroups.clear();
11697 for (int idom = 0; idom < theElems.size(); idom++)
11699 const TIDSortedElemSet& domain = theElems[idom];
11700 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11701 for (; elemItr != domain.end(); ++elemItr)
11703 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11704 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11707 // MESSAGE("aFace=" << aFace->GetID());
11708 bool isQuad = aFace->IsQuadratic();
11709 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11711 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11713 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11714 while (nodeIt->more())
11716 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11717 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11719 ln2.push_back(node);
11721 ln0.push_back(node);
11723 const SMDS_MeshNode* clone = 0;
11724 if (!clonedNodes.count(node))
11726 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11727 clonedNodes[node] = clone;
11730 clone = clonedNodes[node];
11733 ln3.push_back(clone);
11735 ln1.push_back(clone);
11737 const SMDS_MeshNode* inter = 0;
11738 if (isQuad && (!isMedium))
11740 if (!intermediateNodes.count(node))
11742 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11743 intermediateNodes[node] = inter;
11746 inter = intermediateNodes[node];
11747 ln4.push_back(inter);
11751 // --- extrude the face
11753 vector<const SMDS_MeshNode*> ln;
11754 SMDS_MeshVolume* vol = 0;
11755 vtkIdType aType = aFace->GetVtkType();
11759 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11760 // MESSAGE("vol prism " << vol->GetID());
11761 ln.push_back(ln1[0]);
11762 ln.push_back(ln1[1]);
11763 ln.push_back(ln1[2]);
11766 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11767 // MESSAGE("vol hexa " << vol->GetID());
11768 ln.push_back(ln1[0]);
11769 ln.push_back(ln1[1]);
11770 ln.push_back(ln1[2]);
11771 ln.push_back(ln1[3]);
11773 case VTK_QUADRATIC_TRIANGLE:
11774 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11775 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11776 // MESSAGE("vol quad prism " << vol->GetID());
11777 ln.push_back(ln1[0]);
11778 ln.push_back(ln1[1]);
11779 ln.push_back(ln1[2]);
11780 ln.push_back(ln3[0]);
11781 ln.push_back(ln3[1]);
11782 ln.push_back(ln3[2]);
11784 case VTK_QUADRATIC_QUAD:
11785 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11786 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11787 // ln4[0], ln4[1], ln4[2], ln4[3]);
11788 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11789 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11790 ln4[0], ln4[1], ln4[2], ln4[3]);
11791 // MESSAGE("vol quad hexa " << vol->GetID());
11792 ln.push_back(ln1[0]);
11793 ln.push_back(ln1[1]);
11794 ln.push_back(ln1[2]);
11795 ln.push_back(ln1[3]);
11796 ln.push_back(ln3[0]);
11797 ln.push_back(ln3[1]);
11798 ln.push_back(ln3[2]);
11799 ln.push_back(ln3[3]);
11809 stringstream grpname;
11813 string namegrp = grpname.str();
11814 if (!mapOfJunctionGroups.count(namegrp))
11815 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11816 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11818 sgrp->Add(vol->GetID());
11821 // --- modify the face
11823 aFace->ChangeNodes(&ln[0], ln.size());
11830 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11831 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11832 * groups of faces to remove inside the object, (idem edges).
11833 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11835 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11836 const TopoDS_Shape& theShape,
11837 SMESH_NodeSearcher* theNodeSearcher,
11838 const char* groupName,
11839 std::vector<double>& nodesCoords,
11840 std::vector<std::vector<int> >& listOfListOfNodes)
11842 MESSAGE("--------------------------------");
11843 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11844 MESSAGE("--------------------------------");
11846 // --- zone of volumes to remove is given :
11847 // 1 either by a geom shape (one or more vertices) and a radius,
11848 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11849 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11850 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11851 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11852 // defined by it's name.
11854 SMESHDS_GroupBase* groupDS = 0;
11855 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11856 while ( groupIt->more() )
11859 SMESH_Group * group = groupIt->next();
11860 if ( !group ) continue;
11861 groupDS = group->GetGroupDS();
11862 if ( !groupDS || groupDS->IsEmpty() ) continue;
11863 std::string grpName = group->GetName();
11864 //MESSAGE("grpName=" << grpName);
11865 if (grpName == groupName)
11871 bool isNodeGroup = false;
11872 bool isNodeCoords = false;
11875 if (groupDS->GetType() != SMDSAbs_Node)
11877 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11880 if (nodesCoords.size() > 0)
11881 isNodeCoords = true; // a list o nodes given by their coordinates
11882 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11884 // --- define groups to build
11886 int idg; // --- group of SMDS volumes
11887 string grpvName = groupName;
11888 grpvName += "_vol";
11889 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11892 MESSAGE("group not created " << grpvName);
11895 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11897 int idgs; // --- group of SMDS faces on the skin
11898 string grpsName = groupName;
11899 grpsName += "_skin";
11900 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11903 MESSAGE("group not created " << grpsName);
11906 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11908 int idgi; // --- group of SMDS faces internal (several shapes)
11909 string grpiName = groupName;
11910 grpiName += "_internalFaces";
11911 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11914 MESSAGE("group not created " << grpiName);
11917 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11919 int idgei; // --- group of SMDS faces internal (several shapes)
11920 string grpeiName = groupName;
11921 grpeiName += "_internalEdges";
11922 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11925 MESSAGE("group not created " << grpeiName);
11928 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11930 // --- build downward connectivity
11932 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11933 meshDS->BuildDownWardConnectivity(true);
11934 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11936 // --- set of volumes detected inside
11938 std::set<int> setOfInsideVol;
11939 std::set<int> setOfVolToCheck;
11941 std::vector<gp_Pnt> gpnts;
11944 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11946 MESSAGE("group of nodes provided");
11947 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11948 while ( elemIt->more() )
11950 const SMDS_MeshElement* elem = elemIt->next();
11953 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11956 SMDS_MeshElement* vol = 0;
11957 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11958 while (volItr->more())
11960 vol = (SMDS_MeshElement*)volItr->next();
11961 setOfInsideVol.insert(vol->getVtkId());
11962 sgrp->Add(vol->GetID());
11966 else if (isNodeCoords)
11968 MESSAGE("list of nodes coordinates provided");
11971 while (i < nodesCoords.size()-2)
11973 double x = nodesCoords[i++];
11974 double y = nodesCoords[i++];
11975 double z = nodesCoords[i++];
11976 gp_Pnt p = gp_Pnt(x, y ,z);
11977 gpnts.push_back(p);
11978 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11981 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11983 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11984 TopTools_IndexedMapOfShape vertexMap;
11985 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11986 gp_Pnt p = gp_Pnt(0,0,0);
11987 if (vertexMap.Extent() < 1)
11990 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11992 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11993 p = BRep_Tool::Pnt(vertex);
11994 gpnts.push_back(p);
11995 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11999 if (gpnts.size() > 0)
12002 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12004 nodeId = startNode->GetID();
12005 MESSAGE("nodeId " << nodeId);
12007 double radius2 = radius*radius;
12008 MESSAGE("radius2 " << radius2);
12010 // --- volumes on start node
12012 setOfVolToCheck.clear();
12013 SMDS_MeshElement* startVol = 0;
12014 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12015 while (volItr->more())
12017 startVol = (SMDS_MeshElement*)volItr->next();
12018 setOfVolToCheck.insert(startVol->getVtkId());
12020 if (setOfVolToCheck.empty())
12022 MESSAGE("No volumes found");
12026 // --- starting with central volumes then their neighbors, check if they are inside
12027 // or outside the domain, until no more new neighbor volume is inside.
12028 // Fill the group of inside volumes
12030 std::map<int, double> mapOfNodeDistance2;
12031 mapOfNodeDistance2.clear();
12032 std::set<int> setOfOutsideVol;
12033 while (!setOfVolToCheck.empty())
12035 std::set<int>::iterator it = setOfVolToCheck.begin();
12037 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12038 bool volInside = false;
12039 vtkIdType npts = 0;
12040 vtkIdType* pts = 0;
12041 grid->GetCellPoints(vtkId, npts, pts);
12042 for (int i=0; i<npts; i++)
12044 double distance2 = 0;
12045 if (mapOfNodeDistance2.count(pts[i]))
12047 distance2 = mapOfNodeDistance2[pts[i]];
12048 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12052 double *coords = grid->GetPoint(pts[i]);
12053 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12055 for (int j=0; j<gpnts.size(); j++)
12057 double d2 = aPoint.SquareDistance(gpnts[j]);
12058 if (d2 < distance2)
12061 if (distance2 < radius2)
12065 mapOfNodeDistance2[pts[i]] = distance2;
12066 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12068 if (distance2 < radius2)
12070 volInside = true; // one or more nodes inside the domain
12071 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12077 setOfInsideVol.insert(vtkId);
12078 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12079 int neighborsVtkIds[NBMAXNEIGHBORS];
12080 int downIds[NBMAXNEIGHBORS];
12081 unsigned char downTypes[NBMAXNEIGHBORS];
12082 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12083 for (int n = 0; n < nbNeighbors; n++)
12084 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12085 setOfVolToCheck.insert(neighborsVtkIds[n]);
12089 setOfOutsideVol.insert(vtkId);
12090 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12092 setOfVolToCheck.erase(vtkId);
12096 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12097 // If yes, add the volume to the inside set
12099 bool addedInside = true;
12100 std::set<int> setOfVolToReCheck;
12101 while (addedInside)
12103 MESSAGE(" --------------------------- re check");
12104 addedInside = false;
12105 std::set<int>::iterator itv = setOfInsideVol.begin();
12106 for (; itv != setOfInsideVol.end(); ++itv)
12109 int neighborsVtkIds[NBMAXNEIGHBORS];
12110 int downIds[NBMAXNEIGHBORS];
12111 unsigned char downTypes[NBMAXNEIGHBORS];
12112 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12113 for (int n = 0; n < nbNeighbors; n++)
12114 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12115 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12117 setOfVolToCheck = setOfVolToReCheck;
12118 setOfVolToReCheck.clear();
12119 while (!setOfVolToCheck.empty())
12121 std::set<int>::iterator it = setOfVolToCheck.begin();
12123 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12125 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12126 int countInside = 0;
12127 int neighborsVtkIds[NBMAXNEIGHBORS];
12128 int downIds[NBMAXNEIGHBORS];
12129 unsigned char downTypes[NBMAXNEIGHBORS];
12130 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12131 for (int n = 0; n < nbNeighbors; n++)
12132 if (setOfInsideVol.count(neighborsVtkIds[n]))
12134 MESSAGE("countInside " << countInside);
12135 if (countInside > 1)
12137 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12138 setOfInsideVol.insert(vtkId);
12139 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12140 addedInside = true;
12143 setOfVolToReCheck.insert(vtkId);
12145 setOfVolToCheck.erase(vtkId);
12149 // --- map of Downward faces at the boundary, inside the global volume
12150 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12151 // fill group of SMDS faces inside the volume (when several volume shapes)
12152 // fill group of SMDS faces on the skin of the global volume (if skin)
12154 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12155 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12156 std::set<int>::iterator it = setOfInsideVol.begin();
12157 for (; it != setOfInsideVol.end(); ++it)
12160 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12161 int neighborsVtkIds[NBMAXNEIGHBORS];
12162 int downIds[NBMAXNEIGHBORS];
12163 unsigned char downTypes[NBMAXNEIGHBORS];
12164 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12165 for (int n = 0; n < nbNeighbors; n++)
12167 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12168 if (neighborDim == 3)
12170 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12172 DownIdType face(downIds[n], downTypes[n]);
12173 boundaryFaces[face] = vtkId;
12175 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12176 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12177 if (vtkFaceId >= 0)
12179 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12180 // find also the smds edges on this face
12181 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12182 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12183 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12184 for (int i = 0; i < nbEdges; i++)
12186 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12187 if (vtkEdgeId >= 0)
12188 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12192 else if (neighborDim == 2) // skin of the volume
12194 DownIdType face(downIds[n], downTypes[n]);
12195 skinFaces[face] = vtkId;
12196 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12197 if (vtkFaceId >= 0)
12198 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12203 // --- identify the edges constituting the wire of each subshape on the skin
12204 // define polylines with the nodes of edges, equivalent to wires
12205 // project polylines on subshapes, and partition, to get geom faces
12207 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12208 std::set<int> emptySet;
12210 std::set<int> shapeIds;
12212 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12213 while (itelem->more())
12215 const SMDS_MeshElement *elem = itelem->next();
12216 int shapeId = elem->getshapeId();
12217 int vtkId = elem->getVtkId();
12218 if (!shapeIdToVtkIdSet.count(shapeId))
12220 shapeIdToVtkIdSet[shapeId] = emptySet;
12221 shapeIds.insert(shapeId);
12223 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12226 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12227 std::set<DownIdType, DownIdCompare> emptyEdges;
12228 emptyEdges.clear();
12230 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12231 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12233 int shapeId = itShape->first;
12234 MESSAGE(" --- Shape ID --- "<< shapeId);
12235 shapeIdToEdges[shapeId] = emptyEdges;
12237 std::vector<int> nodesEdges;
12239 std::set<int>::iterator its = itShape->second.begin();
12240 for (; its != itShape->second.end(); ++its)
12243 MESSAGE(" " << vtkId);
12244 int neighborsVtkIds[NBMAXNEIGHBORS];
12245 int downIds[NBMAXNEIGHBORS];
12246 unsigned char downTypes[NBMAXNEIGHBORS];
12247 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12248 for (int n = 0; n < nbNeighbors; n++)
12250 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12252 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12253 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12254 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12256 DownIdType edge(downIds[n], downTypes[n]);
12257 if (!shapeIdToEdges[shapeId].count(edge))
12259 shapeIdToEdges[shapeId].insert(edge);
12261 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12262 nodesEdges.push_back(vtkNodeId[0]);
12263 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12264 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12270 std::list<int> order;
12272 if (nodesEdges.size() > 0)
12274 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12275 nodesEdges[0] = -1;
12276 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12277 nodesEdges[1] = -1; // do not reuse this edge
12281 int nodeTofind = order.back(); // try first to push back
12283 for (i = 0; i<nodesEdges.size(); i++)
12284 if (nodesEdges[i] == nodeTofind)
12286 if (i == nodesEdges.size())
12287 found = false; // no follower found on back
12290 if (i%2) // odd ==> use the previous one
12291 if (nodesEdges[i-1] < 0)
12295 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12296 nodesEdges[i-1] = -1;
12298 else // even ==> use the next one
12299 if (nodesEdges[i+1] < 0)
12303 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12304 nodesEdges[i+1] = -1;
12309 // try to push front
12311 nodeTofind = order.front(); // try to push front
12312 for (i = 0; i<nodesEdges.size(); i++)
12313 if (nodesEdges[i] == nodeTofind)
12315 if (i == nodesEdges.size())
12317 found = false; // no predecessor found on front
12320 if (i%2) // odd ==> use the previous one
12321 if (nodesEdges[i-1] < 0)
12325 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12326 nodesEdges[i-1] = -1;
12328 else // even ==> use the next one
12329 if (nodesEdges[i+1] < 0)
12333 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12334 nodesEdges[i+1] = -1;
12340 std::vector<int> nodes;
12341 nodes.push_back(shapeId);
12342 std::list<int>::iterator itl = order.begin();
12343 for (; itl != order.end(); itl++)
12345 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12346 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12348 listOfListOfNodes.push_back(nodes);
12351 // partition geom faces with blocFissure
12352 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12353 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12359 //================================================================================
12361 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12362 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12363 * \return TRUE if operation has been completed successfully, FALSE otherwise
12365 //================================================================================
12367 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12369 // iterates on volume elements and detect all free faces on them
12370 SMESHDS_Mesh* aMesh = GetMeshDS();
12373 //bool res = false;
12374 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12375 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12378 const SMDS_MeshVolume* volume = vIt->next();
12379 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12380 vTool.SetExternalNormal();
12381 //const bool isPoly = volume->IsPoly();
12382 const int iQuad = volume->IsQuadratic();
12383 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12385 if (!vTool.IsFreeFace(iface))
12388 vector<const SMDS_MeshNode *> nodes;
12389 int nbFaceNodes = vTool.NbFaceNodes(iface);
12390 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12392 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12393 nodes.push_back(faceNodes[inode]);
12394 if (iQuad) { // add medium nodes
12395 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12396 nodes.push_back(faceNodes[inode]);
12397 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12398 nodes.push_back(faceNodes[8]);
12400 // add new face based on volume nodes
12401 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12403 continue; // face already exsist
12405 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12409 return ( nbFree==(nbExisted+nbCreated) );
12414 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12416 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12418 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12421 //================================================================================
12423 * \brief Creates missing boundary elements
12424 * \param elements - elements whose boundary is to be checked
12425 * \param dimension - defines type of boundary elements to create
12426 * \param group - a group to store created boundary elements in
12427 * \param targetMesh - a mesh to store created boundary elements in
12428 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12429 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12430 * boundary elements will be copied into the targetMesh
12431 * \param toAddExistingBondary - if true, not only new but also pre-existing
12432 * boundary elements will be added into the new group
12433 * \param aroundElements - if true, elements will be created on boundary of given
12434 * elements else, on boundary of the whole mesh.
12435 * \return nb of added boundary elements
12437 //================================================================================
12439 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12440 Bnd_Dimension dimension,
12441 SMESH_Group* group/*=0*/,
12442 SMESH_Mesh* targetMesh/*=0*/,
12443 bool toCopyElements/*=false*/,
12444 bool toCopyExistingBoundary/*=false*/,
12445 bool toAddExistingBondary/*= false*/,
12446 bool aroundElements/*= false*/)
12448 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12449 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12450 // hope that all elements are of the same type, do not check them all
12451 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12452 throw SALOME_Exception(LOCALIZED("wrong element type"));
12455 toCopyElements = toCopyExistingBoundary = false;
12457 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12458 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12459 int nbAddedBnd = 0;
12461 // editor adding present bnd elements and optionally holding elements to add to the group
12462 SMESH_MeshEditor* presentEditor;
12463 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12464 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12466 SMESH_MesherHelper helper( *myMesh );
12467 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12468 SMDS_VolumeTool vTool;
12469 TIDSortedElemSet avoidSet;
12470 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12473 typedef vector<const SMDS_MeshNode*> TConnectivity;
12475 SMDS_ElemIteratorPtr eIt;
12476 if (elements.empty())
12477 eIt = aMesh->elementsIterator(elemType);
12479 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12481 while (eIt->more())
12483 const SMDS_MeshElement* elem = eIt->next();
12484 const int iQuad = elem->IsQuadratic();
12486 // ------------------------------------------------------------------------------------
12487 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12488 // ------------------------------------------------------------------------------------
12489 vector<const SMDS_MeshElement*> presentBndElems;
12490 vector<TConnectivity> missingBndElems;
12491 TConnectivity nodes;
12492 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12494 vTool.SetExternalNormal();
12495 const SMDS_MeshElement* otherVol = 0;
12496 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12498 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12499 ( !aroundElements || elements.count( otherVol )))
12501 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12502 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12503 if ( missType == SMDSAbs_Edge ) // boundary edges
12505 nodes.resize( 2+iQuad );
12506 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12508 for ( int j = 0; j < nodes.size(); ++j )
12510 if ( const SMDS_MeshElement* edge =
12511 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12512 presentBndElems.push_back( edge );
12514 missingBndElems.push_back( nodes );
12517 else // boundary face
12520 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12521 nodes.push_back( nn[inode] );
12522 if (iQuad) // add medium nodes
12523 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12524 nodes.push_back( nn[inode] );
12525 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12527 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12529 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12530 SMDSAbs_Face, /*noMedium=*/false ))
12531 presentBndElems.push_back( f );
12533 missingBndElems.push_back( nodes );
12535 if ( targetMesh != myMesh )
12537 // add 1D elements on face boundary to be added to a new mesh
12538 const SMDS_MeshElement* edge;
12539 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12542 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12544 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12545 if ( edge && avoidSet.insert( edge ).second )
12546 presentBndElems.push_back( edge );
12552 else // elem is a face ------------------------------------------
12554 avoidSet.clear(), avoidSet.insert( elem );
12555 int nbNodes = elem->NbCornerNodes();
12556 nodes.resize( 2 /*+ iQuad*/);
12557 for ( int i = 0; i < nbNodes; i++ )
12559 nodes[0] = elem->GetNode(i);
12560 nodes[1] = elem->GetNode((i+1)%nbNodes);
12561 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12562 continue; // not free link
12565 //nodes[2] = elem->GetNode( i + nbNodes );
12566 if ( const SMDS_MeshElement* edge =
12567 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12568 presentBndElems.push_back( edge );
12570 missingBndElems.push_back( nodes );
12574 // ---------------------------------
12575 // 2. Add missing boundary elements
12576 // ---------------------------------
12577 if ( targetMesh != myMesh )
12578 // instead of making a map of nodes in this mesh and targetMesh,
12579 // we create nodes with same IDs.
12580 for ( int i = 0; i < missingBndElems.size(); ++i )
12582 TConnectivity& srcNodes = missingBndElems[i];
12583 TConnectivity nodes( srcNodes.size() );
12584 for ( inode = 0; inode < nodes.size(); ++inode )
12585 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12586 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12588 /*noMedium=*/false))
12590 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12594 for ( int i = 0; i < missingBndElems.size(); ++i )
12596 TConnectivity& nodes = missingBndElems[i];
12597 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12599 /*noMedium=*/false))
12601 SMDS_MeshElement* elem =
12602 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12605 // try to set a new element to a shape
12606 if ( myMesh->HasShapeToMesh() )
12609 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12610 const int nbN = nodes.size() / (iQuad+1 );
12611 for ( inode = 0; inode < nbN && ok; ++inode )
12613 pair<int, TopAbs_ShapeEnum> i_stype =
12614 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12615 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12616 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12618 if ( ok && mediumShapes.size() > 1 )
12620 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12621 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12622 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12624 if (( ok = ( stype_i->first != stype_i_0.first )))
12625 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12626 aMesh->IndexToShape( stype_i_0.second ));
12629 if ( ok && mediumShapes.begin()->first == missShapeType )
12630 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12634 // ----------------------------------
12635 // 3. Copy present boundary elements
12636 // ----------------------------------
12637 if ( toCopyExistingBoundary )
12638 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12640 const SMDS_MeshElement* e = presentBndElems[i];
12641 TConnectivity nodes( e->NbNodes() );
12642 for ( inode = 0; inode < nodes.size(); ++inode )
12643 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12644 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12646 else // store present elements to add them to a group
12647 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12649 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12652 } // loop on given elements
12654 // ---------------------------------------------
12655 // 4. Fill group with boundary elements
12656 // ---------------------------------------------
12659 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12660 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12661 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12663 tgtEditor.myLastCreatedElems.Clear();
12664 tgtEditor2.myLastCreatedElems.Clear();
12666 // -----------------------
12667 // 5. Copy given elements
12668 // -----------------------
12669 if ( toCopyElements && targetMesh != myMesh )
12671 if (elements.empty())
12672 eIt = aMesh->elementsIterator(elemType);
12674 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12675 while (eIt->more())
12677 const SMDS_MeshElement* elem = eIt->next();
12678 TConnectivity nodes( elem->NbNodes() );
12679 for ( inode = 0; inode < nodes.size(); ++inode )
12680 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12681 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12683 tgtEditor.myLastCreatedElems.Clear();