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 if ( otherFace && otherFace != theFace)
1212 // link must be reverse in otherFace if orientation ot otherFace
1213 // is same as that of theFace
1214 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1216 // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1217 // << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1218 nbReori += Reorient( otherFace );
1220 startFaces.insert( otherFace );
1221 if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1222 theFaces.erase( otherFace );
1225 std::swap( link.first, link.second ); // reverse the link
1227 startFaces.erase( startFace );
1228 startFace = startFaces.begin();
1233 //=======================================================================
1234 //function : getBadRate
1236 //=======================================================================
1238 static double getBadRate (const SMDS_MeshElement* theElem,
1239 SMESH::Controls::NumericalFunctorPtr& theCrit)
1241 SMESH::Controls::TSequenceOfXYZ P;
1242 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1244 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1245 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1248 //=======================================================================
1249 //function : QuadToTri
1250 //purpose : Cut quadrangles into triangles.
1251 // theCrit is used to select a diagonal to cut
1252 //=======================================================================
1254 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1255 SMESH::Controls::NumericalFunctorPtr theCrit)
1257 myLastCreatedElems.Clear();
1258 myLastCreatedNodes.Clear();
1260 MESSAGE( "::QuadToTri()" );
1262 if ( !theCrit.get() )
1265 SMESHDS_Mesh * aMesh = GetMeshDS();
1267 Handle(Geom_Surface) surface;
1268 SMESH_MesherHelper helper( *GetMesh() );
1270 TIDSortedElemSet::iterator itElem;
1271 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1272 const SMDS_MeshElement* elem = *itElem;
1273 if ( !elem || elem->GetType() != SMDSAbs_Face )
1275 if ( elem->NbCornerNodes() != 4 )
1278 // retrieve element nodes
1279 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1281 // compare two sets of possible triangles
1282 double aBadRate1, aBadRate2; // to what extent a set is bad
1283 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1284 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1285 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1287 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1288 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1289 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1291 int aShapeId = FindShape( elem );
1292 const SMDS_MeshElement* newElem1 = 0;
1293 const SMDS_MeshElement* newElem2 = 0;
1295 if( !elem->IsQuadratic() ) {
1297 // split liner quadrangle
1298 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1299 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1300 if ( aBadRate1 <= aBadRate2 ) {
1301 // tr1 + tr2 is better
1302 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1303 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1306 // tr3 + tr4 is better
1307 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1308 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1313 // split quadratic quadrangle
1315 // get surface elem is on
1316 if ( aShapeId != helper.GetSubShapeID() ) {
1320 shape = aMesh->IndexToShape( aShapeId );
1321 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1322 TopoDS_Face face = TopoDS::Face( shape );
1323 surface = BRep_Tool::Surface( face );
1324 if ( !surface.IsNull() )
1325 helper.SetSubShape( shape );
1328 // find middle point for (0,1,2,3)
1329 // and create a node in this point;
1330 const SMDS_MeshNode* newN = 0;
1331 if ( aNodes.size() == 9 )
1333 // SMDSEntity_BiQuad_Quadrangle
1334 newN = aNodes.back();
1339 if ( surface.IsNull() )
1341 for ( int i = 0; i < 4; i++ )
1342 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1347 const SMDS_MeshNode* inFaceNode = 0;
1348 if ( helper.GetNodeUVneedInFaceNode() )
1349 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1350 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1351 inFaceNode = aNodes[ i ];
1353 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1355 for ( int i = 0; i < 4; i++ )
1356 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1358 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1360 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1361 myLastCreatedNodes.Append(newN);
1363 // create a new element
1364 if ( aBadRate1 <= aBadRate2 ) {
1365 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1366 aNodes[6], aNodes[7], newN );
1367 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1368 newN, aNodes[4], aNodes[5] );
1371 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1372 aNodes[7], aNodes[4], newN );
1373 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1374 newN, aNodes[5], aNodes[6] );
1378 // care of a new element
1380 myLastCreatedElems.Append(newElem1);
1381 myLastCreatedElems.Append(newElem2);
1382 AddToSameGroups( newElem1, elem, aMesh );
1383 AddToSameGroups( newElem2, elem, aMesh );
1385 // put a new triangle on the same shape
1388 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1389 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1391 aMesh->RemoveElement( elem );
1396 //=======================================================================
1397 //function : BestSplit
1398 //purpose : Find better diagonal for cutting.
1399 //=======================================================================
1401 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1410 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1413 if( theQuad->NbNodes()==4 ||
1414 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1416 // retrieve element nodes
1417 const SMDS_MeshNode* aNodes [4];
1418 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1420 //while (itN->more())
1422 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1424 // compare two sets of possible triangles
1425 double aBadRate1, aBadRate2; // to what extent a set is bad
1426 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1427 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1428 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1430 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1431 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1432 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1433 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1434 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1435 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1436 return 1; // diagonal 1-3
1438 return 2; // diagonal 2-4
1445 // Methods of splitting volumes into tetra
1447 const int theHexTo5_1[5*4+1] =
1449 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1451 const int theHexTo5_2[5*4+1] =
1453 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1455 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1457 const int theHexTo6_1[6*4+1] =
1459 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
1461 const int theHexTo6_2[6*4+1] =
1463 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
1465 const int theHexTo6_3[6*4+1] =
1467 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
1469 const int theHexTo6_4[6*4+1] =
1471 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
1473 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1475 const int thePyraTo2_1[2*4+1] =
1477 0, 1, 2, 4, 0, 2, 3, 4, -1
1479 const int thePyraTo2_2[2*4+1] =
1481 1, 2, 3, 4, 1, 3, 0, 4, -1
1483 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1485 const int thePentaTo3_1[3*4+1] =
1487 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1489 const int thePentaTo3_2[3*4+1] =
1491 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1493 const int thePentaTo3_3[3*4+1] =
1495 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1497 const int thePentaTo3_4[3*4+1] =
1499 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1501 const int thePentaTo3_5[3*4+1] =
1503 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1505 const int thePentaTo3_6[3*4+1] =
1507 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1509 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1510 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1512 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1515 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1516 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1517 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1522 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1523 bool _baryNode; //!< additional node is to be created at cell barycenter
1524 bool _ownConn; //!< to delete _connectivity in destructor
1525 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1527 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1528 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1529 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1530 bool hasFacet( const TTriangleFacet& facet ) const
1532 const int* tetConn = _connectivity;
1533 for ( ; tetConn[0] >= 0; tetConn += 4 )
1534 if (( facet.contains( tetConn[0] ) +
1535 facet.contains( tetConn[1] ) +
1536 facet.contains( tetConn[2] ) +
1537 facet.contains( tetConn[3] )) == 3 )
1543 //=======================================================================
1545 * \brief return TSplitMethod for the given element
1547 //=======================================================================
1549 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1551 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1553 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1554 // an edge and a face barycenter; tertaherdons are based on triangles and
1555 // a volume barycenter
1556 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1558 // Find out how adjacent volumes are split
1560 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1561 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1562 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1564 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1565 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1566 if ( nbNodes < 4 ) continue;
1568 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1569 const int* nInd = vol.GetFaceNodesIndices( iF );
1572 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1573 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1574 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1575 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1579 int iCom = 0; // common node of triangle faces to split into
1580 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1582 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1583 nInd[ iQ * ( (iCom+1)%nbNodes )],
1584 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1585 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1586 nInd[ iQ * ( (iCom+2)%nbNodes )],
1587 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1588 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1590 triaSplits.push_back( t012 );
1591 triaSplits.push_back( t023 );
1596 if ( !triaSplits.empty() )
1597 hasAdjacentSplits = true;
1600 // Among variants of split method select one compliant with adjacent volumes
1602 TSplitMethod method;
1603 if ( !vol.Element()->IsPoly() && !is24TetMode )
1605 int nbVariants = 2, nbTet = 0;
1606 const int** connVariants = 0;
1607 switch ( vol.Element()->GetEntityType() )
1609 case SMDSEntity_Hexa:
1610 case SMDSEntity_Quad_Hexa:
1611 case SMDSEntity_TriQuad_Hexa:
1612 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1613 connVariants = theHexTo5, nbTet = 5;
1615 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1617 case SMDSEntity_Pyramid:
1618 case SMDSEntity_Quad_Pyramid:
1619 connVariants = thePyraTo2; nbTet = 2;
1621 case SMDSEntity_Penta:
1622 case SMDSEntity_Quad_Penta:
1623 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1628 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1630 // check method compliancy with adjacent tetras,
1631 // all found splits must be among facets of tetras described by this method
1632 method = TSplitMethod( nbTet, connVariants[variant] );
1633 if ( hasAdjacentSplits && method._nbTetra > 0 )
1635 bool facetCreated = true;
1636 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1638 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1639 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1640 facetCreated = method.hasFacet( *facet );
1642 if ( !facetCreated )
1643 method = TSplitMethod(0); // incompatible method
1647 if ( method._nbTetra < 1 )
1649 // No standard method is applicable, use a generic solution:
1650 // each facet of a volume is split into triangles and
1651 // each of triangles and a volume barycenter form a tetrahedron.
1653 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1655 int* connectivity = new int[ maxTetConnSize + 1 ];
1656 method._connectivity = connectivity;
1657 method._ownConn = true;
1658 method._baryNode = !isHex27; // to create central node or not
1661 int baryCenInd = vol.NbNodes() - int( isHex27 );
1662 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1664 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1665 const int* nInd = vol.GetFaceNodesIndices( iF );
1666 // find common node of triangle facets of tetra to create
1667 int iCommon = 0; // index in linear numeration
1668 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1669 if ( !triaSplits.empty() )
1672 const TTriangleFacet* facet = &triaSplits.front();
1673 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1674 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1675 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1678 else if ( nbNodes > 3 && !is24TetMode )
1680 // find the best method of splitting into triangles by aspect ratio
1681 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1682 map< double, int > badness2iCommon;
1683 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1684 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1685 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1688 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1690 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1691 nodes[ iQ*((iLast-1)%nbNodes)],
1692 nodes[ iQ*((iLast )%nbNodes)]);
1693 badness += getBadRate( &tria, aspectRatio );
1695 badness2iCommon.insert( make_pair( badness, iCommon ));
1697 // use iCommon with lowest badness
1698 iCommon = badness2iCommon.begin()->second;
1700 if ( iCommon >= nbNodes )
1701 iCommon = 0; // something wrong
1703 // fill connectivity of tetrahedra based on a current face
1704 int nbTet = nbNodes - 2;
1705 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1710 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1711 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1715 method._faceBaryNode[ iF ] = 0;
1716 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1719 for ( int i = 0; i < nbTet; ++i )
1721 int i1 = i, i2 = (i+1) % nbNodes;
1722 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1723 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1724 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1725 connectivity[ connSize++ ] = faceBaryCenInd;
1726 connectivity[ connSize++ ] = baryCenInd;
1731 for ( int i = 0; i < nbTet; ++i )
1733 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1734 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1735 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1736 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1737 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1738 connectivity[ connSize++ ] = baryCenInd;
1741 method._nbTetra += nbTet;
1743 } // loop on volume faces
1745 connectivity[ connSize++ ] = -1;
1747 } // end of generic solution
1751 //================================================================================
1753 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1755 //================================================================================
1757 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1759 // find the tetrahedron including the three nodes of facet
1760 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1761 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1762 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1763 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1764 while ( volIt1->more() )
1766 const SMDS_MeshElement* v = volIt1->next();
1767 SMDSAbs_EntityType type = v->GetEntityType();
1768 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1770 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1771 continue; // medium node not allowed
1772 const int ind2 = v->GetNodeIndex( n2 );
1773 if ( ind2 < 0 || 3 < ind2 )
1775 const int ind3 = v->GetNodeIndex( n3 );
1776 if ( ind3 < 0 || 3 < ind3 )
1783 //=======================================================================
1785 * \brief A key of a face of volume
1787 //=======================================================================
1789 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1791 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1793 TIDSortedNodeSet sortedNodes;
1794 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1795 int nbNodes = vol.NbFaceNodes( iF );
1796 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1797 for ( int i = 0; i < nbNodes; i += iQ )
1798 sortedNodes.insert( fNodes[i] );
1799 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1800 first.first = (*(n++))->GetID();
1801 first.second = (*(n++))->GetID();
1802 second.first = (*(n++))->GetID();
1803 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1808 //=======================================================================
1809 //function : SplitVolumesIntoTetra
1810 //purpose : Split volume elements into tetrahedra.
1811 //=======================================================================
1813 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1814 const int theMethodFlags)
1816 // std-like iterator on coordinates of nodes of mesh element
1817 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1818 NXyzIterator xyzEnd;
1820 SMDS_VolumeTool volTool;
1821 SMESH_MesherHelper helper( *GetMesh());
1823 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1824 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1826 SMESH_SequenceOfElemPtr newNodes, newElems;
1828 // map face of volume to it's baricenrtic node
1829 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1832 TIDSortedElemSet::const_iterator elem = theElems.begin();
1833 for ( ; elem != theElems.end(); ++elem )
1835 if ( (*elem)->GetType() != SMDSAbs_Volume )
1837 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1838 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1841 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1843 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1844 if ( splitMethod._nbTetra < 1 ) continue;
1846 // find submesh to add new tetras to
1847 if ( !subMesh || !subMesh->Contains( *elem ))
1849 int shapeID = FindShape( *elem );
1850 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1851 subMesh = GetMeshDS()->MeshElements( shapeID );
1854 if ( (*elem)->IsQuadratic() )
1857 // add quadratic links to the helper
1858 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1860 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1861 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1862 for ( int iN = 0; iN < nbN; iN += iQ )
1863 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1865 helper.SetIsQuadratic( true );
1870 helper.SetIsQuadratic( false );
1872 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1873 helper.SetElementsOnShape( true );
1874 if ( splitMethod._baryNode )
1876 // make a node at barycenter
1877 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1878 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1879 nodes.push_back( gcNode );
1880 newNodes.Append( gcNode );
1882 if ( !splitMethod._faceBaryNode.empty() )
1884 // make or find baricentric nodes of faces
1885 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1886 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1888 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1889 volFace2BaryNode.insert
1890 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1893 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1894 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1896 nodes.push_back( iF_n->second = f_n->second );
1901 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1902 const int* tetConn = splitMethod._connectivity;
1903 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1904 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1905 nodes[ tetConn[1] ],
1906 nodes[ tetConn[2] ],
1907 nodes[ tetConn[3] ]));
1909 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1911 // Split faces on sides of the split volume
1913 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1914 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1916 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1917 if ( nbNodes < 4 ) continue;
1919 // find an existing face
1920 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1921 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1922 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1923 /*noMedium=*/false))
1926 helper.SetElementsOnShape( false );
1927 vector< const SMDS_MeshElement* > triangles;
1929 // find submesh to add new triangles in
1930 if ( !fSubMesh || !fSubMesh->Contains( face ))
1932 int shapeID = FindShape( face );
1933 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1935 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1936 if ( iF_n != splitMethod._faceBaryNode.end() )
1938 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1940 const SMDS_MeshNode* n1 = fNodes[iN];
1941 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1942 const SMDS_MeshNode *n3 = iF_n->second;
1943 if ( !volTool.IsFaceExternal( iF ))
1945 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1947 if ( fSubMesh && n3->getshapeId() < 1 )
1948 fSubMesh->AddNode( n3 );
1953 // among possible triangles create ones discribed by split method
1954 const int* nInd = volTool.GetFaceNodesIndices( iF );
1955 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1956 int iCom = 0; // common node of triangle faces to split into
1957 list< TTriangleFacet > facets;
1958 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1960 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1961 nInd[ iQ * ( (iCom+1)%nbNodes )],
1962 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1963 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1964 nInd[ iQ * ( (iCom+2)%nbNodes )],
1965 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1966 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1968 facets.push_back( t012 );
1969 facets.push_back( t023 );
1970 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1971 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1972 nInd[ iQ * ((iLast-1)%nbNodes )],
1973 nInd[ iQ * ((iLast )%nbNodes )]));
1977 list< TTriangleFacet >::iterator facet = facets.begin();
1978 for ( ; facet != facets.end(); ++facet )
1980 if ( !volTool.IsFaceExternal( iF ))
1981 swap( facet->_n2, facet->_n3 );
1982 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1983 volNodes[ facet->_n2 ],
1984 volNodes[ facet->_n3 ]));
1987 for ( int i = 0; i < triangles.size(); ++i )
1989 if ( !triangles[i] ) continue;
1991 fSubMesh->AddElement( triangles[i]);
1992 newElems.Append( triangles[i] );
1994 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1995 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1998 } // loop on volume faces to split them into triangles
2000 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2002 if ( geomType == SMDSEntity_TriQuad_Hexa )
2004 // remove medium nodes that could become free
2005 for ( int i = 20; i < volTool.NbNodes(); ++i )
2006 if ( volNodes[i]->NbInverseElements() == 0 )
2007 GetMeshDS()->RemoveNode( volNodes[i] );
2009 } // loop on volumes to split
2011 myLastCreatedNodes = newNodes;
2012 myLastCreatedElems = newElems;
2015 //=======================================================================
2016 //function : AddToSameGroups
2017 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2018 //=======================================================================
2020 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2021 const SMDS_MeshElement* elemInGroups,
2022 SMESHDS_Mesh * aMesh)
2024 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2025 if (!groups.empty()) {
2026 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2027 for ( ; grIt != groups.end(); grIt++ ) {
2028 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2029 if ( group && group->Contains( elemInGroups ))
2030 group->SMDSGroup().Add( elemToAdd );
2036 //=======================================================================
2037 //function : RemoveElemFromGroups
2038 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2039 //=======================================================================
2040 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2041 SMESHDS_Mesh * aMesh)
2043 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2044 if (!groups.empty())
2046 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2047 for (; GrIt != groups.end(); GrIt++)
2049 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2050 if (!grp || grp->IsEmpty()) continue;
2051 grp->SMDSGroup().Remove(removeelem);
2056 //================================================================================
2058 * \brief Replace elemToRm by elemToAdd in the all groups
2060 //================================================================================
2062 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2063 const SMDS_MeshElement* elemToAdd,
2064 SMESHDS_Mesh * aMesh)
2066 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2067 if (!groups.empty()) {
2068 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2069 for ( ; grIt != groups.end(); grIt++ ) {
2070 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2071 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2072 group->SMDSGroup().Add( elemToAdd );
2077 //================================================================================
2079 * \brief Replace elemToRm by elemToAdd in the all groups
2081 //================================================================================
2083 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2084 const vector<const SMDS_MeshElement*>& elemToAdd,
2085 SMESHDS_Mesh * aMesh)
2087 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2088 if (!groups.empty())
2090 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2091 for ( ; grIt != groups.end(); grIt++ ) {
2092 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2093 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2094 for ( int i = 0; i < elemToAdd.size(); ++i )
2095 group->SMDSGroup().Add( elemToAdd[ i ] );
2100 //=======================================================================
2101 //function : QuadToTri
2102 //purpose : Cut quadrangles into triangles.
2103 // theCrit is used to select a diagonal to cut
2104 //=======================================================================
2106 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2107 const bool the13Diag)
2109 myLastCreatedElems.Clear();
2110 myLastCreatedNodes.Clear();
2112 MESSAGE( "::QuadToTri()" );
2114 SMESHDS_Mesh * aMesh = GetMeshDS();
2116 Handle(Geom_Surface) surface;
2117 SMESH_MesherHelper helper( *GetMesh() );
2119 TIDSortedElemSet::iterator itElem;
2120 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2121 const SMDS_MeshElement* elem = *itElem;
2122 if ( !elem || elem->GetType() != SMDSAbs_Face )
2124 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2125 if(!isquad) continue;
2127 if(elem->NbNodes()==4) {
2128 // retrieve element nodes
2129 const SMDS_MeshNode* aNodes [4];
2130 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2132 while ( itN->more() )
2133 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2135 int aShapeId = FindShape( elem );
2136 const SMDS_MeshElement* newElem1 = 0;
2137 const SMDS_MeshElement* newElem2 = 0;
2139 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2140 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2143 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2144 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2146 myLastCreatedElems.Append(newElem1);
2147 myLastCreatedElems.Append(newElem2);
2148 // put a new triangle on the same shape and add to the same groups
2151 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2152 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2154 AddToSameGroups( newElem1, elem, aMesh );
2155 AddToSameGroups( newElem2, elem, aMesh );
2156 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2157 aMesh->RemoveElement( elem );
2160 // Quadratic quadrangle
2162 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2164 // get surface elem is on
2165 int aShapeId = FindShape( elem );
2166 if ( aShapeId != helper.GetSubShapeID() ) {
2170 shape = aMesh->IndexToShape( aShapeId );
2171 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2172 TopoDS_Face face = TopoDS::Face( shape );
2173 surface = BRep_Tool::Surface( face );
2174 if ( !surface.IsNull() )
2175 helper.SetSubShape( shape );
2179 const SMDS_MeshNode* aNodes [8];
2180 const SMDS_MeshNode* inFaceNode = 0;
2181 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2183 while ( itN->more() ) {
2184 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2185 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2186 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2188 inFaceNode = aNodes[ i-1 ];
2192 // find middle point for (0,1,2,3)
2193 // and create a node in this point;
2195 if ( surface.IsNull() ) {
2197 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2201 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2204 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2206 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2208 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2209 myLastCreatedNodes.Append(newN);
2211 // create a new element
2212 const SMDS_MeshElement* newElem1 = 0;
2213 const SMDS_MeshElement* newElem2 = 0;
2215 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2216 aNodes[6], aNodes[7], newN );
2217 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2218 newN, aNodes[4], aNodes[5] );
2221 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2222 aNodes[7], aNodes[4], newN );
2223 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2224 newN, aNodes[5], aNodes[6] );
2226 myLastCreatedElems.Append(newElem1);
2227 myLastCreatedElems.Append(newElem2);
2228 // put a new triangle on the same shape and add to the same groups
2231 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2232 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2234 AddToSameGroups( newElem1, elem, aMesh );
2235 AddToSameGroups( newElem2, elem, aMesh );
2236 aMesh->RemoveElement( elem );
2243 //=======================================================================
2244 //function : getAngle
2246 //=======================================================================
2248 double getAngle(const SMDS_MeshElement * tr1,
2249 const SMDS_MeshElement * tr2,
2250 const SMDS_MeshNode * n1,
2251 const SMDS_MeshNode * n2)
2253 double angle = 2. * M_PI; // bad angle
2256 SMESH::Controls::TSequenceOfXYZ P1, P2;
2257 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2258 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2261 if(!tr1->IsQuadratic())
2262 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2264 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2265 if ( N1.SquareMagnitude() <= gp::Resolution() )
2267 if(!tr2->IsQuadratic())
2268 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2270 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2271 if ( N2.SquareMagnitude() <= gp::Resolution() )
2274 // find the first diagonal node n1 in the triangles:
2275 // take in account a diagonal link orientation
2276 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2277 for ( int t = 0; t < 2; t++ ) {
2278 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2279 int i = 0, iDiag = -1;
2280 while ( it->more()) {
2281 const SMDS_MeshElement *n = it->next();
2282 if ( n == n1 || n == n2 ) {
2286 if ( i - iDiag == 1 )
2287 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2296 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2299 angle = N1.Angle( N2 );
2304 // =================================================
2305 // class generating a unique ID for a pair of nodes
2306 // and able to return nodes by that ID
2307 // =================================================
2311 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2312 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2315 long GetLinkID (const SMDS_MeshNode * n1,
2316 const SMDS_MeshNode * n2) const
2318 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2321 bool GetNodes (const long theLinkID,
2322 const SMDS_MeshNode* & theNode1,
2323 const SMDS_MeshNode* & theNode2) const
2325 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2326 if ( !theNode1 ) return false;
2327 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2328 if ( !theNode2 ) return false;
2334 const SMESHDS_Mesh* myMesh;
2339 //=======================================================================
2340 //function : TriToQuad
2341 //purpose : Fuse neighbour triangles into quadrangles.
2342 // theCrit is used to select a neighbour to fuse with.
2343 // theMaxAngle is a max angle between element normals at which
2344 // fusion is still performed.
2345 //=======================================================================
2347 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2348 SMESH::Controls::NumericalFunctorPtr theCrit,
2349 const double theMaxAngle)
2351 myLastCreatedElems.Clear();
2352 myLastCreatedNodes.Clear();
2354 MESSAGE( "::TriToQuad()" );
2356 if ( !theCrit.get() )
2359 SMESHDS_Mesh * aMesh = GetMeshDS();
2361 // Prepare data for algo: build
2362 // 1. map of elements with their linkIDs
2363 // 2. map of linkIDs with their elements
2365 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2366 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2367 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2368 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2370 TIDSortedElemSet::iterator itElem;
2371 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2372 const SMDS_MeshElement* elem = *itElem;
2373 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2374 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2375 if(!IsTria) continue;
2377 // retrieve element nodes
2378 const SMDS_MeshNode* aNodes [4];
2379 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2382 aNodes[ i++ ] = cast2Node( itN->next() );
2383 aNodes[ 3 ] = aNodes[ 0 ];
2386 for ( i = 0; i < 3; i++ ) {
2387 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2388 // check if elements sharing a link can be fused
2389 itLE = mapLi_listEl.find( link );
2390 if ( itLE != mapLi_listEl.end() ) {
2391 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2393 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2394 //if ( FindShape( elem ) != FindShape( elem2 ))
2395 // continue; // do not fuse triangles laying on different shapes
2396 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2397 continue; // avoid making badly shaped quads
2398 (*itLE).second.push_back( elem );
2401 mapLi_listEl[ link ].push_back( elem );
2403 mapEl_setLi [ elem ].insert( link );
2406 // Clean the maps from the links shared by a sole element, ie
2407 // links to which only one element is bound in mapLi_listEl
2409 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2410 int nbElems = (*itLE).second.size();
2411 if ( nbElems < 2 ) {
2412 const SMDS_MeshElement* elem = (*itLE).second.front();
2413 SMESH_TLink link = (*itLE).first;
2414 mapEl_setLi[ elem ].erase( link );
2415 if ( mapEl_setLi[ elem ].empty() )
2416 mapEl_setLi.erase( elem );
2420 // Algo: fuse triangles into quadrangles
2422 while ( ! mapEl_setLi.empty() ) {
2423 // Look for the start element:
2424 // the element having the least nb of shared links
2425 const SMDS_MeshElement* startElem = 0;
2427 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2428 int nbLinks = (*itEL).second.size();
2429 if ( nbLinks < minNbLinks ) {
2430 startElem = (*itEL).first;
2431 minNbLinks = nbLinks;
2432 if ( minNbLinks == 1 )
2437 // search elements to fuse starting from startElem or links of elements
2438 // fused earlyer - startLinks
2439 list< SMESH_TLink > startLinks;
2440 while ( startElem || !startLinks.empty() ) {
2441 while ( !startElem && !startLinks.empty() ) {
2442 // Get an element to start, by a link
2443 SMESH_TLink linkId = startLinks.front();
2444 startLinks.pop_front();
2445 itLE = mapLi_listEl.find( linkId );
2446 if ( itLE != mapLi_listEl.end() ) {
2447 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2448 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2449 for ( ; itE != listElem.end() ; itE++ )
2450 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2452 mapLi_listEl.erase( itLE );
2457 // Get candidates to be fused
2458 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2459 const SMESH_TLink *link12, *link13;
2461 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2462 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2463 ASSERT( !setLi.empty() );
2464 set< SMESH_TLink >::iterator itLi;
2465 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2467 const SMESH_TLink & link = (*itLi);
2468 itLE = mapLi_listEl.find( link );
2469 if ( itLE == mapLi_listEl.end() )
2472 const SMDS_MeshElement* elem = (*itLE).second.front();
2474 elem = (*itLE).second.back();
2475 mapLi_listEl.erase( itLE );
2476 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2487 // add other links of elem to list of links to re-start from
2488 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2489 set< SMESH_TLink >::iterator it;
2490 for ( it = links.begin(); it != links.end(); it++ ) {
2491 const SMESH_TLink& link2 = (*it);
2492 if ( link2 != link )
2493 startLinks.push_back( link2 );
2497 // Get nodes of possible quadrangles
2498 const SMDS_MeshNode *n12 [4], *n13 [4];
2499 bool Ok12 = false, Ok13 = false;
2500 const SMDS_MeshNode *linkNode1, *linkNode2;
2502 linkNode1 = link12->first;
2503 linkNode2 = link12->second;
2504 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2508 linkNode1 = link13->first;
2509 linkNode2 = link13->second;
2510 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2514 // Choose a pair to fuse
2515 if ( Ok12 && Ok13 ) {
2516 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2517 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2518 double aBadRate12 = getBadRate( &quad12, theCrit );
2519 double aBadRate13 = getBadRate( &quad13, theCrit );
2520 if ( aBadRate13 < aBadRate12 )
2527 // and remove fused elems and removed links from the maps
2528 mapEl_setLi.erase( tr1 );
2530 mapEl_setLi.erase( tr2 );
2531 mapLi_listEl.erase( *link12 );
2532 if(tr1->NbNodes()==3) {
2533 const SMDS_MeshElement* newElem = 0;
2534 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2535 myLastCreatedElems.Append(newElem);
2536 AddToSameGroups( newElem, tr1, aMesh );
2537 int aShapeId = tr1->getshapeId();
2540 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2542 aMesh->RemoveElement( tr1 );
2543 aMesh->RemoveElement( tr2 );
2546 const SMDS_MeshNode* N1 [6];
2547 const SMDS_MeshNode* N2 [6];
2548 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2549 // now we receive following N1 and N2 (using numeration as above image)
2550 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2551 // i.e. first nodes from both arrays determ new diagonal
2552 const SMDS_MeshNode* aNodes[8];
2561 const SMDS_MeshElement* newElem = 0;
2562 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2563 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2564 myLastCreatedElems.Append(newElem);
2565 AddToSameGroups( newElem, tr1, aMesh );
2566 int aShapeId = tr1->getshapeId();
2569 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2571 aMesh->RemoveElement( tr1 );
2572 aMesh->RemoveElement( tr2 );
2573 // remove middle node (9)
2574 GetMeshDS()->RemoveNode( N1[4] );
2578 mapEl_setLi.erase( tr3 );
2579 mapLi_listEl.erase( *link13 );
2580 if(tr1->NbNodes()==3) {
2581 const SMDS_MeshElement* newElem = 0;
2582 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2583 myLastCreatedElems.Append(newElem);
2584 AddToSameGroups( newElem, tr1, aMesh );
2585 int aShapeId = tr1->getshapeId();
2588 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2590 aMesh->RemoveElement( tr1 );
2591 aMesh->RemoveElement( tr3 );
2594 const SMDS_MeshNode* N1 [6];
2595 const SMDS_MeshNode* N2 [6];
2596 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2597 // now we receive following N1 and N2 (using numeration as above image)
2598 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2599 // i.e. first nodes from both arrays determ new diagonal
2600 const SMDS_MeshNode* aNodes[8];
2609 const SMDS_MeshElement* newElem = 0;
2610 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2611 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2612 myLastCreatedElems.Append(newElem);
2613 AddToSameGroups( newElem, tr1, aMesh );
2614 int aShapeId = tr1->getshapeId();
2617 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2619 aMesh->RemoveElement( tr1 );
2620 aMesh->RemoveElement( tr3 );
2621 // remove middle node (9)
2622 GetMeshDS()->RemoveNode( N1[4] );
2626 // Next element to fuse: the rejected one
2628 startElem = Ok12 ? tr3 : tr2;
2630 } // if ( startElem )
2631 } // while ( startElem || !startLinks.empty() )
2632 } // while ( ! mapEl_setLi.empty() )
2638 /*#define DUMPSO(txt) \
2639 // cout << txt << endl;
2640 //=============================================================================
2644 //=============================================================================
2645 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2649 int tmp = idNodes[ i1 ];
2650 idNodes[ i1 ] = idNodes[ i2 ];
2651 idNodes[ i2 ] = tmp;
2652 gp_Pnt Ptmp = P[ i1 ];
2655 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2658 //=======================================================================
2659 //function : SortQuadNodes
2660 //purpose : Set 4 nodes of a quadrangle face in a good order.
2661 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2663 //=======================================================================
2665 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2670 for ( i = 0; i < 4; i++ ) {
2671 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2673 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2676 gp_Vec V1(P[0], P[1]);
2677 gp_Vec V2(P[0], P[2]);
2678 gp_Vec V3(P[0], P[3]);
2680 gp_Vec Cross1 = V1 ^ V2;
2681 gp_Vec Cross2 = V2 ^ V3;
2684 if (Cross1.Dot(Cross2) < 0)
2689 if (Cross1.Dot(Cross2) < 0)
2693 swap ( i, i + 1, idNodes, P );
2695 // for ( int ii = 0; ii < 4; ii++ ) {
2696 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2697 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2703 //=======================================================================
2704 //function : SortHexaNodes
2705 //purpose : Set 8 nodes of a hexahedron in a good order.
2706 // Return success status
2707 //=======================================================================
2709 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2714 DUMPSO( "INPUT: ========================================");
2715 for ( i = 0; i < 8; i++ ) {
2716 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2717 if ( !n ) return false;
2718 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2719 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2721 DUMPSO( "========================================");
2724 set<int> faceNodes; // ids of bottom face nodes, to be found
2725 set<int> checkedId1; // ids of tried 2-nd nodes
2726 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2727 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2728 int iMin, iLoop1 = 0;
2730 // Loop to try the 2-nd nodes
2732 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2734 // Find not checked 2-nd node
2735 for ( i = 1; i < 8; i++ )
2736 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2737 int id1 = idNodes[i];
2738 swap ( 1, i, idNodes, P );
2739 checkedId1.insert ( id1 );
2743 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2744 // ie that all but meybe one (id3 which is on the same face) nodes
2745 // lay on the same side from the triangle plane.
2747 bool manyInPlane = false; // more than 4 nodes lay in plane
2749 while ( ++iLoop2 < 6 ) {
2751 // get 1-2-3 plane coeffs
2752 Standard_Real A, B, C, D;
2753 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2754 if ( N.SquareMagnitude() > gp::Resolution() )
2756 gp_Pln pln ( P[0], N );
2757 pln.Coefficients( A, B, C, D );
2759 // find the node (iMin) closest to pln
2760 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2762 for ( i = 3; i < 8; i++ ) {
2763 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2764 if ( fabs( dist[i] ) < minDist ) {
2765 minDist = fabs( dist[i] );
2768 if ( fabs( dist[i] ) <= tol )
2769 idInPln.insert( idNodes[i] );
2772 // there should not be more than 4 nodes in bottom plane
2773 if ( idInPln.size() > 1 )
2775 DUMPSO( "### idInPln.size() = " << idInPln.size());
2776 // idInPlane does not contain the first 3 nodes
2777 if ( manyInPlane || idInPln.size() == 5)
2778 return false; // all nodes in one plane
2781 // set the 1-st node to be not in plane
2782 for ( i = 3; i < 8; i++ ) {
2783 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2784 DUMPSO( "### Reset 0-th node");
2785 swap( 0, i, idNodes, P );
2790 // reset to re-check second nodes
2791 leastDist = DBL_MAX;
2795 break; // from iLoop2;
2798 // check that the other 4 nodes are on the same side
2799 bool sameSide = true;
2800 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2801 for ( i = 3; sameSide && i < 8; i++ ) {
2803 sameSide = ( isNeg == dist[i] <= 0.);
2806 // keep best solution
2807 if ( sameSide && minDist < leastDist ) {
2808 leastDist = minDist;
2810 faceNodes.insert( idNodes[ 1 ] );
2811 faceNodes.insert( idNodes[ 2 ] );
2812 faceNodes.insert( idNodes[ iMin ] );
2813 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2814 << " leastDist = " << leastDist);
2815 if ( leastDist <= DBL_MIN )
2820 // set next 3-d node to check
2821 int iNext = 2 + iLoop2;
2823 DUMPSO( "Try 2-nd");
2824 swap ( 2, iNext, idNodes, P );
2826 } // while ( iLoop2 < 6 )
2829 if ( faceNodes.empty() ) return false;
2831 // Put the faceNodes in proper places
2832 for ( i = 4; i < 8; i++ ) {
2833 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2834 // find a place to put
2836 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2838 DUMPSO( "Set faceNodes");
2839 swap ( iTo, i, idNodes, P );
2844 // Set nodes of the found bottom face in good order
2845 DUMPSO( " Found bottom face: ");
2846 i = SortQuadNodes( theMesh, idNodes );
2848 gp_Pnt Ptmp = P[ i ];
2853 // for ( int ii = 0; ii < 4; ii++ ) {
2854 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2855 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2858 // Gravity center of the top and bottom faces
2859 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2860 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2862 // Get direction from the bottom to the top face
2863 gp_Vec upDir ( aGCb, aGCt );
2864 Standard_Real upDirSize = upDir.Magnitude();
2865 if ( upDirSize <= gp::Resolution() ) return false;
2868 // Assure that the bottom face normal points up
2869 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2870 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2871 if ( Nb.Dot( upDir ) < 0 ) {
2872 DUMPSO( "Reverse bottom face");
2873 swap( 1, 3, idNodes, P );
2876 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2877 Standard_Real minDist = DBL_MAX;
2878 for ( i = 4; i < 8; i++ ) {
2879 // projection of P[i] to the plane defined by P[0] and upDir
2880 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2881 Standard_Real sqDist = P[0].SquareDistance( Pp );
2882 if ( sqDist < minDist ) {
2887 DUMPSO( "Set 4-th");
2888 swap ( 4, iMin, idNodes, P );
2890 // Set nodes of the top face in good order
2891 DUMPSO( "Sort top face");
2892 i = SortQuadNodes( theMesh, &idNodes[4] );
2895 gp_Pnt Ptmp = P[ i ];
2900 // Assure that direction of the top face normal is from the bottom face
2901 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2902 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2903 if ( Nt.Dot( upDir ) < 0 ) {
2904 DUMPSO( "Reverse top face");
2905 swap( 5, 7, idNodes, P );
2908 // DUMPSO( "OUTPUT: ========================================");
2909 // for ( i = 0; i < 8; i++ ) {
2910 // float *p = ugrid->GetPoint(idNodes[i]);
2911 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2917 //================================================================================
2919 * \brief Return nodes linked to the given one
2920 * \param theNode - the node
2921 * \param linkedNodes - the found nodes
2922 * \param type - the type of elements to check
2924 * Medium nodes are ignored
2926 //================================================================================
2928 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2929 TIDSortedElemSet & linkedNodes,
2930 SMDSAbs_ElementType type )
2932 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2933 while ( elemIt->more() )
2935 const SMDS_MeshElement* elem = elemIt->next();
2936 if(elem->GetType() == SMDSAbs_0DElement)
2939 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2940 if ( elem->GetType() == SMDSAbs_Volume )
2942 SMDS_VolumeTool vol( elem );
2943 while ( nodeIt->more() ) {
2944 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2945 if ( theNode != n && vol.IsLinked( theNode, n ))
2946 linkedNodes.insert( n );
2951 for ( int i = 0; nodeIt->more(); ++i ) {
2952 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2953 if ( n == theNode ) {
2954 int iBefore = i - 1;
2956 if ( elem->IsQuadratic() ) {
2957 int nb = elem->NbNodes() / 2;
2958 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2959 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2961 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2962 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2969 //=======================================================================
2970 //function : laplacianSmooth
2971 //purpose : pulls theNode toward the center of surrounding nodes directly
2972 // connected to that node along an element edge
2973 //=======================================================================
2975 void laplacianSmooth(const SMDS_MeshNode* theNode,
2976 const Handle(Geom_Surface)& theSurface,
2977 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2979 // find surrounding nodes
2981 TIDSortedElemSet nodeSet;
2982 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2984 // compute new coodrs
2986 double coord[] = { 0., 0., 0. };
2987 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2988 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2989 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2990 if ( theSurface.IsNull() ) { // smooth in 3D
2991 coord[0] += node->X();
2992 coord[1] += node->Y();
2993 coord[2] += node->Z();
2995 else { // smooth in 2D
2996 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2997 gp_XY* uv = theUVMap[ node ];
2998 coord[0] += uv->X();
2999 coord[1] += uv->Y();
3002 int nbNodes = nodeSet.size();
3005 coord[0] /= nbNodes;
3006 coord[1] /= nbNodes;
3008 if ( !theSurface.IsNull() ) {
3009 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3010 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3011 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3017 coord[2] /= nbNodes;
3021 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3024 //=======================================================================
3025 //function : centroidalSmooth
3026 //purpose : pulls theNode toward the element-area-weighted centroid of the
3027 // surrounding elements
3028 //=======================================================================
3030 void centroidalSmooth(const SMDS_MeshNode* theNode,
3031 const Handle(Geom_Surface)& theSurface,
3032 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3034 gp_XYZ aNewXYZ(0.,0.,0.);
3035 SMESH::Controls::Area anAreaFunc;
3036 double totalArea = 0.;
3041 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3042 while ( elemIt->more() )
3044 const SMDS_MeshElement* elem = elemIt->next();
3047 gp_XYZ elemCenter(0.,0.,0.);
3048 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3049 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3050 int nn = elem->NbNodes();
3051 if(elem->IsQuadratic()) nn = nn/2;
3053 //while ( itN->more() ) {
3055 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3057 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3058 aNodePoints.push_back( aP );
3059 if ( !theSurface.IsNull() ) { // smooth in 2D
3060 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3061 gp_XY* uv = theUVMap[ aNode ];
3062 aP.SetCoord( uv->X(), uv->Y(), 0. );
3066 double elemArea = anAreaFunc.GetValue( aNodePoints );
3067 totalArea += elemArea;
3069 aNewXYZ += elemCenter * elemArea;
3071 aNewXYZ /= totalArea;
3072 if ( !theSurface.IsNull() ) {
3073 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3074 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3079 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3082 //=======================================================================
3083 //function : getClosestUV
3084 //purpose : return UV of closest projection
3085 //=======================================================================
3087 static bool getClosestUV (Extrema_GenExtPS& projector,
3088 const gp_Pnt& point,
3091 projector.Perform( point );
3092 if ( projector.IsDone() ) {
3093 double u, v, minVal = DBL_MAX;
3094 for ( int i = projector.NbExt(); i > 0; i-- )
3095 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3096 if ( projector.SquareDistance( i ) < minVal ) {
3097 minVal = projector.SquareDistance( i );
3099 if ( projector.Value( i ) < minVal ) {
3100 minVal = projector.Value( i );
3102 projector.Point( i ).Parameter( u, v );
3104 result.SetCoord( u, v );
3110 //=======================================================================
3112 //purpose : Smooth theElements during theNbIterations or until a worst
3113 // element has aspect ratio <= theTgtAspectRatio.
3114 // Aspect Ratio varies in range [1.0, inf].
3115 // If theElements is empty, the whole mesh is smoothed.
3116 // theFixedNodes contains additionally fixed nodes. Nodes built
3117 // on edges and boundary nodes are always fixed.
3118 //=======================================================================
3120 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3121 set<const SMDS_MeshNode*> & theFixedNodes,
3122 const SmoothMethod theSmoothMethod,
3123 const int theNbIterations,
3124 double theTgtAspectRatio,
3127 myLastCreatedElems.Clear();
3128 myLastCreatedNodes.Clear();
3130 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3132 if ( theTgtAspectRatio < 1.0 )
3133 theTgtAspectRatio = 1.0;
3135 const double disttol = 1.e-16;
3137 SMESH::Controls::AspectRatio aQualityFunc;
3139 SMESHDS_Mesh* aMesh = GetMeshDS();
3141 if ( theElems.empty() ) {
3142 // add all faces to theElems
3143 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3144 while ( fIt->more() ) {
3145 const SMDS_MeshElement* face = fIt->next();
3146 theElems.insert( theElems.end(), face );
3149 // get all face ids theElems are on
3150 set< int > faceIdSet;
3151 TIDSortedElemSet::iterator itElem;
3153 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3154 int fId = FindShape( *itElem );
3155 // check that corresponding submesh exists and a shape is face
3157 faceIdSet.find( fId ) == faceIdSet.end() &&
3158 aMesh->MeshElements( fId )) {
3159 TopoDS_Shape F = aMesh->IndexToShape( fId );
3160 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3161 faceIdSet.insert( fId );
3164 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3166 // ===============================================
3167 // smooth elements on each TopoDS_Face separately
3168 // ===============================================
3170 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3171 for ( ; fId != faceIdSet.rend(); ++fId ) {
3172 // get face surface and submesh
3173 Handle(Geom_Surface) surface;
3174 SMESHDS_SubMesh* faceSubMesh = 0;
3176 double fToler2 = 0, f,l;
3177 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3178 bool isUPeriodic = false, isVPeriodic = false;
3180 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3181 surface = BRep_Tool::Surface( face );
3182 faceSubMesh = aMesh->MeshElements( *fId );
3183 fToler2 = BRep_Tool::Tolerance( face );
3184 fToler2 *= fToler2 * 10.;
3185 isUPeriodic = surface->IsUPeriodic();
3188 isVPeriodic = surface->IsVPeriodic();
3191 surface->Bounds( u1, u2, v1, v2 );
3193 // ---------------------------------------------------------
3194 // for elements on a face, find movable and fixed nodes and
3195 // compute UV for them
3196 // ---------------------------------------------------------
3197 bool checkBoundaryNodes = false;
3198 bool isQuadratic = false;
3199 set<const SMDS_MeshNode*> setMovableNodes;
3200 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3201 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3202 list< const SMDS_MeshElement* > elemsOnFace;
3204 Extrema_GenExtPS projector;
3205 GeomAdaptor_Surface surfAdaptor;
3206 if ( !surface.IsNull() ) {
3207 surfAdaptor.Load( surface );
3208 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3210 int nbElemOnFace = 0;
3211 itElem = theElems.begin();
3212 // loop on not yet smoothed elements: look for elems on a face
3213 while ( itElem != theElems.end() ) {
3214 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3215 break; // all elements found
3217 const SMDS_MeshElement* elem = *itElem;
3218 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3219 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3223 elemsOnFace.push_back( elem );
3224 theElems.erase( itElem++ );
3228 isQuadratic = elem->IsQuadratic();
3230 // get movable nodes of elem
3231 const SMDS_MeshNode* node;
3232 SMDS_TypeOfPosition posType;
3233 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3234 int nn = 0, nbn = elem->NbNodes();
3235 if(elem->IsQuadratic())
3237 while ( nn++ < nbn ) {
3238 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3239 const SMDS_PositionPtr& pos = node->GetPosition();
3240 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3241 if (posType != SMDS_TOP_EDGE &&
3242 posType != SMDS_TOP_VERTEX &&
3243 theFixedNodes.find( node ) == theFixedNodes.end())
3245 // check if all faces around the node are on faceSubMesh
3246 // because a node on edge may be bound to face
3247 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3249 if ( faceSubMesh ) {
3250 while ( eIt->more() && all ) {
3251 const SMDS_MeshElement* e = eIt->next();
3252 all = faceSubMesh->Contains( e );
3256 setMovableNodes.insert( node );
3258 checkBoundaryNodes = true;
3260 if ( posType == SMDS_TOP_3DSPACE )
3261 checkBoundaryNodes = true;
3264 if ( surface.IsNull() )
3267 // get nodes to check UV
3268 list< const SMDS_MeshNode* > uvCheckNodes;
3269 itN = elem->nodesIterator();
3270 nn = 0; nbn = elem->NbNodes();
3271 if(elem->IsQuadratic())
3273 while ( nn++ < nbn ) {
3274 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3275 if ( uvMap.find( node ) == uvMap.end() )
3276 uvCheckNodes.push_back( node );
3277 // add nodes of elems sharing node
3278 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3279 // while ( eIt->more() ) {
3280 // const SMDS_MeshElement* e = eIt->next();
3281 // if ( e != elem ) {
3282 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3283 // while ( nIt->more() ) {
3284 // const SMDS_MeshNode* n =
3285 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3286 // if ( uvMap.find( n ) == uvMap.end() )
3287 // uvCheckNodes.push_back( n );
3293 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3294 for ( ; n != uvCheckNodes.end(); ++n ) {
3297 const SMDS_PositionPtr& pos = node->GetPosition();
3298 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3300 switch ( posType ) {
3301 case SMDS_TOP_FACE: {
3302 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3303 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3306 case SMDS_TOP_EDGE: {
3307 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3308 Handle(Geom2d_Curve) pcurve;
3309 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3310 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3311 if ( !pcurve.IsNull() ) {
3312 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3313 uv = pcurve->Value( u ).XY();
3317 case SMDS_TOP_VERTEX: {
3318 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3319 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3320 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3325 // check existing UV
3326 bool project = true;
3327 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3328 double dist1 = DBL_MAX, dist2 = 0;
3329 if ( posType != SMDS_TOP_3DSPACE ) {
3330 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3331 project = dist1 > fToler2;
3333 if ( project ) { // compute new UV
3335 if ( !getClosestUV( projector, pNode, newUV )) {
3336 MESSAGE("Node Projection Failed " << node);
3340 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3342 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3344 if ( posType != SMDS_TOP_3DSPACE )
3345 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3346 if ( dist2 < dist1 )
3350 // store UV in the map
3351 listUV.push_back( uv );
3352 uvMap.insert( make_pair( node, &listUV.back() ));
3354 } // loop on not yet smoothed elements
3356 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3357 checkBoundaryNodes = true;
3359 // fix nodes on mesh boundary
3361 if ( checkBoundaryNodes ) {
3362 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3363 map< SMESH_TLink, int >::iterator link_nb;
3364 // put all elements links to linkNbMap
3365 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3366 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3367 const SMDS_MeshElement* elem = (*elemIt);
3368 int nbn = elem->NbCornerNodes();
3369 // loop on elem links: insert them in linkNbMap
3370 for ( int iN = 0; iN < nbn; ++iN ) {
3371 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3372 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3373 SMESH_TLink link( n1, n2 );
3374 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3378 // remove nodes that are in links encountered only once from setMovableNodes
3379 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3380 if ( link_nb->second == 1 ) {
3381 setMovableNodes.erase( link_nb->first.node1() );
3382 setMovableNodes.erase( link_nb->first.node2() );
3387 // -----------------------------------------------------
3388 // for nodes on seam edge, compute one more UV ( uvMap2 );
3389 // find movable nodes linked to nodes on seam and which
3390 // are to be smoothed using the second UV ( uvMap2 )
3391 // -----------------------------------------------------
3393 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3394 if ( !surface.IsNull() ) {
3395 TopExp_Explorer eExp( face, TopAbs_EDGE );
3396 for ( ; eExp.More(); eExp.Next() ) {
3397 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3398 if ( !BRep_Tool::IsClosed( edge, face ))
3400 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3401 if ( !sm ) continue;
3402 // find out which parameter varies for a node on seam
3405 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3406 if ( pcurve.IsNull() ) continue;
3407 uv1 = pcurve->Value( f );
3409 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3410 if ( pcurve.IsNull() ) continue;
3411 uv2 = pcurve->Value( f );
3412 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3414 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3415 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3417 // get nodes on seam and its vertices
3418 list< const SMDS_MeshNode* > seamNodes;
3419 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3420 while ( nSeamIt->more() ) {
3421 const SMDS_MeshNode* node = nSeamIt->next();
3422 if ( !isQuadratic || !IsMedium( node ))
3423 seamNodes.push_back( node );
3425 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3426 for ( ; vExp.More(); vExp.Next() ) {
3427 sm = aMesh->MeshElements( vExp.Current() );
3429 nSeamIt = sm->GetNodes();
3430 while ( nSeamIt->more() )
3431 seamNodes.push_back( nSeamIt->next() );
3434 // loop on nodes on seam
3435 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3436 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3437 const SMDS_MeshNode* nSeam = *noSeIt;
3438 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3439 if ( n_uv == uvMap.end() )
3442 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3443 // set the second UV
3444 listUV.push_back( *n_uv->second );
3445 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3446 if ( uvMap2.empty() )
3447 uvMap2 = uvMap; // copy the uvMap contents
3448 uvMap2[ nSeam ] = &listUV.back();
3450 // collect movable nodes linked to ones on seam in nodesNearSeam
3451 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3452 while ( eIt->more() ) {
3453 const SMDS_MeshElement* e = eIt->next();
3454 int nbUseMap1 = 0, nbUseMap2 = 0;
3455 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3456 int nn = 0, nbn = e->NbNodes();
3457 if(e->IsQuadratic()) nbn = nbn/2;
3458 while ( nn++ < nbn )
3460 const SMDS_MeshNode* n =
3461 static_cast<const SMDS_MeshNode*>( nIt->next() );
3463 setMovableNodes.find( n ) == setMovableNodes.end() )
3465 // add only nodes being closer to uv2 than to uv1
3466 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3467 0.5 * ( n->Y() + nSeam->Y() ),
3468 0.5 * ( n->Z() + nSeam->Z() ));
3470 getClosestUV( projector, pMid, uv );
3471 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3472 nodesNearSeam.insert( n );
3478 // for centroidalSmooth all element nodes must
3479 // be on one side of a seam
3480 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3481 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3483 while ( nn++ < nbn ) {
3484 const SMDS_MeshNode* n =
3485 static_cast<const SMDS_MeshNode*>( nIt->next() );
3486 setMovableNodes.erase( n );
3490 } // loop on nodes on seam
3491 } // loop on edge of a face
3492 } // if ( !face.IsNull() )
3494 if ( setMovableNodes.empty() ) {
3495 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3496 continue; // goto next face
3504 double maxRatio = -1., maxDisplacement = -1.;
3505 set<const SMDS_MeshNode*>::iterator nodeToMove;
3506 for ( it = 0; it < theNbIterations; it++ ) {
3507 maxDisplacement = 0.;
3508 nodeToMove = setMovableNodes.begin();
3509 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3510 const SMDS_MeshNode* node = (*nodeToMove);
3511 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3514 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3515 if ( theSmoothMethod == LAPLACIAN )
3516 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3518 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3520 // node displacement
3521 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3522 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3523 if ( aDispl > maxDisplacement )
3524 maxDisplacement = aDispl;
3526 // no node movement => exit
3527 //if ( maxDisplacement < 1.e-16 ) {
3528 if ( maxDisplacement < disttol ) {
3529 MESSAGE("-- no node movement --");
3533 // check elements quality
3535 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3536 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3537 const SMDS_MeshElement* elem = (*elemIt);
3538 if ( !elem || elem->GetType() != SMDSAbs_Face )
3540 SMESH::Controls::TSequenceOfXYZ aPoints;
3541 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3542 double aValue = aQualityFunc.GetValue( aPoints );
3543 if ( aValue > maxRatio )
3547 if ( maxRatio <= theTgtAspectRatio ) {
3548 MESSAGE("-- quality achived --");
3551 if (it+1 == theNbIterations) {
3552 MESSAGE("-- Iteration limit exceeded --");
3554 } // smoothing iterations
3556 MESSAGE(" Face id: " << *fId <<
3557 " Nb iterstions: " << it <<
3558 " Displacement: " << maxDisplacement <<
3559 " Aspect Ratio " << maxRatio);
3561 // ---------------------------------------
3562 // new nodes positions are computed,
3563 // record movement in DS and set new UV
3564 // ---------------------------------------
3565 nodeToMove = setMovableNodes.begin();
3566 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3567 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3568 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3569 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3570 if ( node_uv != uvMap.end() ) {
3571 gp_XY* uv = node_uv->second;
3573 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3577 // move medium nodes of quadratic elements
3580 SMESH_MesherHelper helper( *GetMesh() );
3581 if ( !face.IsNull() )
3582 helper.SetSubShape( face );
3583 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3584 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3585 const SMDS_VtkFace* QF =
3586 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3587 if(QF && QF->IsQuadratic()) {
3588 vector<const SMDS_MeshNode*> Ns;
3589 Ns.reserve(QF->NbNodes()+1);
3590 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3591 while ( anIter->more() )
3592 Ns.push_back( cast2Node(anIter->next()) );
3593 Ns.push_back( Ns[0] );
3595 for(int i=0; i<QF->NbNodes(); i=i+2) {
3596 if ( !surface.IsNull() ) {
3597 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3598 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3599 gp_XY uv = ( uv1 + uv2 ) / 2.;
3600 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3601 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3604 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3605 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3606 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3608 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3609 fabs( Ns[i+1]->Y() - y ) > disttol ||
3610 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3611 // we have to move i+1 node
3612 aMesh->MoveNode( Ns[i+1], x, y, z );
3619 } // loop on face ids
3623 //=======================================================================
3624 //function : isReverse
3625 //purpose : Return true if normal of prevNodes is not co-directied with
3626 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3627 // iNotSame is where prevNodes and nextNodes are different.
3628 // If result is true then future volume orientation is OK
3629 //=======================================================================
3631 static bool isReverse(const SMDS_MeshElement* face,
3632 const vector<const SMDS_MeshNode*>& prevNodes,
3633 const vector<const SMDS_MeshNode*>& nextNodes,
3637 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3638 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3639 gp_XYZ extrDir( pN - pP ), faceNorm;
3640 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3642 return faceNorm * extrDir < 0.0;
3645 //=======================================================================
3647 * \brief Create elements by sweeping an element
3648 * \param elem - element to sweep
3649 * \param newNodesItVec - nodes generated from each node of the element
3650 * \param newElems - generated elements
3651 * \param nbSteps - number of sweeping steps
3652 * \param srcElements - to append elem for each generated element
3654 //=======================================================================
3656 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3657 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3658 list<const SMDS_MeshElement*>& newElems,
3660 SMESH_SequenceOfElemPtr& srcElements)
3662 //MESSAGE("sweepElement " << nbSteps);
3663 SMESHDS_Mesh* aMesh = GetMeshDS();
3665 const int nbNodes = elem->NbNodes();
3666 const int nbCorners = elem->NbCornerNodes();
3667 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3668 polyhedron creation !!! */
3669 // Loop on elem nodes:
3670 // find new nodes and detect same nodes indices
3671 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3672 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3673 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3674 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3676 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3677 vector<int> sames(nbNodes);
3678 vector<bool> isSingleNode(nbNodes);
3680 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3681 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3682 const SMDS_MeshNode* node = nnIt->first;
3683 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3684 if ( listNewNodes.empty() )
3687 itNN [ iNode ] = listNewNodes.begin();
3688 prevNod[ iNode ] = node;
3689 nextNod[ iNode ] = listNewNodes.front();
3691 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3692 corner node of linear */
3693 if ( prevNod[ iNode ] != nextNod [ iNode ])
3694 nbDouble += !isSingleNode[iNode];
3696 if( iNode < nbCorners ) { // check corners only
3697 if ( prevNod[ iNode ] == nextNod [ iNode ])
3698 sames[nbSame++] = iNode;
3700 iNotSameNode = iNode;
3704 if ( nbSame == nbNodes || nbSame > 2) {
3705 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3709 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3711 // fix nodes order to have bottom normal external
3712 if ( baseType == SMDSEntity_Polygon )
3714 std::reverse( itNN.begin(), itNN.end() );
3715 std::reverse( prevNod.begin(), prevNod.end() );
3716 std::reverse( midlNod.begin(), midlNod.end() );
3717 std::reverse( nextNod.begin(), nextNod.end() );
3718 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3722 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3723 SMDS_MeshCell::applyInterlace( ind, itNN );
3724 SMDS_MeshCell::applyInterlace( ind, prevNod );
3725 SMDS_MeshCell::applyInterlace( ind, nextNod );
3726 SMDS_MeshCell::applyInterlace( ind, midlNod );
3727 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3730 sames[nbSame] = iNotSameNode;
3731 for ( int j = 0; j <= nbSame; ++j )
3732 for ( size_t i = 0; i < ind.size(); ++i )
3733 if ( ind[i] == sames[j] )
3738 iNotSameNode = sames[nbSame];
3743 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3745 iSameNode = sames[ nbSame-1 ];
3746 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3747 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3748 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3751 // make new elements
3752 for (int iStep = 0; iStep < nbSteps; iStep++ )
3755 for ( iNode = 0; iNode < nbNodes; iNode++ )
3757 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3758 nextNod[ iNode ] = *itNN[ iNode ]++;
3761 SMDS_MeshElement* aNewElem = 0;
3762 /*if(!elem->IsPoly())*/ {
3763 switch ( baseType ) {
3765 case SMDSEntity_Node: { // sweep NODE
3766 if ( nbSame == 0 ) {
3767 if ( isSingleNode[0] )
3768 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3770 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3776 case SMDSEntity_Edge: { // sweep EDGE
3777 if ( nbDouble == 0 )
3779 if ( nbSame == 0 ) // ---> quadrangle
3780 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3781 nextNod[ 1 ], nextNod[ 0 ] );
3782 else // ---> triangle
3783 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3784 nextNod[ iNotSameNode ] );
3786 else // ---> polygon
3788 vector<const SMDS_MeshNode*> poly_nodes;
3789 poly_nodes.push_back( prevNod[0] );
3790 poly_nodes.push_back( prevNod[1] );
3791 if ( prevNod[1] != nextNod[1] )
3793 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3794 poly_nodes.push_back( nextNod[1] );
3796 if ( prevNod[0] != nextNod[0] )
3798 poly_nodes.push_back( nextNod[0] );
3799 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3801 switch ( poly_nodes.size() ) {
3803 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3806 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3807 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3810 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3815 case SMDSEntity_Triangle: // TRIANGLE --->
3817 if ( nbDouble > 0 ) break;
3818 if ( nbSame == 0 ) // ---> pentahedron
3819 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3820 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3822 else if ( nbSame == 1 ) // ---> pyramid
3823 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3824 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3825 nextNod[ iSameNode ]);
3827 else // 2 same nodes: ---> tetrahedron
3828 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3829 nextNod[ iNotSameNode ]);
3832 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3836 if ( nbDouble+nbSame == 2 )
3838 if(nbSame==0) { // ---> quadratic quadrangle
3839 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3840 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3842 else { //(nbSame==1) // ---> quadratic triangle
3844 return; // medium node on axis
3846 else if(sames[0]==0)
3847 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3848 nextNod[2], midlNod[1], prevNod[2]);
3850 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3851 midlNod[0], nextNod[2], prevNod[2]);
3854 else if ( nbDouble == 3 )
3856 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3857 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3858 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3865 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3866 if ( nbDouble > 0 ) break;
3868 if ( nbSame == 0 ) // ---> hexahedron
3869 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3870 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3872 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3873 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3874 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3875 nextNod[ iSameNode ]);
3876 newElems.push_back( aNewElem );
3877 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3878 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3879 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3881 else if ( nbSame == 2 ) { // ---> pentahedron
3882 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3883 // iBeforeSame is same too
3884 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3885 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3886 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3888 // iAfterSame is same too
3889 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3890 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3891 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3895 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3896 if ( nbDouble+nbSame != 3 ) break;
3898 // ---> pentahedron with 15 nodes
3899 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3900 nextNod[0], nextNod[1], nextNod[2],
3901 prevNod[3], prevNod[4], prevNod[5],
3902 nextNod[3], nextNod[4], nextNod[5],
3903 midlNod[0], midlNod[1], midlNod[2]);
3905 else if(nbSame==1) {
3906 // ---> 2d order pyramid of 13 nodes
3907 int apex = iSameNode;
3908 int i0 = ( apex + 1 ) % nbCorners;
3909 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3913 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3914 nextNod[i0], nextNod[i1], prevNod[apex],
3915 prevNod[i01], midlNod[i0],
3916 nextNod[i01], midlNod[i1],
3917 prevNod[i1a], prevNod[i0a],
3918 nextNod[i0a], nextNod[i1a]);
3920 else if(nbSame==2) {
3921 // ---> 2d order tetrahedron of 10 nodes
3922 int n1 = iNotSameNode;
3923 int n2 = ( n1 + 1 ) % nbCorners;
3924 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3928 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3929 prevNod[n12], prevNod[n23], prevNod[n31],
3930 midlNod[n1], nextNod[n12], nextNod[n31]);
3934 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3936 if ( nbDouble != 4 ) break;
3937 // ---> hexahedron with 20 nodes
3938 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3939 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3940 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3941 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3942 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3944 else if(nbSame==1) {
3945 // ---> pyramid + pentahedron - can not be created since it is needed
3946 // additional middle node at the center of face
3947 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3950 else if( nbSame == 2 ) {
3951 if ( nbDouble != 2 ) break;
3952 // ---> 2d order Pentahedron with 15 nodes
3954 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3955 // iBeforeSame is same too
3962 // iAfterSame is same too
3972 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3973 prevNod[n4], prevNod[n5], nextNod[n5],
3974 prevNod[n12], midlNod[n2], nextNod[n12],
3975 prevNod[n45], midlNod[n5], nextNod[n45],
3976 prevNod[n14], prevNod[n25], nextNod[n25]);
3980 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3982 if( nbSame == 0 && nbDouble == 9 ) {
3983 // ---> tri-quadratic hexahedron with 27 nodes
3984 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3985 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3986 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3987 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3988 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3989 prevNod[8], // bottom center
3990 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3991 nextNod[8], // top center
3992 midlNod[8]);// elem center
4000 case SMDSEntity_Polygon: { // sweep POLYGON
4002 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4003 // ---> hexagonal prism
4004 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4005 prevNod[3], prevNod[4], prevNod[5],
4006 nextNod[0], nextNod[1], nextNod[2],
4007 nextNod[3], nextNod[4], nextNod[5]);
4011 case SMDSEntity_Ball:
4019 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4021 if ( baseType != SMDSEntity_Polygon )
4023 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4024 SMDS_MeshCell::applyInterlace( ind, prevNod );
4025 SMDS_MeshCell::applyInterlace( ind, nextNod );
4026 SMDS_MeshCell::applyInterlace( ind, midlNod );
4027 SMDS_MeshCell::applyInterlace( ind, itNN );
4028 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4029 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4031 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4032 vector<int> quantities (nbNodes + 2);
4033 polyedre_nodes.clear();
4037 for (int inode = 0; inode < nbNodes; inode++)
4038 polyedre_nodes.push_back( prevNod[inode] );
4039 quantities.push_back( nbNodes );
4042 polyedre_nodes.push_back( nextNod[0] );
4043 for (int inode = nbNodes; inode-1; --inode )
4044 polyedre_nodes.push_back( nextNod[inode-1] );
4045 quantities.push_back( nbNodes );
4048 for (int iface = 0; iface < nbNodes; iface++)
4050 const int prevNbNodes = polyedre_nodes.size();
4051 int inextface = (iface+1) % nbNodes;
4052 polyedre_nodes.push_back( prevNod[inextface] );
4053 polyedre_nodes.push_back( prevNod[iface] );
4054 if ( prevNod[iface] != nextNod[iface] )
4056 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4057 polyedre_nodes.push_back( nextNod[iface] );
4059 if ( prevNod[inextface] != nextNod[inextface] )
4061 polyedre_nodes.push_back( nextNod[inextface] );
4062 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4064 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4065 if ( nbFaceNodes > 2 )
4066 quantities.push_back( nbFaceNodes );
4067 else // degenerated face
4068 polyedre_nodes.resize( prevNbNodes );
4070 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4074 newElems.push_back( aNewElem );
4075 myLastCreatedElems.Append(aNewElem);
4076 srcElements.Append( elem );
4079 // set new prev nodes
4080 for ( iNode = 0; iNode < nbNodes; iNode++ )
4081 prevNod[ iNode ] = nextNod[ iNode ];
4086 //=======================================================================
4088 * \brief Create 1D and 2D elements around swept elements
4089 * \param mapNewNodes - source nodes and ones generated from them
4090 * \param newElemsMap - source elements and ones generated from them
4091 * \param elemNewNodesMap - nodes generated from each node of each element
4092 * \param elemSet - all swept elements
4093 * \param nbSteps - number of sweeping steps
4094 * \param srcElements - to append elem for each generated element
4096 //=======================================================================
4098 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4099 TElemOfElemListMap & newElemsMap,
4100 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4101 TIDSortedElemSet& elemSet,
4103 SMESH_SequenceOfElemPtr& srcElements)
4105 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4106 SMESHDS_Mesh* aMesh = GetMeshDS();
4108 // Find nodes belonging to only one initial element - sweep them to get edges.
4110 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4111 for ( ; nList != mapNewNodes.end(); nList++ )
4113 const SMDS_MeshNode* node =
4114 static_cast<const SMDS_MeshNode*>( nList->first );
4115 if ( newElemsMap.count( node ))
4116 continue; // node was extruded into edge
4117 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4118 int nbInitElems = 0;
4119 const SMDS_MeshElement* el = 0;
4120 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4121 while ( eIt->more() && nbInitElems < 2 ) {
4123 SMDSAbs_ElementType type = el->GetType();
4124 if ( type == SMDSAbs_Volume || type < highType ) continue;
4125 if ( type > highType ) {
4129 nbInitElems += elemSet.count(el);
4131 if ( nbInitElems < 2 ) {
4132 bool NotCreateEdge = el && el->IsMediumNode(node);
4133 if(!NotCreateEdge) {
4134 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4135 list<const SMDS_MeshElement*> newEdges;
4136 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4141 // Make a ceiling for each element ie an equal element of last new nodes.
4142 // Find free links of faces - make edges and sweep them into faces.
4144 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4145 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4146 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4148 const SMDS_MeshElement* elem = itElem->first;
4149 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4151 if(itElem->second.size()==0) continue;
4153 const bool isQuadratic = elem->IsQuadratic();
4155 if ( elem->GetType() == SMDSAbs_Edge ) {
4156 // create a ceiling edge
4157 if ( !isQuadratic ) {
4158 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4159 vecNewNodes[ 1 ]->second.back())) {
4160 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4161 vecNewNodes[ 1 ]->second.back()));
4162 srcElements.Append( myLastCreatedElems.Last() );
4166 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4167 vecNewNodes[ 1 ]->second.back(),
4168 vecNewNodes[ 2 ]->second.back())) {
4169 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4170 vecNewNodes[ 1 ]->second.back(),
4171 vecNewNodes[ 2 ]->second.back()));
4172 srcElements.Append( myLastCreatedElems.Last() );
4176 if ( elem->GetType() != SMDSAbs_Face )
4179 bool hasFreeLinks = false;
4181 TIDSortedElemSet avoidSet;
4182 avoidSet.insert( elem );
4184 set<const SMDS_MeshNode*> aFaceLastNodes;
4185 int iNode, nbNodes = vecNewNodes.size();
4186 if ( !isQuadratic ) {
4187 // loop on the face nodes
4188 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4189 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4190 // look for free links of the face
4191 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4192 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4193 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4194 // check if a link is free
4195 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4196 hasFreeLinks = true;
4197 // make an edge and a ceiling for a new edge
4198 if ( !aMesh->FindEdge( n1, n2 )) {
4199 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4200 srcElements.Append( myLastCreatedElems.Last() );
4202 n1 = vecNewNodes[ iNode ]->second.back();
4203 n2 = vecNewNodes[ iNext ]->second.back();
4204 if ( !aMesh->FindEdge( n1, n2 )) {
4205 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4206 srcElements.Append( myLastCreatedElems.Last() );
4211 else { // elem is quadratic face
4212 int nbn = nbNodes/2;
4213 for ( iNode = 0; iNode < nbn; iNode++ ) {
4214 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4215 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4216 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4217 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4218 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4219 // check if a link is free
4220 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4221 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4222 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4223 hasFreeLinks = true;
4224 // make an edge and a ceiling for a new edge
4226 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4227 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4228 srcElements.Append( myLastCreatedElems.Last() );
4230 n1 = vecNewNodes[ iNode ]->second.back();
4231 n2 = vecNewNodes[ iNext ]->second.back();
4232 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4233 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4234 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4235 srcElements.Append( myLastCreatedElems.Last() );
4239 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4240 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4244 // sweep free links into faces
4246 if ( hasFreeLinks ) {
4247 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4248 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4250 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4251 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4252 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4253 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4255 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4256 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4257 std::advance( v, volNb );
4258 // find indices of free faces of a volume and their source edges
4259 list< int > freeInd;
4260 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4261 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4262 int iF, nbF = vTool.NbFaces();
4263 for ( iF = 0; iF < nbF; iF ++ ) {
4264 if (vTool.IsFreeFace( iF ) &&
4265 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4266 initNodeSet != faceNodeSet) // except an initial face
4268 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4270 freeInd.push_back( iF );
4271 // find source edge of a free face iF
4272 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4273 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4274 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4275 initNodeSet.begin(), initNodeSet.end(),
4276 commonNodes.begin());
4277 if ( (*v)->IsQuadratic() )
4278 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4280 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4282 if ( !srcEdges.back() )
4284 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4285 << iF << " of volume #" << vTool.ID() << endl;
4290 if ( freeInd.empty() )
4293 // create faces for all steps;
4294 // if such a face has been already created by sweep of edge,
4295 // assure that its orientation is OK
4296 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4297 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4298 vTool.SetExternalNormal();
4299 const int nextShift = vTool.IsForward() ? +1 : -1;
4300 list< int >::iterator ind = freeInd.begin();
4301 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4302 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4304 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4305 int nbn = vTool.NbFaceNodes( *ind );
4306 const SMDS_MeshElement * f = 0;
4307 if ( nbn == 3 ) ///// triangle
4309 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4311 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4313 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4315 nodes[ 1 + nextShift ] };
4317 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4319 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4323 else if ( nbn == 4 ) ///// quadrangle
4325 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4327 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4329 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4330 nodes[ 2 ], nodes[ 2+nextShift ] };
4332 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4334 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4335 newOrder[ 2 ], newOrder[ 3 ]));
4338 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4340 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4342 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4344 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4346 nodes[2 + 2*nextShift],
4347 nodes[3 - 2*nextShift],
4349 nodes[3 + 2*nextShift]};
4351 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4353 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4361 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4363 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4364 nodes[1], nodes[3], nodes[5], nodes[7] );
4366 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4368 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4369 nodes[4 - 2*nextShift],
4371 nodes[4 + 2*nextShift],
4373 nodes[5 - 2*nextShift],
4375 nodes[5 + 2*nextShift] };
4377 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4379 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4380 newOrder[ 2 ], newOrder[ 3 ],
4381 newOrder[ 4 ], newOrder[ 5 ],
4382 newOrder[ 6 ], newOrder[ 7 ]));
4385 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4387 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4388 SMDSAbs_Face, /*noMedium=*/false);
4390 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4392 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4393 nodes[4 - 2*nextShift],
4395 nodes[4 + 2*nextShift],
4397 nodes[5 - 2*nextShift],
4399 nodes[5 + 2*nextShift],
4402 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4404 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4405 newOrder[ 2 ], newOrder[ 3 ],
4406 newOrder[ 4 ], newOrder[ 5 ],
4407 newOrder[ 6 ], newOrder[ 7 ],
4411 else //////// polygon
4413 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4414 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4416 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4418 if ( !vTool.IsForward() )
4419 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4421 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4423 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4427 while ( srcElements.Length() < myLastCreatedElems.Length() )
4428 srcElements.Append( *srcEdge );
4430 } // loop on free faces
4432 // go to the next volume
4434 while ( iVol++ < nbVolumesByStep ) v++;
4437 } // loop on volumes of one step
4438 } // sweep free links into faces
4440 // Make a ceiling face with a normal external to a volume
4442 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4444 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4446 lastVol.SetExternalNormal();
4447 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4448 int nbn = lastVol.NbFaceNodes( iF );
4450 if (!hasFreeLinks ||
4451 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4452 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4454 else if ( nbn == 4 )
4456 if (!hasFreeLinks ||
4457 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4458 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4460 else if ( nbn == 6 && isQuadratic )
4462 if (!hasFreeLinks ||
4463 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4464 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4465 nodes[1], nodes[3], nodes[5]));
4467 else if ( nbn == 8 && isQuadratic )
4469 if (!hasFreeLinks ||
4470 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4471 nodes[1], nodes[3], nodes[5], nodes[7]) )
4472 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4473 nodes[1], nodes[3], nodes[5], nodes[7]));
4475 else if ( nbn == 9 && isQuadratic )
4477 if (!hasFreeLinks ||
4478 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4479 SMDSAbs_Face, /*noMedium=*/false) )
4480 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4481 nodes[1], nodes[3], nodes[5], nodes[7],
4485 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4486 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4487 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4490 while ( srcElements.Length() < myLastCreatedElems.Length() )
4491 srcElements.Append( myLastCreatedElems.Last() );
4493 } // loop on swept elements
4496 //=======================================================================
4497 //function : RotationSweep
4499 //=======================================================================
4501 SMESH_MeshEditor::PGroupIDs
4502 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4503 const gp_Ax1& theAxis,
4504 const double theAngle,
4505 const int theNbSteps,
4506 const double theTol,
4507 const bool theMakeGroups,
4508 const bool theMakeWalls)
4510 myLastCreatedElems.Clear();
4511 myLastCreatedNodes.Clear();
4513 // source elements for each generated one
4514 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4516 MESSAGE( "RotationSweep()");
4518 aTrsf.SetRotation( theAxis, theAngle );
4520 aTrsf2.SetRotation( theAxis, theAngle/2. );
4522 gp_Lin aLine( theAxis );
4523 double aSqTol = theTol * theTol;
4525 SMESHDS_Mesh* aMesh = GetMeshDS();
4527 TNodeOfNodeListMap mapNewNodes;
4528 TElemOfVecOfNnlmiMap mapElemNewNodes;
4529 TElemOfElemListMap newElemsMap;
4531 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4532 myMesh->NbFaces(ORDER_QUADRATIC) +
4533 myMesh->NbVolumes(ORDER_QUADRATIC) );
4535 TIDSortedElemSet::iterator itElem;
4536 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4537 const SMDS_MeshElement* elem = *itElem;
4538 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4540 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4541 newNodesItVec.reserve( elem->NbNodes() );
4543 // loop on elem nodes
4544 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4545 while ( itN->more() )
4547 // check if a node has been already sweeped
4548 const SMDS_MeshNode* node = cast2Node( itN->next() );
4550 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4552 aXYZ.Coord( coord[0], coord[1], coord[2] );
4553 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4555 TNodeOfNodeListMapItr nIt =
4556 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4557 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4558 if ( listNewNodes.empty() )
4560 // check if we are to create medium nodes between corner ones
4561 bool needMediumNodes = false;
4562 if ( isQuadraticMesh )
4564 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4565 while (it->more() && !needMediumNodes )
4567 const SMDS_MeshElement* invElem = it->next();
4568 if ( invElem != elem && !theElems.count( invElem )) continue;
4569 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4570 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4571 needMediumNodes = true;
4576 const SMDS_MeshNode * newNode = node;
4577 for ( int i = 0; i < theNbSteps; i++ ) {
4579 if ( needMediumNodes ) // create a medium node
4581 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4582 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4583 myLastCreatedNodes.Append(newNode);
4584 srcNodes.Append( node );
4585 listNewNodes.push_back( newNode );
4586 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4589 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4591 // create a corner node
4592 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4593 myLastCreatedNodes.Append(newNode);
4594 srcNodes.Append( node );
4595 listNewNodes.push_back( newNode );
4598 listNewNodes.push_back( newNode );
4599 // if ( needMediumNodes )
4600 // listNewNodes.push_back( newNode );
4604 newNodesItVec.push_back( nIt );
4606 // make new elements
4607 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4611 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4613 PGroupIDs newGroupIDs;
4614 if ( theMakeGroups )
4615 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4621 //=======================================================================
4622 //function : CreateNode
4624 //=======================================================================
4625 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4628 const double tolnode,
4629 SMESH_SequenceOfNode& aNodes)
4631 // myLastCreatedElems.Clear();
4632 // myLastCreatedNodes.Clear();
4635 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4637 // try to search in sequence of existing nodes
4638 // if aNodes.Length()>0 we 'nave to use given sequence
4639 // else - use all nodes of mesh
4640 if(aNodes.Length()>0) {
4642 for(i=1; i<=aNodes.Length(); i++) {
4643 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4644 if(P1.Distance(P2)<tolnode)
4645 return aNodes.Value(i);
4649 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4650 while(itn->more()) {
4651 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4652 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4653 if(P1.Distance(P2)<tolnode)
4658 // create new node and return it
4659 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4660 //myLastCreatedNodes.Append(NewNode);
4665 //=======================================================================
4666 //function : ExtrusionSweep
4668 //=======================================================================
4670 SMESH_MeshEditor::PGroupIDs
4671 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4672 const gp_Vec& theStep,
4673 const int theNbSteps,
4674 TElemOfElemListMap& newElemsMap,
4675 const bool theMakeGroups,
4677 const double theTolerance)
4679 ExtrusParam aParams;
4680 aParams.myDir = gp_Dir(theStep);
4681 aParams.myNodes.Clear();
4682 aParams.mySteps = new TColStd_HSequenceOfReal;
4684 for(i=1; i<=theNbSteps; i++)
4685 aParams.mySteps->Append(theStep.Magnitude());
4688 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4692 //=======================================================================
4693 //function : ExtrusionSweep
4695 //=======================================================================
4697 SMESH_MeshEditor::PGroupIDs
4698 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4699 ExtrusParam& theParams,
4700 TElemOfElemListMap& newElemsMap,
4701 const bool theMakeGroups,
4703 const double theTolerance)
4705 myLastCreatedElems.Clear();
4706 myLastCreatedNodes.Clear();
4708 // source elements for each generated one
4709 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4711 SMESHDS_Mesh* aMesh = GetMeshDS();
4713 int nbsteps = theParams.mySteps->Length();
4715 TNodeOfNodeListMap mapNewNodes;
4716 //TNodeOfNodeVecMap mapNewNodes;
4717 TElemOfVecOfNnlmiMap mapElemNewNodes;
4718 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4720 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4721 myMesh->NbFaces(ORDER_QUADRATIC) +
4722 myMesh->NbVolumes(ORDER_QUADRATIC) );
4724 TIDSortedElemSet::iterator itElem;
4725 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4726 // check element type
4727 const SMDS_MeshElement* elem = *itElem;
4728 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4731 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4732 newNodesItVec.reserve( elem->NbNodes() );
4734 // loop on elem nodes
4735 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4736 while ( itN->more() )
4738 // check if a node has been already sweeped
4739 const SMDS_MeshNode* node = cast2Node( itN->next() );
4740 TNodeOfNodeListMap::iterator nIt =
4741 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4742 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4743 if ( listNewNodes.empty() )
4747 // check if we are to create medium nodes between corner ones
4748 bool needMediumNodes = false;
4749 if ( isQuadraticMesh )
4751 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4752 while (it->more() && !needMediumNodes )
4754 const SMDS_MeshElement* invElem = it->next();
4755 if ( invElem != elem && !theElems.count( invElem )) continue;
4756 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4757 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4758 needMediumNodes = true;
4762 double coord[] = { node->X(), node->Y(), node->Z() };
4763 for ( int i = 0; i < nbsteps; i++ )
4765 if ( needMediumNodes ) // create a medium node
4767 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4768 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4769 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4770 if( theFlags & EXTRUSION_FLAG_SEW ) {
4771 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4772 theTolerance, theParams.myNodes);
4773 listNewNodes.push_back( newNode );
4776 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4777 myLastCreatedNodes.Append(newNode);
4778 srcNodes.Append( node );
4779 listNewNodes.push_back( newNode );
4782 // create a corner node
4783 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4784 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4785 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4786 if( theFlags & EXTRUSION_FLAG_SEW ) {
4787 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4788 theTolerance, theParams.myNodes);
4789 listNewNodes.push_back( newNode );
4792 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4793 myLastCreatedNodes.Append(newNode);
4794 srcNodes.Append( node );
4795 listNewNodes.push_back( newNode );
4799 newNodesItVec.push_back( nIt );
4801 // make new elements
4802 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4805 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4806 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4808 PGroupIDs newGroupIDs;
4809 if ( theMakeGroups )
4810 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4815 //=======================================================================
4816 //function : ExtrusionAlongTrack
4818 //=======================================================================
4819 SMESH_MeshEditor::Extrusion_Error
4820 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4821 SMESH_subMesh* theTrack,
4822 const SMDS_MeshNode* theN1,
4823 const bool theHasAngles,
4824 list<double>& theAngles,
4825 const bool theLinearVariation,
4826 const bool theHasRefPoint,
4827 const gp_Pnt& theRefPoint,
4828 const bool theMakeGroups)
4830 MESSAGE("ExtrusionAlongTrack");
4831 myLastCreatedElems.Clear();
4832 myLastCreatedNodes.Clear();
4835 std::list<double> aPrms;
4836 TIDSortedElemSet::iterator itElem;
4839 TopoDS_Edge aTrackEdge;
4840 TopoDS_Vertex aV1, aV2;
4842 SMDS_ElemIteratorPtr aItE;
4843 SMDS_NodeIteratorPtr aItN;
4844 SMDSAbs_ElementType aTypeE;
4846 TNodeOfNodeListMap mapNewNodes;
4849 aNbE = theElements.size();
4852 return EXTR_NO_ELEMENTS;
4854 // 1.1 Track Pattern
4857 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4859 aItE = pSubMeshDS->GetElements();
4860 while ( aItE->more() ) {
4861 const SMDS_MeshElement* pE = aItE->next();
4862 aTypeE = pE->GetType();
4863 // Pattern must contain links only
4864 if ( aTypeE != SMDSAbs_Edge )
4865 return EXTR_PATH_NOT_EDGE;
4868 list<SMESH_MeshEditor_PathPoint> fullList;
4870 const TopoDS_Shape& aS = theTrack->GetSubShape();
4871 // Sub-shape for the Pattern must be an Edge or Wire
4872 if( aS.ShapeType() == TopAbs_EDGE ) {
4873 aTrackEdge = TopoDS::Edge( aS );
4874 // the Edge must not be degenerated
4875 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4876 return EXTR_BAD_PATH_SHAPE;
4877 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4878 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4879 const SMDS_MeshNode* aN1 = aItN->next();
4880 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4881 const SMDS_MeshNode* aN2 = aItN->next();
4882 // starting node must be aN1 or aN2
4883 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4884 return EXTR_BAD_STARTING_NODE;
4885 aItN = pSubMeshDS->GetNodes();
4886 while ( aItN->more() ) {
4887 const SMDS_MeshNode* pNode = aItN->next();
4888 const SMDS_EdgePosition* pEPos =
4889 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4890 double aT = pEPos->GetUParameter();
4891 aPrms.push_back( aT );
4893 //Extrusion_Error err =
4894 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4895 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4896 list< SMESH_subMesh* > LSM;
4897 TopTools_SequenceOfShape Edges;
4898 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4899 while(itSM->more()) {
4900 SMESH_subMesh* SM = itSM->next();
4902 const TopoDS_Shape& aS = SM->GetSubShape();
4905 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4906 int startNid = theN1->GetID();
4907 TColStd_MapOfInteger UsedNums;
4909 int NbEdges = Edges.Length();
4911 for(; i<=NbEdges; i++) {
4913 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4914 for(; itLSM!=LSM.end(); itLSM++) {
4916 if(UsedNums.Contains(k)) continue;
4917 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4918 SMESH_subMesh* locTrack = *itLSM;
4919 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4920 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4921 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4922 const SMDS_MeshNode* aN1 = aItN->next();
4923 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4924 const SMDS_MeshNode* aN2 = aItN->next();
4925 // starting node must be aN1 or aN2
4926 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4927 // 2. Collect parameters on the track edge
4929 aItN = locMeshDS->GetNodes();
4930 while ( aItN->more() ) {
4931 const SMDS_MeshNode* pNode = aItN->next();
4932 const SMDS_EdgePosition* pEPos =
4933 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4934 double aT = pEPos->GetUParameter();
4935 aPrms.push_back( aT );
4937 list<SMESH_MeshEditor_PathPoint> LPP;
4938 //Extrusion_Error err =
4939 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4940 LLPPs.push_back(LPP);
4942 // update startN for search following egde
4943 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4944 else startNid = aN1->GetID();
4948 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4949 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4950 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4951 for(; itPP!=firstList.end(); itPP++) {
4952 fullList.push_back( *itPP );
4954 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4955 fullList.pop_back();
4957 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4958 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4959 itPP = currList.begin();
4960 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4961 gp_Dir D1 = PP1.Tangent();
4962 gp_Dir D2 = PP2.Tangent();
4963 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4964 (D1.Z()+D2.Z())/2 ) );
4965 PP1.SetTangent(Dnew);
4966 fullList.push_back(PP1);
4968 for(; itPP!=firstList.end(); itPP++) {
4969 fullList.push_back( *itPP );
4971 PP1 = fullList.back();
4972 fullList.pop_back();
4974 // if wire not closed
4975 fullList.push_back(PP1);
4979 return EXTR_BAD_PATH_SHAPE;
4982 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4983 theHasRefPoint, theRefPoint, theMakeGroups);
4987 //=======================================================================
4988 //function : ExtrusionAlongTrack
4990 //=======================================================================
4991 SMESH_MeshEditor::Extrusion_Error
4992 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4993 SMESH_Mesh* theTrack,
4994 const SMDS_MeshNode* theN1,
4995 const bool theHasAngles,
4996 list<double>& theAngles,
4997 const bool theLinearVariation,
4998 const bool theHasRefPoint,
4999 const gp_Pnt& theRefPoint,
5000 const bool theMakeGroups)
5002 myLastCreatedElems.Clear();
5003 myLastCreatedNodes.Clear();
5006 std::list<double> aPrms;
5007 TIDSortedElemSet::iterator itElem;
5010 TopoDS_Edge aTrackEdge;
5011 TopoDS_Vertex aV1, aV2;
5013 SMDS_ElemIteratorPtr aItE;
5014 SMDS_NodeIteratorPtr aItN;
5015 SMDSAbs_ElementType aTypeE;
5017 TNodeOfNodeListMap mapNewNodes;
5020 aNbE = theElements.size();
5023 return EXTR_NO_ELEMENTS;
5025 // 1.1 Track Pattern
5028 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5030 aItE = pMeshDS->elementsIterator();
5031 while ( aItE->more() ) {
5032 const SMDS_MeshElement* pE = aItE->next();
5033 aTypeE = pE->GetType();
5034 // Pattern must contain links only
5035 if ( aTypeE != SMDSAbs_Edge )
5036 return EXTR_PATH_NOT_EDGE;
5039 list<SMESH_MeshEditor_PathPoint> fullList;
5041 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5043 if( aS == SMESH_Mesh::PseudoShape() ) {
5044 //Mesh without shape
5045 const SMDS_MeshNode* currentNode = NULL;
5046 const SMDS_MeshNode* prevNode = theN1;
5047 std::vector<const SMDS_MeshNode*> aNodesList;
5048 aNodesList.push_back(theN1);
5049 int nbEdges = 0, conn=0;
5050 const SMDS_MeshElement* prevElem = NULL;
5051 const SMDS_MeshElement* currentElem = NULL;
5052 int totalNbEdges = theTrack->NbEdges();
5053 SMDS_ElemIteratorPtr nIt;
5056 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5057 return EXTR_BAD_STARTING_NODE;
5060 conn = nbEdgeConnectivity(theN1);
5062 return EXTR_PATH_NOT_EDGE;
5064 aItE = theN1->GetInverseElementIterator();
5065 prevElem = aItE->next();
5066 currentElem = prevElem;
5068 if(totalNbEdges == 1 ) {
5069 nIt = currentElem->nodesIterator();
5070 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5071 if(currentNode == prevNode)
5072 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5073 aNodesList.push_back(currentNode);
5075 nIt = currentElem->nodesIterator();
5076 while( nIt->more() ) {
5077 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5078 if(currentNode == prevNode)
5079 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5080 aNodesList.push_back(currentNode);
5082 //case of the closed mesh
5083 if(currentNode == theN1) {
5088 conn = nbEdgeConnectivity(currentNode);
5090 return EXTR_PATH_NOT_EDGE;
5091 }else if( conn == 1 && nbEdges > 0 ) {
5096 prevNode = currentNode;
5097 aItE = currentNode->GetInverseElementIterator();
5098 currentElem = aItE->next();
5099 if( currentElem == prevElem)
5100 currentElem = aItE->next();
5101 nIt = currentElem->nodesIterator();
5102 prevElem = currentElem;
5108 if(nbEdges != totalNbEdges)
5109 return EXTR_PATH_NOT_EDGE;
5111 TopTools_SequenceOfShape Edges;
5112 double x1,x2,y1,y2,z1,z2;
5113 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5114 int startNid = theN1->GetID();
5115 for(int i = 1; i < aNodesList.size(); i++) {
5116 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5117 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5118 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5119 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5120 list<SMESH_MeshEditor_PathPoint> LPP;
5122 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5123 LLPPs.push_back(LPP);
5124 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5125 else startNid = aNodesList[i-1]->GetID();
5129 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5130 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5131 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5132 for(; itPP!=firstList.end(); itPP++) {
5133 fullList.push_back( *itPP );
5136 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5137 SMESH_MeshEditor_PathPoint PP2;
5138 fullList.pop_back();
5140 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5141 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5142 itPP = currList.begin();
5143 PP2 = currList.front();
5144 gp_Dir D1 = PP1.Tangent();
5145 gp_Dir D2 = PP2.Tangent();
5146 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5147 (D1.Z()+D2.Z())/2 ) );
5148 PP1.SetTangent(Dnew);
5149 fullList.push_back(PP1);
5151 for(; itPP!=currList.end(); itPP++) {
5152 fullList.push_back( *itPP );
5154 PP1 = fullList.back();
5155 fullList.pop_back();
5157 fullList.push_back(PP1);
5159 } // Sub-shape for the Pattern must be an Edge or Wire
5160 else if( aS.ShapeType() == TopAbs_EDGE ) {
5161 aTrackEdge = TopoDS::Edge( aS );
5162 // the Edge must not be degenerated
5163 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5164 return EXTR_BAD_PATH_SHAPE;
5165 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5166 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5167 const SMDS_MeshNode* aN1 = aItN->next();
5168 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5169 const SMDS_MeshNode* aN2 = aItN->next();
5170 // starting node must be aN1 or aN2
5171 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5172 return EXTR_BAD_STARTING_NODE;
5173 aItN = pMeshDS->nodesIterator();
5174 while ( aItN->more() ) {
5175 const SMDS_MeshNode* pNode = aItN->next();
5176 if( pNode==aN1 || pNode==aN2 ) continue;
5177 const SMDS_EdgePosition* pEPos =
5178 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5179 double aT = pEPos->GetUParameter();
5180 aPrms.push_back( aT );
5182 //Extrusion_Error err =
5183 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5185 else if( aS.ShapeType() == TopAbs_WIRE ) {
5186 list< SMESH_subMesh* > LSM;
5187 TopTools_SequenceOfShape Edges;
5188 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5189 for(; eExp.More(); eExp.Next()) {
5190 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5191 if( BRep_Tool::Degenerated(E) ) continue;
5192 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5198 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5199 int startNid = theN1->GetID();
5200 TColStd_MapOfInteger UsedNums;
5201 int NbEdges = Edges.Length();
5203 for(; i<=NbEdges; i++) {
5205 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5206 for(; itLSM!=LSM.end(); itLSM++) {
5208 if(UsedNums.Contains(k)) continue;
5209 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5210 SMESH_subMesh* locTrack = *itLSM;
5211 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5212 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5213 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5214 const SMDS_MeshNode* aN1 = aItN->next();
5215 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5216 const SMDS_MeshNode* aN2 = aItN->next();
5217 // starting node must be aN1 or aN2
5218 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5219 // 2. Collect parameters on the track edge
5221 aItN = locMeshDS->GetNodes();
5222 while ( aItN->more() ) {
5223 const SMDS_MeshNode* pNode = aItN->next();
5224 const SMDS_EdgePosition* pEPos =
5225 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5226 double aT = pEPos->GetUParameter();
5227 aPrms.push_back( aT );
5229 list<SMESH_MeshEditor_PathPoint> LPP;
5230 //Extrusion_Error err =
5231 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5232 LLPPs.push_back(LPP);
5234 // update startN for search following egde
5235 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5236 else startNid = aN1->GetID();
5240 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5241 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5242 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5243 for(; itPP!=firstList.end(); itPP++) {
5244 fullList.push_back( *itPP );
5246 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5247 fullList.pop_back();
5249 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5250 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5251 itPP = currList.begin();
5252 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5253 gp_Dir D1 = PP1.Tangent();
5254 gp_Dir D2 = PP2.Tangent();
5255 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5256 (D1.Z()+D2.Z())/2 ) );
5257 PP1.SetTangent(Dnew);
5258 fullList.push_back(PP1);
5260 for(; itPP!=currList.end(); itPP++) {
5261 fullList.push_back( *itPP );
5263 PP1 = fullList.back();
5264 fullList.pop_back();
5266 // if wire not closed
5267 fullList.push_back(PP1);
5271 return EXTR_BAD_PATH_SHAPE;
5274 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5275 theHasRefPoint, theRefPoint, theMakeGroups);
5279 //=======================================================================
5280 //function : MakeEdgePathPoints
5281 //purpose : auxilary for ExtrusionAlongTrack
5282 //=======================================================================
5283 SMESH_MeshEditor::Extrusion_Error
5284 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5285 const TopoDS_Edge& aTrackEdge,
5287 list<SMESH_MeshEditor_PathPoint>& LPP)
5289 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5291 aTolVec2=aTolVec*aTolVec;
5293 TopoDS_Vertex aV1, aV2;
5294 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5295 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5296 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5297 // 2. Collect parameters on the track edge
5298 aPrms.push_front( aT1 );
5299 aPrms.push_back( aT2 );
5302 if( FirstIsStart ) {
5313 SMESH_MeshEditor_PathPoint aPP;
5314 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5315 std::list<double>::iterator aItD = aPrms.begin();
5316 for(; aItD != aPrms.end(); ++aItD) {
5320 aC3D->D1( aT, aP3D, aVec );
5321 aL2 = aVec.SquareMagnitude();
5322 if ( aL2 < aTolVec2 )
5323 return EXTR_CANT_GET_TANGENT;
5324 gp_Dir aTgt( aVec );
5326 aPP.SetTangent( aTgt );
5327 aPP.SetParameter( aT );
5334 //=======================================================================
5335 //function : MakeExtrElements
5336 //purpose : auxilary for ExtrusionAlongTrack
5337 //=======================================================================
5338 SMESH_MeshEditor::Extrusion_Error
5339 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5340 list<SMESH_MeshEditor_PathPoint>& fullList,
5341 const bool theHasAngles,
5342 list<double>& theAngles,
5343 const bool theLinearVariation,
5344 const bool theHasRefPoint,
5345 const gp_Pnt& theRefPoint,
5346 const bool theMakeGroups)
5348 MESSAGE("MakeExtrElements");
5349 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5350 int aNbTP = fullList.size();
5351 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5353 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5354 LinearAngleVariation(aNbTP-1, theAngles);
5356 vector<double> aAngles( aNbTP );
5358 for(; j<aNbTP; ++j) {
5361 if ( theHasAngles ) {
5363 std::list<double>::iterator aItD = theAngles.begin();
5364 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5366 aAngles[j] = anAngle;
5369 // fill vector of path points with angles
5370 //aPPs.resize(fullList.size());
5372 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5373 for(; itPP!=fullList.end(); itPP++) {
5375 SMESH_MeshEditor_PathPoint PP = *itPP;
5376 PP.SetAngle(aAngles[j]);
5380 TNodeOfNodeListMap mapNewNodes;
5381 TElemOfVecOfNnlmiMap mapElemNewNodes;
5382 TElemOfElemListMap newElemsMap;
5383 TIDSortedElemSet::iterator itElem;
5386 SMDSAbs_ElementType aTypeE;
5387 // source elements for each generated one
5388 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5390 // 3. Center of rotation aV0
5391 gp_Pnt aV0 = theRefPoint;
5393 if ( !theHasRefPoint ) {
5395 aGC.SetCoord( 0.,0.,0. );
5397 itElem = theElements.begin();
5398 for ( ; itElem != theElements.end(); itElem++ ) {
5399 const SMDS_MeshElement* elem = *itElem;
5401 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5402 while ( itN->more() ) {
5403 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5408 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5409 list<const SMDS_MeshNode*> aLNx;
5410 mapNewNodes[node] = aLNx;
5412 gp_XYZ aXYZ( aX, aY, aZ );
5420 } // if (!theHasRefPoint) {
5421 mapNewNodes.clear();
5423 // 4. Processing the elements
5424 SMESHDS_Mesh* aMesh = GetMeshDS();
5426 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5427 // check element type
5428 const SMDS_MeshElement* elem = *itElem;
5429 aTypeE = elem->GetType();
5430 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5433 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5434 newNodesItVec.reserve( elem->NbNodes() );
5436 // loop on elem nodes
5438 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5439 while ( itN->more() )
5442 // check if a node has been already processed
5443 const SMDS_MeshNode* node =
5444 static_cast<const SMDS_MeshNode*>( itN->next() );
5445 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5446 if ( nIt == mapNewNodes.end() ) {
5447 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5448 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5451 aX = node->X(); aY = node->Y(); aZ = node->Z();
5453 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5454 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5455 gp_Ax1 anAx1, anAxT1T0;
5456 gp_Dir aDT1x, aDT0x, aDT1T0;
5461 aPN0.SetCoord(aX, aY, aZ);
5463 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5465 aDT0x= aPP0.Tangent();
5466 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5468 for ( j = 1; j < aNbTP; ++j ) {
5469 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5471 aDT1x = aPP1.Tangent();
5472 aAngle1x = aPP1.Angle();
5474 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5476 gp_Vec aV01x( aP0x, aP1x );
5477 aTrsf.SetTranslation( aV01x );
5480 aV1x = aV0x.Transformed( aTrsf );
5481 aPN1 = aPN0.Transformed( aTrsf );
5483 // rotation 1 [ T1,T0 ]
5484 aAngleT1T0=-aDT1x.Angle( aDT0x );
5485 if (fabs(aAngleT1T0) > aTolAng) {
5487 anAxT1T0.SetLocation( aV1x );
5488 anAxT1T0.SetDirection( aDT1T0 );
5489 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5491 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5495 if ( theHasAngles ) {
5496 anAx1.SetLocation( aV1x );
5497 anAx1.SetDirection( aDT1x );
5498 aTrsfRot.SetRotation( anAx1, aAngle1x );
5500 aPN1 = aPN1.Transformed( aTrsfRot );
5504 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5505 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5506 // create additional node
5507 double x = ( aPN1.X() + aPN0.X() )/2.;
5508 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5509 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5510 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5511 myLastCreatedNodes.Append(newNode);
5512 srcNodes.Append( node );
5513 listNewNodes.push_back( newNode );
5518 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5519 myLastCreatedNodes.Append(newNode);
5520 srcNodes.Append( node );
5521 listNewNodes.push_back( newNode );
5531 // if current elem is quadratic and current node is not medium
5532 // we have to check - may be it is needed to insert additional nodes
5533 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5534 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5535 if(listNewNodes.size()==aNbTP-1) {
5536 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5537 gp_XYZ P(node->X(), node->Y(), node->Z());
5538 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5540 for(i=0; i<aNbTP-1; i++) {
5541 const SMDS_MeshNode* N = *it;
5542 double x = ( N->X() + P.X() )/2.;
5543 double y = ( N->Y() + P.Y() )/2.;
5544 double z = ( N->Z() + P.Z() )/2.;
5545 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5546 srcNodes.Append( node );
5547 myLastCreatedNodes.Append(newN);
5550 P = gp_XYZ(N->X(),N->Y(),N->Z());
5552 listNewNodes.clear();
5553 for(i=0; i<2*(aNbTP-1); i++) {
5554 listNewNodes.push_back(aNodes[i]);
5560 newNodesItVec.push_back( nIt );
5562 // make new elements
5563 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5564 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5565 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5568 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5570 if ( theMakeGroups )
5571 generateGroups( srcNodes, srcElems, "extruded");
5577 //=======================================================================
5578 //function : LinearAngleVariation
5579 //purpose : auxilary for ExtrusionAlongTrack
5580 //=======================================================================
5581 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5582 list<double>& Angles)
5584 int nbAngles = Angles.size();
5585 if( nbSteps > nbAngles ) {
5586 vector<double> theAngles(nbAngles);
5587 list<double>::iterator it = Angles.begin();
5589 for(; it!=Angles.end(); it++) {
5591 theAngles[i] = (*it);
5594 double rAn2St = double( nbAngles ) / double( nbSteps );
5595 double angPrev = 0, angle;
5596 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5597 double angCur = rAn2St * ( iSt+1 );
5598 double angCurFloor = floor( angCur );
5599 double angPrevFloor = floor( angPrev );
5600 if ( angPrevFloor == angCurFloor )
5601 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5603 int iP = int( angPrevFloor );
5604 double angPrevCeil = ceil(angPrev);
5605 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5607 int iC = int( angCurFloor );
5608 if ( iC < nbAngles )
5609 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5611 iP = int( angPrevCeil );
5613 angle += theAngles[ iC ];
5615 res.push_back(angle);
5620 for(; it!=res.end(); it++)
5621 Angles.push_back( *it );
5626 //================================================================================
5628 * \brief Move or copy theElements applying theTrsf to their nodes
5629 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5630 * \param theTrsf - transformation to apply
5631 * \param theCopy - if true, create translated copies of theElems
5632 * \param theMakeGroups - if true and theCopy, create translated groups
5633 * \param theTargetMesh - mesh to copy translated elements into
5634 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5636 //================================================================================
5638 SMESH_MeshEditor::PGroupIDs
5639 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5640 const gp_Trsf& theTrsf,
5642 const bool theMakeGroups,
5643 SMESH_Mesh* theTargetMesh)
5645 myLastCreatedElems.Clear();
5646 myLastCreatedNodes.Clear();
5648 bool needReverse = false;
5649 string groupPostfix;
5650 switch ( theTrsf.Form() ) {
5652 MESSAGE("gp_PntMirror");
5654 groupPostfix = "mirrored";
5657 MESSAGE("gp_Ax1Mirror");
5658 groupPostfix = "mirrored";
5661 MESSAGE("gp_Ax2Mirror");
5663 groupPostfix = "mirrored";
5666 MESSAGE("gp_Rotation");
5667 groupPostfix = "rotated";
5669 case gp_Translation:
5670 MESSAGE("gp_Translation");
5671 groupPostfix = "translated";
5674 MESSAGE("gp_Scale");
5675 groupPostfix = "scaled";
5677 case gp_CompoundTrsf: // different scale by axis
5678 MESSAGE("gp_CompoundTrsf");
5679 groupPostfix = "scaled";
5683 needReverse = false;
5684 groupPostfix = "transformed";
5687 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5688 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5689 SMESHDS_Mesh* aMesh = GetMeshDS();
5692 // map old node to new one
5693 TNodeNodeMap nodeMap;
5695 // elements sharing moved nodes; those of them which have all
5696 // nodes mirrored but are not in theElems are to be reversed
5697 TIDSortedElemSet inverseElemSet;
5699 // source elements for each generated one
5700 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5702 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5703 TIDSortedElemSet orphanNode;
5705 if ( theElems.empty() ) // transform the whole mesh
5708 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5709 while ( eIt->more() ) theElems.insert( eIt->next() );
5711 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5712 while ( nIt->more() )
5714 const SMDS_MeshNode* node = nIt->next();
5715 if ( node->NbInverseElements() == 0)
5716 orphanNode.insert( node );
5720 // loop on elements to transform nodes : first orphan nodes then elems
5721 TIDSortedElemSet::iterator itElem;
5722 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5723 for (int i=0; i<2; i++)
5724 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5725 const SMDS_MeshElement* elem = *itElem;
5729 // loop on elem nodes
5730 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5731 while ( itN->more() ) {
5733 const SMDS_MeshNode* node = cast2Node( itN->next() );
5734 // check if a node has been already transformed
5735 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5736 nodeMap.insert( make_pair ( node, node ));
5737 if ( !n2n_isnew.second )
5741 coord[0] = node->X();
5742 coord[1] = node->Y();
5743 coord[2] = node->Z();
5744 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5745 if ( theTargetMesh ) {
5746 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5747 n2n_isnew.first->second = newNode;
5748 myLastCreatedNodes.Append(newNode);
5749 srcNodes.Append( node );
5751 else if ( theCopy ) {
5752 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5753 n2n_isnew.first->second = newNode;
5754 myLastCreatedNodes.Append(newNode);
5755 srcNodes.Append( node );
5758 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5759 // node position on shape becomes invalid
5760 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5761 ( SMDS_SpacePosition::originSpacePosition() );
5764 // keep inverse elements
5765 if ( !theCopy && !theTargetMesh && needReverse ) {
5766 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5767 while ( invElemIt->more() ) {
5768 const SMDS_MeshElement* iel = invElemIt->next();
5769 inverseElemSet.insert( iel );
5775 // either create new elements or reverse mirrored ones
5776 if ( !theCopy && !needReverse && !theTargetMesh )
5779 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5780 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5781 theElems.insert( *invElemIt );
5783 // Replicate or reverse elements
5785 std::vector<int> iForw;
5786 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5788 const SMDS_MeshElement* elem = *itElem;
5789 if ( !elem ) continue;
5791 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5792 int nbNodes = elem->NbNodes();
5793 if ( geomType == SMDSGeom_NONE ) continue; // node
5795 switch ( geomType ) {
5797 case SMDSGeom_POLYGON: // ---------------------- polygon
5799 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5801 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5802 while (itN->more()) {
5803 const SMDS_MeshNode* node =
5804 static_cast<const SMDS_MeshNode*>(itN->next());
5805 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5806 if (nodeMapIt == nodeMap.end())
5807 break; // not all nodes transformed
5809 // reverse mirrored faces and volumes
5810 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5812 poly_nodes[iNode] = (*nodeMapIt).second;
5816 if ( iNode != nbNodes )
5817 continue; // not all nodes transformed
5819 if ( theTargetMesh ) {
5820 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5821 srcElems.Append( elem );
5823 else if ( theCopy ) {
5824 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5825 srcElems.Append( elem );
5828 aMesh->ChangePolygonNodes(elem, poly_nodes);
5833 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5835 const SMDS_VtkVolume* aPolyedre =
5836 dynamic_cast<const SMDS_VtkVolume*>( elem );
5838 MESSAGE("Warning: bad volumic element");
5842 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5843 vector<int> quantities; quantities.reserve( nbNodes );
5845 bool allTransformed = true;
5846 int nbFaces = aPolyedre->NbFaces();
5847 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5848 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5849 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5850 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5851 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5852 if (nodeMapIt == nodeMap.end()) {
5853 allTransformed = false; // not all nodes transformed
5855 poly_nodes.push_back((*nodeMapIt).second);
5857 if ( needReverse && allTransformed )
5858 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5860 quantities.push_back(nbFaceNodes);
5862 if ( !allTransformed )
5863 continue; // not all nodes transformed
5865 if ( theTargetMesh ) {
5866 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5867 srcElems.Append( elem );
5869 else if ( theCopy ) {
5870 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5871 srcElems.Append( elem );
5874 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5879 case SMDSGeom_BALL: // -------------------- Ball
5881 if ( !theCopy && !theTargetMesh ) continue;
5883 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5884 if (nodeMapIt == nodeMap.end())
5885 continue; // not all nodes transformed
5887 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5888 if ( theTargetMesh ) {
5889 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5890 srcElems.Append( elem );
5893 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5894 srcElems.Append( elem );
5899 default: // ----------------------- Regular elements
5901 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5902 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5903 const std::vector<int>& i = needReverse ? iRev : iForw;
5905 // find transformed nodes
5906 vector<const SMDS_MeshNode*> nodes(nbNodes);
5908 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5909 while ( itN->more() ) {
5910 const SMDS_MeshNode* node =
5911 static_cast<const SMDS_MeshNode*>( itN->next() );
5912 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5913 if ( nodeMapIt == nodeMap.end() )
5914 break; // not all nodes transformed
5915 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5917 if ( iNode != nbNodes )
5918 continue; // not all nodes transformed
5920 if ( theTargetMesh ) {
5921 if ( SMDS_MeshElement* copy =
5922 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5923 myLastCreatedElems.Append( copy );
5924 srcElems.Append( elem );
5927 else if ( theCopy ) {
5928 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5929 srcElems.Append( elem );
5932 // reverse element as it was reversed by transformation
5934 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5936 } // switch ( geomType )
5938 } // loop on elements
5940 PGroupIDs newGroupIDs;
5942 if ( ( theMakeGroups && theCopy ) ||
5943 ( theMakeGroups && theTargetMesh ) )
5944 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5949 //=======================================================================
5951 * \brief Create groups of elements made during transformation
5952 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5953 * \param elemGens - elements making corresponding myLastCreatedElems
5954 * \param postfix - to append to names of new groups
5956 //=======================================================================
5958 SMESH_MeshEditor::PGroupIDs
5959 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5960 const SMESH_SequenceOfElemPtr& elemGens,
5961 const std::string& postfix,
5962 SMESH_Mesh* targetMesh)
5964 PGroupIDs newGroupIDs( new list<int> );
5965 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5967 // Sort existing groups by types and collect their names
5969 // to store an old group and a generated new one
5970 typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5971 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5972 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5974 set< string > groupNames;
5976 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5977 if ( !groupIt->more() ) return newGroupIDs;
5979 int newGroupID = mesh->GetGroupIds().back()+1;
5980 while ( groupIt->more() )
5982 SMESH_Group * group = groupIt->next();
5983 if ( !group ) continue;
5984 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5985 if ( !groupDS || groupDS->IsEmpty() ) continue;
5986 groupNames.insert( group->GetName() );
5987 groupDS->SetStoreName( group->GetName() );
5988 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5989 groupDS->GetType() );
5990 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5991 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5994 // Loop on nodes and elements to add them in new groups
5996 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5998 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5999 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6000 if ( gens.Length() != elems.Length() )
6001 throw SALOME_Exception(LOCALIZED("invalid args"));
6003 // loop on created elements
6004 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6006 const SMDS_MeshElement* sourceElem = gens( iElem );
6007 if ( !sourceElem ) {
6008 MESSAGE("generateGroups(): NULL source element");
6011 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6012 if ( groupsOldNew.empty() ) { // no groups of this type at all
6013 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6014 ++iElem; // skip all elements made by sourceElem
6017 // collect all elements made by sourceElem
6018 list< const SMDS_MeshElement* > resultElems;
6019 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6020 if ( resElem != sourceElem )
6021 resultElems.push_back( resElem );
6022 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6023 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6024 if ( resElem != sourceElem )
6025 resultElems.push_back( resElem );
6027 // add resultElems to groups made by ones the sourceElem belongs to
6028 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6029 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6031 SMESHDS_GroupBase* oldGroup = gOldNew->first;
6032 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6034 // fill in a new group
6035 SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6036 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6037 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6038 newGroup.Add( *resElemIt );
6041 } // loop on created elements
6042 }// loop on nodes and elements
6044 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6046 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6048 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6049 SMESHDS_Group* newGroupDS = orderedOldNewGroups[i]->second;
6050 if ( newGroupDS->IsEmpty() )
6052 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6057 string name = oldGroupDS->GetStoreName();
6058 if ( !targetMesh ) {
6062 while ( !groupNames.insert( name ).second ) // name exists
6063 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6065 newGroupDS->SetStoreName( name.c_str() );
6067 // make a SMESH_Groups
6068 mesh->AddGroup( newGroupDS );
6069 newGroupIDs->push_back( newGroupDS->GetID() );
6072 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6079 //================================================================================
6081 * \brief Return list of group of nodes close to each other within theTolerance
6082 * Search among theNodes or in the whole mesh if theNodes is empty using
6083 * an Octree algorithm
6085 //================================================================================
6087 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6088 const double theTolerance,
6089 TListOfListOfNodes & theGroupsOfNodes)
6091 myLastCreatedElems.Clear();
6092 myLastCreatedNodes.Clear();
6094 if ( theNodes.empty() )
6095 { // get all nodes in the mesh
6096 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6097 while ( nIt->more() )
6098 theNodes.insert( theNodes.end(),nIt->next());
6101 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6105 //=======================================================================
6107 * \brief Implementation of search for the node closest to point
6109 //=======================================================================
6111 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6113 //---------------------------------------------------------------------
6115 * \brief Constructor
6117 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6119 myMesh = ( SMESHDS_Mesh* ) theMesh;
6121 TIDSortedNodeSet nodes;
6123 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6124 while ( nIt->more() )
6125 nodes.insert( nodes.end(), nIt->next() );
6127 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6129 // get max size of a leaf box
6130 SMESH_OctreeNode* tree = myOctreeNode;
6131 while ( !tree->isLeaf() )
6133 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6137 myHalfLeafSize = tree->maxSize() / 2.;
6140 //---------------------------------------------------------------------
6142 * \brief Move node and update myOctreeNode accordingly
6144 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6146 myOctreeNode->UpdateByMoveNode( node, toPnt );
6147 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6150 //---------------------------------------------------------------------
6152 * \brief Do it's job
6154 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6156 map<double, const SMDS_MeshNode*> dist2Nodes;
6157 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6158 if ( !dist2Nodes.empty() )
6159 return dist2Nodes.begin()->second;
6160 list<const SMDS_MeshNode*> nodes;
6161 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6163 double minSqDist = DBL_MAX;
6164 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6166 // sort leafs by their distance from thePnt
6167 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6168 TDistTreeMap treeMap;
6169 list< SMESH_OctreeNode* > treeList;
6170 list< SMESH_OctreeNode* >::iterator trIt;
6171 treeList.push_back( myOctreeNode );
6173 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6174 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6175 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6177 SMESH_OctreeNode* tree = *trIt;
6178 if ( !tree->isLeaf() ) // put children to the queue
6180 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6181 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6182 while ( cIt->more() )
6183 treeList.push_back( cIt->next() );
6185 else if ( tree->NbNodes() ) // put a tree to the treeMap
6187 const Bnd_B3d& box = *tree->getBox();
6188 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6189 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6190 if ( !it_in.second ) // not unique distance to box center
6191 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6194 // find distance after which there is no sense to check tree's
6195 double sqLimit = DBL_MAX;
6196 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6197 if ( treeMap.size() > 5 ) {
6198 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6199 const Bnd_B3d& box = *closestTree->getBox();
6200 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6201 sqLimit = limit * limit;
6203 // get all nodes from trees
6204 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6205 if ( sqDist_tree->first > sqLimit )
6207 SMESH_OctreeNode* tree = sqDist_tree->second;
6208 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6211 // find closest among nodes
6212 minSqDist = DBL_MAX;
6213 const SMDS_MeshNode* closestNode = 0;
6214 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6215 for ( ; nIt != nodes.end(); ++nIt ) {
6216 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6217 if ( minSqDist > sqDist ) {
6225 //---------------------------------------------------------------------
6229 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6231 //---------------------------------------------------------------------
6233 * \brief Return the node tree
6235 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6238 SMESH_OctreeNode* myOctreeNode;
6239 SMESHDS_Mesh* myMesh;
6240 double myHalfLeafSize; // max size of a leaf box
6243 //=======================================================================
6245 * \brief Return SMESH_NodeSearcher
6247 //=======================================================================
6249 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6251 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6254 // ========================================================================
6255 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6257 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6258 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6259 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6261 //=======================================================================
6263 * \brief Octal tree of bounding boxes of elements
6265 //=======================================================================
6267 class ElementBndBoxTree : public SMESH_Octree
6271 ElementBndBoxTree(const SMDS_Mesh& mesh,
6272 SMDSAbs_ElementType elemType,
6273 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6274 double tolerance = NodeRadius );
6275 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6276 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6277 void getElementsInSphere ( const gp_XYZ& center,
6278 const double radius, TIDSortedElemSet& foundElems);
6279 size_t getSize() { return std::max( _size, _elements.size() ); }
6280 ~ElementBndBoxTree();
6283 ElementBndBoxTree():_size(0) {}
6284 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6285 void buildChildrenData();
6286 Bnd_B3d* buildRootBox();
6288 //!< Bounding box of element
6289 struct ElementBox : public Bnd_B3d
6291 const SMDS_MeshElement* _element;
6292 int _refCount; // an ElementBox can be included in several tree branches
6293 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6295 vector< ElementBox* > _elements;
6299 //================================================================================
6301 * \brief ElementBndBoxTree creation
6303 //================================================================================
6305 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6306 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6308 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6309 _elements.reserve( nbElems );
6311 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6312 while ( elemIt->more() )
6313 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6318 //================================================================================
6322 //================================================================================
6324 ElementBndBoxTree::~ElementBndBoxTree()
6326 for ( int i = 0; i < _elements.size(); ++i )
6327 if ( --_elements[i]->_refCount <= 0 )
6328 delete _elements[i];
6331 //================================================================================
6333 * \brief Return the maximal box
6335 //================================================================================
6337 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6339 Bnd_B3d* box = new Bnd_B3d;
6340 for ( int i = 0; i < _elements.size(); ++i )
6341 box->Add( *_elements[i] );
6345 //================================================================================
6347 * \brief Redistrubute element boxes among children
6349 //================================================================================
6351 void ElementBndBoxTree::buildChildrenData()
6353 for ( int i = 0; i < _elements.size(); ++i )
6355 for (int j = 0; j < 8; j++)
6357 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6359 _elements[i]->_refCount++;
6360 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6363 _elements[i]->_refCount--;
6365 _size = _elements.size();
6366 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6368 for (int j = 0; j < 8; j++)
6370 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6371 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6372 child->myIsLeaf = true;
6374 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6375 SMESHUtils::CompactVector( child->_elements );
6379 //================================================================================
6381 * \brief Return elements which can include the point
6383 //================================================================================
6385 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6386 TIDSortedElemSet& foundElems)
6388 if ( getBox()->IsOut( point.XYZ() ))
6393 for ( int i = 0; i < _elements.size(); ++i )
6394 if ( !_elements[i]->IsOut( point.XYZ() ))
6395 foundElems.insert( _elements[i]->_element );
6399 for (int i = 0; i < 8; i++)
6400 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6404 //================================================================================
6406 * \brief Return elements which can be intersected by the line
6408 //================================================================================
6410 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6411 TIDSortedElemSet& foundElems)
6413 if ( getBox()->IsOut( line ))
6418 for ( int i = 0; i < _elements.size(); ++i )
6419 if ( !_elements[i]->IsOut( line ))
6420 foundElems.insert( _elements[i]->_element );
6424 for (int i = 0; i < 8; i++)
6425 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6429 //================================================================================
6431 * \brief Return elements from leaves intersecting the sphere
6433 //================================================================================
6435 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6436 const double radius,
6437 TIDSortedElemSet& foundElems)
6439 if ( getBox()->IsOut( center, radius ))
6444 for ( int i = 0; i < _elements.size(); ++i )
6445 if ( !_elements[i]->IsOut( center, radius ))
6446 foundElems.insert( _elements[i]->_element );
6450 for (int i = 0; i < 8; i++)
6451 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6455 //================================================================================
6457 * \brief Construct the element box
6459 //================================================================================
6461 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6465 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6466 while ( nIt->more() )
6467 Add( SMESH_TNodeXYZ( nIt->next() ));
6468 Enlarge( tolerance );
6473 //=======================================================================
6475 * \brief Implementation of search for the elements by point and
6476 * of classification of point in 2D mesh
6478 //=======================================================================
6480 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6482 SMESHDS_Mesh* _mesh;
6483 SMDS_ElemIteratorPtr _meshPartIt;
6484 ElementBndBoxTree* _ebbTree;
6485 SMESH_NodeSearcherImpl* _nodeSearcher;
6486 SMDSAbs_ElementType _elementType;
6488 bool _outerFacesFound;
6489 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6491 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6492 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6493 ~SMESH_ElementSearcherImpl()
6495 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6496 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6498 virtual int FindElementsByPoint(const gp_Pnt& point,
6499 SMDSAbs_ElementType type,
6500 vector< const SMDS_MeshElement* >& foundElements);
6501 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6502 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6503 SMDSAbs_ElementType type );
6505 void GetElementsNearLine( const gp_Ax1& line,
6506 SMDSAbs_ElementType type,
6507 vector< const SMDS_MeshElement* >& foundElems);
6508 double getTolerance();
6509 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6510 const double tolerance, double & param);
6511 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6512 bool isOuterBoundary(const SMDS_MeshElement* face) const
6514 return _outerFaces.empty() || _outerFaces.count(face);
6516 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6518 const SMDS_MeshElement* _face;
6520 bool _coincides; //!< the line lays in face plane
6521 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6522 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6524 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6527 TIDSortedElemSet _faces;
6528 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6529 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6533 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6535 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6536 << ", _coincides="<<i._coincides << ")";
6539 //=======================================================================
6541 * \brief define tolerance for search
6543 //=======================================================================
6545 double SMESH_ElementSearcherImpl::getTolerance()
6547 if ( _tolerance < 0 )
6549 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6552 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6554 double boxSize = _nodeSearcher->getTree()->maxSize();
6555 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6557 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6559 double boxSize = _ebbTree->maxSize();
6560 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6562 if ( _tolerance == 0 )
6564 // define tolerance by size of a most complex element
6565 int complexType = SMDSAbs_Volume;
6566 while ( complexType > SMDSAbs_All &&
6567 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6569 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6571 if ( complexType == int( SMDSAbs_Node ))
6573 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6575 if ( meshInfo.NbNodes() > 2 )
6576 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6580 SMDS_ElemIteratorPtr elemIt =
6581 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6582 const SMDS_MeshElement* elem = elemIt->next();
6583 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6584 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6586 while ( nodeIt->more() )
6588 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6589 elemSize = max( dist, elemSize );
6592 _tolerance = 1e-4 * elemSize;
6598 //================================================================================
6600 * \brief Find intersection of the line and an edge of face and return parameter on line
6602 //================================================================================
6604 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6605 const SMDS_MeshElement* face,
6612 GeomAPI_ExtremaCurveCurve anExtCC;
6613 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6615 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6616 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6618 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6619 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6620 anExtCC.Init( lineCurve, edge);
6621 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6623 Quantity_Parameter pl, pe;
6624 anExtCC.LowerDistanceParameters( pl, pe );
6626 if ( ++nbInts == 2 )
6630 if ( nbInts > 0 ) param /= nbInts;
6633 //================================================================================
6635 * \brief Find all faces belonging to the outer boundary of mesh
6637 //================================================================================
6639 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6641 if ( _outerFacesFound ) return;
6643 // Collect all outer faces by passing from one outer face to another via their links
6644 // and BTW find out if there are internal faces at all.
6646 // checked links and links where outer boundary meets internal one
6647 set< SMESH_TLink > visitedLinks, seamLinks;
6649 // links to treat with already visited faces sharing them
6650 list < TFaceLink > startLinks;
6652 // load startLinks with the first outerFace
6653 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6654 _outerFaces.insert( outerFace );
6656 TIDSortedElemSet emptySet;
6657 while ( !startLinks.empty() )
6659 const SMESH_TLink& link = startLinks.front()._link;
6660 TIDSortedElemSet& faces = startLinks.front()._faces;
6662 outerFace = *faces.begin();
6663 // find other faces sharing the link
6664 const SMDS_MeshElement* f;
6665 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6668 // select another outer face among the found
6669 const SMDS_MeshElement* outerFace2 = 0;
6670 if ( faces.size() == 2 )
6672 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6674 else if ( faces.size() > 2 )
6676 seamLinks.insert( link );
6678 // link direction within the outerFace
6679 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6680 SMESH_TNodeXYZ( link.node2()));
6681 int i1 = outerFace->GetNodeIndex( link.node1() );
6682 int i2 = outerFace->GetNodeIndex( link.node2() );
6683 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6684 if ( rev ) n1n2.Reverse();
6686 gp_XYZ ofNorm, fNorm;
6687 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6689 // direction from the link inside outerFace
6690 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6691 // sort all other faces by angle with the dirInOF
6692 map< double, const SMDS_MeshElement* > angle2Face;
6693 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6694 for ( ; face != faces.end(); ++face )
6696 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6698 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6699 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6700 if ( angle < 0 ) angle += 2. * M_PI;
6701 angle2Face.insert( make_pair( angle, *face ));
6703 if ( !angle2Face.empty() )
6704 outerFace2 = angle2Face.begin()->second;
6707 // store the found outer face and add its links to continue seaching from
6710 _outerFaces.insert( outerFace );
6711 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6712 for ( int i = 0; i < nbNodes; ++i )
6714 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6715 if ( visitedLinks.insert( link2 ).second )
6716 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6719 startLinks.pop_front();
6721 _outerFacesFound = true;
6723 if ( !seamLinks.empty() )
6725 // There are internal boundaries touching the outher one,
6726 // find all faces of internal boundaries in order to find
6727 // faces of boundaries of holes, if any.
6732 _outerFaces.clear();
6736 //=======================================================================
6738 * \brief Find elements of given type where the given point is IN or ON.
6739 * Returns nb of found elements and elements them-selves.
6741 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6743 //=======================================================================
6745 int SMESH_ElementSearcherImpl::
6746 FindElementsByPoint(const gp_Pnt& point,
6747 SMDSAbs_ElementType type,
6748 vector< const SMDS_MeshElement* >& foundElements)
6750 foundElements.clear();
6752 double tolerance = getTolerance();
6754 // =================================================================================
6755 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6757 if ( !_nodeSearcher )
6758 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6760 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6761 if ( !closeNode ) return foundElements.size();
6763 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6764 return foundElements.size(); // to far from any node
6766 if ( type == SMDSAbs_Node )
6768 foundElements.push_back( closeNode );
6772 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6773 while ( elemIt->more() )
6774 foundElements.push_back( elemIt->next() );
6777 // =================================================================================
6778 else // elements more complex than 0D
6780 if ( !_ebbTree || _elementType != type )
6782 if ( _ebbTree ) delete _ebbTree;
6783 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6785 TIDSortedElemSet suspectElems;
6786 _ebbTree->getElementsNearPoint( point, suspectElems );
6787 TIDSortedElemSet::iterator elem = suspectElems.begin();
6788 for ( ; elem != suspectElems.end(); ++elem )
6789 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6790 foundElements.push_back( *elem );
6792 return foundElements.size();
6795 //=======================================================================
6797 * \brief Find an element of given type most close to the given point
6799 * WARNING: Only face search is implemeneted so far
6801 //=======================================================================
6803 const SMDS_MeshElement*
6804 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6805 SMDSAbs_ElementType type )
6807 const SMDS_MeshElement* closestElem = 0;
6809 if ( type == SMDSAbs_Face )
6811 if ( !_ebbTree || _elementType != type )
6813 if ( _ebbTree ) delete _ebbTree;
6814 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6816 TIDSortedElemSet suspectElems;
6817 _ebbTree->getElementsNearPoint( point, suspectElems );
6819 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6821 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6822 _ebbTree->getBox()->CornerMax() );
6824 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6825 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6827 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6828 while ( suspectElems.empty() )
6830 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6834 double minDist = std::numeric_limits<double>::max();
6835 multimap< double, const SMDS_MeshElement* > dist2face;
6836 TIDSortedElemSet::iterator elem = suspectElems.begin();
6837 for ( ; elem != suspectElems.end(); ++elem )
6839 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6841 if ( dist < minDist + 1e-10)
6844 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6847 if ( !dist2face.empty() )
6849 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6850 closestElem = d2f->second;
6851 // if there are several elements at the same distance, select one
6852 // with GC closest to the point
6853 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6854 double minDistToGC = 0;
6855 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6857 if ( minDistToGC == 0 )
6860 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6861 TXyzIterator(), gc ) / closestElem->NbNodes();
6862 minDistToGC = point.SquareDistance( gc );
6865 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6866 TXyzIterator(), gc ) / d2f->second->NbNodes();
6867 double d = point.SquareDistance( gc );
6868 if ( d < minDistToGC )
6871 closestElem = d2f->second;
6874 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6875 // <<closestElem->GetID() << " DIST " << minDist << endl;
6880 // NOT IMPLEMENTED SO FAR
6886 //================================================================================
6888 * \brief Classify the given point in the closed 2D mesh
6890 //================================================================================
6892 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6894 double tolerance = getTolerance();
6895 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6897 if ( _ebbTree ) delete _ebbTree;
6898 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6900 // Algo: analyse transition of a line starting at the point through mesh boundary;
6901 // try three lines parallel to axis of the coordinate system and perform rough
6902 // analysis. If solution is not clear perform thorough analysis.
6904 const int nbAxes = 3;
6905 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6906 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6907 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6908 multimap< int, int > nbInt2Axis; // to find the simplest case
6909 for ( int axis = 0; axis < nbAxes; ++axis )
6911 gp_Ax1 lineAxis( point, axisDir[axis]);
6912 gp_Lin line ( lineAxis );
6914 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6915 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6917 // Intersect faces with the line
6919 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6920 TIDSortedElemSet::iterator face = suspectFaces.begin();
6921 for ( ; face != suspectFaces.end(); ++face )
6925 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6926 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6928 // perform intersection
6929 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6930 if ( !intersection.IsDone() )
6932 if ( intersection.IsInQuadric() )
6934 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6936 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6938 gp_Pnt intersectionPoint = intersection.Point(1);
6939 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6940 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6943 // Analyse intersections roughly
6945 int nbInter = u2inters.size();
6949 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6950 if ( nbInter == 1 ) // not closed mesh
6951 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6953 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6956 if ( (f<0) == (l<0) )
6959 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6960 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6961 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6964 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6966 if ( _outerFacesFound ) break; // pass to thorough analysis
6968 } // three attempts - loop on CS axes
6970 // Analyse intersections thoroughly.
6971 // We make two loops maximum, on the first one we only exclude touching intersections,
6972 // on the second, if situation is still unclear, we gather and use information on
6973 // position of faces (internal or outer). If faces position is already gathered,
6974 // we make the second loop right away.
6976 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6978 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6979 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6981 int axis = nb_axis->second;
6982 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6984 gp_Ax1 lineAxis( point, axisDir[axis]);
6985 gp_Lin line ( lineAxis );
6987 // add tangent intersections to u2inters
6989 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6990 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6991 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6992 u2inters.insert(make_pair( param, *tgtInt ));
6993 tangentInters[ axis ].clear();
6995 // Count intersections before and after the point excluding touching ones.
6996 // If hasPositionInfo we count intersections of outer boundary only
6998 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6999 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7000 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7001 bool ok = ! u_int1->second._coincides;
7002 while ( ok && u_int1 != u2inters.end() )
7004 double u = u_int1->first;
7005 bool touchingInt = false;
7006 if ( ++u_int2 != u2inters.end() )
7008 // skip intersections at the same point (if the line passes through edge or node)
7010 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7016 // skip tangent intersections
7018 const SMDS_MeshElement* prevFace = u_int1->second._face;
7019 while ( ok && u_int2->second._coincides )
7021 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7027 ok = ( u_int2 != u2inters.end() );
7032 // skip intersections at the same point after tangent intersections
7035 double u2 = u_int2->first;
7037 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7043 // decide if we skipped a touching intersection
7044 if ( nbSamePnt + nbTgt > 0 )
7046 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7047 map< double, TInters >::iterator u_int = u_int1;
7048 for ( ; u_int != u_int2; ++u_int )
7050 if ( u_int->second._coincides ) continue;
7051 double dot = u_int->second._faceNorm * line.Direction();
7052 if ( dot > maxDot ) maxDot = dot;
7053 if ( dot < minDot ) minDot = dot;
7055 touchingInt = ( minDot*maxDot < 0 );
7060 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7071 u_int1 = u_int2; // to next intersection
7073 } // loop on intersections with one line
7077 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7080 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7083 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7084 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7086 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7089 if ( (f<0) == (l<0) )
7092 if ( hasPositionInfo )
7093 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7095 } // loop on intersections of the tree lines - thorough analysis
7097 if ( !hasPositionInfo )
7099 // gather info on faces position - is face in the outer boundary or not
7100 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7101 findOuterBoundary( u2inters.begin()->second._face );
7104 } // two attempts - with and w/o faces position info in the mesh
7106 return TopAbs_UNKNOWN;
7109 //=======================================================================
7111 * \brief Return elements possibly intersecting the line
7113 //=======================================================================
7115 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7116 SMDSAbs_ElementType type,
7117 vector< const SMDS_MeshElement* >& foundElems)
7119 if ( !_ebbTree || _elementType != type )
7121 if ( _ebbTree ) delete _ebbTree;
7122 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7124 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7125 _ebbTree->getElementsNearLine( line, suspectFaces );
7126 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7129 //=======================================================================
7131 * \brief Return SMESH_ElementSearcher
7133 //=======================================================================
7135 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7137 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7140 //=======================================================================
7142 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7144 //=======================================================================
7146 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7148 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7151 //=======================================================================
7153 * \brief Return true if the point is IN or ON of the element
7155 //=======================================================================
7157 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7159 if ( element->GetType() == SMDSAbs_Volume)
7161 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7164 // get ordered nodes
7166 vector< gp_XYZ > xyz;
7167 vector<const SMDS_MeshNode*> nodeList;
7169 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7170 if ( element->IsQuadratic() ) {
7171 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7172 nodeIt = f->interlacedNodesElemIterator();
7173 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7174 nodeIt = e->interlacedNodesElemIterator();
7176 while ( nodeIt->more() )
7178 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7179 xyz.push_back( SMESH_TNodeXYZ(node) );
7180 nodeList.push_back(node);
7183 int i, nbNodes = element->NbNodes();
7185 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7187 // compute face normal
7188 gp_Vec faceNorm(0,0,0);
7189 xyz.push_back( xyz.front() );
7190 nodeList.push_back( nodeList.front() );
7191 for ( i = 0; i < nbNodes; ++i )
7193 gp_Vec edge1( xyz[i+1], xyz[i]);
7194 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7195 faceNorm += edge1 ^ edge2;
7197 double normSize = faceNorm.Magnitude();
7198 if ( normSize <= tol )
7200 // degenerated face: point is out if it is out of all face edges
7201 for ( i = 0; i < nbNodes; ++i )
7203 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7204 if ( !IsOut( &edge, point, tol ))
7209 faceNorm /= normSize;
7211 // check if the point lays on face plane
7212 gp_Vec n2p( xyz[0], point );
7213 if ( fabs( n2p * faceNorm ) > tol )
7214 return true; // not on face plane
7216 // check if point is out of face boundary:
7217 // define it by closest transition of a ray point->infinity through face boundary
7218 // on the face plane.
7219 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7220 // to find intersections of the ray with the boundary.
7222 gp_Vec plnNorm = ray ^ faceNorm;
7223 normSize = plnNorm.Magnitude();
7224 if ( normSize <= tol ) return false; // point coincides with the first node
7225 plnNorm /= normSize;
7226 // for each node of the face, compute its signed distance to the plane
7227 vector<double> dist( nbNodes + 1);
7228 for ( i = 0; i < nbNodes; ++i )
7230 gp_Vec n2p( xyz[i], point );
7231 dist[i] = n2p * plnNorm;
7233 dist.back() = dist.front();
7234 // find the closest intersection
7236 double rClosest, distClosest = 1e100;;
7238 for ( i = 0; i < nbNodes; ++i )
7241 if ( fabs( dist[i]) < tol )
7243 else if ( fabs( dist[i+1]) < tol )
7245 else if ( dist[i] * dist[i+1] < 0 )
7246 r = dist[i] / ( dist[i] - dist[i+1] );
7248 continue; // no intersection
7249 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7250 gp_Vec p2int ( point, pInt);
7251 if ( p2int * ray > -tol ) // right half-space
7253 double intDist = p2int.SquareMagnitude();
7254 if ( intDist < distClosest )
7259 distClosest = intDist;
7264 return true; // no intesections - out
7266 // analyse transition
7267 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7268 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7269 gp_Vec p2int ( point, pClosest );
7270 bool out = (edgeNorm * p2int) < -tol;
7271 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7274 // ray pass through a face node; analyze transition through an adjacent edge
7275 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7276 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7277 gp_Vec edgeAdjacent( p1, p2 );
7278 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7279 bool out2 = (edgeNorm2 * p2int) < -tol;
7281 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7282 return covexCorner ? (out || out2) : (out && out2);
7284 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7286 // point is out of edge if it is NOT ON any straight part of edge
7287 // (we consider quadratic edge as being composed of two straight parts)
7288 for ( i = 1; i < nbNodes; ++i )
7290 gp_Vec edge( xyz[i-1], xyz[i]);
7291 gp_Vec n1p ( xyz[i-1], point);
7292 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7295 gp_Vec n2p( xyz[i], point );
7296 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7298 return false; // point is ON this part
7302 // Node or 0D element -------------------------------------------------------------------------
7304 gp_Vec n2p ( xyz[0], point );
7305 return n2p.Magnitude() <= tol;
7310 //=======================================================================
7314 // Position of a point relative to a segment
7318 // VERTEX 1 o----ON-----> VERTEX 2
7322 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7323 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7327 int _index; // index of vertex or segment
7329 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7330 bool operator < (const PointPos& other ) const
7332 if ( _name == other._name )
7333 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7334 return _name < other._name;
7338 //================================================================================
7340 * \brief Return of a point relative to a segment
7341 * \param point2D - the point to analyze position of
7342 * \param xyVec - end points of segments
7343 * \param index0 - 0-based index of the first point of segment
7344 * \param posToFindOut - flags of positions to detect
7345 * \retval PointPos - point position
7347 //================================================================================
7349 PointPos getPointPosition( const gp_XY& point2D,
7350 const gp_XY* segEnds,
7351 const int index0 = 0,
7352 const int posToFindOut = POS_ALL)
7354 const gp_XY& p1 = segEnds[ index0 ];
7355 const gp_XY& p2 = segEnds[ index0+1 ];
7356 const gp_XY grad = p2 - p1;
7358 if ( posToFindOut & POS_VERTEX )
7360 // check if the point2D is at "vertex 1" zone
7361 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7362 p1.Y() + grad.X() ) };
7363 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7364 return PointPos( POS_VERTEX, index0 );
7366 // check if the point2D is at "vertex 2" zone
7367 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7368 p2.Y() + grad.X() ) };
7369 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7370 return PointPos( POS_VERTEX, index0 + 1);
7372 double edgeEquation =
7373 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7374 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7378 //=======================================================================
7380 * \brief Return minimal distance from a point to a face
7382 * Currently we ignore non-planarity and 2nd order of face
7384 //=======================================================================
7386 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7387 const gp_Pnt& point )
7389 double badDistance = -1;
7390 if ( !face ) return badDistance;
7392 // coordinates of nodes (medium nodes, if any, ignored)
7393 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7394 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7395 xyz.resize( face->NbCornerNodes()+1 );
7397 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7398 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7400 gp_Vec OZ ( xyz[0], xyz[1] );
7401 gp_Vec OX ( xyz[0], xyz[2] );
7402 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7404 if ( xyz.size() < 4 ) return badDistance;
7405 OZ = gp_Vec ( xyz[0], xyz[2] );
7406 OX = gp_Vec ( xyz[0], xyz[3] );
7410 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7412 catch ( Standard_Failure ) {
7415 trsf.SetTransformation( tgtCS );
7417 // move all the nodes to 2D
7418 vector<gp_XY> xy( xyz.size() );
7419 for ( size_t i = 0;i < xyz.size()-1; ++i )
7421 gp_XYZ p3d = xyz[i];
7422 trsf.Transforms( p3d );
7423 xy[i].SetCoord( p3d.X(), p3d.Z() );
7425 xyz.back() = xyz.front();
7426 xy.back() = xy.front();
7428 // // move the point in 2D
7429 gp_XYZ tmpPnt = point.XYZ();
7430 trsf.Transforms( tmpPnt );
7431 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7433 // loop on segments of the face to analyze point position ralative to the face
7434 set< PointPos > pntPosSet;
7435 for ( size_t i = 1; i < xy.size(); ++i )
7437 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7438 pntPosSet.insert( pos );
7442 PointPos pos = *pntPosSet.begin();
7443 // cout << "Face " << face->GetID() << " DIST: ";
7444 switch ( pos._name )
7447 // point is most close to a segment
7448 gp_Vec p0p1( point, xyz[ pos._index ] );
7449 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7451 double projDist = p0p1 * p1p2; // distance projected to the segment
7452 gp_Vec projVec = p1p2 * projDist;
7453 gp_Vec distVec = p0p1 - projVec;
7454 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7455 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7456 return distVec.Magnitude();
7459 // point is inside the face
7460 double distToFacePlane = tmpPnt.Y();
7461 // cout << distToFacePlane << ", INSIDE " << endl;
7462 return Abs( distToFacePlane );
7465 // point is most close to a node
7466 gp_Vec distVec( point, xyz[ pos._index ]);
7467 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7468 return distVec.Magnitude();
7474 //=======================================================================
7475 //function : SimplifyFace
7477 //=======================================================================
7478 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7479 vector<const SMDS_MeshNode *>& poly_nodes,
7480 vector<int>& quantities) const
7482 int nbNodes = faceNodes.size();
7487 set<const SMDS_MeshNode*> nodeSet;
7489 // get simple seq of nodes
7490 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7491 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7492 int iSimple = 0, nbUnique = 0;
7494 simpleNodes[iSimple++] = faceNodes[0];
7496 for (int iCur = 1; iCur < nbNodes; iCur++) {
7497 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7498 simpleNodes[iSimple++] = faceNodes[iCur];
7499 if (nodeSet.insert( faceNodes[iCur] ).second)
7503 int nbSimple = iSimple;
7504 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7514 bool foundLoop = (nbSimple > nbUnique);
7517 set<const SMDS_MeshNode*> loopSet;
7518 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7519 const SMDS_MeshNode* n = simpleNodes[iSimple];
7520 if (!loopSet.insert( n ).second) {
7524 int iC = 0, curLast = iSimple;
7525 for (; iC < curLast; iC++) {
7526 if (simpleNodes[iC] == n) break;
7528 int loopLen = curLast - iC;
7530 // create sub-element
7532 quantities.push_back(loopLen);
7533 for (; iC < curLast; iC++) {
7534 poly_nodes.push_back(simpleNodes[iC]);
7537 // shift the rest nodes (place from the first loop position)
7538 for (iC = curLast + 1; iC < nbSimple; iC++) {
7539 simpleNodes[iC - loopLen] = simpleNodes[iC];
7541 nbSimple -= loopLen;
7544 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7545 } // while (foundLoop)
7549 quantities.push_back(iSimple);
7550 for (int i = 0; i < iSimple; i++)
7551 poly_nodes.push_back(simpleNodes[i]);
7557 //=======================================================================
7558 //function : MergeNodes
7559 //purpose : In each group, the cdr of nodes are substituted by the first one
7561 //=======================================================================
7563 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7565 MESSAGE("MergeNodes");
7566 myLastCreatedElems.Clear();
7567 myLastCreatedNodes.Clear();
7569 SMESHDS_Mesh* aMesh = GetMeshDS();
7571 TNodeNodeMap nodeNodeMap; // node to replace - new node
7572 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7573 list< int > rmElemIds, rmNodeIds;
7575 // Fill nodeNodeMap and elems
7577 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7578 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7579 list<const SMDS_MeshNode*>& nodes = *grIt;
7580 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7581 const SMDS_MeshNode* nToKeep = *nIt;
7582 //MESSAGE("node to keep " << nToKeep->GetID());
7583 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7584 const SMDS_MeshNode* nToRemove = *nIt;
7585 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7586 if ( nToRemove != nToKeep ) {
7587 //MESSAGE(" node to remove " << nToRemove->GetID());
7588 rmNodeIds.push_back( nToRemove->GetID() );
7589 AddToSameGroups( nToKeep, nToRemove, aMesh );
7592 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7593 while ( invElemIt->more() ) {
7594 const SMDS_MeshElement* elem = invElemIt->next();
7599 // Change element nodes or remove an element
7601 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7602 for ( ; eIt != elems.end(); eIt++ ) {
7603 const SMDS_MeshElement* elem = *eIt;
7604 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7605 int nbNodes = elem->NbNodes();
7606 int aShapeId = FindShape( elem );
7608 set<const SMDS_MeshNode*> nodeSet;
7609 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7610 int iUnique = 0, iCur = 0, nbRepl = 0;
7611 vector<int> iRepl( nbNodes );
7613 // get new seq of nodes
7614 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7615 while ( itN->more() ) {
7616 const SMDS_MeshNode* n =
7617 static_cast<const SMDS_MeshNode*>( itN->next() );
7619 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7620 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7622 // BUG 0020185: begin
7624 bool stopRecur = false;
7625 set<const SMDS_MeshNode*> nodesRecur;
7626 nodesRecur.insert(n);
7627 while (!stopRecur) {
7628 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7629 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7630 n = (*nnIt_i).second;
7631 if (!nodesRecur.insert(n).second) {
7632 // error: recursive dependancy
7642 curNodes[ iCur ] = n;
7643 bool isUnique = nodeSet.insert( n ).second;
7645 uniqueNodes[ iUnique++ ] = n;
7647 iRepl[ nbRepl++ ] = iCur;
7651 // Analyse element topology after replacement
7654 int nbUniqueNodes = nodeSet.size();
7655 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7656 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7657 // Polygons and Polyhedral volumes
7658 if (elem->IsPoly()) {
7660 if (elem->GetType() == SMDSAbs_Face) {
7662 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7664 for (; inode < nbNodes; inode++) {
7665 face_nodes[inode] = curNodes[inode];
7668 vector<const SMDS_MeshNode *> polygons_nodes;
7669 vector<int> quantities;
7670 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7673 for (int iface = 0; iface < nbNew; iface++) {
7674 int nbNodes = quantities[iface];
7675 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7676 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7677 poly_nodes[ii] = polygons_nodes[inode];
7679 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7680 myLastCreatedElems.Append(newElem);
7682 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7685 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7686 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7687 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7689 if (nbNew > 0) quid = nbNew - 1;
7690 vector<int> newquant(quantities.begin()+quid, quantities.end());
7691 const SMDS_MeshElement* newElem = 0;
7692 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7693 myLastCreatedElems.Append(newElem);
7694 if ( aShapeId && newElem )
7695 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7696 rmElemIds.push_back(elem->GetID());
7699 rmElemIds.push_back(elem->GetID());
7703 else if (elem->GetType() == SMDSAbs_Volume) {
7704 // Polyhedral volume
7705 if (nbUniqueNodes < 4) {
7706 rmElemIds.push_back(elem->GetID());
7709 // each face has to be analyzed in order to check volume validity
7710 const SMDS_VtkVolume* aPolyedre =
7711 dynamic_cast<const SMDS_VtkVolume*>( elem );
7713 int nbFaces = aPolyedre->NbFaces();
7715 vector<const SMDS_MeshNode *> poly_nodes;
7716 vector<int> quantities;
7718 for (int iface = 1; iface <= nbFaces; iface++) {
7719 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7720 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7722 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7723 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7724 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7725 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7726 faceNode = (*nnIt).second;
7728 faceNodes[inode - 1] = faceNode;
7731 SimplifyFace(faceNodes, poly_nodes, quantities);
7734 if (quantities.size() > 3) {
7735 // to be done: remove coincident faces
7738 if (quantities.size() > 3)
7740 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7741 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7742 const SMDS_MeshElement* newElem = 0;
7743 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7744 myLastCreatedElems.Append(newElem);
7745 if ( aShapeId && newElem )
7746 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7747 rmElemIds.push_back(elem->GetID());
7751 rmElemIds.push_back(elem->GetID());
7762 // TODO not all the possible cases are solved. Find something more generic?
7763 switch ( nbNodes ) {
7764 case 2: ///////////////////////////////////// EDGE
7765 isOk = false; break;
7766 case 3: ///////////////////////////////////// TRIANGLE
7767 isOk = false; break;
7769 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7771 else { //////////////////////////////////// QUADRANGLE
7772 if ( nbUniqueNodes < 3 )
7774 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7775 isOk = false; // opposite nodes stick
7776 //MESSAGE("isOk " << isOk);
7779 case 6: ///////////////////////////////////// PENTAHEDRON
7780 if ( nbUniqueNodes == 4 ) {
7781 // ---------------------------------> tetrahedron
7783 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7784 // all top nodes stick: reverse a bottom
7785 uniqueNodes[ 0 ] = curNodes [ 1 ];
7786 uniqueNodes[ 1 ] = curNodes [ 0 ];
7788 else if (nbRepl == 3 &&
7789 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7790 // all bottom nodes stick: set a top before
7791 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7792 uniqueNodes[ 0 ] = curNodes [ 3 ];
7793 uniqueNodes[ 1 ] = curNodes [ 4 ];
7794 uniqueNodes[ 2 ] = curNodes [ 5 ];
7796 else if (nbRepl == 4 &&
7797 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7798 // a lateral face turns into a line: reverse a bottom
7799 uniqueNodes[ 0 ] = curNodes [ 1 ];
7800 uniqueNodes[ 1 ] = curNodes [ 0 ];
7805 else if ( nbUniqueNodes == 5 ) {
7806 // PENTAHEDRON --------------------> 2 tetrahedrons
7807 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7808 // a bottom node sticks with a linked top one
7810 SMDS_MeshElement* newElem =
7811 aMesh->AddVolume(curNodes[ 3 ],
7814 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7815 myLastCreatedElems.Append(newElem);
7817 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7818 // 2. : reverse a bottom
7819 uniqueNodes[ 0 ] = curNodes [ 1 ];
7820 uniqueNodes[ 1 ] = curNodes [ 0 ];
7830 if(elem->IsQuadratic()) { // Quadratic quadrangle
7842 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7845 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7847 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7848 uniqueNodes[0] = curNodes[0];
7849 uniqueNodes[1] = curNodes[2];
7850 uniqueNodes[2] = curNodes[3];
7851 uniqueNodes[3] = curNodes[5];
7852 uniqueNodes[4] = curNodes[6];
7853 uniqueNodes[5] = curNodes[7];
7856 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7857 uniqueNodes[0] = curNodes[0];
7858 uniqueNodes[1] = curNodes[1];
7859 uniqueNodes[2] = curNodes[2];
7860 uniqueNodes[3] = curNodes[4];
7861 uniqueNodes[4] = curNodes[5];
7862 uniqueNodes[5] = curNodes[6];
7865 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7866 uniqueNodes[0] = curNodes[1];
7867 uniqueNodes[1] = curNodes[2];
7868 uniqueNodes[2] = curNodes[3];
7869 uniqueNodes[3] = curNodes[5];
7870 uniqueNodes[4] = curNodes[6];
7871 uniqueNodes[5] = curNodes[0];
7874 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7875 uniqueNodes[0] = curNodes[0];
7876 uniqueNodes[1] = curNodes[1];
7877 uniqueNodes[2] = curNodes[3];
7878 uniqueNodes[3] = curNodes[4];
7879 uniqueNodes[4] = curNodes[6];
7880 uniqueNodes[5] = curNodes[7];
7883 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7884 uniqueNodes[0] = curNodes[0];
7885 uniqueNodes[1] = curNodes[2];
7886 uniqueNodes[2] = curNodes[3];
7887 uniqueNodes[3] = curNodes[1];
7888 uniqueNodes[4] = curNodes[6];
7889 uniqueNodes[5] = curNodes[7];
7892 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7893 uniqueNodes[0] = curNodes[0];
7894 uniqueNodes[1] = curNodes[1];
7895 uniqueNodes[2] = curNodes[2];
7896 uniqueNodes[3] = curNodes[4];
7897 uniqueNodes[4] = curNodes[5];
7898 uniqueNodes[5] = curNodes[7];
7901 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7902 uniqueNodes[0] = curNodes[0];
7903 uniqueNodes[1] = curNodes[1];
7904 uniqueNodes[2] = curNodes[3];
7905 uniqueNodes[3] = curNodes[4];
7906 uniqueNodes[4] = curNodes[2];
7907 uniqueNodes[5] = curNodes[7];
7910 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7911 uniqueNodes[0] = curNodes[0];
7912 uniqueNodes[1] = curNodes[1];
7913 uniqueNodes[2] = curNodes[2];
7914 uniqueNodes[3] = curNodes[4];
7915 uniqueNodes[4] = curNodes[5];
7916 uniqueNodes[5] = curNodes[3];
7921 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7924 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7928 //////////////////////////////////// HEXAHEDRON
7930 SMDS_VolumeTool hexa (elem);
7931 hexa.SetExternalNormal();
7932 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7933 //////////////////////// HEX ---> 1 tetrahedron
7934 for ( int iFace = 0; iFace < 6; iFace++ ) {
7935 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7936 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7937 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7938 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7939 // one face turns into a point ...
7940 int iOppFace = hexa.GetOppFaceIndex( iFace );
7941 ind = hexa.GetFaceNodesIndices( iOppFace );
7943 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7944 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7947 if ( nbStick == 1 ) {
7948 // ... and the opposite one - into a triangle.
7950 ind = hexa.GetFaceNodesIndices( iFace );
7951 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7958 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7959 //////////////////////// HEX ---> 1 prism
7960 int nbTria = 0, iTria[3];
7961 const int *ind; // indices of face nodes
7962 // look for triangular faces
7963 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7964 ind = hexa.GetFaceNodesIndices( iFace );
7965 TIDSortedNodeSet faceNodes;
7966 for ( iCur = 0; iCur < 4; iCur++ )
7967 faceNodes.insert( curNodes[ind[iCur]] );
7968 if ( faceNodes.size() == 3 )
7969 iTria[ nbTria++ ] = iFace;
7971 // check if triangles are opposite
7972 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7975 // set nodes of the bottom triangle
7976 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7978 for ( iCur = 0; iCur < 4; iCur++ )
7979 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7980 indB.push_back( ind[iCur] );
7981 if ( !hexa.IsForward() )
7982 std::swap( indB[0], indB[2] );
7983 for ( iCur = 0; iCur < 3; iCur++ )
7984 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7985 // set nodes of the top triangle
7986 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7987 for ( iCur = 0; iCur < 3; ++iCur )
7988 for ( int j = 0; j < 4; ++j )
7989 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7991 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7997 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7998 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7999 for ( int iFace = 0; iFace < 6; iFace++ ) {
8000 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8001 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8002 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8003 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8004 // one face turns into a point ...
8005 int iOppFace = hexa.GetOppFaceIndex( iFace );
8006 ind = hexa.GetFaceNodesIndices( iOppFace );
8008 iUnique = 2; // reverse a tetrahedron 1 bottom
8009 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8010 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8012 else if ( iUnique >= 0 )
8013 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8015 if ( nbStick == 0 ) {
8016 // ... and the opposite one is a quadrangle
8018 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8019 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8022 SMDS_MeshElement* newElem =
8023 aMesh->AddVolume(curNodes[ind[ 0 ]],
8026 curNodes[indTop[ 0 ]]);
8027 myLastCreatedElems.Append(newElem);
8029 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8036 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8037 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8038 // find indices of quad and tri faces
8039 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8040 for ( iFace = 0; iFace < 6; iFace++ ) {
8041 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8043 for ( iCur = 0; iCur < 4; iCur++ )
8044 nodeSet.insert( curNodes[ind[ iCur ]] );
8045 nbUniqueNodes = nodeSet.size();
8046 if ( nbUniqueNodes == 3 )
8047 iTriFace[ nbTri++ ] = iFace;
8048 else if ( nbUniqueNodes == 4 )
8049 iQuadFace[ nbQuad++ ] = iFace;
8051 if (nbQuad == 2 && nbTri == 4 &&
8052 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8053 // 2 opposite quadrangles stuck with a diagonal;
8054 // sample groups of merged indices: (0-4)(2-6)
8055 // --------------------------------------------> 2 tetrahedrons
8056 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8057 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8058 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8059 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8060 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8061 // stuck with 0-2 diagonal
8069 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8070 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8071 // stuck with 1-3 diagonal
8083 uniqueNodes[ 0 ] = curNodes [ i0 ];
8084 uniqueNodes[ 1 ] = curNodes [ i1d ];
8085 uniqueNodes[ 2 ] = curNodes [ i3d ];
8086 uniqueNodes[ 3 ] = curNodes [ i0t ];
8089 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8093 myLastCreatedElems.Append(newElem);
8095 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8098 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8099 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8100 // --------------------------------------------> prism
8101 // find 2 opposite triangles
8103 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8104 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8105 // find indices of kept and replaced nodes
8106 // and fill unique nodes of 2 opposite triangles
8107 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8108 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8109 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8110 // fill unique nodes
8113 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8114 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8115 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8117 // iCur of a linked node of the opposite face (make normals co-directed):
8118 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8119 // check that correspondent corners of triangles are linked
8120 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8123 uniqueNodes[ iUnique ] = n;
8124 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8133 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8136 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8143 } // switch ( nbNodes )
8145 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8147 if ( isOk ) { // the elem remains valid after sticking nodes
8148 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8150 // Change nodes of polyedre
8151 const SMDS_VtkVolume* aPolyedre =
8152 dynamic_cast<const SMDS_VtkVolume*>( elem );
8154 int nbFaces = aPolyedre->NbFaces();
8156 vector<const SMDS_MeshNode *> poly_nodes;
8157 vector<int> quantities (nbFaces);
8159 for (int iface = 1; iface <= nbFaces; iface++) {
8160 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8161 quantities[iface - 1] = nbFaceNodes;
8163 for (inode = 1; inode <= nbFaceNodes; inode++) {
8164 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8166 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8167 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8168 curNode = (*nnIt).second;
8170 poly_nodes.push_back(curNode);
8173 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8176 else // replace non-polyhedron elements
8178 const SMDSAbs_ElementType etyp = elem->GetType();
8179 const int elemId = elem->GetID();
8180 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8181 uniqueNodes.resize(nbUniqueNodes);
8183 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8185 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8186 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8187 if ( sm && newElem )
8188 sm->AddElement( newElem );
8189 if ( elem != newElem )
8190 ReplaceElemInGroups( elem, newElem, aMesh );
8194 // Remove invalid regular element or invalid polygon
8195 rmElemIds.push_back( elem->GetID() );
8198 } // loop on elements
8200 // Remove bad elements, then equal nodes (order important)
8202 Remove( rmElemIds, false );
8203 Remove( rmNodeIds, true );
8208 // ========================================================
8209 // class : SortableElement
8210 // purpose : allow sorting elements basing on their nodes
8211 // ========================================================
8212 class SortableElement : public set <const SMDS_MeshElement*>
8216 SortableElement( const SMDS_MeshElement* theElem )
8219 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8220 while ( nodeIt->more() )
8221 this->insert( nodeIt->next() );
8224 const SMDS_MeshElement* Get() const
8227 void Set(const SMDS_MeshElement* e) const
8232 mutable const SMDS_MeshElement* myElem;
8235 //=======================================================================
8236 //function : FindEqualElements
8237 //purpose : Return list of group of elements built on the same nodes.
8238 // Search among theElements or in the whole mesh if theElements is empty
8239 //=======================================================================
8241 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8242 TListOfListOfElementsID & theGroupsOfElementsID)
8244 myLastCreatedElems.Clear();
8245 myLastCreatedNodes.Clear();
8247 typedef map< SortableElement, int > TMapOfNodeSet;
8248 typedef list<int> TGroupOfElems;
8250 if ( theElements.empty() )
8251 { // get all elements in the mesh
8252 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8253 while ( eIt->more() )
8254 theElements.insert( theElements.end(), eIt->next());
8257 vector< TGroupOfElems > arrayOfGroups;
8258 TGroupOfElems groupOfElems;
8259 TMapOfNodeSet mapOfNodeSet;
8261 TIDSortedElemSet::iterator elemIt = theElements.begin();
8262 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8263 const SMDS_MeshElement* curElem = *elemIt;
8264 SortableElement SE(curElem);
8267 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8268 if( !(pp.second) ) {
8269 TMapOfNodeSet::iterator& itSE = pp.first;
8270 ind = (*itSE).second;
8271 arrayOfGroups[ind].push_back(curElem->GetID());
8274 groupOfElems.clear();
8275 groupOfElems.push_back(curElem->GetID());
8276 arrayOfGroups.push_back(groupOfElems);
8281 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8282 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8283 groupOfElems = *groupIt;
8284 if ( groupOfElems.size() > 1 ) {
8285 groupOfElems.sort();
8286 theGroupsOfElementsID.push_back(groupOfElems);
8291 //=======================================================================
8292 //function : MergeElements
8293 //purpose : In each given group, substitute all elements by the first one.
8294 //=======================================================================
8296 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8298 myLastCreatedElems.Clear();
8299 myLastCreatedNodes.Clear();
8301 typedef list<int> TListOfIDs;
8302 TListOfIDs rmElemIds; // IDs of elems to remove
8304 SMESHDS_Mesh* aMesh = GetMeshDS();
8306 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8307 while ( groupsIt != theGroupsOfElementsID.end() ) {
8308 TListOfIDs& aGroupOfElemID = *groupsIt;
8309 aGroupOfElemID.sort();
8310 int elemIDToKeep = aGroupOfElemID.front();
8311 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8312 aGroupOfElemID.pop_front();
8313 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8314 while ( idIt != aGroupOfElemID.end() ) {
8315 int elemIDToRemove = *idIt;
8316 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8317 // add the kept element in groups of removed one (PAL15188)
8318 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8319 rmElemIds.push_back( elemIDToRemove );
8325 Remove( rmElemIds, false );
8328 //=======================================================================
8329 //function : MergeEqualElements
8330 //purpose : Remove all but one of elements built on the same nodes.
8331 //=======================================================================
8333 void SMESH_MeshEditor::MergeEqualElements()
8335 TIDSortedElemSet aMeshElements; /* empty input ==
8336 to merge equal elements in the whole mesh */
8337 TListOfListOfElementsID aGroupsOfElementsID;
8338 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8339 MergeElements(aGroupsOfElementsID);
8342 //=======================================================================
8343 //function : FindFaceInSet
8344 //purpose : Return a face having linked nodes n1 and n2 and which is
8345 // - not in avoidSet,
8346 // - in elemSet provided that !elemSet.empty()
8347 // i1 and i2 optionally returns indices of n1 and n2
8348 //=======================================================================
8350 const SMDS_MeshElement*
8351 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8352 const SMDS_MeshNode* n2,
8353 const TIDSortedElemSet& elemSet,
8354 const TIDSortedElemSet& avoidSet,
8360 const SMDS_MeshElement* face = 0;
8362 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8363 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8364 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8366 //MESSAGE("in while ( invElemIt->more() && !face )");
8367 const SMDS_MeshElement* elem = invElemIt->next();
8368 if (avoidSet.count( elem ))
8370 if ( !elemSet.empty() && !elemSet.count( elem ))
8373 i1 = elem->GetNodeIndex( n1 );
8374 // find a n2 linked to n1
8375 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8376 for ( int di = -1; di < 2 && !face; di += 2 )
8378 i2 = (i1+di+nbN) % nbN;
8379 if ( elem->GetNode( i2 ) == n2 )
8382 if ( !face && elem->IsQuadratic())
8384 // analysis for quadratic elements using all nodes
8385 const SMDS_VtkFace* F =
8386 dynamic_cast<const SMDS_VtkFace*>(elem);
8387 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8388 // use special nodes iterator
8389 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8390 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8391 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8393 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8394 if ( n1 == prevN && n2 == n )
8398 else if ( n2 == prevN && n1 == n )
8400 face = elem; swap( i1, i2 );
8406 if ( n1ind ) *n1ind = i1;
8407 if ( n2ind ) *n2ind = i2;
8411 //=======================================================================
8412 //function : findAdjacentFace
8414 //=======================================================================
8416 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8417 const SMDS_MeshNode* n2,
8418 const SMDS_MeshElement* elem)
8420 TIDSortedElemSet elemSet, avoidSet;
8422 avoidSet.insert ( elem );
8423 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8426 //=======================================================================
8427 //function : FindFreeBorder
8429 //=======================================================================
8431 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8433 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8434 const SMDS_MeshNode* theSecondNode,
8435 const SMDS_MeshNode* theLastNode,
8436 list< const SMDS_MeshNode* > & theNodes,
8437 list< const SMDS_MeshElement* >& theFaces)
8439 if ( !theFirstNode || !theSecondNode )
8441 // find border face between theFirstNode and theSecondNode
8442 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8446 theFaces.push_back( curElem );
8447 theNodes.push_back( theFirstNode );
8448 theNodes.push_back( theSecondNode );
8450 //vector<const SMDS_MeshNode*> nodes;
8451 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8452 TIDSortedElemSet foundElems;
8453 bool needTheLast = ( theLastNode != 0 );
8455 while ( nStart != theLastNode ) {
8456 if ( nStart == theFirstNode )
8457 return !needTheLast;
8459 // find all free border faces sharing form nStart
8461 list< const SMDS_MeshElement* > curElemList;
8462 list< const SMDS_MeshNode* > nStartList;
8463 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8464 while ( invElemIt->more() ) {
8465 const SMDS_MeshElement* e = invElemIt->next();
8466 if ( e == curElem || foundElems.insert( e ).second ) {
8468 int iNode = 0, nbNodes = e->NbNodes();
8469 //const SMDS_MeshNode* nodes[nbNodes+1];
8470 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8472 if(e->IsQuadratic()) {
8473 const SMDS_VtkFace* F =
8474 dynamic_cast<const SMDS_VtkFace*>(e);
8475 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8476 // use special nodes iterator
8477 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8478 while( anIter->more() ) {
8479 nodes[ iNode++ ] = cast2Node(anIter->next());
8483 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8484 while ( nIt->more() )
8485 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8487 nodes[ iNode ] = nodes[ 0 ];
8489 for ( iNode = 0; iNode < nbNodes; iNode++ )
8490 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8491 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8492 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8494 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8495 curElemList.push_back( e );
8499 // analyse the found
8501 int nbNewBorders = curElemList.size();
8502 if ( nbNewBorders == 0 ) {
8503 // no free border furthermore
8504 return !needTheLast;
8506 else if ( nbNewBorders == 1 ) {
8507 // one more element found
8509 nStart = nStartList.front();
8510 curElem = curElemList.front();
8511 theFaces.push_back( curElem );
8512 theNodes.push_back( nStart );
8515 // several continuations found
8516 list< const SMDS_MeshElement* >::iterator curElemIt;
8517 list< const SMDS_MeshNode* >::iterator nStartIt;
8518 // check if one of them reached the last node
8519 if ( needTheLast ) {
8520 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8521 curElemIt!= curElemList.end();
8522 curElemIt++, nStartIt++ )
8523 if ( *nStartIt == theLastNode ) {
8524 theFaces.push_back( *curElemIt );
8525 theNodes.push_back( *nStartIt );
8529 // find the best free border by the continuations
8530 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8531 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8532 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8533 curElemIt!= curElemList.end();
8534 curElemIt++, nStartIt++ )
8536 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8537 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8538 // find one more free border
8539 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8543 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8544 // choice: clear a worse one
8545 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8546 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8547 contNodes[ iWorse ].clear();
8548 contFaces[ iWorse ].clear();
8551 if ( contNodes[0].empty() && contNodes[1].empty() )
8554 // append the best free border
8555 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8556 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8557 theNodes.pop_back(); // remove nIgnore
8558 theNodes.pop_back(); // remove nStart
8559 theFaces.pop_back(); // remove curElem
8560 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8561 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8562 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8563 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8566 } // several continuations found
8567 } // while ( nStart != theLastNode )
8572 //=======================================================================
8573 //function : CheckFreeBorderNodes
8574 //purpose : Return true if the tree nodes are on a free border
8575 //=======================================================================
8577 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8578 const SMDS_MeshNode* theNode2,
8579 const SMDS_MeshNode* theNode3)
8581 list< const SMDS_MeshNode* > nodes;
8582 list< const SMDS_MeshElement* > faces;
8583 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8586 //=======================================================================
8587 //function : SewFreeBorder
8589 //=======================================================================
8591 SMESH_MeshEditor::Sew_Error
8592 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8593 const SMDS_MeshNode* theBordSecondNode,
8594 const SMDS_MeshNode* theBordLastNode,
8595 const SMDS_MeshNode* theSideFirstNode,
8596 const SMDS_MeshNode* theSideSecondNode,
8597 const SMDS_MeshNode* theSideThirdNode,
8598 const bool theSideIsFreeBorder,
8599 const bool toCreatePolygons,
8600 const bool toCreatePolyedrs)
8602 myLastCreatedElems.Clear();
8603 myLastCreatedNodes.Clear();
8605 MESSAGE("::SewFreeBorder()");
8606 Sew_Error aResult = SEW_OK;
8608 // ====================================
8609 // find side nodes and elements
8610 // ====================================
8612 list< const SMDS_MeshNode* > nSide[ 2 ];
8613 list< const SMDS_MeshElement* > eSide[ 2 ];
8614 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8615 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8619 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8620 nSide[0], eSide[0])) {
8621 MESSAGE(" Free Border 1 not found " );
8622 aResult = SEW_BORDER1_NOT_FOUND;
8624 if (theSideIsFreeBorder) {
8627 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8628 nSide[1], eSide[1])) {
8629 MESSAGE(" Free Border 2 not found " );
8630 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8633 if ( aResult != SEW_OK )
8636 if (!theSideIsFreeBorder) {
8640 // -------------------------------------------------------------------------
8642 // 1. If nodes to merge are not coincident, move nodes of the free border
8643 // from the coord sys defined by the direction from the first to last
8644 // nodes of the border to the correspondent sys of the side 2
8645 // 2. On the side 2, find the links most co-directed with the correspondent
8646 // links of the free border
8647 // -------------------------------------------------------------------------
8649 // 1. Since sewing may break if there are volumes to split on the side 2,
8650 // we wont move nodes but just compute new coordinates for them
8651 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8652 TNodeXYZMap nBordXYZ;
8653 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8654 list< const SMDS_MeshNode* >::iterator nBordIt;
8656 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8657 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8658 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8659 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8660 double tol2 = 1.e-8;
8661 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8662 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8663 // Need node movement.
8665 // find X and Z axes to create trsf
8666 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8668 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8670 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8673 gp_Ax3 toBordAx( Pb1, Zb, X );
8674 gp_Ax3 fromSideAx( Ps1, Zs, X );
8675 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8677 gp_Trsf toBordSys, fromSide2Sys;
8678 toBordSys.SetTransformation( toBordAx );
8679 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8680 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8683 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8684 const SMDS_MeshNode* n = *nBordIt;
8685 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8686 toBordSys.Transforms( xyz );
8687 fromSide2Sys.Transforms( xyz );
8688 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8692 // just insert nodes XYZ in the nBordXYZ map
8693 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8694 const SMDS_MeshNode* n = *nBordIt;
8695 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8699 // 2. On the side 2, find the links most co-directed with the correspondent
8700 // links of the free border
8702 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8703 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8704 sideNodes.push_back( theSideFirstNode );
8706 bool hasVolumes = false;
8707 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8708 set<long> foundSideLinkIDs, checkedLinkIDs;
8709 SMDS_VolumeTool volume;
8710 //const SMDS_MeshNode* faceNodes[ 4 ];
8712 const SMDS_MeshNode* sideNode;
8713 const SMDS_MeshElement* sideElem;
8714 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8715 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8716 nBordIt = bordNodes.begin();
8718 // border node position and border link direction to compare with
8719 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8720 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8721 // choose next side node by link direction or by closeness to
8722 // the current border node:
8723 bool searchByDir = ( *nBordIt != theBordLastNode );
8725 // find the next node on the Side 2
8727 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8729 checkedLinkIDs.clear();
8730 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8732 // loop on inverse elements of current node (prevSideNode) on the Side 2
8733 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8734 while ( invElemIt->more() )
8736 const SMDS_MeshElement* elem = invElemIt->next();
8737 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8738 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8739 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8740 bool isVolume = volume.Set( elem );
8741 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8742 if ( isVolume ) // --volume
8744 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8745 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8746 if(elem->IsQuadratic()) {
8747 const SMDS_VtkFace* F =
8748 dynamic_cast<const SMDS_VtkFace*>(elem);
8749 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8750 // use special nodes iterator
8751 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8752 while( anIter->more() ) {
8753 nodes[ iNode ] = cast2Node(anIter->next());
8754 if ( nodes[ iNode++ ] == prevSideNode )
8755 iPrevNode = iNode - 1;
8759 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8760 while ( nIt->more() ) {
8761 nodes[ iNode ] = cast2Node( nIt->next() );
8762 if ( nodes[ iNode++ ] == prevSideNode )
8763 iPrevNode = iNode - 1;
8766 // there are 2 links to check
8771 // loop on links, to be precise, on the second node of links
8772 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8773 const SMDS_MeshNode* n = nodes[ iNode ];
8775 if ( !volume.IsLinked( n, prevSideNode ))
8779 if ( iNode ) // a node before prevSideNode
8780 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8781 else // a node after prevSideNode
8782 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8784 // check if this link was already used
8785 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8786 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8787 if (!isJustChecked &&
8788 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8790 // test a link geometrically
8791 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8792 bool linkIsBetter = false;
8793 double dot = 0.0, dist = 0.0;
8794 if ( searchByDir ) { // choose most co-directed link
8795 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8796 linkIsBetter = ( dot > maxDot );
8798 else { // choose link with the node closest to bordPos
8799 dist = ( nextXYZ - bordPos ).SquareModulus();
8800 linkIsBetter = ( dist < minDist );
8802 if ( linkIsBetter ) {
8811 } // loop on inverse elements of prevSideNode
8814 MESSAGE(" Cant find path by links of the Side 2 ");
8815 return SEW_BAD_SIDE_NODES;
8817 sideNodes.push_back( sideNode );
8818 sideElems.push_back( sideElem );
8819 foundSideLinkIDs.insert ( linkID );
8820 prevSideNode = sideNode;
8822 if ( *nBordIt == theBordLastNode )
8823 searchByDir = false;
8825 // find the next border link to compare with
8826 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8827 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8828 // move to next border node if sideNode is before forward border node (bordPos)
8829 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8830 prevBordNode = *nBordIt;
8832 bordPos = nBordXYZ[ *nBordIt ];
8833 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8834 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8838 while ( sideNode != theSideSecondNode );
8840 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8841 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8842 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8844 } // end nodes search on the side 2
8846 // ============================
8847 // sew the border to the side 2
8848 // ============================
8850 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8851 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8853 TListOfListOfNodes nodeGroupsToMerge;
8854 if ( nbNodes[0] == nbNodes[1] ||
8855 ( theSideIsFreeBorder && !theSideThirdNode)) {
8857 // all nodes are to be merged
8859 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8860 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8861 nIt[0]++, nIt[1]++ )
8863 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8864 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8865 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8870 // insert new nodes into the border and the side to get equal nb of segments
8872 // get normalized parameters of nodes on the borders
8873 //double param[ 2 ][ maxNbNodes ];
8875 param[0] = new double [ maxNbNodes ];
8876 param[1] = new double [ maxNbNodes ];
8878 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8879 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8880 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8881 const SMDS_MeshNode* nPrev = *nIt;
8882 double bordLength = 0;
8883 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8884 const SMDS_MeshNode* nCur = *nIt;
8885 gp_XYZ segment (nCur->X() - nPrev->X(),
8886 nCur->Y() - nPrev->Y(),
8887 nCur->Z() - nPrev->Z());
8888 double segmentLen = segment.Modulus();
8889 bordLength += segmentLen;
8890 param[ iBord ][ iNode ] = bordLength;
8893 // normalize within [0,1]
8894 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8895 param[ iBord ][ iNode ] /= bordLength;
8899 // loop on border segments
8900 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8901 int i[ 2 ] = { 0, 0 };
8902 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8903 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8905 TElemOfNodeListMap insertMap;
8906 TElemOfNodeListMap::iterator insertMapIt;
8908 // key: elem to insert nodes into
8909 // value: 2 nodes to insert between + nodes to be inserted
8911 bool next[ 2 ] = { false, false };
8913 // find min adjacent segment length after sewing
8914 double nextParam = 10., prevParam = 0;
8915 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8916 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8917 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8918 if ( i[ iBord ] > 0 )
8919 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8921 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8922 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8923 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8925 // choose to insert or to merge nodes
8926 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8927 if ( Abs( du ) <= minSegLen * 0.2 ) {
8930 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8931 const SMDS_MeshNode* n0 = *nIt[0];
8932 const SMDS_MeshNode* n1 = *nIt[1];
8933 nodeGroupsToMerge.back().push_back( n1 );
8934 nodeGroupsToMerge.back().push_back( n0 );
8935 // position of node of the border changes due to merge
8936 param[ 0 ][ i[0] ] += du;
8937 // move n1 for the sake of elem shape evaluation during insertion.
8938 // n1 will be removed by MergeNodes() anyway
8939 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8940 next[0] = next[1] = true;
8945 int intoBord = ( du < 0 ) ? 0 : 1;
8946 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8947 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8948 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8949 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8950 if ( intoBord == 1 ) {
8951 // move node of the border to be on a link of elem of the side
8952 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8953 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8954 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8955 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8956 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8958 insertMapIt = insertMap.find( elem );
8959 bool notFound = ( insertMapIt == insertMap.end() );
8960 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8962 // insert into another link of the same element:
8963 // 1. perform insertion into the other link of the elem
8964 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8965 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8966 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8967 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8968 // 2. perform insertion into the link of adjacent faces
8970 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8972 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8976 if (toCreatePolyedrs) {
8977 // perform insertion into the links of adjacent volumes
8978 UpdateVolumes(n12, n22, nodeList);
8980 // 3. find an element appeared on n1 and n2 after the insertion
8981 insertMap.erase( elem );
8982 elem = findAdjacentFace( n1, n2, 0 );
8984 if ( notFound || otherLink ) {
8985 // add element and nodes of the side into the insertMap
8986 insertMapIt = insertMap.insert
8987 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8988 (*insertMapIt).second.push_back( n1 );
8989 (*insertMapIt).second.push_back( n2 );
8991 // add node to be inserted into elem
8992 (*insertMapIt).second.push_back( nIns );
8993 next[ 1 - intoBord ] = true;
8996 // go to the next segment
8997 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8998 if ( next[ iBord ] ) {
8999 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9001 nPrev[ iBord ] = *nIt[ iBord ];
9002 nIt[ iBord ]++; i[ iBord ]++;
9006 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9008 // perform insertion of nodes into elements
9010 for (insertMapIt = insertMap.begin();
9011 insertMapIt != insertMap.end();
9014 const SMDS_MeshElement* elem = (*insertMapIt).first;
9015 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9016 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9017 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9019 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9021 if ( !theSideIsFreeBorder ) {
9022 // look for and insert nodes into the faces adjacent to elem
9024 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9026 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9031 if (toCreatePolyedrs) {
9032 // perform insertion into the links of adjacent volumes
9033 UpdateVolumes(n1, n2, nodeList);
9039 } // end: insert new nodes
9041 MergeNodes ( nodeGroupsToMerge );
9046 //=======================================================================
9047 //function : InsertNodesIntoLink
9048 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9049 // and theBetweenNode2 and split theElement
9050 //=======================================================================
9052 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9053 const SMDS_MeshNode* theBetweenNode1,
9054 const SMDS_MeshNode* theBetweenNode2,
9055 list<const SMDS_MeshNode*>& theNodesToInsert,
9056 const bool toCreatePoly)
9058 if ( theFace->GetType() != SMDSAbs_Face ) return;
9060 // find indices of 2 link nodes and of the rest nodes
9061 int iNode = 0, il1, il2, i3, i4;
9062 il1 = il2 = i3 = i4 = -1;
9063 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9064 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9066 if(theFace->IsQuadratic()) {
9067 const SMDS_VtkFace* F =
9068 dynamic_cast<const SMDS_VtkFace*>(theFace);
9069 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9070 // use special nodes iterator
9071 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9072 while( anIter->more() ) {
9073 const SMDS_MeshNode* n = cast2Node(anIter->next());
9074 if ( n == theBetweenNode1 )
9076 else if ( n == theBetweenNode2 )
9082 nodes[ iNode++ ] = n;
9086 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9087 while ( nodeIt->more() ) {
9088 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9089 if ( n == theBetweenNode1 )
9091 else if ( n == theBetweenNode2 )
9097 nodes[ iNode++ ] = n;
9100 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9103 // arrange link nodes to go one after another regarding the face orientation
9104 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9105 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9110 aNodesToInsert.reverse();
9112 // check that not link nodes of a quadrangles are in good order
9113 int nbFaceNodes = theFace->NbNodes();
9114 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9120 if (toCreatePoly || theFace->IsPoly()) {
9123 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9125 // add nodes of face up to first node of link
9128 if(theFace->IsQuadratic()) {
9129 const SMDS_VtkFace* F =
9130 dynamic_cast<const SMDS_VtkFace*>(theFace);
9131 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9132 // use special nodes iterator
9133 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9134 while( anIter->more() && !isFLN ) {
9135 const SMDS_MeshNode* n = cast2Node(anIter->next());
9136 poly_nodes[iNode++] = n;
9137 if (n == nodes[il1]) {
9141 // add nodes to insert
9142 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9143 for (; nIt != aNodesToInsert.end(); nIt++) {
9144 poly_nodes[iNode++] = *nIt;
9146 // add nodes of face starting from last node of link
9147 while ( anIter->more() ) {
9148 poly_nodes[iNode++] = cast2Node(anIter->next());
9152 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9153 while ( nodeIt->more() && !isFLN ) {
9154 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9155 poly_nodes[iNode++] = n;
9156 if (n == nodes[il1]) {
9160 // add nodes to insert
9161 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9162 for (; nIt != aNodesToInsert.end(); nIt++) {
9163 poly_nodes[iNode++] = *nIt;
9165 // add nodes of face starting from last node of link
9166 while ( nodeIt->more() ) {
9167 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9168 poly_nodes[iNode++] = n;
9172 // edit or replace the face
9173 SMESHDS_Mesh *aMesh = GetMeshDS();
9175 if (theFace->IsPoly()) {
9176 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9179 int aShapeId = FindShape( theFace );
9181 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9182 myLastCreatedElems.Append(newElem);
9183 if ( aShapeId && newElem )
9184 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9186 aMesh->RemoveElement(theFace);
9191 SMESHDS_Mesh *aMesh = GetMeshDS();
9192 if( !theFace->IsQuadratic() ) {
9194 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9195 int nbLinkNodes = 2 + aNodesToInsert.size();
9196 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9197 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9198 linkNodes[ 0 ] = nodes[ il1 ];
9199 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9200 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9201 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9202 linkNodes[ iNode++ ] = *nIt;
9204 // decide how to split a quadrangle: compare possible variants
9205 // and choose which of splits to be a quadrangle
9206 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9207 if ( nbFaceNodes == 3 ) {
9208 iBestQuad = nbSplits;
9211 else if ( nbFaceNodes == 4 ) {
9212 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9213 double aBestRate = DBL_MAX;
9214 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9216 double aBadRate = 0;
9217 // evaluate elements quality
9218 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9219 if ( iSplit == iQuad ) {
9220 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9224 aBadRate += getBadRate( &quad, aCrit );
9227 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9229 nodes[ iSplit < iQuad ? i4 : i3 ]);
9230 aBadRate += getBadRate( &tria, aCrit );
9234 if ( aBadRate < aBestRate ) {
9236 aBestRate = aBadRate;
9241 // create new elements
9242 int aShapeId = FindShape( theFace );
9245 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9246 SMDS_MeshElement* newElem = 0;
9247 if ( iSplit == iBestQuad )
9248 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9253 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9255 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9256 myLastCreatedElems.Append(newElem);
9257 if ( aShapeId && newElem )
9258 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9261 // change nodes of theFace
9262 const SMDS_MeshNode* newNodes[ 4 ];
9263 newNodes[ 0 ] = linkNodes[ i1 ];
9264 newNodes[ 1 ] = linkNodes[ i2 ];
9265 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9266 newNodes[ 3 ] = nodes[ i4 ];
9267 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9268 const SMDS_MeshElement* newElem = 0;
9269 if (iSplit == iBestQuad)
9270 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9272 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9273 myLastCreatedElems.Append(newElem);
9274 if ( aShapeId && newElem )
9275 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9276 } // end if(!theFace->IsQuadratic())
9277 else { // theFace is quadratic
9278 // we have to split theFace on simple triangles and one simple quadrangle
9280 int nbshift = tmp*2;
9281 // shift nodes in nodes[] by nbshift
9283 for(i=0; i<nbshift; i++) {
9284 const SMDS_MeshNode* n = nodes[0];
9285 for(j=0; j<nbFaceNodes-1; j++) {
9286 nodes[j] = nodes[j+1];
9288 nodes[nbFaceNodes-1] = n;
9290 il1 = il1 - nbshift;
9291 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9292 // n0 n1 n2 n0 n1 n2
9293 // +-----+-----+ +-----+-----+
9302 // create new elements
9303 int aShapeId = FindShape( theFace );
9306 if(nbFaceNodes==6) { // quadratic triangle
9307 SMDS_MeshElement* newElem =
9308 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9309 myLastCreatedElems.Append(newElem);
9310 if ( aShapeId && newElem )
9311 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9312 if(theFace->IsMediumNode(nodes[il1])) {
9313 // create quadrangle
9314 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9315 myLastCreatedElems.Append(newElem);
9316 if ( aShapeId && newElem )
9317 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9323 // create quadrangle
9324 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9325 myLastCreatedElems.Append(newElem);
9326 if ( aShapeId && newElem )
9327 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9333 else { // nbFaceNodes==8 - quadratic quadrangle
9334 SMDS_MeshElement* newElem =
9335 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9336 myLastCreatedElems.Append(newElem);
9337 if ( aShapeId && newElem )
9338 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9339 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9340 myLastCreatedElems.Append(newElem);
9341 if ( aShapeId && newElem )
9342 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9343 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9344 myLastCreatedElems.Append(newElem);
9345 if ( aShapeId && newElem )
9346 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9347 if(theFace->IsMediumNode(nodes[il1])) {
9348 // create quadrangle
9349 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9350 myLastCreatedElems.Append(newElem);
9351 if ( aShapeId && newElem )
9352 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9358 // create quadrangle
9359 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9360 myLastCreatedElems.Append(newElem);
9361 if ( aShapeId && newElem )
9362 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9368 // create needed triangles using n1,n2,n3 and inserted nodes
9369 int nbn = 2 + aNodesToInsert.size();
9370 //const SMDS_MeshNode* aNodes[nbn];
9371 vector<const SMDS_MeshNode*> aNodes(nbn);
9372 aNodes[0] = nodes[n1];
9373 aNodes[nbn-1] = nodes[n2];
9374 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9375 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9376 aNodes[iNode++] = *nIt;
9378 for(i=1; i<nbn; i++) {
9379 SMDS_MeshElement* newElem =
9380 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9381 myLastCreatedElems.Append(newElem);
9382 if ( aShapeId && newElem )
9383 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9387 aMesh->RemoveElement(theFace);
9390 //=======================================================================
9391 //function : UpdateVolumes
9393 //=======================================================================
9394 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9395 const SMDS_MeshNode* theBetweenNode2,
9396 list<const SMDS_MeshNode*>& theNodesToInsert)
9398 myLastCreatedElems.Clear();
9399 myLastCreatedNodes.Clear();
9401 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9402 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9403 const SMDS_MeshElement* elem = invElemIt->next();
9405 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9406 SMDS_VolumeTool aVolume (elem);
9407 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9410 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9411 int iface, nbFaces = aVolume.NbFaces();
9412 vector<const SMDS_MeshNode *> poly_nodes;
9413 vector<int> quantities (nbFaces);
9415 for (iface = 0; iface < nbFaces; iface++) {
9416 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9417 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9418 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9420 for (int inode = 0; inode < nbFaceNodes; inode++) {
9421 poly_nodes.push_back(faceNodes[inode]);
9423 if (nbInserted == 0) {
9424 if (faceNodes[inode] == theBetweenNode1) {
9425 if (faceNodes[inode + 1] == theBetweenNode2) {
9426 nbInserted = theNodesToInsert.size();
9428 // add nodes to insert
9429 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9430 for (; nIt != theNodesToInsert.end(); nIt++) {
9431 poly_nodes.push_back(*nIt);
9435 else if (faceNodes[inode] == theBetweenNode2) {
9436 if (faceNodes[inode + 1] == theBetweenNode1) {
9437 nbInserted = theNodesToInsert.size();
9439 // add nodes to insert in reversed order
9440 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9442 for (; nIt != theNodesToInsert.begin(); nIt--) {
9443 poly_nodes.push_back(*nIt);
9445 poly_nodes.push_back(*nIt);
9452 quantities[iface] = nbFaceNodes + nbInserted;
9455 // Replace or update the volume
9456 SMESHDS_Mesh *aMesh = GetMeshDS();
9458 if (elem->IsPoly()) {
9459 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9463 int aShapeId = FindShape( elem );
9465 SMDS_MeshElement* newElem =
9466 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9467 myLastCreatedElems.Append(newElem);
9468 if (aShapeId && newElem)
9469 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9471 aMesh->RemoveElement(elem);
9478 //================================================================================
9480 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9482 //================================================================================
9484 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9485 vector<const SMDS_MeshNode *> & nodes,
9486 vector<int> & nbNodeInFaces )
9489 nbNodeInFaces.clear();
9490 SMDS_VolumeTool vTool ( elem );
9491 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9493 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9494 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9495 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9500 //=======================================================================
9502 * \brief Convert elements contained in a submesh to quadratic
9503 * \return int - nb of checked elements
9505 //=======================================================================
9507 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9508 SMESH_MesherHelper& theHelper,
9509 const bool theForce3d)
9512 if( !theSm ) return nbElem;
9514 vector<int> nbNodeInFaces;
9515 vector<const SMDS_MeshNode *> nodes;
9516 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9517 while(ElemItr->more())
9520 const SMDS_MeshElement* elem = ElemItr->next();
9521 if( !elem || elem->IsQuadratic() ) continue;
9523 // get elem data needed to re-create it
9525 const int id = elem->GetID();
9526 const int nbNodes = elem->NbNodes();
9527 const SMDSAbs_ElementType aType = elem->GetType();
9528 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9529 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9530 if ( aGeomType == SMDSEntity_Polyhedra )
9531 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9532 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9533 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9535 // remove a linear element
9536 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9538 const SMDS_MeshElement* NewElem = 0;
9544 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9552 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9555 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9558 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9563 case SMDSAbs_Volume :
9567 case SMDSEntity_Tetra:
9568 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9570 case SMDSEntity_Pyramid:
9571 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9573 case SMDSEntity_Penta:
9574 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9576 case SMDSEntity_Hexa:
9577 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9578 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9580 case SMDSEntity_Hexagonal_Prism:
9582 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9589 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9591 theSm->AddElement( NewElem );
9596 //=======================================================================
9597 //function : ConvertToQuadratic
9599 //=======================================================================
9601 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9603 SMESHDS_Mesh* meshDS = GetMeshDS();
9605 SMESH_MesherHelper aHelper(*myMesh);
9606 aHelper.SetIsQuadratic( true );
9608 int nbCheckedElems = 0;
9609 if ( myMesh->HasShapeToMesh() )
9611 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9613 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9614 while ( smIt->more() ) {
9615 SMESH_subMesh* sm = smIt->next();
9616 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9617 aHelper.SetSubShape( sm->GetSubShape() );
9618 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9623 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9624 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9626 SMESHDS_SubMesh *smDS = 0;
9627 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9628 while(aEdgeItr->more())
9630 const SMDS_MeshEdge* edge = aEdgeItr->next();
9631 if(edge && !edge->IsQuadratic())
9633 int id = edge->GetID();
9634 //MESSAGE("edge->GetID() " << id);
9635 const SMDS_MeshNode* n1 = edge->GetNode(0);
9636 const SMDS_MeshNode* n2 = edge->GetNode(1);
9638 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9640 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9641 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9644 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9645 while(aFaceItr->more())
9647 const SMDS_MeshFace* face = aFaceItr->next();
9648 if(!face || face->IsQuadratic() ) continue;
9650 const int id = face->GetID();
9651 const SMDSAbs_EntityType type = face->GetEntityType();
9652 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9654 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9656 SMDS_MeshFace * NewFace = 0;
9659 case SMDSEntity_Triangle:
9660 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9662 case SMDSEntity_Quadrangle:
9663 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9666 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9668 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9670 vector<int> nbNodeInFaces;
9671 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9672 while(aVolumeItr->more())
9674 const SMDS_MeshVolume* volume = aVolumeItr->next();
9675 if(!volume || volume->IsQuadratic() ) continue;
9677 const int id = volume->GetID();
9678 const SMDSAbs_EntityType type = volume->GetEntityType();
9679 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9680 if ( type == SMDSEntity_Polyhedra )
9681 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9682 else if ( type == SMDSEntity_Hexagonal_Prism )
9683 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9685 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9687 SMDS_MeshVolume * NewVolume = 0;
9690 case SMDSEntity_Tetra:
9691 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9693 case SMDSEntity_Hexa:
9694 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9695 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9697 case SMDSEntity_Pyramid:
9698 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9699 nodes[3], nodes[4], id, theForce3d);
9701 case SMDSEntity_Penta:
9702 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9703 nodes[3], nodes[4], nodes[5], id, theForce3d);
9705 case SMDSEntity_Hexagonal_Prism:
9707 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9709 ReplaceElemInGroups(volume, NewVolume, meshDS);
9714 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9715 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9716 aHelper.FixQuadraticElements(myError);
9720 //================================================================================
9722 * \brief Makes given elements quadratic
9723 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9724 * \param theElements - elements to make quadratic
9726 //================================================================================
9728 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9729 TIDSortedElemSet& theElements)
9731 if ( theElements.empty() ) return;
9733 // we believe that all theElements are of the same type
9734 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9736 // get all nodes shared by theElements
9737 TIDSortedNodeSet allNodes;
9738 TIDSortedElemSet::iterator eIt = theElements.begin();
9739 for ( ; eIt != theElements.end(); ++eIt )
9740 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9742 // complete theElements with elements of lower dim whose all nodes are in allNodes
9744 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9745 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9746 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9747 for ( ; nIt != allNodes.end(); ++nIt )
9749 const SMDS_MeshNode* n = *nIt;
9750 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9751 while ( invIt->more() )
9753 const SMDS_MeshElement* e = invIt->next();
9754 if ( e->IsQuadratic() )
9756 quadAdjacentElems[ e->GetType() ].insert( e );
9759 if ( e->GetType() >= elemType )
9761 continue; // same type of more complex linear element
9764 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9765 continue; // e is already checked
9769 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9770 while ( nodeIt->more() && allIn )
9771 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9773 theElements.insert(e );
9777 SMESH_MesherHelper helper(*myMesh);
9778 helper.SetIsQuadratic( true );
9780 // add links of quadratic adjacent elements to the helper
9782 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9783 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9784 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9786 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9788 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9789 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9790 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9792 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9794 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9795 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9796 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9798 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9801 // make quadratic elements instead of linear ones
9803 SMESHDS_Mesh* meshDS = GetMeshDS();
9804 SMESHDS_SubMesh* smDS = 0;
9805 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9807 const SMDS_MeshElement* elem = *eIt;
9808 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9811 const int id = elem->GetID();
9812 const SMDSAbs_ElementType type = elem->GetType();
9813 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9815 if ( !smDS || !smDS->Contains( elem ))
9816 smDS = meshDS->MeshElements( elem->getshapeId() );
9817 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9819 SMDS_MeshElement * newElem = 0;
9820 switch( nodes.size() )
9822 case 4: // cases for most frequently used element types go first (for optimization)
9823 if ( type == SMDSAbs_Volume )
9824 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9826 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9829 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9830 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9833 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9836 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9839 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9840 nodes[4], id, theForce3d);
9843 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9844 nodes[4], nodes[5], id, theForce3d);
9848 ReplaceElemInGroups( elem, newElem, meshDS);
9849 if( newElem && smDS )
9850 smDS->AddElement( newElem );
9853 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9854 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9855 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9856 helper.FixQuadraticElements( myError );
9860 //=======================================================================
9862 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9863 * \return int - nb of checked elements
9865 //=======================================================================
9867 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9868 SMDS_ElemIteratorPtr theItr,
9869 const int theShapeID)
9872 SMESHDS_Mesh* meshDS = GetMeshDS();
9874 while( theItr->more() )
9876 const SMDS_MeshElement* elem = theItr->next();
9878 if( elem && elem->IsQuadratic())
9880 int id = elem->GetID();
9881 int nbCornerNodes = elem->NbCornerNodes();
9882 SMDSAbs_ElementType aType = elem->GetType();
9884 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9886 //remove a quadratic element
9887 if ( !theSm || !theSm->Contains( elem ))
9888 theSm = meshDS->MeshElements( elem->getshapeId() );
9889 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9891 // remove medium nodes
9892 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9893 if ( nodes[i]->NbInverseElements() == 0 )
9894 meshDS->RemoveFreeNode( nodes[i], theSm );
9896 // add a linear element
9897 nodes.resize( nbCornerNodes );
9898 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9899 ReplaceElemInGroups(elem, newElem, meshDS);
9900 if( theSm && newElem )
9901 theSm->AddElement( newElem );
9907 //=======================================================================
9908 //function : ConvertFromQuadratic
9910 //=======================================================================
9912 bool SMESH_MeshEditor::ConvertFromQuadratic()
9914 int nbCheckedElems = 0;
9915 if ( myMesh->HasShapeToMesh() )
9917 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9919 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9920 while ( smIt->more() ) {
9921 SMESH_subMesh* sm = smIt->next();
9922 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9923 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9929 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9930 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9932 SMESHDS_SubMesh *aSM = 0;
9933 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9941 //================================================================================
9943 * \brief Return true if all medium nodes of the element are in the node set
9945 //================================================================================
9947 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9949 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9950 if ( !nodeSet.count( elem->GetNode(i) ))
9956 //================================================================================
9958 * \brief Makes given elements linear
9960 //================================================================================
9962 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9964 if ( theElements.empty() ) return;
9966 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9967 set<int> mediumNodeIDs;
9968 TIDSortedElemSet::iterator eIt = theElements.begin();
9969 for ( ; eIt != theElements.end(); ++eIt )
9971 const SMDS_MeshElement* e = *eIt;
9972 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9973 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9976 // replace given elements by linear ones
9977 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9978 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9979 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9981 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9982 // except those elements sharing medium nodes of quadratic element whose medium nodes
9983 // are not all in mediumNodeIDs
9985 // get remaining medium nodes
9986 TIDSortedNodeSet mediumNodes;
9987 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9988 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9989 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9990 mediumNodes.insert( mediumNodes.end(), n );
9992 // find more quadratic elements to convert
9993 TIDSortedElemSet moreElemsToConvert;
9994 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9995 for ( ; nIt != mediumNodes.end(); ++nIt )
9997 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9998 while ( invIt->more() )
10000 const SMDS_MeshElement* e = invIt->next();
10001 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10003 // find a more complex element including e and
10004 // whose medium nodes are not in mediumNodes
10005 bool complexFound = false;
10006 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10008 SMDS_ElemIteratorPtr invIt2 =
10009 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10010 while ( invIt2->more() )
10012 const SMDS_MeshElement* eComplex = invIt2->next();
10013 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10015 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10016 if ( nbCommonNodes == e->NbNodes())
10018 complexFound = true;
10019 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10025 if ( !complexFound )
10026 moreElemsToConvert.insert( e );
10030 elemIt = SMDS_ElemIteratorPtr
10031 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10032 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10035 //=======================================================================
10036 //function : SewSideElements
10038 //=======================================================================
10040 SMESH_MeshEditor::Sew_Error
10041 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10042 TIDSortedElemSet& theSide2,
10043 const SMDS_MeshNode* theFirstNode1,
10044 const SMDS_MeshNode* theFirstNode2,
10045 const SMDS_MeshNode* theSecondNode1,
10046 const SMDS_MeshNode* theSecondNode2)
10048 myLastCreatedElems.Clear();
10049 myLastCreatedNodes.Clear();
10051 MESSAGE ("::::SewSideElements()");
10052 if ( theSide1.size() != theSide2.size() )
10053 return SEW_DIFF_NB_OF_ELEMENTS;
10055 Sew_Error aResult = SEW_OK;
10057 // 1. Build set of faces representing each side
10058 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10059 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10061 // =======================================================================
10062 // 1. Build set of faces representing each side:
10063 // =======================================================================
10064 // a. build set of nodes belonging to faces
10065 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10066 // c. create temporary faces representing side of volumes if correspondent
10067 // face does not exist
10069 SMESHDS_Mesh* aMesh = GetMeshDS();
10070 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10071 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10072 TIDSortedElemSet faceSet1, faceSet2;
10073 set<const SMDS_MeshElement*> volSet1, volSet2;
10074 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10075 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10076 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10077 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10078 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10079 int iSide, iFace, iNode;
10081 list<const SMDS_MeshElement* > tempFaceList;
10082 for ( iSide = 0; iSide < 2; iSide++ ) {
10083 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10084 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10085 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10086 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10087 set<const SMDS_MeshElement*>::iterator vIt;
10088 TIDSortedElemSet::iterator eIt;
10089 set<const SMDS_MeshNode*>::iterator nIt;
10091 // check that given nodes belong to given elements
10092 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10093 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10094 int firstIndex = -1, secondIndex = -1;
10095 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10096 const SMDS_MeshElement* elem = *eIt;
10097 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10098 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10099 if ( firstIndex > -1 && secondIndex > -1 ) break;
10101 if ( firstIndex < 0 || secondIndex < 0 ) {
10102 // we can simply return until temporary faces created
10103 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10106 // -----------------------------------------------------------
10107 // 1a. Collect nodes of existing faces
10108 // and build set of face nodes in order to detect missing
10109 // faces corresponding to sides of volumes
10110 // -----------------------------------------------------------
10112 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10114 // loop on the given element of a side
10115 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10116 //const SMDS_MeshElement* elem = *eIt;
10117 const SMDS_MeshElement* elem = *eIt;
10118 if ( elem->GetType() == SMDSAbs_Face ) {
10119 faceSet->insert( elem );
10120 set <const SMDS_MeshNode*> faceNodeSet;
10121 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10122 while ( nodeIt->more() ) {
10123 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10124 nodeSet->insert( n );
10125 faceNodeSet.insert( n );
10127 setOfFaceNodeSet.insert( faceNodeSet );
10129 else if ( elem->GetType() == SMDSAbs_Volume )
10130 volSet->insert( elem );
10132 // ------------------------------------------------------------------------------
10133 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10134 // ------------------------------------------------------------------------------
10136 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10137 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10138 while ( fIt->more() ) { // loop on faces sharing a node
10139 const SMDS_MeshElement* f = fIt->next();
10140 if ( faceSet->find( f ) == faceSet->end() ) {
10141 // check if all nodes are in nodeSet and
10142 // complete setOfFaceNodeSet if they are
10143 set <const SMDS_MeshNode*> faceNodeSet;
10144 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10145 bool allInSet = true;
10146 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10147 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10148 if ( nodeSet->find( n ) == nodeSet->end() )
10151 faceNodeSet.insert( n );
10154 faceSet->insert( f );
10155 setOfFaceNodeSet.insert( faceNodeSet );
10161 // -------------------------------------------------------------------------
10162 // 1c. Create temporary faces representing sides of volumes if correspondent
10163 // face does not exist
10164 // -------------------------------------------------------------------------
10166 if ( !volSet->empty() ) {
10167 //int nodeSetSize = nodeSet->size();
10169 // loop on given volumes
10170 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10171 SMDS_VolumeTool vol (*vIt);
10172 // loop on volume faces: find free faces
10173 // --------------------------------------
10174 list<const SMDS_MeshElement* > freeFaceList;
10175 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10176 if ( !vol.IsFreeFace( iFace ))
10178 // check if there is already a face with same nodes in a face set
10179 const SMDS_MeshElement* aFreeFace = 0;
10180 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10181 int nbNodes = vol.NbFaceNodes( iFace );
10182 set <const SMDS_MeshNode*> faceNodeSet;
10183 vol.GetFaceNodes( iFace, faceNodeSet );
10184 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10186 // no such a face is given but it still can exist, check it
10187 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10188 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10190 if ( !aFreeFace ) {
10191 // create a temporary face
10192 if ( nbNodes == 3 ) {
10193 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10194 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10196 else if ( nbNodes == 4 ) {
10197 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10198 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10201 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10202 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10203 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10206 tempFaceList.push_back( aFreeFace );
10210 freeFaceList.push_back( aFreeFace );
10212 } // loop on faces of a volume
10214 // choose one of several free faces of a volume
10215 // --------------------------------------------
10216 if ( freeFaceList.size() > 1 ) {
10217 // choose a face having max nb of nodes shared by other elems of a side
10218 int maxNbNodes = -1;
10219 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10220 while ( fIt != freeFaceList.end() ) { // loop on free faces
10221 int nbSharedNodes = 0;
10222 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10223 while ( nodeIt->more() ) { // loop on free face nodes
10224 const SMDS_MeshNode* n =
10225 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10226 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10227 while ( invElemIt->more() ) {
10228 const SMDS_MeshElement* e = invElemIt->next();
10229 nbSharedNodes += faceSet->count( e );
10230 nbSharedNodes += elemSet->count( e );
10233 if ( nbSharedNodes > maxNbNodes ) {
10234 maxNbNodes = nbSharedNodes;
10235 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10237 else if ( nbSharedNodes == maxNbNodes ) {
10241 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10244 if ( freeFaceList.size() > 1 )
10246 // could not choose one face, use another way
10247 // choose a face most close to the bary center of the opposite side
10248 gp_XYZ aBC( 0., 0., 0. );
10249 set <const SMDS_MeshNode*> addedNodes;
10250 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10251 eIt = elemSet2->begin();
10252 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10253 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10254 while ( nodeIt->more() ) { // loop on free face nodes
10255 const SMDS_MeshNode* n =
10256 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10257 if ( addedNodes.insert( n ).second )
10258 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10261 aBC /= addedNodes.size();
10262 double minDist = DBL_MAX;
10263 fIt = freeFaceList.begin();
10264 while ( fIt != freeFaceList.end() ) { // loop on free faces
10266 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10267 while ( nodeIt->more() ) { // loop on free face nodes
10268 const SMDS_MeshNode* n =
10269 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10270 gp_XYZ p( n->X(),n->Y(),n->Z() );
10271 dist += ( aBC - p ).SquareModulus();
10273 if ( dist < minDist ) {
10275 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10278 fIt = freeFaceList.erase( fIt++ );
10281 } // choose one of several free faces of a volume
10283 if ( freeFaceList.size() == 1 ) {
10284 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10285 faceSet->insert( aFreeFace );
10286 // complete a node set with nodes of a found free face
10287 // for ( iNode = 0; iNode < ; iNode++ )
10288 // nodeSet->insert( fNodes[ iNode ] );
10291 } // loop on volumes of a side
10293 // // complete a set of faces if new nodes in a nodeSet appeared
10294 // // ----------------------------------------------------------
10295 // if ( nodeSetSize != nodeSet->size() ) {
10296 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10297 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10298 // while ( fIt->more() ) { // loop on faces sharing a node
10299 // const SMDS_MeshElement* f = fIt->next();
10300 // if ( faceSet->find( f ) == faceSet->end() ) {
10301 // // check if all nodes are in nodeSet and
10302 // // complete setOfFaceNodeSet if they are
10303 // set <const SMDS_MeshNode*> faceNodeSet;
10304 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10305 // bool allInSet = true;
10306 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10307 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10308 // if ( nodeSet->find( n ) == nodeSet->end() )
10309 // allInSet = false;
10311 // faceNodeSet.insert( n );
10313 // if ( allInSet ) {
10314 // faceSet->insert( f );
10315 // setOfFaceNodeSet.insert( faceNodeSet );
10321 } // Create temporary faces, if there are volumes given
10324 if ( faceSet1.size() != faceSet2.size() ) {
10325 // delete temporary faces: they are in reverseElements of actual nodes
10326 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10327 // while ( tmpFaceIt->more() )
10328 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10329 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10330 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10331 // aMesh->RemoveElement(*tmpFaceIt);
10332 MESSAGE("Diff nb of faces");
10333 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10336 // ============================================================
10337 // 2. Find nodes to merge:
10338 // bind a node to remove to a node to put instead
10339 // ============================================================
10341 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10342 if ( theFirstNode1 != theFirstNode2 )
10343 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10344 if ( theSecondNode1 != theSecondNode2 )
10345 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10347 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10348 set< long > linkIdSet; // links to process
10349 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10351 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10352 list< NLink > linkList[2];
10353 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10354 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10355 // loop on links in linkList; find faces by links and append links
10356 // of the found faces to linkList
10357 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10358 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10360 NLink link[] = { *linkIt[0], *linkIt[1] };
10361 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10362 if ( !linkIdSet.count( linkID ) )
10365 // by links, find faces in the face sets,
10366 // and find indices of link nodes in the found faces;
10367 // in a face set, there is only one or no face sharing a link
10368 // ---------------------------------------------------------------
10370 const SMDS_MeshElement* face[] = { 0, 0 };
10371 vector<const SMDS_MeshNode*> fnodes[2];
10372 int iLinkNode[2][2];
10373 TIDSortedElemSet avoidSet;
10374 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10375 const SMDS_MeshNode* n1 = link[iSide].first;
10376 const SMDS_MeshNode* n2 = link[iSide].second;
10377 //cout << "Side " << iSide << " ";
10378 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10379 // find a face by two link nodes
10380 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10381 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10382 if ( face[ iSide ])
10384 //cout << " F " << face[ iSide]->GetID() <<endl;
10385 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10386 // put face nodes to fnodes
10387 if ( face[ iSide ]->IsQuadratic() )
10389 // use interlaced nodes iterator
10390 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10391 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10392 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10393 while ( nIter->more() )
10394 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10398 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10399 face[ iSide ]->end_nodes() );
10401 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10405 // check similarity of elements of the sides
10406 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10407 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10408 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10409 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10412 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10414 break; // do not return because it's necessary to remove tmp faces
10417 // set nodes to merge
10418 // -------------------
10420 if ( face[0] && face[1] ) {
10421 const int nbNodes = face[0]->NbNodes();
10422 if ( nbNodes != face[1]->NbNodes() ) {
10423 MESSAGE("Diff nb of face nodes");
10424 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10425 break; // do not return because it s necessary to remove tmp faces
10427 bool reverse[] = { false, false }; // order of nodes in the link
10428 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10429 // analyse link orientation in faces
10430 int i1 = iLinkNode[ iSide ][ 0 ];
10431 int i2 = iLinkNode[ iSide ][ 1 ];
10432 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10434 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10435 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10436 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10438 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10439 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10442 // add other links of the faces to linkList
10443 // -----------------------------------------
10445 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10446 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10447 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10448 if ( !iter_isnew.second ) { // already in a set: no need to process
10449 linkIdSet.erase( iter_isnew.first );
10451 else // new in set == encountered for the first time: add
10453 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10454 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10455 linkList[0].push_back ( NLink( n1, n2 ));
10456 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10461 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10464 } // loop on link lists
10466 if ( aResult == SEW_OK &&
10467 ( //linkIt[0] != linkList[0].end() ||
10468 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10469 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10470 " " << (faceSetPtr[1]->empty()));
10471 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10474 // ====================================================================
10475 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10476 // ====================================================================
10478 // delete temporary faces
10479 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10480 // while ( tmpFaceIt->more() )
10481 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10482 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10483 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10484 aMesh->RemoveElement(*tmpFaceIt);
10486 if ( aResult != SEW_OK)
10489 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10490 // loop on nodes replacement map
10491 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10492 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10493 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10494 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10495 nodeIDsToRemove.push_back( nToRemove->GetID() );
10496 // loop on elements sharing nToRemove
10497 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10498 while ( invElemIt->more() ) {
10499 const SMDS_MeshElement* e = invElemIt->next();
10500 // get a new suite of nodes: make replacement
10501 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10502 vector< const SMDS_MeshNode*> nodes( nbNodes );
10503 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10504 while ( nIt->more() ) {
10505 const SMDS_MeshNode* n =
10506 static_cast<const SMDS_MeshNode*>( nIt->next() );
10507 nnIt = nReplaceMap.find( n );
10508 if ( nnIt != nReplaceMap.end() ) {
10510 n = (*nnIt).second;
10514 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10515 // elemIDsToRemove.push_back( e->GetID() );
10519 SMDSAbs_ElementType etyp = e->GetType();
10520 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10523 myLastCreatedElems.Append(newElem);
10524 AddToSameGroups(newElem, e, aMesh);
10525 int aShapeId = e->getshapeId();
10528 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10531 aMesh->RemoveElement(e);
10536 Remove( nodeIDsToRemove, true );
10541 //================================================================================
10543 * \brief Find corresponding nodes in two sets of faces
10544 * \param theSide1 - first face set
10545 * \param theSide2 - second first face
10546 * \param theFirstNode1 - a boundary node of set 1
10547 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10548 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10549 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10550 * \param nReplaceMap - output map of corresponding nodes
10551 * \return bool - is a success or not
10553 //================================================================================
10556 //#define DEBUG_MATCHING_NODES
10559 SMESH_MeshEditor::Sew_Error
10560 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10561 set<const SMDS_MeshElement*>& theSide2,
10562 const SMDS_MeshNode* theFirstNode1,
10563 const SMDS_MeshNode* theFirstNode2,
10564 const SMDS_MeshNode* theSecondNode1,
10565 const SMDS_MeshNode* theSecondNode2,
10566 TNodeNodeMap & nReplaceMap)
10568 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10570 nReplaceMap.clear();
10571 if ( theFirstNode1 != theFirstNode2 )
10572 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10573 if ( theSecondNode1 != theSecondNode2 )
10574 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10576 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10577 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10579 list< NLink > linkList[2];
10580 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10581 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10583 // loop on links in linkList; find faces by links and append links
10584 // of the found faces to linkList
10585 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10586 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10587 NLink link[] = { *linkIt[0], *linkIt[1] };
10588 if ( linkSet.find( link[0] ) == linkSet.end() )
10591 // by links, find faces in the face sets,
10592 // and find indices of link nodes in the found faces;
10593 // in a face set, there is only one or no face sharing a link
10594 // ---------------------------------------------------------------
10596 const SMDS_MeshElement* face[] = { 0, 0 };
10597 list<const SMDS_MeshNode*> notLinkNodes[2];
10598 //bool reverse[] = { false, false }; // order of notLinkNodes
10600 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10602 const SMDS_MeshNode* n1 = link[iSide].first;
10603 const SMDS_MeshNode* n2 = link[iSide].second;
10604 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10605 set< const SMDS_MeshElement* > facesOfNode1;
10606 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10608 // during a loop of the first node, we find all faces around n1,
10609 // during a loop of the second node, we find one face sharing both n1 and n2
10610 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10611 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10612 while ( fIt->more() ) { // loop on faces sharing a node
10613 const SMDS_MeshElement* f = fIt->next();
10614 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10615 ! facesOfNode1.insert( f ).second ) // f encounters twice
10617 if ( face[ iSide ] ) {
10618 MESSAGE( "2 faces per link " );
10619 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10622 faceSet->erase( f );
10624 // get not link nodes
10625 int nbN = f->NbNodes();
10626 if ( f->IsQuadratic() )
10628 nbNodes[ iSide ] = nbN;
10629 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10630 int i1 = f->GetNodeIndex( n1 );
10631 int i2 = f->GetNodeIndex( n2 );
10632 int iEnd = nbN, iBeg = -1, iDelta = 1;
10633 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10635 std::swap( iEnd, iBeg ); iDelta = -1;
10640 if ( i == iEnd ) i = iBeg + iDelta;
10641 if ( i == i1 ) break;
10642 nodes.push_back ( f->GetNode( i ) );
10648 // check similarity of elements of the sides
10649 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10650 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10651 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10652 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10655 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10659 // set nodes to merge
10660 // -------------------
10662 if ( face[0] && face[1] ) {
10663 if ( nbNodes[0] != nbNodes[1] ) {
10664 MESSAGE("Diff nb of face nodes");
10665 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10667 #ifdef DEBUG_MATCHING_NODES
10668 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10669 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10670 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10672 int nbN = nbNodes[0];
10674 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10675 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10676 for ( int i = 0 ; i < nbN - 2; ++i ) {
10677 #ifdef DEBUG_MATCHING_NODES
10678 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10680 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10684 // add other links of the face 1 to linkList
10685 // -----------------------------------------
10687 const SMDS_MeshElement* f0 = face[0];
10688 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10689 for ( int i = 0; i < nbN; i++ )
10691 const SMDS_MeshNode* n2 = f0->GetNode( i );
10692 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10693 linkSet.insert( SMESH_TLink( n1, n2 ));
10694 if ( !iter_isnew.second ) { // already in a set: no need to process
10695 linkSet.erase( iter_isnew.first );
10697 else // new in set == encountered for the first time: add
10699 #ifdef DEBUG_MATCHING_NODES
10700 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10701 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10703 linkList[0].push_back ( NLink( n1, n2 ));
10704 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10709 } // loop on link lists
10714 //================================================================================
10716 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10717 \param theElems - the list of elements (edges or faces) to be replicated
10718 The nodes for duplication could be found from these elements
10719 \param theNodesNot - list of nodes to NOT replicate
10720 \param theAffectedElems - the list of elements (cells and edges) to which the
10721 replicated nodes should be associated to.
10722 \return TRUE if operation has been completed successfully, FALSE otherwise
10724 //================================================================================
10726 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10727 const TIDSortedElemSet& theNodesNot,
10728 const TIDSortedElemSet& theAffectedElems )
10730 myLastCreatedElems.Clear();
10731 myLastCreatedNodes.Clear();
10733 if ( theElems.size() == 0 )
10736 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10741 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10742 // duplicate elements and nodes
10743 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10744 // replce nodes by duplications
10745 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10749 //================================================================================
10751 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10752 \param theMeshDS - mesh instance
10753 \param theElems - the elements replicated or modified (nodes should be changed)
10754 \param theNodesNot - nodes to NOT replicate
10755 \param theNodeNodeMap - relation of old node to new created node
10756 \param theIsDoubleElem - flag os to replicate element or modify
10757 \return TRUE if operation has been completed successfully, FALSE otherwise
10759 //================================================================================
10761 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10762 const TIDSortedElemSet& theElems,
10763 const TIDSortedElemSet& theNodesNot,
10764 std::map< const SMDS_MeshNode*,
10765 const SMDS_MeshNode* >& theNodeNodeMap,
10766 const bool theIsDoubleElem )
10768 MESSAGE("doubleNodes");
10769 // iterate on through element and duplicate them (by nodes duplication)
10771 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10772 for ( ; elemItr != theElems.end(); ++elemItr )
10774 const SMDS_MeshElement* anElem = *elemItr;
10778 bool isDuplicate = false;
10779 // duplicate nodes to duplicate element
10780 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10781 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10783 while ( anIter->more() )
10786 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10787 SMDS_MeshNode* aNewNode = aCurrNode;
10788 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10789 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10790 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10793 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10794 theNodeNodeMap[ aCurrNode ] = aNewNode;
10795 myLastCreatedNodes.Append( aNewNode );
10797 isDuplicate |= (aCurrNode != aNewNode);
10798 newNodes[ ind++ ] = aNewNode;
10800 if ( !isDuplicate )
10803 if ( theIsDoubleElem )
10804 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10807 MESSAGE("ChangeElementNodes");
10808 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10815 //================================================================================
10817 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10818 \param theNodes - identifiers of nodes to be doubled
10819 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10820 nodes. If list of element identifiers is empty then nodes are doubled but
10821 they not assigned to elements
10822 \return TRUE if operation has been completed successfully, FALSE otherwise
10824 //================================================================================
10826 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10827 const std::list< int >& theListOfModifiedElems )
10829 MESSAGE("DoubleNodes");
10830 myLastCreatedElems.Clear();
10831 myLastCreatedNodes.Clear();
10833 if ( theListOfNodes.size() == 0 )
10836 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10840 // iterate through nodes and duplicate them
10842 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10844 std::list< int >::const_iterator aNodeIter;
10845 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10847 int aCurr = *aNodeIter;
10848 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10854 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10857 anOldNodeToNewNode[ aNode ] = aNewNode;
10858 myLastCreatedNodes.Append( aNewNode );
10862 // Create map of new nodes for modified elements
10864 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10866 std::list< int >::const_iterator anElemIter;
10867 for ( anElemIter = theListOfModifiedElems.begin();
10868 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10870 int aCurr = *anElemIter;
10871 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10875 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10877 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10879 while ( anIter->more() )
10881 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10882 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10884 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10885 aNodeArr[ ind++ ] = aNewNode;
10888 aNodeArr[ ind++ ] = aCurrNode;
10890 anElemToNodes[ anElem ] = aNodeArr;
10893 // Change nodes of elements
10895 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10896 anElemToNodesIter = anElemToNodes.begin();
10897 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10899 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10900 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10903 MESSAGE("ChangeElementNodes");
10904 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10913 //================================================================================
10915 \brief Check if element located inside shape
10916 \return TRUE if IN or ON shape, FALSE otherwise
10918 //================================================================================
10920 template<class Classifier>
10921 bool isInside(const SMDS_MeshElement* theElem,
10922 Classifier& theClassifier,
10923 const double theTol)
10925 gp_XYZ centerXYZ (0, 0, 0);
10926 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10927 while (aNodeItr->more())
10928 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10930 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10931 theClassifier.Perform(aPnt, theTol);
10932 TopAbs_State aState = theClassifier.State();
10933 return (aState == TopAbs_IN || aState == TopAbs_ON );
10936 //================================================================================
10938 * \brief Classifier of the 3D point on the TopoDS_Face
10939 * with interaface suitable for isInside()
10941 //================================================================================
10943 struct _FaceClassifier
10945 Extrema_ExtPS _extremum;
10946 BRepAdaptor_Surface _surface;
10947 TopAbs_State _state;
10949 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10951 _extremum.Initialize( _surface,
10952 _surface.FirstUParameter(), _surface.LastUParameter(),
10953 _surface.FirstVParameter(), _surface.LastVParameter(),
10954 _surface.Tolerance(), _surface.Tolerance() );
10956 void Perform(const gp_Pnt& aPnt, double theTol)
10958 _state = TopAbs_OUT;
10959 _extremum.Perform(aPnt);
10960 if ( _extremum.IsDone() )
10961 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10962 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10963 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10965 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10968 TopAbs_State State() const
10975 //================================================================================
10977 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10978 This method is the first step of DoubleNodeElemGroupsInRegion.
10979 \param theElems - list of groups of elements (edges or faces) to be replicated
10980 \param theNodesNot - list of groups of nodes not to replicated
10981 \param theShape - shape to detect affected elements (element which geometric center
10982 located on or inside shape).
10983 The replicated nodes should be associated to affected elements.
10984 \return groups of affected elements
10985 \sa DoubleNodeElemGroupsInRegion()
10987 //================================================================================
10989 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10990 const TIDSortedElemSet& theNodesNot,
10991 const TopoDS_Shape& theShape,
10992 TIDSortedElemSet& theAffectedElems)
10994 if ( theShape.IsNull() )
10997 const double aTol = Precision::Confusion();
10998 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10999 auto_ptr<_FaceClassifier> aFaceClassifier;
11000 if ( theShape.ShapeType() == TopAbs_SOLID )
11002 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11003 bsc3d->PerformInfinitePoint(aTol);
11005 else if (theShape.ShapeType() == TopAbs_FACE )
11007 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11010 // iterates on indicated elements and get elements by back references from their nodes
11011 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11012 for ( ; elemItr != theElems.end(); ++elemItr )
11014 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11018 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11019 while ( nodeItr->more() )
11021 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11022 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11024 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11025 while ( backElemItr->more() )
11027 const SMDS_MeshElement* curElem = backElemItr->next();
11028 if ( curElem && theElems.find(curElem) == theElems.end() &&
11030 isInside( curElem, *bsc3d, aTol ) :
11031 isInside( curElem, *aFaceClassifier, aTol )))
11032 theAffectedElems.insert( curElem );
11039 //================================================================================
11041 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11042 \param theElems - group of of elements (edges or faces) to be replicated
11043 \param theNodesNot - group of nodes not to replicate
11044 \param theShape - shape to detect affected elements (element which geometric center
11045 located on or inside shape).
11046 The replicated nodes should be associated to affected elements.
11047 \return TRUE if operation has been completed successfully, FALSE otherwise
11049 //================================================================================
11051 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11052 const TIDSortedElemSet& theNodesNot,
11053 const TopoDS_Shape& theShape )
11055 if ( theShape.IsNull() )
11058 const double aTol = Precision::Confusion();
11059 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11060 auto_ptr<_FaceClassifier> aFaceClassifier;
11061 if ( theShape.ShapeType() == TopAbs_SOLID )
11063 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11064 bsc3d->PerformInfinitePoint(aTol);
11066 else if (theShape.ShapeType() == TopAbs_FACE )
11068 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11071 // iterates on indicated elements and get elements by back references from their nodes
11072 TIDSortedElemSet anAffected;
11073 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11074 for ( ; elemItr != theElems.end(); ++elemItr )
11076 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11080 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11081 while ( nodeItr->more() )
11083 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11084 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11086 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11087 while ( backElemItr->more() )
11089 const SMDS_MeshElement* curElem = backElemItr->next();
11090 if ( curElem && theElems.find(curElem) == theElems.end() &&
11092 isInside( curElem, *bsc3d, aTol ) :
11093 isInside( curElem, *aFaceClassifier, aTol )))
11094 anAffected.insert( curElem );
11098 return DoubleNodes( theElems, theNodesNot, anAffected );
11102 * \brief compute an oriented angle between two planes defined by four points.
11103 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11104 * @param p0 base of the rotation axe
11105 * @param p1 extremity of the rotation axe
11106 * @param g1 belongs to the first plane
11107 * @param g2 belongs to the second plane
11109 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11111 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11112 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11113 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11114 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11115 gp_Vec vref(p0, p1);
11118 gp_Vec n1 = vref.Crossed(v1);
11119 gp_Vec n2 = vref.Crossed(v2);
11120 return n2.AngleWithRef(n1, vref);
11124 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11125 * The list of groups must describe a partition of the mesh volumes.
11126 * The nodes of the internal faces at the boundaries of the groups are doubled.
11127 * In option, the internal faces are replaced by flat elements.
11128 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11129 * The flat elements are stored in groups of volumes.
11130 * @param theElems - list of groups of volumes, where a group of volume is a set of
11131 * SMDS_MeshElements sorted by Id.
11132 * @param createJointElems - if TRUE, create the elements
11133 * @return TRUE if operation has been completed successfully, FALSE otherwise
11135 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11136 bool createJointElems)
11138 MESSAGE("----------------------------------------------");
11139 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11140 MESSAGE("----------------------------------------------");
11142 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11143 meshDS->BuildDownWardConnectivity(true);
11145 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11147 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11148 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11149 // build the list of nodes shared by 2 or more domains, with their domain indexes
11151 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11152 std::map<int,int>celldom; // cell vtkId --> domain
11153 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11154 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11155 faceDomains.clear();
11157 cellDomains.clear();
11158 nodeDomains.clear();
11159 std::map<int,int> emptyMap;
11160 std::set<int> emptySet;
11163 for (int idom = 0; idom < theElems.size(); idom++)
11166 // --- build a map (face to duplicate --> volume to modify)
11167 // with all the faces shared by 2 domains (group of elements)
11168 // and corresponding volume of this domain, for each shared face.
11169 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11171 //MESSAGE("Domain " << idom);
11172 const TIDSortedElemSet& domain = theElems[idom];
11173 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11174 for (; elemItr != domain.end(); ++elemItr)
11176 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11179 int vtkId = anElem->getVtkId();
11180 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11181 int neighborsVtkIds[NBMAXNEIGHBORS];
11182 int downIds[NBMAXNEIGHBORS];
11183 unsigned char downTypes[NBMAXNEIGHBORS];
11184 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11185 for (int n = 0; n < nbNeighbors; n++)
11187 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11188 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11189 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11191 DownIdType face(downIds[n], downTypes[n]);
11192 if (!faceDomains.count(face))
11193 faceDomains[face] = emptyMap; // create an empty entry for face
11194 if (!faceDomains[face].count(idom))
11196 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11197 celldom[vtkId] = idom;
11198 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11205 //MESSAGE("Number of shared faces " << faceDomains.size());
11206 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11208 // --- explore the shared faces domain by domain,
11209 // explore the nodes of the face and see if they belong to a cell in the domain,
11210 // which has only a node or an edge on the border (not a shared face)
11212 for (int idomain = 0; idomain < theElems.size(); idomain++)
11214 //MESSAGE("Domain " << idomain);
11215 const TIDSortedElemSet& domain = theElems[idomain];
11216 itface = faceDomains.begin();
11217 for (; itface != faceDomains.end(); ++itface)
11219 std::map<int, int> domvol = itface->second;
11220 if (!domvol.count(idomain))
11222 DownIdType face = itface->first;
11223 //MESSAGE(" --- face " << face.cellId);
11224 std::set<int> oldNodes;
11226 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11227 std::set<int>::iterator itn = oldNodes.begin();
11228 for (; itn != oldNodes.end(); ++itn)
11231 //MESSAGE(" node " << oldId);
11232 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11233 for (int i=0; i<l.ncells; i++)
11235 int vtkId = l.cells[i];
11236 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11237 if (!domain.count(anElem))
11239 int vtkType = grid->GetCellType(vtkId);
11240 int downId = grid->CellIdToDownId(vtkId);
11243 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11244 continue; // not OK at this stage of the algorithm:
11245 //no cells created after BuildDownWardConnectivity
11247 DownIdType aCell(downId, vtkType);
11248 if (!cellDomains.count(aCell))
11249 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11250 cellDomains[aCell][idomain] = vtkId;
11251 celldom[vtkId] = idomain;
11252 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11258 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11259 // for each shared face, get the nodes
11260 // for each node, for each domain of the face, create a clone of the node
11262 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11263 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11264 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11266 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11267 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11268 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11270 for (int idomain = 0; idomain < theElems.size(); idomain++)
11272 itface = faceDomains.begin();
11273 for (; itface != faceDomains.end(); ++itface)
11275 std::map<int, int> domvol = itface->second;
11276 if (!domvol.count(idomain))
11278 DownIdType face = itface->first;
11279 //MESSAGE(" --- face " << face.cellId);
11280 std::set<int> oldNodes;
11282 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11283 std::set<int>::iterator itn = oldNodes.begin();
11284 for (; itn != oldNodes.end(); ++itn)
11287 //MESSAGE("-+-+-a node " << oldId);
11288 if (!nodeDomains.count(oldId))
11289 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11290 if (nodeDomains[oldId].empty())
11292 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11293 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11295 std::map<int, int>::iterator itdom = domvol.begin();
11296 for (; itdom != domvol.end(); ++itdom)
11298 int idom = itdom->first;
11299 //MESSAGE(" domain " << idom);
11300 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11302 if (nodeDomains[oldId].size() >= 2) // a multiple node
11304 vector<int> orderedDoms;
11305 //MESSAGE("multiple node " << oldId);
11306 if (mutipleNodes.count(oldId))
11307 orderedDoms = mutipleNodes[oldId];
11310 map<int,int>::iterator it = nodeDomains[oldId].begin();
11311 for (; it != nodeDomains[oldId].end(); ++it)
11312 orderedDoms.push_back(it->first);
11314 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11315 //stringstream txt;
11316 //for (int i=0; i<orderedDoms.size(); i++)
11317 // txt << orderedDoms[i] << " ";
11318 //MESSAGE("orderedDoms " << txt.str());
11319 mutipleNodes[oldId] = orderedDoms;
11321 double *coords = grid->GetPoint(oldId);
11322 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11323 int newId = newNode->getVtkId();
11324 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11325 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11332 for (int idomain = 0; idomain < theElems.size(); idomain++)
11334 itface = faceDomains.begin();
11335 for (; itface != faceDomains.end(); ++itface)
11337 std::map<int, int> domvol = itface->second;
11338 if (!domvol.count(idomain))
11340 DownIdType face = itface->first;
11341 //MESSAGE(" --- face " << face.cellId);
11342 std::set<int> oldNodes;
11344 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11345 int nbMultipleNodes = 0;
11346 std::set<int>::iterator itn = oldNodes.begin();
11347 for (; itn != oldNodes.end(); ++itn)
11350 if (mutipleNodes.count(oldId))
11353 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11355 //MESSAGE("multiple Nodes detected on a shared face");
11356 int downId = itface->first.cellId;
11357 unsigned char cellType = itface->first.cellType;
11358 // --- shared edge or shared face ?
11359 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11362 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11363 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11364 if (mutipleNodes.count(nodes[i]))
11365 if (!mutipleNodesToFace.count(nodes[i]))
11366 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11368 else // shared face (between two volumes)
11370 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11371 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11372 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11373 for (int ie =0; ie < nbEdges; ie++)
11376 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11377 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11379 vector<int> vn0 = mutipleNodes[nodes[0]];
11380 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11382 for (int i0 = 0; i0 < vn0.size(); i0++)
11383 for (int i1 = 0; i1 < vn1.size(); i1++)
11384 if (vn0[i0] == vn1[i1])
11385 doms.push_back(vn0[i0]);
11386 if (doms.size() >2)
11388 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11389 double *coords = grid->GetPoint(nodes[0]);
11390 gp_Pnt p0(coords[0], coords[1], coords[2]);
11391 coords = grid->GetPoint(nodes[nbNodes - 1]);
11392 gp_Pnt p1(coords[0], coords[1], coords[2]);
11394 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11395 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11396 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11397 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11398 for (int id=0; id < doms.size(); id++)
11400 int idom = doms[id];
11401 for (int ivol=0; ivol<nbvol; ivol++)
11403 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11404 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11405 if (theElems[idom].count(elem))
11407 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11408 domvol[idom] = svol;
11409 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11411 vtkIdType npts = 0;
11412 vtkIdType* pts = 0;
11413 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11414 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11417 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11418 angleDom[idom] = 0;
11422 gp_Pnt g(values[0], values[1], values[2]);
11423 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11424 //MESSAGE(" angle=" << angleDom[idom]);
11430 map<double, int> sortedDom; // sort domains by angle
11431 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11432 sortedDom[ia->second] = ia->first;
11433 vector<int> vnodes;
11435 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11437 vdom.push_back(ib->second);
11438 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11440 for (int ino = 0; ino < nbNodes; ino++)
11441 vnodes.push_back(nodes[ino]);
11442 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11451 // --- iterate on shared faces (volumes to modify, face to extrude)
11452 // get node id's of the face (id SMDS = id VTK)
11453 // create flat element with old and new nodes if requested
11455 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11456 // (domain1 X domain2) = domain1 + MAXINT*domain2
11458 std::map<int, std::map<long,int> > nodeQuadDomains;
11459 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11461 if (createJointElems)
11464 string joints2DName = "joints2D";
11465 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11466 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11467 string joints3DName = "joints3D";
11468 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11469 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11471 itface = faceDomains.begin();
11472 for (; itface != faceDomains.end(); ++itface)
11474 DownIdType face = itface->first;
11475 std::set<int> oldNodes;
11476 std::set<int>::iterator itn;
11478 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11480 std::map<int, int> domvol = itface->second;
11481 std::map<int, int>::iterator itdom = domvol.begin();
11482 int dom1 = itdom->first;
11483 int vtkVolId = itdom->second;
11485 int dom2 = itdom->first;
11486 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11488 stringstream grpname;
11491 grpname << dom1 << "_" << dom2;
11493 grpname << dom2 << "_" << dom1;
11494 string namegrp = grpname.str();
11495 if (!mapOfJunctionGroups.count(namegrp))
11496 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11497 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11499 sgrp->Add(vol->GetID());
11500 if (vol->GetType() == SMDSAbs_Volume)
11501 joints3DGrp->Add(vol->GetID());
11502 else if (vol->GetType() == SMDSAbs_Face)
11503 joints2DGrp->Add(vol->GetID());
11507 // --- create volumes on multiple domain intersection if requested
11508 // iterate on mutipleNodesToFace
11509 // iterate on edgesMultiDomains
11511 if (createJointElems)
11513 // --- iterate on mutipleNodesToFace
11515 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11516 for (; itn != mutipleNodesToFace.end(); ++itn)
11518 int node = itn->first;
11519 vector<int> orderDom = itn->second;
11520 vector<vtkIdType> orderedNodes;
11521 for (int idom = 0; idom <orderDom.size(); idom++)
11522 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11523 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11525 stringstream grpname;
11527 grpname << 0 << "_" << 0;
11529 string namegrp = grpname.str();
11530 if (!mapOfJunctionGroups.count(namegrp))
11531 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11532 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11534 sgrp->Add(face->GetID());
11537 // --- iterate on edgesMultiDomains
11539 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11540 for (; ite != edgesMultiDomains.end(); ++ite)
11542 vector<int> nodes = ite->first;
11543 vector<int> orderDom = ite->second;
11544 vector<vtkIdType> orderedNodes;
11545 if (nodes.size() == 2)
11547 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11548 for (int ino=0; ino < nodes.size(); ino++)
11549 if (orderDom.size() == 3)
11550 for (int idom = 0; idom <orderDom.size(); idom++)
11551 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11553 for (int idom = orderDom.size()-1; idom >=0; idom--)
11554 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11555 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11558 string namegrp = "jointsMultiples";
11559 if (!mapOfJunctionGroups.count(namegrp))
11560 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11561 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11563 sgrp->Add(vol->GetID());
11567 INFOS("Quadratic multiple joints not implemented");
11568 // TODO quadratic nodes
11573 // --- list the explicit faces and edges of the mesh that need to be modified,
11574 // i.e. faces and edges built with one or more duplicated nodes.
11575 // associate these faces or edges to their corresponding domain.
11576 // only the first domain found is kept when a face or edge is shared
11578 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11579 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11580 faceOrEdgeDom.clear();
11583 for (int idomain = 0; idomain < theElems.size(); idomain++)
11585 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11586 for (; itnod != nodeDomains.end(); ++itnod)
11588 int oldId = itnod->first;
11589 //MESSAGE(" node " << oldId);
11590 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11591 for (int i = 0; i < l.ncells; i++)
11593 int vtkId = l.cells[i];
11594 int vtkType = grid->GetCellType(vtkId);
11595 int downId = grid->CellIdToDownId(vtkId);
11597 continue; // new cells: not to be modified
11598 DownIdType aCell(downId, vtkType);
11599 int volParents[1000];
11600 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11601 for (int j = 0; j < nbvol; j++)
11602 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11603 if (!feDom.count(vtkId))
11605 feDom[vtkId] = idomain;
11606 faceOrEdgeDom[aCell] = emptyMap;
11607 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11608 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11609 // << " type " << vtkType << " downId " << downId);
11615 // --- iterate on shared faces (volumes to modify, face to extrude)
11616 // get node id's of the face
11617 // replace old nodes by new nodes in volumes, and update inverse connectivity
11619 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11620 for (int m=0; m<3; m++)
11622 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11623 itface = (*amap).begin();
11624 for (; itface != (*amap).end(); ++itface)
11626 DownIdType face = itface->first;
11627 std::set<int> oldNodes;
11628 std::set<int>::iterator itn;
11630 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11631 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11632 std::map<int, int> localClonedNodeIds;
11634 std::map<int, int> domvol = itface->second;
11635 std::map<int, int>::iterator itdom = domvol.begin();
11636 for (; itdom != domvol.end(); ++itdom)
11638 int idom = itdom->first;
11639 int vtkVolId = itdom->second;
11640 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11641 localClonedNodeIds.clear();
11642 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11645 if (nodeDomains[oldId].count(idom))
11647 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11648 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11651 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11656 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11657 grid->BuildLinks();
11665 * \brief Double nodes on some external faces and create flat elements.
11666 * Flat elements are mainly used by some types of mechanic calculations.
11668 * Each group of the list must be constituted of faces.
11669 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11670 * @param theElems - list of groups of faces, where a group of faces is a set of
11671 * SMDS_MeshElements sorted by Id.
11672 * @return TRUE if operation has been completed successfully, FALSE otherwise
11674 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11676 MESSAGE("-------------------------------------------------");
11677 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11678 MESSAGE("-------------------------------------------------");
11680 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11682 // --- For each group of faces
11683 // duplicate the nodes, create a flat element based on the face
11684 // replace the nodes of the faces by their clones
11686 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11687 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11688 clonedNodes.clear();
11689 intermediateNodes.clear();
11690 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11691 mapOfJunctionGroups.clear();
11693 for (int idom = 0; idom < theElems.size(); idom++)
11695 const TIDSortedElemSet& domain = theElems[idom];
11696 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11697 for (; elemItr != domain.end(); ++elemItr)
11699 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11700 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11703 // MESSAGE("aFace=" << aFace->GetID());
11704 bool isQuad = aFace->IsQuadratic();
11705 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11707 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11709 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11710 while (nodeIt->more())
11712 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11713 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11715 ln2.push_back(node);
11717 ln0.push_back(node);
11719 const SMDS_MeshNode* clone = 0;
11720 if (!clonedNodes.count(node))
11722 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11723 clonedNodes[node] = clone;
11726 clone = clonedNodes[node];
11729 ln3.push_back(clone);
11731 ln1.push_back(clone);
11733 const SMDS_MeshNode* inter = 0;
11734 if (isQuad && (!isMedium))
11736 if (!intermediateNodes.count(node))
11738 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11739 intermediateNodes[node] = inter;
11742 inter = intermediateNodes[node];
11743 ln4.push_back(inter);
11747 // --- extrude the face
11749 vector<const SMDS_MeshNode*> ln;
11750 SMDS_MeshVolume* vol = 0;
11751 vtkIdType aType = aFace->GetVtkType();
11755 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11756 // MESSAGE("vol prism " << vol->GetID());
11757 ln.push_back(ln1[0]);
11758 ln.push_back(ln1[1]);
11759 ln.push_back(ln1[2]);
11762 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11763 // MESSAGE("vol hexa " << vol->GetID());
11764 ln.push_back(ln1[0]);
11765 ln.push_back(ln1[1]);
11766 ln.push_back(ln1[2]);
11767 ln.push_back(ln1[3]);
11769 case VTK_QUADRATIC_TRIANGLE:
11770 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11771 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11772 // MESSAGE("vol quad prism " << vol->GetID());
11773 ln.push_back(ln1[0]);
11774 ln.push_back(ln1[1]);
11775 ln.push_back(ln1[2]);
11776 ln.push_back(ln3[0]);
11777 ln.push_back(ln3[1]);
11778 ln.push_back(ln3[2]);
11780 case VTK_QUADRATIC_QUAD:
11781 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11782 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11783 // ln4[0], ln4[1], ln4[2], ln4[3]);
11784 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11785 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11786 ln4[0], ln4[1], ln4[2], ln4[3]);
11787 // MESSAGE("vol quad hexa " << vol->GetID());
11788 ln.push_back(ln1[0]);
11789 ln.push_back(ln1[1]);
11790 ln.push_back(ln1[2]);
11791 ln.push_back(ln1[3]);
11792 ln.push_back(ln3[0]);
11793 ln.push_back(ln3[1]);
11794 ln.push_back(ln3[2]);
11795 ln.push_back(ln3[3]);
11805 stringstream grpname;
11809 string namegrp = grpname.str();
11810 if (!mapOfJunctionGroups.count(namegrp))
11811 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11812 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11814 sgrp->Add(vol->GetID());
11817 // --- modify the face
11819 aFace->ChangeNodes(&ln[0], ln.size());
11826 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11827 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11828 * groups of faces to remove inside the object, (idem edges).
11829 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11831 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11832 const TopoDS_Shape& theShape,
11833 SMESH_NodeSearcher* theNodeSearcher,
11834 const char* groupName,
11835 std::vector<double>& nodesCoords,
11836 std::vector<std::vector<int> >& listOfListOfNodes)
11838 MESSAGE("--------------------------------");
11839 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11840 MESSAGE("--------------------------------");
11842 // --- zone of volumes to remove is given :
11843 // 1 either by a geom shape (one or more vertices) and a radius,
11844 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11845 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11846 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11847 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11848 // defined by it's name.
11850 SMESHDS_GroupBase* groupDS = 0;
11851 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11852 while ( groupIt->more() )
11855 SMESH_Group * group = groupIt->next();
11856 if ( !group ) continue;
11857 groupDS = group->GetGroupDS();
11858 if ( !groupDS || groupDS->IsEmpty() ) continue;
11859 std::string grpName = group->GetName();
11860 //MESSAGE("grpName=" << grpName);
11861 if (grpName == groupName)
11867 bool isNodeGroup = false;
11868 bool isNodeCoords = false;
11871 if (groupDS->GetType() != SMDSAbs_Node)
11873 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11876 if (nodesCoords.size() > 0)
11877 isNodeCoords = true; // a list o nodes given by their coordinates
11878 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11880 // --- define groups to build
11882 int idg; // --- group of SMDS volumes
11883 string grpvName = groupName;
11884 grpvName += "_vol";
11885 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11888 MESSAGE("group not created " << grpvName);
11891 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11893 int idgs; // --- group of SMDS faces on the skin
11894 string grpsName = groupName;
11895 grpsName += "_skin";
11896 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11899 MESSAGE("group not created " << grpsName);
11902 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11904 int idgi; // --- group of SMDS faces internal (several shapes)
11905 string grpiName = groupName;
11906 grpiName += "_internalFaces";
11907 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11910 MESSAGE("group not created " << grpiName);
11913 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11915 int idgei; // --- group of SMDS faces internal (several shapes)
11916 string grpeiName = groupName;
11917 grpeiName += "_internalEdges";
11918 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11921 MESSAGE("group not created " << grpeiName);
11924 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11926 // --- build downward connectivity
11928 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11929 meshDS->BuildDownWardConnectivity(true);
11930 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11932 // --- set of volumes detected inside
11934 std::set<int> setOfInsideVol;
11935 std::set<int> setOfVolToCheck;
11937 std::vector<gp_Pnt> gpnts;
11940 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11942 MESSAGE("group of nodes provided");
11943 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11944 while ( elemIt->more() )
11946 const SMDS_MeshElement* elem = elemIt->next();
11949 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11952 SMDS_MeshElement* vol = 0;
11953 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11954 while (volItr->more())
11956 vol = (SMDS_MeshElement*)volItr->next();
11957 setOfInsideVol.insert(vol->getVtkId());
11958 sgrp->Add(vol->GetID());
11962 else if (isNodeCoords)
11964 MESSAGE("list of nodes coordinates provided");
11967 while (i < nodesCoords.size()-2)
11969 double x = nodesCoords[i++];
11970 double y = nodesCoords[i++];
11971 double z = nodesCoords[i++];
11972 gp_Pnt p = gp_Pnt(x, y ,z);
11973 gpnts.push_back(p);
11974 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11977 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11979 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11980 TopTools_IndexedMapOfShape vertexMap;
11981 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11982 gp_Pnt p = gp_Pnt(0,0,0);
11983 if (vertexMap.Extent() < 1)
11986 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11988 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11989 p = BRep_Tool::Pnt(vertex);
11990 gpnts.push_back(p);
11991 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11995 if (gpnts.size() > 0)
11998 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12000 nodeId = startNode->GetID();
12001 MESSAGE("nodeId " << nodeId);
12003 double radius2 = radius*radius;
12004 MESSAGE("radius2 " << radius2);
12006 // --- volumes on start node
12008 setOfVolToCheck.clear();
12009 SMDS_MeshElement* startVol = 0;
12010 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12011 while (volItr->more())
12013 startVol = (SMDS_MeshElement*)volItr->next();
12014 setOfVolToCheck.insert(startVol->getVtkId());
12016 if (setOfVolToCheck.empty())
12018 MESSAGE("No volumes found");
12022 // --- starting with central volumes then their neighbors, check if they are inside
12023 // or outside the domain, until no more new neighbor volume is inside.
12024 // Fill the group of inside volumes
12026 std::map<int, double> mapOfNodeDistance2;
12027 mapOfNodeDistance2.clear();
12028 std::set<int> setOfOutsideVol;
12029 while (!setOfVolToCheck.empty())
12031 std::set<int>::iterator it = setOfVolToCheck.begin();
12033 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12034 bool volInside = false;
12035 vtkIdType npts = 0;
12036 vtkIdType* pts = 0;
12037 grid->GetCellPoints(vtkId, npts, pts);
12038 for (int i=0; i<npts; i++)
12040 double distance2 = 0;
12041 if (mapOfNodeDistance2.count(pts[i]))
12043 distance2 = mapOfNodeDistance2[pts[i]];
12044 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12048 double *coords = grid->GetPoint(pts[i]);
12049 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12051 for (int j=0; j<gpnts.size(); j++)
12053 double d2 = aPoint.SquareDistance(gpnts[j]);
12054 if (d2 < distance2)
12057 if (distance2 < radius2)
12061 mapOfNodeDistance2[pts[i]] = distance2;
12062 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12064 if (distance2 < radius2)
12066 volInside = true; // one or more nodes inside the domain
12067 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12073 setOfInsideVol.insert(vtkId);
12074 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12075 int neighborsVtkIds[NBMAXNEIGHBORS];
12076 int downIds[NBMAXNEIGHBORS];
12077 unsigned char downTypes[NBMAXNEIGHBORS];
12078 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12079 for (int n = 0; n < nbNeighbors; n++)
12080 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12081 setOfVolToCheck.insert(neighborsVtkIds[n]);
12085 setOfOutsideVol.insert(vtkId);
12086 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12088 setOfVolToCheck.erase(vtkId);
12092 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12093 // If yes, add the volume to the inside set
12095 bool addedInside = true;
12096 std::set<int> setOfVolToReCheck;
12097 while (addedInside)
12099 MESSAGE(" --------------------------- re check");
12100 addedInside = false;
12101 std::set<int>::iterator itv = setOfInsideVol.begin();
12102 for (; itv != setOfInsideVol.end(); ++itv)
12105 int neighborsVtkIds[NBMAXNEIGHBORS];
12106 int downIds[NBMAXNEIGHBORS];
12107 unsigned char downTypes[NBMAXNEIGHBORS];
12108 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12109 for (int n = 0; n < nbNeighbors; n++)
12110 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12111 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12113 setOfVolToCheck = setOfVolToReCheck;
12114 setOfVolToReCheck.clear();
12115 while (!setOfVolToCheck.empty())
12117 std::set<int>::iterator it = setOfVolToCheck.begin();
12119 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12121 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12122 int countInside = 0;
12123 int neighborsVtkIds[NBMAXNEIGHBORS];
12124 int downIds[NBMAXNEIGHBORS];
12125 unsigned char downTypes[NBMAXNEIGHBORS];
12126 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12127 for (int n = 0; n < nbNeighbors; n++)
12128 if (setOfInsideVol.count(neighborsVtkIds[n]))
12130 MESSAGE("countInside " << countInside);
12131 if (countInside > 1)
12133 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12134 setOfInsideVol.insert(vtkId);
12135 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12136 addedInside = true;
12139 setOfVolToReCheck.insert(vtkId);
12141 setOfVolToCheck.erase(vtkId);
12145 // --- map of Downward faces at the boundary, inside the global volume
12146 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12147 // fill group of SMDS faces inside the volume (when several volume shapes)
12148 // fill group of SMDS faces on the skin of the global volume (if skin)
12150 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12151 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12152 std::set<int>::iterator it = setOfInsideVol.begin();
12153 for (; it != setOfInsideVol.end(); ++it)
12156 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12157 int neighborsVtkIds[NBMAXNEIGHBORS];
12158 int downIds[NBMAXNEIGHBORS];
12159 unsigned char downTypes[NBMAXNEIGHBORS];
12160 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12161 for (int n = 0; n < nbNeighbors; n++)
12163 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12164 if (neighborDim == 3)
12166 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12168 DownIdType face(downIds[n], downTypes[n]);
12169 boundaryFaces[face] = vtkId;
12171 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12172 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12173 if (vtkFaceId >= 0)
12175 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12176 // find also the smds edges on this face
12177 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12178 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12179 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12180 for (int i = 0; i < nbEdges; i++)
12182 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12183 if (vtkEdgeId >= 0)
12184 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12188 else if (neighborDim == 2) // skin of the volume
12190 DownIdType face(downIds[n], downTypes[n]);
12191 skinFaces[face] = vtkId;
12192 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12193 if (vtkFaceId >= 0)
12194 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12199 // --- identify the edges constituting the wire of each subshape on the skin
12200 // define polylines with the nodes of edges, equivalent to wires
12201 // project polylines on subshapes, and partition, to get geom faces
12203 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12204 std::set<int> emptySet;
12206 std::set<int> shapeIds;
12208 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12209 while (itelem->more())
12211 const SMDS_MeshElement *elem = itelem->next();
12212 int shapeId = elem->getshapeId();
12213 int vtkId = elem->getVtkId();
12214 if (!shapeIdToVtkIdSet.count(shapeId))
12216 shapeIdToVtkIdSet[shapeId] = emptySet;
12217 shapeIds.insert(shapeId);
12219 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12222 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12223 std::set<DownIdType, DownIdCompare> emptyEdges;
12224 emptyEdges.clear();
12226 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12227 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12229 int shapeId = itShape->first;
12230 MESSAGE(" --- Shape ID --- "<< shapeId);
12231 shapeIdToEdges[shapeId] = emptyEdges;
12233 std::vector<int> nodesEdges;
12235 std::set<int>::iterator its = itShape->second.begin();
12236 for (; its != itShape->second.end(); ++its)
12239 MESSAGE(" " << vtkId);
12240 int neighborsVtkIds[NBMAXNEIGHBORS];
12241 int downIds[NBMAXNEIGHBORS];
12242 unsigned char downTypes[NBMAXNEIGHBORS];
12243 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12244 for (int n = 0; n < nbNeighbors; n++)
12246 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12248 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12249 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12250 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12252 DownIdType edge(downIds[n], downTypes[n]);
12253 if (!shapeIdToEdges[shapeId].count(edge))
12255 shapeIdToEdges[shapeId].insert(edge);
12257 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12258 nodesEdges.push_back(vtkNodeId[0]);
12259 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12260 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12266 std::list<int> order;
12268 if (nodesEdges.size() > 0)
12270 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12271 nodesEdges[0] = -1;
12272 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12273 nodesEdges[1] = -1; // do not reuse this edge
12277 int nodeTofind = order.back(); // try first to push back
12279 for (i = 0; i<nodesEdges.size(); i++)
12280 if (nodesEdges[i] == nodeTofind)
12282 if (i == nodesEdges.size())
12283 found = false; // no follower found on back
12286 if (i%2) // odd ==> use the previous one
12287 if (nodesEdges[i-1] < 0)
12291 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12292 nodesEdges[i-1] = -1;
12294 else // even ==> use the next one
12295 if (nodesEdges[i+1] < 0)
12299 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12300 nodesEdges[i+1] = -1;
12305 // try to push front
12307 nodeTofind = order.front(); // try to push front
12308 for (i = 0; i<nodesEdges.size(); i++)
12309 if (nodesEdges[i] == nodeTofind)
12311 if (i == nodesEdges.size())
12313 found = false; // no predecessor found on front
12316 if (i%2) // odd ==> use the previous one
12317 if (nodesEdges[i-1] < 0)
12321 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12322 nodesEdges[i-1] = -1;
12324 else // even ==> use the next one
12325 if (nodesEdges[i+1] < 0)
12329 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12330 nodesEdges[i+1] = -1;
12336 std::vector<int> nodes;
12337 nodes.push_back(shapeId);
12338 std::list<int>::iterator itl = order.begin();
12339 for (; itl != order.end(); itl++)
12341 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12342 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12344 listOfListOfNodes.push_back(nodes);
12347 // partition geom faces with blocFissure
12348 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12349 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12355 //================================================================================
12357 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12358 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12359 * \return TRUE if operation has been completed successfully, FALSE otherwise
12361 //================================================================================
12363 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12365 // iterates on volume elements and detect all free faces on them
12366 SMESHDS_Mesh* aMesh = GetMeshDS();
12369 //bool res = false;
12370 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12371 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12374 const SMDS_MeshVolume* volume = vIt->next();
12375 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12376 vTool.SetExternalNormal();
12377 //const bool isPoly = volume->IsPoly();
12378 const int iQuad = volume->IsQuadratic();
12379 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12381 if (!vTool.IsFreeFace(iface))
12384 vector<const SMDS_MeshNode *> nodes;
12385 int nbFaceNodes = vTool.NbFaceNodes(iface);
12386 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12388 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12389 nodes.push_back(faceNodes[inode]);
12390 if (iQuad) { // add medium nodes
12391 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12392 nodes.push_back(faceNodes[inode]);
12393 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12394 nodes.push_back(faceNodes[8]);
12396 // add new face based on volume nodes
12397 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12399 continue; // face already exsist
12401 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12405 return ( nbFree==(nbExisted+nbCreated) );
12410 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12412 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12414 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12417 //================================================================================
12419 * \brief Creates missing boundary elements
12420 * \param elements - elements whose boundary is to be checked
12421 * \param dimension - defines type of boundary elements to create
12422 * \param group - a group to store created boundary elements in
12423 * \param targetMesh - a mesh to store created boundary elements in
12424 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12425 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12426 * boundary elements will be copied into the targetMesh
12427 * \param toAddExistingBondary - if true, not only new but also pre-existing
12428 * boundary elements will be added into the new group
12429 * \param aroundElements - if true, elements will be created on boundary of given
12430 * elements else, on boundary of the whole mesh.
12431 * \return nb of added boundary elements
12433 //================================================================================
12435 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12436 Bnd_Dimension dimension,
12437 SMESH_Group* group/*=0*/,
12438 SMESH_Mesh* targetMesh/*=0*/,
12439 bool toCopyElements/*=false*/,
12440 bool toCopyExistingBoundary/*=false*/,
12441 bool toAddExistingBondary/*= false*/,
12442 bool aroundElements/*= false*/)
12444 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12445 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12446 // hope that all elements are of the same type, do not check them all
12447 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12448 throw SALOME_Exception(LOCALIZED("wrong element type"));
12451 toCopyElements = toCopyExistingBoundary = false;
12453 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12454 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12455 int nbAddedBnd = 0;
12457 // editor adding present bnd elements and optionally holding elements to add to the group
12458 SMESH_MeshEditor* presentEditor;
12459 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12460 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12462 SMESH_MesherHelper helper( *myMesh );
12463 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12464 SMDS_VolumeTool vTool;
12465 TIDSortedElemSet avoidSet;
12466 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12469 typedef vector<const SMDS_MeshNode*> TConnectivity;
12471 SMDS_ElemIteratorPtr eIt;
12472 if (elements.empty())
12473 eIt = aMesh->elementsIterator(elemType);
12475 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12477 while (eIt->more())
12479 const SMDS_MeshElement* elem = eIt->next();
12480 const int iQuad = elem->IsQuadratic();
12482 // ------------------------------------------------------------------------------------
12483 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12484 // ------------------------------------------------------------------------------------
12485 vector<const SMDS_MeshElement*> presentBndElems;
12486 vector<TConnectivity> missingBndElems;
12487 TConnectivity nodes;
12488 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12490 vTool.SetExternalNormal();
12491 const SMDS_MeshElement* otherVol = 0;
12492 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12494 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12495 ( !aroundElements || elements.count( otherVol )))
12497 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12498 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12499 if ( missType == SMDSAbs_Edge ) // boundary edges
12501 nodes.resize( 2+iQuad );
12502 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12504 for ( int j = 0; j < nodes.size(); ++j )
12506 if ( const SMDS_MeshElement* edge =
12507 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12508 presentBndElems.push_back( edge );
12510 missingBndElems.push_back( nodes );
12513 else // boundary face
12516 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12517 nodes.push_back( nn[inode] );
12518 if (iQuad) // add medium nodes
12519 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12520 nodes.push_back( nn[inode] );
12521 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12523 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12525 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12526 SMDSAbs_Face, /*noMedium=*/false ))
12527 presentBndElems.push_back( f );
12529 missingBndElems.push_back( nodes );
12531 if ( targetMesh != myMesh )
12533 // add 1D elements on face boundary to be added to a new mesh
12534 const SMDS_MeshElement* edge;
12535 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12538 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12540 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12541 if ( edge && avoidSet.insert( edge ).second )
12542 presentBndElems.push_back( edge );
12548 else // elem is a face ------------------------------------------
12550 avoidSet.clear(), avoidSet.insert( elem );
12551 int nbNodes = elem->NbCornerNodes();
12552 nodes.resize( 2 /*+ iQuad*/);
12553 for ( int i = 0; i < nbNodes; i++ )
12555 nodes[0] = elem->GetNode(i);
12556 nodes[1] = elem->GetNode((i+1)%nbNodes);
12557 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12558 continue; // not free link
12561 //nodes[2] = elem->GetNode( i + nbNodes );
12562 if ( const SMDS_MeshElement* edge =
12563 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12564 presentBndElems.push_back( edge );
12566 missingBndElems.push_back( nodes );
12570 // ---------------------------------
12571 // 2. Add missing boundary elements
12572 // ---------------------------------
12573 if ( targetMesh != myMesh )
12574 // instead of making a map of nodes in this mesh and targetMesh,
12575 // we create nodes with same IDs.
12576 for ( int i = 0; i < missingBndElems.size(); ++i )
12578 TConnectivity& srcNodes = missingBndElems[i];
12579 TConnectivity nodes( srcNodes.size() );
12580 for ( inode = 0; inode < nodes.size(); ++inode )
12581 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12582 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12584 /*noMedium=*/false))
12586 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12590 for ( int i = 0; i < missingBndElems.size(); ++i )
12592 TConnectivity& nodes = missingBndElems[i];
12593 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12595 /*noMedium=*/false))
12597 SMDS_MeshElement* elem =
12598 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12601 // try to set a new element to a shape
12602 if ( myMesh->HasShapeToMesh() )
12605 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12606 const int nbN = nodes.size() / (iQuad+1 );
12607 for ( inode = 0; inode < nbN && ok; ++inode )
12609 pair<int, TopAbs_ShapeEnum> i_stype =
12610 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12611 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12612 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12614 if ( ok && mediumShapes.size() > 1 )
12616 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12617 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12618 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12620 if (( ok = ( stype_i->first != stype_i_0.first )))
12621 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12622 aMesh->IndexToShape( stype_i_0.second ));
12625 if ( ok && mediumShapes.begin()->first == missShapeType )
12626 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12630 // ----------------------------------
12631 // 3. Copy present boundary elements
12632 // ----------------------------------
12633 if ( toCopyExistingBoundary )
12634 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12636 const SMDS_MeshElement* e = presentBndElems[i];
12637 TConnectivity nodes( e->NbNodes() );
12638 for ( inode = 0; inode < nodes.size(); ++inode )
12639 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12640 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12642 else // store present elements to add them to a group
12643 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12645 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12648 } // loop on given elements
12650 // ---------------------------------------------
12651 // 4. Fill group with boundary elements
12652 // ---------------------------------------------
12655 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12656 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12657 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12659 tgtEditor.myLastCreatedElems.Clear();
12660 tgtEditor2.myLastCreatedElems.Clear();
12662 // -----------------------
12663 // 5. Copy given elements
12664 // -----------------------
12665 if ( toCopyElements && targetMesh != myMesh )
12667 if (elements.empty())
12668 eIt = aMesh->elementsIterator(elemType);
12670 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12671 while (eIt->more())
12673 const SMDS_MeshElement* elem = eIt->next();
12674 TConnectivity nodes( elem->NbNodes() );
12675 for ( inode = 0; inode < nodes.size(); ++inode )
12676 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12677 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12679 tgtEditor.myLastCreatedElems.Clear();