1 // Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
86 #include <gp_Trsf.hxx>
100 #include <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
109 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
111 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
113 //=======================================================================
114 //function : SMESH_MeshEditor
116 //=======================================================================
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119 :myMesh( theMesh ) // theMesh may be NULL
123 //================================================================================
125 * \brief Clears myLastCreatedNodes and myLastCreatedElems
127 //================================================================================
129 void SMESH_MeshEditor::CrearLastCreated()
131 myLastCreatedNodes.Clear();
132 myLastCreatedElems.Clear();
136 //=======================================================================
140 //=======================================================================
143 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
144 const SMDSAbs_ElementType type,
147 const double ballDiameter)
149 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
150 SMDS_MeshElement* e = 0;
151 int nbnode = node.size();
152 SMESHDS_Mesh* mesh = GetMeshDS();
157 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
158 else e = mesh->AddFace (node[0], node[1], node[2] );
160 else if (nbnode == 4) {
161 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
162 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
164 else if (nbnode == 6) {
165 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
166 node[4], node[5], ID);
167 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
170 else if (nbnode == 8) {
171 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
172 node[4], node[5], node[6], node[7], ID);
173 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
174 node[4], node[5], node[6], node[7] );
176 else if (nbnode == 9) {
177 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
178 node[4], node[5], node[6], node[7], node[8], ID);
179 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
180 node[4], node[5], node[6], node[7], node[8] );
183 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
184 else e = mesh->AddPolygonalFace (node );
191 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
192 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
194 else if (nbnode == 5) {
195 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
197 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
200 else if (nbnode == 6) {
201 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
202 node[4], node[5], ID);
203 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
206 else if (nbnode == 8) {
207 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208 node[4], node[5], node[6], node[7], ID);
209 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], node[7] );
212 else if (nbnode == 10) {
213 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6], node[7],
215 node[8], node[9], ID);
216 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7],
220 else if (nbnode == 12) {
221 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7],
223 node[8], node[9], node[10], node[11], ID);
224 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7],
226 node[8], node[9], node[10], node[11] );
228 else if (nbnode == 13) {
229 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
230 node[4], node[5], node[6], node[7],
231 node[8], node[9], node[10],node[11],
233 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
234 node[4], node[5], node[6], node[7],
235 node[8], node[9], node[10],node[11],
238 else if (nbnode == 15) {
239 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240 node[4], node[5], node[6], node[7],
241 node[8], node[9], node[10],node[11],
242 node[12],node[13],node[14],ID);
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
244 node[4], node[5], node[6], node[7],
245 node[8], node[9], node[10],node[11],
246 node[12],node[13],node[14] );
248 else if (nbnode == 20) {
249 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 node[4], node[5], node[6], node[7],
251 node[8], node[9], node[10],node[11],
252 node[12],node[13],node[14],node[15],
253 node[16],node[17],node[18],node[19],ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
255 node[4], node[5], node[6], node[7],
256 node[8], node[9], node[10],node[11],
257 node[12],node[13],node[14],node[15],
258 node[16],node[17],node[18],node[19] );
260 else if (nbnode == 27) {
261 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262 node[4], node[5], node[6], node[7],
263 node[8], node[9], node[10],node[11],
264 node[12],node[13],node[14],node[15],
265 node[16],node[17],node[18],node[19],
266 node[20],node[21],node[22],node[23],
267 node[24],node[25],node[26], ID);
268 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7],
270 node[8], node[9], node[10],node[11],
271 node[12],node[13],node[14],node[15],
272 node[16],node[17],node[18],node[19],
273 node[20],node[21],node[22],node[23],
274 node[24],node[25],node[26] );
281 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
282 else e = mesh->AddEdge (node[0], node[1] );
284 else if ( nbnode == 3 ) {
285 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
286 else e = mesh->AddEdge (node[0], node[1], node[2] );
290 case SMDSAbs_0DElement:
292 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
293 else e = mesh->Add0DElement (node[0] );
298 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
299 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
303 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
304 else e = mesh->AddBall (node[0], ballDiameter);
309 if ( e ) myLastCreatedElems.Append( e );
313 //=======================================================================
317 //=======================================================================
319 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
320 const SMDSAbs_ElementType type,
324 vector<const SMDS_MeshNode*> nodes;
325 nodes.reserve( nodeIDs.size() );
326 vector<int>::const_iterator id = nodeIDs.begin();
327 while ( id != nodeIDs.end() ) {
328 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
329 nodes.push_back( node );
333 return AddElement( nodes, type, isPoly, ID );
336 //=======================================================================
338 //purpose : Remove a node or an element.
339 // Modify a compute state of sub-meshes which become empty
340 //=======================================================================
342 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
345 myLastCreatedElems.Clear();
346 myLastCreatedNodes.Clear();
348 SMESHDS_Mesh* aMesh = GetMeshDS();
349 set< SMESH_subMesh *> smmap;
352 list<int>::const_iterator it = theIDs.begin();
353 for ( ; it != theIDs.end(); it++ ) {
354 const SMDS_MeshElement * elem;
356 elem = aMesh->FindNode( *it );
358 elem = aMesh->FindElement( *it );
362 // Notify VERTEX sub-meshes about modification
364 const SMDS_MeshNode* node = cast2Node( elem );
365 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
366 if ( int aShapeID = node->getshapeId() )
367 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
370 // Find sub-meshes to notify about modification
371 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
372 // while ( nodeIt->more() ) {
373 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
374 // const SMDS_PositionPtr& aPosition = node->GetPosition();
375 // if ( aPosition.get() ) {
376 // if ( int aShapeID = aPosition->GetShapeId() ) {
377 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378 // smmap.insert( sm );
385 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
387 aMesh->RemoveElement( elem );
391 // Notify sub-meshes about modification
392 if ( !smmap.empty() ) {
393 set< SMESH_subMesh *>::iterator smIt;
394 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
395 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
398 // // Check if the whole mesh becomes empty
399 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
400 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
405 //================================================================================
407 * \brief Create 0D elements on all nodes of the given object except those
408 * nodes on which a 0D element already exists.
409 * \param elements - Elements on whose nodes to create 0D elements; if empty,
410 * the all mesh is treated
411 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
413 //================================================================================
415 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
416 TIDSortedElemSet& all0DElems )
418 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
419 SMDS_ElemIteratorPtr elemIt;
420 if ( elements.empty() )
421 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
423 elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
425 while ( elemIt->more() )
427 const SMDS_MeshElement* e = elemIt->next();
428 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
429 while ( nodeIt->more() )
431 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
432 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
434 all0DElems.insert( it0D->next() );
436 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
437 all0DElems.insert( myLastCreatedElems.Last() );
443 //=======================================================================
444 //function : FindShape
445 //purpose : Return an index of the shape theElem is on
446 // or zero if a shape not found
447 //=======================================================================
449 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
451 myLastCreatedElems.Clear();
452 myLastCreatedNodes.Clear();
454 SMESHDS_Mesh * aMesh = GetMeshDS();
455 if ( aMesh->ShapeToMesh().IsNull() )
458 int aShapeID = theElem->getshapeId();
462 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
463 if ( sm->Contains( theElem ))
466 if ( theElem->GetType() == SMDSAbs_Node ) {
467 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
470 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
473 TopoDS_Shape aShape; // the shape a node of theElem is on
474 if ( theElem->GetType() != SMDSAbs_Node )
476 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
477 while ( nodeIt->more() ) {
478 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
479 if ((aShapeID = node->getshapeId()) > 0) {
480 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
481 if ( sm->Contains( theElem ))
483 if ( aShape.IsNull() )
484 aShape = aMesh->IndexToShape( aShapeID );
490 // None of nodes is on a proper shape,
491 // find the shape among ancestors of aShape on which a node is
492 if ( !aShape.IsNull() ) {
493 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
494 for ( ; ancIt.More(); ancIt.Next() ) {
495 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
496 if ( sm && sm->Contains( theElem ))
497 return aMesh->ShapeToIndex( ancIt.Value() );
502 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
503 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
504 for ( ; id_sm != id2sm.end(); ++id_sm )
505 if ( id_sm->second->Contains( theElem ))
509 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
513 //=======================================================================
514 //function : IsMedium
516 //=======================================================================
518 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
519 const SMDSAbs_ElementType typeToCheck)
521 bool isMedium = false;
522 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
523 while (it->more() && !isMedium ) {
524 const SMDS_MeshElement* elem = it->next();
525 isMedium = elem->IsMediumNode(node);
530 //=======================================================================
531 //function : ShiftNodesQuadTria
533 // Shift nodes in the array corresponded to quadratic triangle
534 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
535 //=======================================================================
536 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
538 const SMDS_MeshNode* nd1 = aNodes[0];
539 aNodes[0] = aNodes[1];
540 aNodes[1] = aNodes[2];
542 const SMDS_MeshNode* nd2 = aNodes[3];
543 aNodes[3] = aNodes[4];
544 aNodes[4] = aNodes[5];
548 //=======================================================================
549 //function : edgeConnectivity
551 // return number of the edges connected with the theNode.
552 // if theEdges has connections with the other type of the
553 // elements, return -1
554 //=======================================================================
555 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
557 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
559 while(elemIt->more()) {
567 //=======================================================================
568 //function : GetNodesFromTwoTria
570 // Shift nodes in the array corresponded to quadratic triangle
571 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
572 //=======================================================================
573 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
574 const SMDS_MeshElement * theTria2,
575 const SMDS_MeshNode* N1[],
576 const SMDS_MeshNode* N2[])
578 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
581 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
584 if(it->more()) return false;
585 it = theTria2->nodesIterator();
588 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
591 if(it->more()) return false;
593 int sames[3] = {-1,-1,-1};
605 if(nbsames!=2) return false;
607 ShiftNodesQuadTria(N1);
609 ShiftNodesQuadTria(N1);
612 i = sames[0] + sames[1] + sames[2];
614 ShiftNodesQuadTria(N2);
616 // now we receive following N1 and N2 (using numeration as above image)
617 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
618 // i.e. first nodes from both arrays determ new diagonal
622 //=======================================================================
623 //function : InverseDiag
624 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
625 // but having other common link.
626 // Return False if args are improper
627 //=======================================================================
629 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
630 const SMDS_MeshElement * theTria2 )
632 MESSAGE("InverseDiag");
633 myLastCreatedElems.Clear();
634 myLastCreatedNodes.Clear();
636 if (!theTria1 || !theTria2)
639 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
640 if (!F1) return false;
641 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
642 if (!F2) return false;
643 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
644 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
646 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
647 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
651 // put nodes in array and find out indices of the same ones
652 const SMDS_MeshNode* aNodes [6];
653 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
655 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
656 while ( it->more() ) {
657 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
659 if ( i > 2 ) // theTria2
660 // find same node of theTria1
661 for ( int j = 0; j < 3; j++ )
662 if ( aNodes[ i ] == aNodes[ j ]) {
671 return false; // theTria1 is not a triangle
672 it = theTria2->nodesIterator();
674 if ( i == 6 && it->more() )
675 return false; // theTria2 is not a triangle
678 // find indices of 1,2 and of A,B in theTria1
679 int iA = 0, iB = 0, i1 = 0, i2 = 0;
680 for ( i = 0; i < 6; i++ ) {
681 if ( sameInd [ i ] == 0 ) {
690 // nodes 1 and 2 should not be the same
691 if ( aNodes[ i1 ] == aNodes[ i2 ] )
695 aNodes[ iA ] = aNodes[ i2 ];
697 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
699 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
700 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
704 } // end if(F1 && F2)
706 // check case of quadratic faces
707 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
709 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
713 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
714 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
722 const SMDS_MeshNode* N1 [6];
723 const SMDS_MeshNode* N2 [6];
724 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
726 // now we receive following N1 and N2 (using numeration as above image)
727 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
728 // i.e. first nodes from both arrays determ new diagonal
730 const SMDS_MeshNode* N1new [6];
731 const SMDS_MeshNode* N2new [6];
744 // replaces nodes in faces
745 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
746 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
751 //=======================================================================
752 //function : findTriangles
753 //purpose : find triangles sharing theNode1-theNode2 link
754 //=======================================================================
756 static bool findTriangles(const SMDS_MeshNode * theNode1,
757 const SMDS_MeshNode * theNode2,
758 const SMDS_MeshElement*& theTria1,
759 const SMDS_MeshElement*& theTria2)
761 if ( !theNode1 || !theNode2 ) return false;
763 theTria1 = theTria2 = 0;
765 set< const SMDS_MeshElement* > emap;
766 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
768 const SMDS_MeshElement* elem = it->next();
769 if ( elem->NbNodes() == 3 )
772 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
774 const SMDS_MeshElement* elem = it->next();
775 if ( emap.find( elem ) != emap.end() ) {
777 // theTria1 must be element with minimum ID
778 if( theTria1->GetID() < elem->GetID() ) {
792 return ( theTria1 && theTria2 );
795 //=======================================================================
796 //function : InverseDiag
797 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
798 // with ones built on the same 4 nodes but having other common link.
799 // Return false if proper faces not found
800 //=======================================================================
802 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
803 const SMDS_MeshNode * theNode2)
805 myLastCreatedElems.Clear();
806 myLastCreatedNodes.Clear();
808 MESSAGE( "::InverseDiag()" );
810 const SMDS_MeshElement *tr1, *tr2;
811 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
814 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
815 if (!F1) return false;
816 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
817 if (!F2) return false;
818 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
819 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
821 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
822 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
826 // put nodes in array
827 // and find indices of 1,2 and of A in tr1 and of B in tr2
828 int i, iA1 = 0, i1 = 0;
829 const SMDS_MeshNode* aNodes1 [3];
830 SMDS_ElemIteratorPtr it;
831 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
832 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
833 if ( aNodes1[ i ] == theNode1 )
834 iA1 = i; // node A in tr1
835 else if ( aNodes1[ i ] != theNode2 )
839 const SMDS_MeshNode* aNodes2 [3];
840 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
841 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
842 if ( aNodes2[ i ] == theNode2 )
843 iB2 = i; // node B in tr2
844 else if ( aNodes2[ i ] != theNode1 )
848 // nodes 1 and 2 should not be the same
849 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
853 aNodes1[ iA1 ] = aNodes2[ i2 ];
855 aNodes2[ iB2 ] = aNodes1[ i1 ];
857 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
858 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
863 // check case of quadratic faces
864 return InverseDiag(tr1,tr2);
867 //=======================================================================
868 //function : getQuadrangleNodes
869 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
870 // fusion of triangles tr1 and tr2 having shared link on
871 // theNode1 and theNode2
872 //=======================================================================
874 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
875 const SMDS_MeshNode * theNode1,
876 const SMDS_MeshNode * theNode2,
877 const SMDS_MeshElement * tr1,
878 const SMDS_MeshElement * tr2 )
880 if( tr1->NbNodes() != tr2->NbNodes() )
882 // find the 4-th node to insert into tr1
883 const SMDS_MeshNode* n4 = 0;
884 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
886 while ( !n4 && i<3 ) {
887 const SMDS_MeshNode * n = cast2Node( it->next() );
889 bool isDiag = ( n == theNode1 || n == theNode2 );
893 // Make an array of nodes to be in a quadrangle
894 int iNode = 0, iFirstDiag = -1;
895 it = tr1->nodesIterator();
898 const SMDS_MeshNode * n = cast2Node( it->next() );
900 bool isDiag = ( n == theNode1 || n == theNode2 );
902 if ( iFirstDiag < 0 )
904 else if ( iNode - iFirstDiag == 1 )
905 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
907 else if ( n == n4 ) {
908 return false; // tr1 and tr2 should not have all the same nodes
910 theQuadNodes[ iNode++ ] = n;
912 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
913 theQuadNodes[ iNode ] = n4;
918 //=======================================================================
919 //function : DeleteDiag
920 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
921 // with a quadrangle built on the same 4 nodes.
922 // Return false if proper faces not found
923 //=======================================================================
925 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
926 const SMDS_MeshNode * theNode2)
928 myLastCreatedElems.Clear();
929 myLastCreatedNodes.Clear();
931 MESSAGE( "::DeleteDiag()" );
933 const SMDS_MeshElement *tr1, *tr2;
934 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
937 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
938 if (!F1) return false;
939 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
940 if (!F2) return false;
941 SMESHDS_Mesh * aMesh = GetMeshDS();
943 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
944 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
946 const SMDS_MeshNode* aNodes [ 4 ];
947 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
950 const SMDS_MeshElement* newElem = 0;
951 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
952 myLastCreatedElems.Append(newElem);
953 AddToSameGroups( newElem, tr1, aMesh );
954 int aShapeId = tr1->getshapeId();
957 aMesh->SetMeshElementOnShape( newElem, aShapeId );
959 aMesh->RemoveElement( tr1 );
960 aMesh->RemoveElement( tr2 );
965 // check case of quadratic faces
966 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
968 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
972 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
973 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
981 const SMDS_MeshNode* N1 [6];
982 const SMDS_MeshNode* N2 [6];
983 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
985 // now we receive following N1 and N2 (using numeration as above image)
986 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
987 // i.e. first nodes from both arrays determ new diagonal
989 const SMDS_MeshNode* aNodes[8];
999 const SMDS_MeshElement* newElem = 0;
1000 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1001 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1002 myLastCreatedElems.Append(newElem);
1003 AddToSameGroups( newElem, tr1, aMesh );
1004 int aShapeId = tr1->getshapeId();
1007 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1009 aMesh->RemoveElement( tr1 );
1010 aMesh->RemoveElement( tr2 );
1012 // remove middle node (9)
1013 GetMeshDS()->RemoveNode( N1[4] );
1018 //=======================================================================
1019 //function : Reorient
1020 //purpose : Reverse theElement orientation
1021 //=======================================================================
1023 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1025 MESSAGE("Reorient");
1026 myLastCreatedElems.Clear();
1027 myLastCreatedNodes.Clear();
1031 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1032 if ( !it || !it->more() )
1035 switch ( theElem->GetType() ) {
1038 case SMDSAbs_Face: {
1039 if(!theElem->IsQuadratic()) {
1040 int i = theElem->NbNodes();
1041 vector<const SMDS_MeshNode*> aNodes( i );
1042 while ( it->more() )
1043 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1044 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1047 // quadratic elements
1048 if(theElem->GetType()==SMDSAbs_Edge) {
1049 vector<const SMDS_MeshNode*> aNodes(3);
1050 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1051 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1052 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1053 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1056 int nbn = theElem->NbNodes();
1057 vector<const SMDS_MeshNode*> aNodes(nbn);
1058 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1060 for(; i<nbn/2; i++) {
1061 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1063 for(i=0; i<nbn/2; i++) {
1064 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1066 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1070 case SMDSAbs_Volume: {
1071 if (theElem->IsPoly()) {
1072 // TODO reorient vtk polyhedron
1073 MESSAGE("reorient vtk polyhedron ?");
1074 const SMDS_VtkVolume* aPolyedre =
1075 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1077 MESSAGE("Warning: bad volumic element");
1081 int nbFaces = aPolyedre->NbFaces();
1082 vector<const SMDS_MeshNode *> poly_nodes;
1083 vector<int> quantities (nbFaces);
1085 // reverse each face of the polyedre
1086 for (int iface = 1; iface <= nbFaces; iface++) {
1087 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1088 quantities[iface - 1] = nbFaceNodes;
1090 for (inode = nbFaceNodes; inode >= 1; inode--) {
1091 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1092 poly_nodes.push_back(curNode);
1096 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100 SMDS_VolumeTool vTool;
1101 if ( !vTool.Set( theElem ))
1104 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1105 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1114 //================================================================================
1116 * \brief Reorient faces.
1117 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1118 * \param theDirection - desired direction of normal of \a theFace
1119 * \param theFace - one of \a theFaces that sould be oriented according to
1120 * \a theDirection and whose orientation defines orientation of other faces
1121 * \return number of reoriented faces.
1123 //================================================================================
1125 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1126 const gp_Dir& theDirection,
1127 const SMDS_MeshElement * theFace)
1130 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1132 if ( theFaces.empty() )
1134 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1135 while ( fIt->more() )
1136 theFaces.insert( theFaces.end(), fIt->next() );
1139 // orient theFace according to theDirection
1141 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1142 if ( normal * theDirection.XYZ() < 0 )
1143 nbReori += Reorient( theFace );
1145 // Orient other faces
1147 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1148 TIDSortedElemSet avoidSet;
1149 set< SMESH_TLink > checkedLinks;
1150 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1152 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1153 theFaces.erase( theFace );
1154 startFaces.insert( theFace );
1156 int nodeInd1, nodeInd2;
1157 const SMDS_MeshElement* otherFace;
1158 vector< const SMDS_MeshElement* > facesNearLink;
1159 vector< std::pair< int, int > > nodeIndsOfFace;
1161 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1162 while ( !startFaces.empty() )
1164 startFace = startFaces.begin();
1165 theFace = *startFace;
1166 startFaces.erase( startFace );
1167 if ( !visitedFaces.insert( theFace ).second )
1171 avoidSet.insert(theFace);
1173 NLink link( theFace->GetNode( 0 ), 0 );
1175 const int nbNodes = theFace->NbCornerNodes();
1176 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1178 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1179 linkIt_isNew = checkedLinks.insert( link );
1180 if ( !linkIt_isNew.second )
1182 // link has already been checked and won't be encountered more
1183 // if the group (theFaces) is manifold
1184 //checkedLinks.erase( linkIt_isNew.first );
1188 facesNearLink.clear();
1189 nodeIndsOfFace.clear();
1190 while (( otherFace = FindFaceInSet( link.first, link.second,
1191 theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1192 if ( otherFace != theFace)
1194 facesNearLink.push_back( otherFace );
1195 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1196 avoidSet.insert( otherFace );
1198 if ( facesNearLink.size() > 1 )
1200 // NON-MANIFOLD mesh shell !
1201 // select a face most co-directed with theFace,
1202 // other faces won't be visited this time
1204 SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1205 double proj, maxProj = -1;
1206 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1207 SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1208 if (( proj = Abs( NF * NOF )) > maxProj ) {
1210 otherFace = facesNearLink[i];
1211 nodeInd1 = nodeIndsOfFace[i].first;
1212 nodeInd2 = nodeIndsOfFace[i].second;
1215 // not to visit rejected faces
1216 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1217 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1218 visitedFaces.insert( facesNearLink[i] );
1220 else if ( facesNearLink.size() == 1 )
1222 otherFace = facesNearLink[0];
1223 nodeInd1 = nodeIndsOfFace.back().first;
1224 nodeInd2 = nodeIndsOfFace.back().second;
1226 if ( otherFace && otherFace != theFace)
1228 // link must be reverse in otherFace if orientation ot otherFace
1229 // is same as that of theFace
1230 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1232 nbReori += Reorient( otherFace );
1234 startFaces.insert( otherFace );
1237 std::swap( link.first, link.second ); // reverse the link
1243 //=======================================================================
1244 //function : getBadRate
1246 //=======================================================================
1248 static double getBadRate (const SMDS_MeshElement* theElem,
1249 SMESH::Controls::NumericalFunctorPtr& theCrit)
1251 SMESH::Controls::TSequenceOfXYZ P;
1252 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1254 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1255 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1258 //=======================================================================
1259 //function : QuadToTri
1260 //purpose : Cut quadrangles into triangles.
1261 // theCrit is used to select a diagonal to cut
1262 //=======================================================================
1264 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1265 SMESH::Controls::NumericalFunctorPtr theCrit)
1267 myLastCreatedElems.Clear();
1268 myLastCreatedNodes.Clear();
1270 MESSAGE( "::QuadToTri()" );
1272 if ( !theCrit.get() )
1275 SMESHDS_Mesh * aMesh = GetMeshDS();
1277 Handle(Geom_Surface) surface;
1278 SMESH_MesherHelper helper( *GetMesh() );
1280 TIDSortedElemSet::iterator itElem;
1281 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1282 const SMDS_MeshElement* elem = *itElem;
1283 if ( !elem || elem->GetType() != SMDSAbs_Face )
1285 if ( elem->NbCornerNodes() != 4 )
1288 // retrieve element nodes
1289 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1291 // compare two sets of possible triangles
1292 double aBadRate1, aBadRate2; // to what extent a set is bad
1293 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1294 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1295 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1297 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1298 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1299 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1301 int aShapeId = FindShape( elem );
1302 const SMDS_MeshElement* newElem1 = 0;
1303 const SMDS_MeshElement* newElem2 = 0;
1305 if( !elem->IsQuadratic() ) {
1307 // split liner quadrangle
1308 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1309 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1310 if ( aBadRate1 <= aBadRate2 ) {
1311 // tr1 + tr2 is better
1312 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1313 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1316 // tr3 + tr4 is better
1317 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1318 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1323 // split quadratic quadrangle
1325 // get surface elem is on
1326 if ( aShapeId != helper.GetSubShapeID() ) {
1330 shape = aMesh->IndexToShape( aShapeId );
1331 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1332 TopoDS_Face face = TopoDS::Face( shape );
1333 surface = BRep_Tool::Surface( face );
1334 if ( !surface.IsNull() )
1335 helper.SetSubShape( shape );
1338 // find middle point for (0,1,2,3)
1339 // and create a node in this point;
1340 const SMDS_MeshNode* newN = 0;
1341 if ( aNodes.size() == 9 )
1343 // SMDSEntity_BiQuad_Quadrangle
1344 newN = aNodes.back();
1349 if ( surface.IsNull() )
1351 for ( int i = 0; i < 4; i++ )
1352 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1357 const SMDS_MeshNode* inFaceNode = 0;
1358 if ( helper.GetNodeUVneedInFaceNode() )
1359 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1360 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1361 inFaceNode = aNodes[ i ];
1363 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1365 for ( int i = 0; i < 4; i++ )
1366 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1368 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1370 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1371 myLastCreatedNodes.Append(newN);
1373 // create a new element
1374 if ( aBadRate1 <= aBadRate2 ) {
1375 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1376 aNodes[6], aNodes[7], newN );
1377 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1378 newN, aNodes[4], aNodes[5] );
1381 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1382 aNodes[7], aNodes[4], newN );
1383 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1384 newN, aNodes[5], aNodes[6] );
1388 // care of a new element
1390 myLastCreatedElems.Append(newElem1);
1391 myLastCreatedElems.Append(newElem2);
1392 AddToSameGroups( newElem1, elem, aMesh );
1393 AddToSameGroups( newElem2, elem, aMesh );
1395 // put a new triangle on the same shape
1398 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1399 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1401 aMesh->RemoveElement( elem );
1406 //=======================================================================
1407 //function : BestSplit
1408 //purpose : Find better diagonal for cutting.
1409 //=======================================================================
1411 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1412 SMESH::Controls::NumericalFunctorPtr theCrit)
1414 myLastCreatedElems.Clear();
1415 myLastCreatedNodes.Clear();
1420 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1423 if( theQuad->NbNodes()==4 ||
1424 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1426 // retrieve element nodes
1427 const SMDS_MeshNode* aNodes [4];
1428 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1430 //while (itN->more())
1432 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1434 // compare two sets of possible triangles
1435 double aBadRate1, aBadRate2; // to what extent a set is bad
1436 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1437 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1438 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1440 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1441 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1442 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1446 return 1; // diagonal 1-3
1448 return 2; // diagonal 2-4
1455 // Methods of splitting volumes into tetra
1457 const int theHexTo5_1[5*4+1] =
1459 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1461 const int theHexTo5_2[5*4+1] =
1463 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1465 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1467 const int theHexTo6_1[6*4+1] =
1469 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1471 const int theHexTo6_2[6*4+1] =
1473 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1475 const int theHexTo6_3[6*4+1] =
1477 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1479 const int theHexTo6_4[6*4+1] =
1481 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1483 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1485 const int thePyraTo2_1[2*4+1] =
1487 0, 1, 2, 4, 0, 2, 3, 4, -1
1489 const int thePyraTo2_2[2*4+1] =
1491 1, 2, 3, 4, 1, 3, 0, 4, -1
1493 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1495 const int thePentaTo3_1[3*4+1] =
1497 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1499 const int thePentaTo3_2[3*4+1] =
1501 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1503 const int thePentaTo3_3[3*4+1] =
1505 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1507 const int thePentaTo3_4[3*4+1] =
1509 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1511 const int thePentaTo3_5[3*4+1] =
1513 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1515 const int thePentaTo3_6[3*4+1] =
1517 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1519 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1520 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1522 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1525 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1526 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1527 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1532 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1533 bool _baryNode; //!< additional node is to be created at cell barycenter
1534 bool _ownConn; //!< to delete _connectivity in destructor
1535 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1537 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1538 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1539 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1540 bool hasFacet( const TTriangleFacet& facet ) const
1542 const int* tetConn = _connectivity;
1543 for ( ; tetConn[0] >= 0; tetConn += 4 )
1544 if (( facet.contains( tetConn[0] ) +
1545 facet.contains( tetConn[1] ) +
1546 facet.contains( tetConn[2] ) +
1547 facet.contains( tetConn[3] )) == 3 )
1553 //=======================================================================
1555 * \brief return TSplitMethod for the given element
1557 //=======================================================================
1559 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1561 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1563 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1564 // an edge and a face barycenter; tertaherdons are based on triangles and
1565 // a volume barycenter
1566 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1568 // Find out how adjacent volumes are split
1570 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1571 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1572 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1574 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1575 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1576 if ( nbNodes < 4 ) continue;
1578 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1579 const int* nInd = vol.GetFaceNodesIndices( iF );
1582 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1583 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1584 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1585 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1589 int iCom = 0; // common node of triangle faces to split into
1590 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1592 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1593 nInd[ iQ * ( (iCom+1)%nbNodes )],
1594 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1595 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1596 nInd[ iQ * ( (iCom+2)%nbNodes )],
1597 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1598 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1600 triaSplits.push_back( t012 );
1601 triaSplits.push_back( t023 );
1606 if ( !triaSplits.empty() )
1607 hasAdjacentSplits = true;
1610 // Among variants of split method select one compliant with adjacent volumes
1612 TSplitMethod method;
1613 if ( !vol.Element()->IsPoly() && !is24TetMode )
1615 int nbVariants = 2, nbTet = 0;
1616 const int** connVariants = 0;
1617 switch ( vol.Element()->GetEntityType() )
1619 case SMDSEntity_Hexa:
1620 case SMDSEntity_Quad_Hexa:
1621 case SMDSEntity_TriQuad_Hexa:
1622 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1623 connVariants = theHexTo5, nbTet = 5;
1625 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1627 case SMDSEntity_Pyramid:
1628 case SMDSEntity_Quad_Pyramid:
1629 connVariants = thePyraTo2; nbTet = 2;
1631 case SMDSEntity_Penta:
1632 case SMDSEntity_Quad_Penta:
1633 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1638 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1640 // check method compliancy with adjacent tetras,
1641 // all found splits must be among facets of tetras described by this method
1642 method = TSplitMethod( nbTet, connVariants[variant] );
1643 if ( hasAdjacentSplits && method._nbTetra > 0 )
1645 bool facetCreated = true;
1646 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1648 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1649 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1650 facetCreated = method.hasFacet( *facet );
1652 if ( !facetCreated )
1653 method = TSplitMethod(0); // incompatible method
1657 if ( method._nbTetra < 1 )
1659 // No standard method is applicable, use a generic solution:
1660 // each facet of a volume is split into triangles and
1661 // each of triangles and a volume barycenter form a tetrahedron.
1663 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1665 int* connectivity = new int[ maxTetConnSize + 1 ];
1666 method._connectivity = connectivity;
1667 method._ownConn = true;
1668 method._baryNode = !isHex27; // to create central node or not
1671 int baryCenInd = vol.NbNodes() - int( isHex27 );
1672 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1674 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1675 const int* nInd = vol.GetFaceNodesIndices( iF );
1676 // find common node of triangle facets of tetra to create
1677 int iCommon = 0; // index in linear numeration
1678 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1679 if ( !triaSplits.empty() )
1682 const TTriangleFacet* facet = &triaSplits.front();
1683 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1684 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1685 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1688 else if ( nbNodes > 3 && !is24TetMode )
1690 // find the best method of splitting into triangles by aspect ratio
1691 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1692 map< double, int > badness2iCommon;
1693 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1694 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1695 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1698 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1700 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1701 nodes[ iQ*((iLast-1)%nbNodes)],
1702 nodes[ iQ*((iLast )%nbNodes)]);
1703 badness += getBadRate( &tria, aspectRatio );
1705 badness2iCommon.insert( make_pair( badness, iCommon ));
1707 // use iCommon with lowest badness
1708 iCommon = badness2iCommon.begin()->second;
1710 if ( iCommon >= nbNodes )
1711 iCommon = 0; // something wrong
1713 // fill connectivity of tetrahedra based on a current face
1714 int nbTet = nbNodes - 2;
1715 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1720 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1721 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1725 method._faceBaryNode[ iF ] = 0;
1726 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1729 for ( int i = 0; i < nbTet; ++i )
1731 int i1 = i, i2 = (i+1) % nbNodes;
1732 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1733 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1734 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1735 connectivity[ connSize++ ] = faceBaryCenInd;
1736 connectivity[ connSize++ ] = baryCenInd;
1741 for ( int i = 0; i < nbTet; ++i )
1743 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1744 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1745 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1746 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1747 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1748 connectivity[ connSize++ ] = baryCenInd;
1751 method._nbTetra += nbTet;
1753 } // loop on volume faces
1755 connectivity[ connSize++ ] = -1;
1757 } // end of generic solution
1761 //================================================================================
1763 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1765 //================================================================================
1767 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1769 // find the tetrahedron including the three nodes of facet
1770 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1771 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1772 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1773 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1774 while ( volIt1->more() )
1776 const SMDS_MeshElement* v = volIt1->next();
1777 SMDSAbs_EntityType type = v->GetEntityType();
1778 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1780 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1781 continue; // medium node not allowed
1782 const int ind2 = v->GetNodeIndex( n2 );
1783 if ( ind2 < 0 || 3 < ind2 )
1785 const int ind3 = v->GetNodeIndex( n3 );
1786 if ( ind3 < 0 || 3 < ind3 )
1793 //=======================================================================
1795 * \brief A key of a face of volume
1797 //=======================================================================
1799 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1801 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1803 TIDSortedNodeSet sortedNodes;
1804 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1805 int nbNodes = vol.NbFaceNodes( iF );
1806 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1807 for ( int i = 0; i < nbNodes; i += iQ )
1808 sortedNodes.insert( fNodes[i] );
1809 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1810 first.first = (*(n++))->GetID();
1811 first.second = (*(n++))->GetID();
1812 second.first = (*(n++))->GetID();
1813 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1818 //=======================================================================
1819 //function : SplitVolumesIntoTetra
1820 //purpose : Split volume elements into tetrahedra.
1821 //=======================================================================
1823 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1824 const int theMethodFlags)
1826 // std-like iterator on coordinates of nodes of mesh element
1827 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1828 NXyzIterator xyzEnd;
1830 SMDS_VolumeTool volTool;
1831 SMESH_MesherHelper helper( *GetMesh());
1833 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1834 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1836 SMESH_SequenceOfElemPtr newNodes, newElems;
1838 // map face of volume to it's baricenrtic node
1839 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1842 TIDSortedElemSet::const_iterator elem = theElems.begin();
1843 for ( ; elem != theElems.end(); ++elem )
1845 if ( (*elem)->GetType() != SMDSAbs_Volume )
1847 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1848 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1851 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1853 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1854 if ( splitMethod._nbTetra < 1 ) continue;
1856 // find submesh to add new tetras to
1857 if ( !subMesh || !subMesh->Contains( *elem ))
1859 int shapeID = FindShape( *elem );
1860 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1861 subMesh = GetMeshDS()->MeshElements( shapeID );
1864 if ( (*elem)->IsQuadratic() )
1867 // add quadratic links to the helper
1868 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1870 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1871 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1872 for ( int iN = 0; iN < nbN; iN += iQ )
1873 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1875 helper.SetIsQuadratic( true );
1880 helper.SetIsQuadratic( false );
1882 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1883 helper.SetElementsOnShape( true );
1884 if ( splitMethod._baryNode )
1886 // make a node at barycenter
1887 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1888 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1889 nodes.push_back( gcNode );
1890 newNodes.Append( gcNode );
1892 if ( !splitMethod._faceBaryNode.empty() )
1894 // make or find baricentric nodes of faces
1895 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1896 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1898 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1899 volFace2BaryNode.insert
1900 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1903 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1904 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1906 nodes.push_back( iF_n->second = f_n->second );
1911 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1912 const int* tetConn = splitMethod._connectivity;
1913 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1914 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1915 nodes[ tetConn[1] ],
1916 nodes[ tetConn[2] ],
1917 nodes[ tetConn[3] ]));
1919 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1921 // Split faces on sides of the split volume
1923 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1924 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1926 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1927 if ( nbNodes < 4 ) continue;
1929 // find an existing face
1930 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1931 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1932 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1933 /*noMedium=*/false))
1936 helper.SetElementsOnShape( false );
1937 vector< const SMDS_MeshElement* > triangles;
1939 // find submesh to add new triangles in
1940 if ( !fSubMesh || !fSubMesh->Contains( face ))
1942 int shapeID = FindShape( face );
1943 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1945 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1946 if ( iF_n != splitMethod._faceBaryNode.end() )
1948 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1950 const SMDS_MeshNode* n1 = fNodes[iN];
1951 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1952 const SMDS_MeshNode *n3 = iF_n->second;
1953 if ( !volTool.IsFaceExternal( iF ))
1955 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1957 if ( fSubMesh && n3->getshapeId() < 1 )
1958 fSubMesh->AddNode( n3 );
1963 // among possible triangles create ones discribed by split method
1964 const int* nInd = volTool.GetFaceNodesIndices( iF );
1965 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1966 int iCom = 0; // common node of triangle faces to split into
1967 list< TTriangleFacet > facets;
1968 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1970 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1971 nInd[ iQ * ( (iCom+1)%nbNodes )],
1972 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1973 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1974 nInd[ iQ * ( (iCom+2)%nbNodes )],
1975 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1976 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1978 facets.push_back( t012 );
1979 facets.push_back( t023 );
1980 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1981 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1982 nInd[ iQ * ((iLast-1)%nbNodes )],
1983 nInd[ iQ * ((iLast )%nbNodes )]));
1987 list< TTriangleFacet >::iterator facet = facets.begin();
1988 for ( ; facet != facets.end(); ++facet )
1990 if ( !volTool.IsFaceExternal( iF ))
1991 swap( facet->_n2, facet->_n3 );
1992 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1993 volNodes[ facet->_n2 ],
1994 volNodes[ facet->_n3 ]));
1997 for ( int i = 0; i < triangles.size(); ++i )
1999 if ( !triangles[i] ) continue;
2001 fSubMesh->AddElement( triangles[i]);
2002 newElems.Append( triangles[i] );
2004 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2005 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2008 } // loop on volume faces to split them into triangles
2010 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2012 if ( geomType == SMDSEntity_TriQuad_Hexa )
2014 // remove medium nodes that could become free
2015 for ( int i = 20; i < volTool.NbNodes(); ++i )
2016 if ( volNodes[i]->NbInverseElements() == 0 )
2017 GetMeshDS()->RemoveNode( volNodes[i] );
2019 } // loop on volumes to split
2021 myLastCreatedNodes = newNodes;
2022 myLastCreatedElems = newElems;
2025 //=======================================================================
2026 //function : AddToSameGroups
2027 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2028 //=======================================================================
2030 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2031 const SMDS_MeshElement* elemInGroups,
2032 SMESHDS_Mesh * aMesh)
2034 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2035 if (!groups.empty()) {
2036 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2037 for ( ; grIt != groups.end(); grIt++ ) {
2038 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2039 if ( group && group->Contains( elemInGroups ))
2040 group->SMDSGroup().Add( elemToAdd );
2046 //=======================================================================
2047 //function : RemoveElemFromGroups
2048 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2049 //=======================================================================
2050 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2051 SMESHDS_Mesh * aMesh)
2053 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2054 if (!groups.empty())
2056 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2057 for (; GrIt != groups.end(); GrIt++)
2059 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2060 if (!grp || grp->IsEmpty()) continue;
2061 grp->SMDSGroup().Remove(removeelem);
2066 //================================================================================
2068 * \brief Replace elemToRm by elemToAdd in the all groups
2070 //================================================================================
2072 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2073 const SMDS_MeshElement* elemToAdd,
2074 SMESHDS_Mesh * aMesh)
2076 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2077 if (!groups.empty()) {
2078 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2079 for ( ; grIt != groups.end(); grIt++ ) {
2080 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2081 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2082 group->SMDSGroup().Add( elemToAdd );
2087 //================================================================================
2089 * \brief Replace elemToRm by elemToAdd in the all groups
2091 //================================================================================
2093 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2094 const vector<const SMDS_MeshElement*>& elemToAdd,
2095 SMESHDS_Mesh * aMesh)
2097 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2098 if (!groups.empty())
2100 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2101 for ( ; grIt != groups.end(); grIt++ ) {
2102 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2103 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2104 for ( int i = 0; i < elemToAdd.size(); ++i )
2105 group->SMDSGroup().Add( elemToAdd[ i ] );
2110 //=======================================================================
2111 //function : QuadToTri
2112 //purpose : Cut quadrangles into triangles.
2113 // theCrit is used to select a diagonal to cut
2114 //=======================================================================
2116 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2117 const bool the13Diag)
2119 myLastCreatedElems.Clear();
2120 myLastCreatedNodes.Clear();
2122 MESSAGE( "::QuadToTri()" );
2124 SMESHDS_Mesh * aMesh = GetMeshDS();
2126 Handle(Geom_Surface) surface;
2127 SMESH_MesherHelper helper( *GetMesh() );
2129 TIDSortedElemSet::iterator itElem;
2130 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2131 const SMDS_MeshElement* elem = *itElem;
2132 if ( !elem || elem->GetType() != SMDSAbs_Face )
2134 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2135 if(!isquad) continue;
2137 if(elem->NbNodes()==4) {
2138 // retrieve element nodes
2139 const SMDS_MeshNode* aNodes [4];
2140 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2142 while ( itN->more() )
2143 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2145 int aShapeId = FindShape( elem );
2146 const SMDS_MeshElement* newElem1 = 0;
2147 const SMDS_MeshElement* newElem2 = 0;
2149 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2150 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2153 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2154 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2156 myLastCreatedElems.Append(newElem1);
2157 myLastCreatedElems.Append(newElem2);
2158 // put a new triangle on the same shape and add to the same groups
2161 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2162 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2164 AddToSameGroups( newElem1, elem, aMesh );
2165 AddToSameGroups( newElem2, elem, aMesh );
2166 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2167 aMesh->RemoveElement( elem );
2170 // Quadratic quadrangle
2172 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2174 // get surface elem is on
2175 int aShapeId = FindShape( elem );
2176 if ( aShapeId != helper.GetSubShapeID() ) {
2180 shape = aMesh->IndexToShape( aShapeId );
2181 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2182 TopoDS_Face face = TopoDS::Face( shape );
2183 surface = BRep_Tool::Surface( face );
2184 if ( !surface.IsNull() )
2185 helper.SetSubShape( shape );
2189 const SMDS_MeshNode* aNodes [8];
2190 const SMDS_MeshNode* inFaceNode = 0;
2191 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2193 while ( itN->more() ) {
2194 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2195 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2196 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2198 inFaceNode = aNodes[ i-1 ];
2202 // find middle point for (0,1,2,3)
2203 // and create a node in this point;
2205 if ( surface.IsNull() ) {
2207 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2211 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2214 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2216 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2218 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2219 myLastCreatedNodes.Append(newN);
2221 // create a new element
2222 const SMDS_MeshElement* newElem1 = 0;
2223 const SMDS_MeshElement* newElem2 = 0;
2225 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2226 aNodes[6], aNodes[7], newN );
2227 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2228 newN, aNodes[4], aNodes[5] );
2231 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2232 aNodes[7], aNodes[4], newN );
2233 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2234 newN, aNodes[5], aNodes[6] );
2236 myLastCreatedElems.Append(newElem1);
2237 myLastCreatedElems.Append(newElem2);
2238 // put a new triangle on the same shape and add to the same groups
2241 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2242 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2244 AddToSameGroups( newElem1, elem, aMesh );
2245 AddToSameGroups( newElem2, elem, aMesh );
2246 aMesh->RemoveElement( elem );
2253 //=======================================================================
2254 //function : getAngle
2256 //=======================================================================
2258 double getAngle(const SMDS_MeshElement * tr1,
2259 const SMDS_MeshElement * tr2,
2260 const SMDS_MeshNode * n1,
2261 const SMDS_MeshNode * n2)
2263 double angle = 2. * M_PI; // bad angle
2266 SMESH::Controls::TSequenceOfXYZ P1, P2;
2267 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2268 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2271 if(!tr1->IsQuadratic())
2272 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2274 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2275 if ( N1.SquareMagnitude() <= gp::Resolution() )
2277 if(!tr2->IsQuadratic())
2278 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2280 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2281 if ( N2.SquareMagnitude() <= gp::Resolution() )
2284 // find the first diagonal node n1 in the triangles:
2285 // take in account a diagonal link orientation
2286 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2287 for ( int t = 0; t < 2; t++ ) {
2288 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2289 int i = 0, iDiag = -1;
2290 while ( it->more()) {
2291 const SMDS_MeshElement *n = it->next();
2292 if ( n == n1 || n == n2 ) {
2296 if ( i - iDiag == 1 )
2297 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2306 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2309 angle = N1.Angle( N2 );
2314 // =================================================
2315 // class generating a unique ID for a pair of nodes
2316 // and able to return nodes by that ID
2317 // =================================================
2321 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2322 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2325 long GetLinkID (const SMDS_MeshNode * n1,
2326 const SMDS_MeshNode * n2) const
2328 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2331 bool GetNodes (const long theLinkID,
2332 const SMDS_MeshNode* & theNode1,
2333 const SMDS_MeshNode* & theNode2) const
2335 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2336 if ( !theNode1 ) return false;
2337 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2338 if ( !theNode2 ) return false;
2344 const SMESHDS_Mesh* myMesh;
2349 //=======================================================================
2350 //function : TriToQuad
2351 //purpose : Fuse neighbour triangles into quadrangles.
2352 // theCrit is used to select a neighbour to fuse with.
2353 // theMaxAngle is a max angle between element normals at which
2354 // fusion is still performed.
2355 //=======================================================================
2357 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2358 SMESH::Controls::NumericalFunctorPtr theCrit,
2359 const double theMaxAngle)
2361 myLastCreatedElems.Clear();
2362 myLastCreatedNodes.Clear();
2364 MESSAGE( "::TriToQuad()" );
2366 if ( !theCrit.get() )
2369 SMESHDS_Mesh * aMesh = GetMeshDS();
2371 // Prepare data for algo: build
2372 // 1. map of elements with their linkIDs
2373 // 2. map of linkIDs with their elements
2375 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2376 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2377 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2378 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2380 TIDSortedElemSet::iterator itElem;
2381 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2382 const SMDS_MeshElement* elem = *itElem;
2383 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2384 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2385 if(!IsTria) continue;
2387 // retrieve element nodes
2388 const SMDS_MeshNode* aNodes [4];
2389 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2392 aNodes[ i++ ] = cast2Node( itN->next() );
2393 aNodes[ 3 ] = aNodes[ 0 ];
2396 for ( i = 0; i < 3; i++ ) {
2397 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2398 // check if elements sharing a link can be fused
2399 itLE = mapLi_listEl.find( link );
2400 if ( itLE != mapLi_listEl.end() ) {
2401 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2403 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2404 //if ( FindShape( elem ) != FindShape( elem2 ))
2405 // continue; // do not fuse triangles laying on different shapes
2406 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2407 continue; // avoid making badly shaped quads
2408 (*itLE).second.push_back( elem );
2411 mapLi_listEl[ link ].push_back( elem );
2413 mapEl_setLi [ elem ].insert( link );
2416 // Clean the maps from the links shared by a sole element, ie
2417 // links to which only one element is bound in mapLi_listEl
2419 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2420 int nbElems = (*itLE).second.size();
2421 if ( nbElems < 2 ) {
2422 const SMDS_MeshElement* elem = (*itLE).second.front();
2423 SMESH_TLink link = (*itLE).first;
2424 mapEl_setLi[ elem ].erase( link );
2425 if ( mapEl_setLi[ elem ].empty() )
2426 mapEl_setLi.erase( elem );
2430 // Algo: fuse triangles into quadrangles
2432 while ( ! mapEl_setLi.empty() ) {
2433 // Look for the start element:
2434 // the element having the least nb of shared links
2435 const SMDS_MeshElement* startElem = 0;
2437 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2438 int nbLinks = (*itEL).second.size();
2439 if ( nbLinks < minNbLinks ) {
2440 startElem = (*itEL).first;
2441 minNbLinks = nbLinks;
2442 if ( minNbLinks == 1 )
2447 // search elements to fuse starting from startElem or links of elements
2448 // fused earlyer - startLinks
2449 list< SMESH_TLink > startLinks;
2450 while ( startElem || !startLinks.empty() ) {
2451 while ( !startElem && !startLinks.empty() ) {
2452 // Get an element to start, by a link
2453 SMESH_TLink linkId = startLinks.front();
2454 startLinks.pop_front();
2455 itLE = mapLi_listEl.find( linkId );
2456 if ( itLE != mapLi_listEl.end() ) {
2457 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2458 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2459 for ( ; itE != listElem.end() ; itE++ )
2460 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2462 mapLi_listEl.erase( itLE );
2467 // Get candidates to be fused
2468 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2469 const SMESH_TLink *link12, *link13;
2471 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2472 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2473 ASSERT( !setLi.empty() );
2474 set< SMESH_TLink >::iterator itLi;
2475 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2477 const SMESH_TLink & link = (*itLi);
2478 itLE = mapLi_listEl.find( link );
2479 if ( itLE == mapLi_listEl.end() )
2482 const SMDS_MeshElement* elem = (*itLE).second.front();
2484 elem = (*itLE).second.back();
2485 mapLi_listEl.erase( itLE );
2486 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2497 // add other links of elem to list of links to re-start from
2498 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2499 set< SMESH_TLink >::iterator it;
2500 for ( it = links.begin(); it != links.end(); it++ ) {
2501 const SMESH_TLink& link2 = (*it);
2502 if ( link2 != link )
2503 startLinks.push_back( link2 );
2507 // Get nodes of possible quadrangles
2508 const SMDS_MeshNode *n12 [4], *n13 [4];
2509 bool Ok12 = false, Ok13 = false;
2510 const SMDS_MeshNode *linkNode1, *linkNode2;
2512 linkNode1 = link12->first;
2513 linkNode2 = link12->second;
2514 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2518 linkNode1 = link13->first;
2519 linkNode2 = link13->second;
2520 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2524 // Choose a pair to fuse
2525 if ( Ok12 && Ok13 ) {
2526 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2527 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2528 double aBadRate12 = getBadRate( &quad12, theCrit );
2529 double aBadRate13 = getBadRate( &quad13, theCrit );
2530 if ( aBadRate13 < aBadRate12 )
2537 // and remove fused elems and removed links from the maps
2538 mapEl_setLi.erase( tr1 );
2540 mapEl_setLi.erase( tr2 );
2541 mapLi_listEl.erase( *link12 );
2542 if(tr1->NbNodes()==3) {
2543 const SMDS_MeshElement* newElem = 0;
2544 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2545 myLastCreatedElems.Append(newElem);
2546 AddToSameGroups( newElem, tr1, aMesh );
2547 int aShapeId = tr1->getshapeId();
2550 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2552 aMesh->RemoveElement( tr1 );
2553 aMesh->RemoveElement( tr2 );
2556 const SMDS_MeshNode* N1 [6];
2557 const SMDS_MeshNode* N2 [6];
2558 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2559 // now we receive following N1 and N2 (using numeration as above image)
2560 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2561 // i.e. first nodes from both arrays determ new diagonal
2562 const SMDS_MeshNode* aNodes[8];
2571 const SMDS_MeshElement* newElem = 0;
2572 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2573 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2574 myLastCreatedElems.Append(newElem);
2575 AddToSameGroups( newElem, tr1, aMesh );
2576 int aShapeId = tr1->getshapeId();
2579 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2581 aMesh->RemoveElement( tr1 );
2582 aMesh->RemoveElement( tr2 );
2583 // remove middle node (9)
2584 GetMeshDS()->RemoveNode( N1[4] );
2588 mapEl_setLi.erase( tr3 );
2589 mapLi_listEl.erase( *link13 );
2590 if(tr1->NbNodes()==3) {
2591 const SMDS_MeshElement* newElem = 0;
2592 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2593 myLastCreatedElems.Append(newElem);
2594 AddToSameGroups( newElem, tr1, aMesh );
2595 int aShapeId = tr1->getshapeId();
2598 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2600 aMesh->RemoveElement( tr1 );
2601 aMesh->RemoveElement( tr3 );
2604 const SMDS_MeshNode* N1 [6];
2605 const SMDS_MeshNode* N2 [6];
2606 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2607 // now we receive following N1 and N2 (using numeration as above image)
2608 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2609 // i.e. first nodes from both arrays determ new diagonal
2610 const SMDS_MeshNode* aNodes[8];
2619 const SMDS_MeshElement* newElem = 0;
2620 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2621 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2622 myLastCreatedElems.Append(newElem);
2623 AddToSameGroups( newElem, tr1, aMesh );
2624 int aShapeId = tr1->getshapeId();
2627 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2629 aMesh->RemoveElement( tr1 );
2630 aMesh->RemoveElement( tr3 );
2631 // remove middle node (9)
2632 GetMeshDS()->RemoveNode( N1[4] );
2636 // Next element to fuse: the rejected one
2638 startElem = Ok12 ? tr3 : tr2;
2640 } // if ( startElem )
2641 } // while ( startElem || !startLinks.empty() )
2642 } // while ( ! mapEl_setLi.empty() )
2648 /*#define DUMPSO(txt) \
2649 // cout << txt << endl;
2650 //=============================================================================
2654 //=============================================================================
2655 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2659 int tmp = idNodes[ i1 ];
2660 idNodes[ i1 ] = idNodes[ i2 ];
2661 idNodes[ i2 ] = tmp;
2662 gp_Pnt Ptmp = P[ i1 ];
2665 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2668 //=======================================================================
2669 //function : SortQuadNodes
2670 //purpose : Set 4 nodes of a quadrangle face in a good order.
2671 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2673 //=======================================================================
2675 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2680 for ( i = 0; i < 4; i++ ) {
2681 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2683 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2686 gp_Vec V1(P[0], P[1]);
2687 gp_Vec V2(P[0], P[2]);
2688 gp_Vec V3(P[0], P[3]);
2690 gp_Vec Cross1 = V1 ^ V2;
2691 gp_Vec Cross2 = V2 ^ V3;
2694 if (Cross1.Dot(Cross2) < 0)
2699 if (Cross1.Dot(Cross2) < 0)
2703 swap ( i, i + 1, idNodes, P );
2705 // for ( int ii = 0; ii < 4; ii++ ) {
2706 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2707 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2713 //=======================================================================
2714 //function : SortHexaNodes
2715 //purpose : Set 8 nodes of a hexahedron in a good order.
2716 // Return success status
2717 //=======================================================================
2719 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2724 DUMPSO( "INPUT: ========================================");
2725 for ( i = 0; i < 8; i++ ) {
2726 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2727 if ( !n ) return false;
2728 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2729 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2731 DUMPSO( "========================================");
2734 set<int> faceNodes; // ids of bottom face nodes, to be found
2735 set<int> checkedId1; // ids of tried 2-nd nodes
2736 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2737 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2738 int iMin, iLoop1 = 0;
2740 // Loop to try the 2-nd nodes
2742 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2744 // Find not checked 2-nd node
2745 for ( i = 1; i < 8; i++ )
2746 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2747 int id1 = idNodes[i];
2748 swap ( 1, i, idNodes, P );
2749 checkedId1.insert ( id1 );
2753 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2754 // ie that all but meybe one (id3 which is on the same face) nodes
2755 // lay on the same side from the triangle plane.
2757 bool manyInPlane = false; // more than 4 nodes lay in plane
2759 while ( ++iLoop2 < 6 ) {
2761 // get 1-2-3 plane coeffs
2762 Standard_Real A, B, C, D;
2763 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2764 if ( N.SquareMagnitude() > gp::Resolution() )
2766 gp_Pln pln ( P[0], N );
2767 pln.Coefficients( A, B, C, D );
2769 // find the node (iMin) closest to pln
2770 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2772 for ( i = 3; i < 8; i++ ) {
2773 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2774 if ( fabs( dist[i] ) < minDist ) {
2775 minDist = fabs( dist[i] );
2778 if ( fabs( dist[i] ) <= tol )
2779 idInPln.insert( idNodes[i] );
2782 // there should not be more than 4 nodes in bottom plane
2783 if ( idInPln.size() > 1 )
2785 DUMPSO( "### idInPln.size() = " << idInPln.size());
2786 // idInPlane does not contain the first 3 nodes
2787 if ( manyInPlane || idInPln.size() == 5)
2788 return false; // all nodes in one plane
2791 // set the 1-st node to be not in plane
2792 for ( i = 3; i < 8; i++ ) {
2793 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2794 DUMPSO( "### Reset 0-th node");
2795 swap( 0, i, idNodes, P );
2800 // reset to re-check second nodes
2801 leastDist = DBL_MAX;
2805 break; // from iLoop2;
2808 // check that the other 4 nodes are on the same side
2809 bool sameSide = true;
2810 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2811 for ( i = 3; sameSide && i < 8; i++ ) {
2813 sameSide = ( isNeg == dist[i] <= 0.);
2816 // keep best solution
2817 if ( sameSide && minDist < leastDist ) {
2818 leastDist = minDist;
2820 faceNodes.insert( idNodes[ 1 ] );
2821 faceNodes.insert( idNodes[ 2 ] );
2822 faceNodes.insert( idNodes[ iMin ] );
2823 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2824 << " leastDist = " << leastDist);
2825 if ( leastDist <= DBL_MIN )
2830 // set next 3-d node to check
2831 int iNext = 2 + iLoop2;
2833 DUMPSO( "Try 2-nd");
2834 swap ( 2, iNext, idNodes, P );
2836 } // while ( iLoop2 < 6 )
2839 if ( faceNodes.empty() ) return false;
2841 // Put the faceNodes in proper places
2842 for ( i = 4; i < 8; i++ ) {
2843 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2844 // find a place to put
2846 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2848 DUMPSO( "Set faceNodes");
2849 swap ( iTo, i, idNodes, P );
2854 // Set nodes of the found bottom face in good order
2855 DUMPSO( " Found bottom face: ");
2856 i = SortQuadNodes( theMesh, idNodes );
2858 gp_Pnt Ptmp = P[ i ];
2863 // for ( int ii = 0; ii < 4; ii++ ) {
2864 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2865 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2868 // Gravity center of the top and bottom faces
2869 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2870 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2872 // Get direction from the bottom to the top face
2873 gp_Vec upDir ( aGCb, aGCt );
2874 Standard_Real upDirSize = upDir.Magnitude();
2875 if ( upDirSize <= gp::Resolution() ) return false;
2878 // Assure that the bottom face normal points up
2879 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2880 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2881 if ( Nb.Dot( upDir ) < 0 ) {
2882 DUMPSO( "Reverse bottom face");
2883 swap( 1, 3, idNodes, P );
2886 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2887 Standard_Real minDist = DBL_MAX;
2888 for ( i = 4; i < 8; i++ ) {
2889 // projection of P[i] to the plane defined by P[0] and upDir
2890 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2891 Standard_Real sqDist = P[0].SquareDistance( Pp );
2892 if ( sqDist < minDist ) {
2897 DUMPSO( "Set 4-th");
2898 swap ( 4, iMin, idNodes, P );
2900 // Set nodes of the top face in good order
2901 DUMPSO( "Sort top face");
2902 i = SortQuadNodes( theMesh, &idNodes[4] );
2905 gp_Pnt Ptmp = P[ i ];
2910 // Assure that direction of the top face normal is from the bottom face
2911 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2912 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2913 if ( Nt.Dot( upDir ) < 0 ) {
2914 DUMPSO( "Reverse top face");
2915 swap( 5, 7, idNodes, P );
2918 // DUMPSO( "OUTPUT: ========================================");
2919 // for ( i = 0; i < 8; i++ ) {
2920 // float *p = ugrid->GetPoint(idNodes[i]);
2921 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2927 //================================================================================
2929 * \brief Return nodes linked to the given one
2930 * \param theNode - the node
2931 * \param linkedNodes - the found nodes
2932 * \param type - the type of elements to check
2934 * Medium nodes are ignored
2936 //================================================================================
2938 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2939 TIDSortedElemSet & linkedNodes,
2940 SMDSAbs_ElementType type )
2942 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2943 while ( elemIt->more() )
2945 const SMDS_MeshElement* elem = elemIt->next();
2946 if(elem->GetType() == SMDSAbs_0DElement)
2949 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2950 if ( elem->GetType() == SMDSAbs_Volume )
2952 SMDS_VolumeTool vol( elem );
2953 while ( nodeIt->more() ) {
2954 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2955 if ( theNode != n && vol.IsLinked( theNode, n ))
2956 linkedNodes.insert( n );
2961 for ( int i = 0; nodeIt->more(); ++i ) {
2962 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2963 if ( n == theNode ) {
2964 int iBefore = i - 1;
2966 if ( elem->IsQuadratic() ) {
2967 int nb = elem->NbNodes() / 2;
2968 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2969 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2971 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2972 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2979 //=======================================================================
2980 //function : laplacianSmooth
2981 //purpose : pulls theNode toward the center of surrounding nodes directly
2982 // connected to that node along an element edge
2983 //=======================================================================
2985 void laplacianSmooth(const SMDS_MeshNode* theNode,
2986 const Handle(Geom_Surface)& theSurface,
2987 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2989 // find surrounding nodes
2991 TIDSortedElemSet nodeSet;
2992 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2994 // compute new coodrs
2996 double coord[] = { 0., 0., 0. };
2997 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2998 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2999 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3000 if ( theSurface.IsNull() ) { // smooth in 3D
3001 coord[0] += node->X();
3002 coord[1] += node->Y();
3003 coord[2] += node->Z();
3005 else { // smooth in 2D
3006 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3007 gp_XY* uv = theUVMap[ node ];
3008 coord[0] += uv->X();
3009 coord[1] += uv->Y();
3012 int nbNodes = nodeSet.size();
3015 coord[0] /= nbNodes;
3016 coord[1] /= nbNodes;
3018 if ( !theSurface.IsNull() ) {
3019 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3020 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3021 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3027 coord[2] /= nbNodes;
3031 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3034 //=======================================================================
3035 //function : centroidalSmooth
3036 //purpose : pulls theNode toward the element-area-weighted centroid of the
3037 // surrounding elements
3038 //=======================================================================
3040 void centroidalSmooth(const SMDS_MeshNode* theNode,
3041 const Handle(Geom_Surface)& theSurface,
3042 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3044 gp_XYZ aNewXYZ(0.,0.,0.);
3045 SMESH::Controls::Area anAreaFunc;
3046 double totalArea = 0.;
3051 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3052 while ( elemIt->more() )
3054 const SMDS_MeshElement* elem = elemIt->next();
3057 gp_XYZ elemCenter(0.,0.,0.);
3058 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3059 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3060 int nn = elem->NbNodes();
3061 if(elem->IsQuadratic()) nn = nn/2;
3063 //while ( itN->more() ) {
3065 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3067 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3068 aNodePoints.push_back( aP );
3069 if ( !theSurface.IsNull() ) { // smooth in 2D
3070 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3071 gp_XY* uv = theUVMap[ aNode ];
3072 aP.SetCoord( uv->X(), uv->Y(), 0. );
3076 double elemArea = anAreaFunc.GetValue( aNodePoints );
3077 totalArea += elemArea;
3079 aNewXYZ += elemCenter * elemArea;
3081 aNewXYZ /= totalArea;
3082 if ( !theSurface.IsNull() ) {
3083 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3084 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3089 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3092 //=======================================================================
3093 //function : getClosestUV
3094 //purpose : return UV of closest projection
3095 //=======================================================================
3097 static bool getClosestUV (Extrema_GenExtPS& projector,
3098 const gp_Pnt& point,
3101 projector.Perform( point );
3102 if ( projector.IsDone() ) {
3103 double u, v, minVal = DBL_MAX;
3104 for ( int i = projector.NbExt(); i > 0; i-- )
3105 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3106 if ( projector.SquareDistance( i ) < minVal ) {
3107 minVal = projector.SquareDistance( i );
3109 if ( projector.Value( i ) < minVal ) {
3110 minVal = projector.Value( i );
3112 projector.Point( i ).Parameter( u, v );
3114 result.SetCoord( u, v );
3120 //=======================================================================
3122 //purpose : Smooth theElements during theNbIterations or until a worst
3123 // element has aspect ratio <= theTgtAspectRatio.
3124 // Aspect Ratio varies in range [1.0, inf].
3125 // If theElements is empty, the whole mesh is smoothed.
3126 // theFixedNodes contains additionally fixed nodes. Nodes built
3127 // on edges and boundary nodes are always fixed.
3128 //=======================================================================
3130 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3131 set<const SMDS_MeshNode*> & theFixedNodes,
3132 const SmoothMethod theSmoothMethod,
3133 const int theNbIterations,
3134 double theTgtAspectRatio,
3137 myLastCreatedElems.Clear();
3138 myLastCreatedNodes.Clear();
3140 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3142 if ( theTgtAspectRatio < 1.0 )
3143 theTgtAspectRatio = 1.0;
3145 const double disttol = 1.e-16;
3147 SMESH::Controls::AspectRatio aQualityFunc;
3149 SMESHDS_Mesh* aMesh = GetMeshDS();
3151 if ( theElems.empty() ) {
3152 // add all faces to theElems
3153 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3154 while ( fIt->more() ) {
3155 const SMDS_MeshElement* face = fIt->next();
3156 theElems.insert( theElems.end(), face );
3159 // get all face ids theElems are on
3160 set< int > faceIdSet;
3161 TIDSortedElemSet::iterator itElem;
3163 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3164 int fId = FindShape( *itElem );
3165 // check that corresponding submesh exists and a shape is face
3167 faceIdSet.find( fId ) == faceIdSet.end() &&
3168 aMesh->MeshElements( fId )) {
3169 TopoDS_Shape F = aMesh->IndexToShape( fId );
3170 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3171 faceIdSet.insert( fId );
3174 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3176 // ===============================================
3177 // smooth elements on each TopoDS_Face separately
3178 // ===============================================
3180 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3181 for ( ; fId != faceIdSet.rend(); ++fId ) {
3182 // get face surface and submesh
3183 Handle(Geom_Surface) surface;
3184 SMESHDS_SubMesh* faceSubMesh = 0;
3186 double fToler2 = 0, f,l;
3187 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3188 bool isUPeriodic = false, isVPeriodic = false;
3190 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3191 surface = BRep_Tool::Surface( face );
3192 faceSubMesh = aMesh->MeshElements( *fId );
3193 fToler2 = BRep_Tool::Tolerance( face );
3194 fToler2 *= fToler2 * 10.;
3195 isUPeriodic = surface->IsUPeriodic();
3198 isVPeriodic = surface->IsVPeriodic();
3201 surface->Bounds( u1, u2, v1, v2 );
3203 // ---------------------------------------------------------
3204 // for elements on a face, find movable and fixed nodes and
3205 // compute UV for them
3206 // ---------------------------------------------------------
3207 bool checkBoundaryNodes = false;
3208 bool isQuadratic = false;
3209 set<const SMDS_MeshNode*> setMovableNodes;
3210 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3211 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3212 list< const SMDS_MeshElement* > elemsOnFace;
3214 Extrema_GenExtPS projector;
3215 GeomAdaptor_Surface surfAdaptor;
3216 if ( !surface.IsNull() ) {
3217 surfAdaptor.Load( surface );
3218 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3220 int nbElemOnFace = 0;
3221 itElem = theElems.begin();
3222 // loop on not yet smoothed elements: look for elems on a face
3223 while ( itElem != theElems.end() ) {
3224 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3225 break; // all elements found
3227 const SMDS_MeshElement* elem = *itElem;
3228 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3229 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3233 elemsOnFace.push_back( elem );
3234 theElems.erase( itElem++ );
3238 isQuadratic = elem->IsQuadratic();
3240 // get movable nodes of elem
3241 const SMDS_MeshNode* node;
3242 SMDS_TypeOfPosition posType;
3243 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3244 int nn = 0, nbn = elem->NbNodes();
3245 if(elem->IsQuadratic())
3247 while ( nn++ < nbn ) {
3248 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3249 const SMDS_PositionPtr& pos = node->GetPosition();
3250 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3251 if (posType != SMDS_TOP_EDGE &&
3252 posType != SMDS_TOP_VERTEX &&
3253 theFixedNodes.find( node ) == theFixedNodes.end())
3255 // check if all faces around the node are on faceSubMesh
3256 // because a node on edge may be bound to face
3257 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3259 if ( faceSubMesh ) {
3260 while ( eIt->more() && all ) {
3261 const SMDS_MeshElement* e = eIt->next();
3262 all = faceSubMesh->Contains( e );
3266 setMovableNodes.insert( node );
3268 checkBoundaryNodes = true;
3270 if ( posType == SMDS_TOP_3DSPACE )
3271 checkBoundaryNodes = true;
3274 if ( surface.IsNull() )
3277 // get nodes to check UV
3278 list< const SMDS_MeshNode* > uvCheckNodes;
3279 itN = elem->nodesIterator();
3280 nn = 0; nbn = elem->NbNodes();
3281 if(elem->IsQuadratic())
3283 while ( nn++ < nbn ) {
3284 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3285 if ( uvMap.find( node ) == uvMap.end() )
3286 uvCheckNodes.push_back( node );
3287 // add nodes of elems sharing node
3288 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3289 // while ( eIt->more() ) {
3290 // const SMDS_MeshElement* e = eIt->next();
3291 // if ( e != elem ) {
3292 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3293 // while ( nIt->more() ) {
3294 // const SMDS_MeshNode* n =
3295 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3296 // if ( uvMap.find( n ) == uvMap.end() )
3297 // uvCheckNodes.push_back( n );
3303 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3304 for ( ; n != uvCheckNodes.end(); ++n ) {
3307 const SMDS_PositionPtr& pos = node->GetPosition();
3308 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3310 switch ( posType ) {
3311 case SMDS_TOP_FACE: {
3312 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3313 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3316 case SMDS_TOP_EDGE: {
3317 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3318 Handle(Geom2d_Curve) pcurve;
3319 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3320 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3321 if ( !pcurve.IsNull() ) {
3322 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3323 uv = pcurve->Value( u ).XY();
3327 case SMDS_TOP_VERTEX: {
3328 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3329 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3330 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3335 // check existing UV
3336 bool project = true;
3337 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3338 double dist1 = DBL_MAX, dist2 = 0;
3339 if ( posType != SMDS_TOP_3DSPACE ) {
3340 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3341 project = dist1 > fToler2;
3343 if ( project ) { // compute new UV
3345 if ( !getClosestUV( projector, pNode, newUV )) {
3346 MESSAGE("Node Projection Failed " << node);
3350 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3352 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3354 if ( posType != SMDS_TOP_3DSPACE )
3355 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3356 if ( dist2 < dist1 )
3360 // store UV in the map
3361 listUV.push_back( uv );
3362 uvMap.insert( make_pair( node, &listUV.back() ));
3364 } // loop on not yet smoothed elements
3366 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3367 checkBoundaryNodes = true;
3369 // fix nodes on mesh boundary
3371 if ( checkBoundaryNodes ) {
3372 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3373 map< SMESH_TLink, int >::iterator link_nb;
3374 // put all elements links to linkNbMap
3375 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3376 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3377 const SMDS_MeshElement* elem = (*elemIt);
3378 int nbn = elem->NbCornerNodes();
3379 // loop on elem links: insert them in linkNbMap
3380 for ( int iN = 0; iN < nbn; ++iN ) {
3381 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3382 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3383 SMESH_TLink link( n1, n2 );
3384 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3388 // remove nodes that are in links encountered only once from setMovableNodes
3389 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3390 if ( link_nb->second == 1 ) {
3391 setMovableNodes.erase( link_nb->first.node1() );
3392 setMovableNodes.erase( link_nb->first.node2() );
3397 // -----------------------------------------------------
3398 // for nodes on seam edge, compute one more UV ( uvMap2 );
3399 // find movable nodes linked to nodes on seam and which
3400 // are to be smoothed using the second UV ( uvMap2 )
3401 // -----------------------------------------------------
3403 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3404 if ( !surface.IsNull() ) {
3405 TopExp_Explorer eExp( face, TopAbs_EDGE );
3406 for ( ; eExp.More(); eExp.Next() ) {
3407 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3408 if ( !BRep_Tool::IsClosed( edge, face ))
3410 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3411 if ( !sm ) continue;
3412 // find out which parameter varies for a node on seam
3415 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3416 if ( pcurve.IsNull() ) continue;
3417 uv1 = pcurve->Value( f );
3419 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3420 if ( pcurve.IsNull() ) continue;
3421 uv2 = pcurve->Value( f );
3422 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3424 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3425 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3427 // get nodes on seam and its vertices
3428 list< const SMDS_MeshNode* > seamNodes;
3429 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3430 while ( nSeamIt->more() ) {
3431 const SMDS_MeshNode* node = nSeamIt->next();
3432 if ( !isQuadratic || !IsMedium( node ))
3433 seamNodes.push_back( node );
3435 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3436 for ( ; vExp.More(); vExp.Next() ) {
3437 sm = aMesh->MeshElements( vExp.Current() );
3439 nSeamIt = sm->GetNodes();
3440 while ( nSeamIt->more() )
3441 seamNodes.push_back( nSeamIt->next() );
3444 // loop on nodes on seam
3445 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3446 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3447 const SMDS_MeshNode* nSeam = *noSeIt;
3448 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3449 if ( n_uv == uvMap.end() )
3452 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3453 // set the second UV
3454 listUV.push_back( *n_uv->second );
3455 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3456 if ( uvMap2.empty() )
3457 uvMap2 = uvMap; // copy the uvMap contents
3458 uvMap2[ nSeam ] = &listUV.back();
3460 // collect movable nodes linked to ones on seam in nodesNearSeam
3461 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3462 while ( eIt->more() ) {
3463 const SMDS_MeshElement* e = eIt->next();
3464 int nbUseMap1 = 0, nbUseMap2 = 0;
3465 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3466 int nn = 0, nbn = e->NbNodes();
3467 if(e->IsQuadratic()) nbn = nbn/2;
3468 while ( nn++ < nbn )
3470 const SMDS_MeshNode* n =
3471 static_cast<const SMDS_MeshNode*>( nIt->next() );
3473 setMovableNodes.find( n ) == setMovableNodes.end() )
3475 // add only nodes being closer to uv2 than to uv1
3476 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3477 0.5 * ( n->Y() + nSeam->Y() ),
3478 0.5 * ( n->Z() + nSeam->Z() ));
3480 getClosestUV( projector, pMid, uv );
3481 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3482 nodesNearSeam.insert( n );
3488 // for centroidalSmooth all element nodes must
3489 // be on one side of a seam
3490 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3491 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3493 while ( nn++ < nbn ) {
3494 const SMDS_MeshNode* n =
3495 static_cast<const SMDS_MeshNode*>( nIt->next() );
3496 setMovableNodes.erase( n );
3500 } // loop on nodes on seam
3501 } // loop on edge of a face
3502 } // if ( !face.IsNull() )
3504 if ( setMovableNodes.empty() ) {
3505 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3506 continue; // goto next face
3514 double maxRatio = -1., maxDisplacement = -1.;
3515 set<const SMDS_MeshNode*>::iterator nodeToMove;
3516 for ( it = 0; it < theNbIterations; it++ ) {
3517 maxDisplacement = 0.;
3518 nodeToMove = setMovableNodes.begin();
3519 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3520 const SMDS_MeshNode* node = (*nodeToMove);
3521 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3524 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3525 if ( theSmoothMethod == LAPLACIAN )
3526 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3528 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3530 // node displacement
3531 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3532 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3533 if ( aDispl > maxDisplacement )
3534 maxDisplacement = aDispl;
3536 // no node movement => exit
3537 //if ( maxDisplacement < 1.e-16 ) {
3538 if ( maxDisplacement < disttol ) {
3539 MESSAGE("-- no node movement --");
3543 // check elements quality
3545 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3546 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3547 const SMDS_MeshElement* elem = (*elemIt);
3548 if ( !elem || elem->GetType() != SMDSAbs_Face )
3550 SMESH::Controls::TSequenceOfXYZ aPoints;
3551 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3552 double aValue = aQualityFunc.GetValue( aPoints );
3553 if ( aValue > maxRatio )
3557 if ( maxRatio <= theTgtAspectRatio ) {
3558 MESSAGE("-- quality achived --");
3561 if (it+1 == theNbIterations) {
3562 MESSAGE("-- Iteration limit exceeded --");
3564 } // smoothing iterations
3566 MESSAGE(" Face id: " << *fId <<
3567 " Nb iterstions: " << it <<
3568 " Displacement: " << maxDisplacement <<
3569 " Aspect Ratio " << maxRatio);
3571 // ---------------------------------------
3572 // new nodes positions are computed,
3573 // record movement in DS and set new UV
3574 // ---------------------------------------
3575 nodeToMove = setMovableNodes.begin();
3576 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3577 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3578 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3579 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3580 if ( node_uv != uvMap.end() ) {
3581 gp_XY* uv = node_uv->second;
3583 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3587 // move medium nodes of quadratic elements
3590 SMESH_MesherHelper helper( *GetMesh() );
3591 if ( !face.IsNull() )
3592 helper.SetSubShape( face );
3593 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3594 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3595 const SMDS_VtkFace* QF =
3596 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3597 if(QF && QF->IsQuadratic()) {
3598 vector<const SMDS_MeshNode*> Ns;
3599 Ns.reserve(QF->NbNodes()+1);
3600 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3601 while ( anIter->more() )
3602 Ns.push_back( cast2Node(anIter->next()) );
3603 Ns.push_back( Ns[0] );
3605 for(int i=0; i<QF->NbNodes(); i=i+2) {
3606 if ( !surface.IsNull() ) {
3607 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3608 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3609 gp_XY uv = ( uv1 + uv2 ) / 2.;
3610 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3611 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3614 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3615 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3616 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3618 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3619 fabs( Ns[i+1]->Y() - y ) > disttol ||
3620 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3621 // we have to move i+1 node
3622 aMesh->MoveNode( Ns[i+1], x, y, z );
3629 } // loop on face ids
3633 //=======================================================================
3634 //function : isReverse
3635 //purpose : Return true if normal of prevNodes is not co-directied with
3636 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3637 // iNotSame is where prevNodes and nextNodes are different.
3638 // If result is true then future volume orientation is OK
3639 //=======================================================================
3641 static bool isReverse(const SMDS_MeshElement* face,
3642 const vector<const SMDS_MeshNode*>& prevNodes,
3643 const vector<const SMDS_MeshNode*>& nextNodes,
3647 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3648 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3649 gp_XYZ extrDir( pN - pP ), faceNorm;
3650 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3652 return faceNorm * extrDir < 0.0;
3655 //=======================================================================
3657 * \brief Create elements by sweeping an element
3658 * \param elem - element to sweep
3659 * \param newNodesItVec - nodes generated from each node of the element
3660 * \param newElems - generated elements
3661 * \param nbSteps - number of sweeping steps
3662 * \param srcElements - to append elem for each generated element
3664 //=======================================================================
3666 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3667 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3668 list<const SMDS_MeshElement*>& newElems,
3670 SMESH_SequenceOfElemPtr& srcElements)
3672 //MESSAGE("sweepElement " << nbSteps);
3673 SMESHDS_Mesh* aMesh = GetMeshDS();
3675 const int nbNodes = elem->NbNodes();
3676 const int nbCorners = elem->NbCornerNodes();
3677 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3678 polyhedron creation !!! */
3679 // Loop on elem nodes:
3680 // find new nodes and detect same nodes indices
3681 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3682 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3683 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3684 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3686 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3687 vector<int> sames(nbNodes);
3688 vector<bool> isSingleNode(nbNodes);
3690 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3691 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3692 const SMDS_MeshNode* node = nnIt->first;
3693 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3694 if ( listNewNodes.empty() )
3697 itNN [ iNode ] = listNewNodes.begin();
3698 prevNod[ iNode ] = node;
3699 nextNod[ iNode ] = listNewNodes.front();
3701 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3702 corner node of linear */
3703 if ( prevNod[ iNode ] != nextNod [ iNode ])
3704 nbDouble += !isSingleNode[iNode];
3706 if( iNode < nbCorners ) { // check corners only
3707 if ( prevNod[ iNode ] == nextNod [ iNode ])
3708 sames[nbSame++] = iNode;
3710 iNotSameNode = iNode;
3714 if ( nbSame == nbNodes || nbSame > 2) {
3715 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3719 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3721 // fix nodes order to have bottom normal external
3722 if ( baseType == SMDSEntity_Polygon )
3724 std::reverse( itNN.begin(), itNN.end() );
3725 std::reverse( prevNod.begin(), prevNod.end() );
3726 std::reverse( midlNod.begin(), midlNod.end() );
3727 std::reverse( nextNod.begin(), nextNod.end() );
3728 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3732 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3733 SMDS_MeshCell::applyInterlace( ind, itNN );
3734 SMDS_MeshCell::applyInterlace( ind, prevNod );
3735 SMDS_MeshCell::applyInterlace( ind, nextNod );
3736 SMDS_MeshCell::applyInterlace( ind, midlNod );
3737 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3740 sames[nbSame] = iNotSameNode;
3741 for ( int j = 0; j <= nbSame; ++j )
3742 for ( size_t i = 0; i < ind.size(); ++i )
3743 if ( ind[i] == sames[j] )
3748 iNotSameNode = sames[nbSame];
3753 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3755 iSameNode = sames[ nbSame-1 ];
3756 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3757 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3758 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3761 // make new elements
3762 for (int iStep = 0; iStep < nbSteps; iStep++ )
3765 for ( iNode = 0; iNode < nbNodes; iNode++ )
3767 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3768 nextNod[ iNode ] = *itNN[ iNode ]++;
3771 SMDS_MeshElement* aNewElem = 0;
3772 /*if(!elem->IsPoly())*/ {
3773 switch ( baseType ) {
3775 case SMDSEntity_Node: { // sweep NODE
3776 if ( nbSame == 0 ) {
3777 if ( isSingleNode[0] )
3778 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3780 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3786 case SMDSEntity_Edge: { // sweep EDGE
3787 if ( nbDouble == 0 )
3789 if ( nbSame == 0 ) // ---> quadrangle
3790 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3791 nextNod[ 1 ], nextNod[ 0 ] );
3792 else // ---> triangle
3793 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3794 nextNod[ iNotSameNode ] );
3796 else // ---> polygon
3798 vector<const SMDS_MeshNode*> poly_nodes;
3799 poly_nodes.push_back( prevNod[0] );
3800 poly_nodes.push_back( prevNod[1] );
3801 if ( prevNod[1] != nextNod[1] )
3803 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3804 poly_nodes.push_back( nextNod[1] );
3806 if ( prevNod[0] != nextNod[0] )
3808 poly_nodes.push_back( nextNod[0] );
3809 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3811 switch ( poly_nodes.size() ) {
3813 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3816 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3817 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3820 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3825 case SMDSEntity_Triangle: // TRIANGLE --->
3827 if ( nbDouble > 0 ) break;
3828 if ( nbSame == 0 ) // ---> pentahedron
3829 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3830 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3832 else if ( nbSame == 1 ) // ---> pyramid
3833 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3834 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3835 nextNod[ iSameNode ]);
3837 else // 2 same nodes: ---> tetrahedron
3838 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3839 nextNod[ iNotSameNode ]);
3842 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3846 if ( nbDouble+nbSame == 2 )
3848 if(nbSame==0) { // ---> quadratic quadrangle
3849 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3850 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3852 else { //(nbSame==1) // ---> quadratic triangle
3854 return; // medium node on axis
3856 else if(sames[0]==0)
3857 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3858 nextNod[2], midlNod[1], prevNod[2]);
3860 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3861 midlNod[0], nextNod[2], prevNod[2]);
3864 else if ( nbDouble == 3 )
3866 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3867 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3868 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3875 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3876 if ( nbDouble > 0 ) break;
3878 if ( nbSame == 0 ) // ---> hexahedron
3879 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3880 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3882 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3883 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3884 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3885 nextNod[ iSameNode ]);
3886 newElems.push_back( aNewElem );
3887 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3888 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3889 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3891 else if ( nbSame == 2 ) { // ---> pentahedron
3892 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3893 // iBeforeSame is same too
3894 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3895 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3896 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3898 // iAfterSame is same too
3899 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3900 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3901 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3905 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3906 if ( nbDouble+nbSame != 3 ) break;
3908 // ---> pentahedron with 15 nodes
3909 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3910 nextNod[0], nextNod[1], nextNod[2],
3911 prevNod[3], prevNod[4], prevNod[5],
3912 nextNod[3], nextNod[4], nextNod[5],
3913 midlNod[0], midlNod[1], midlNod[2]);
3915 else if(nbSame==1) {
3916 // ---> 2d order pyramid of 13 nodes
3917 int apex = iSameNode;
3918 int i0 = ( apex + 1 ) % nbCorners;
3919 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3923 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3924 nextNod[i0], nextNod[i1], prevNod[apex],
3925 prevNod[i01], midlNod[i0],
3926 nextNod[i01], midlNod[i1],
3927 prevNod[i1a], prevNod[i0a],
3928 nextNod[i0a], nextNod[i1a]);
3930 else if(nbSame==2) {
3931 // ---> 2d order tetrahedron of 10 nodes
3932 int n1 = iNotSameNode;
3933 int n2 = ( n1 + 1 ) % nbCorners;
3934 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3938 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3939 prevNod[n12], prevNod[n23], prevNod[n31],
3940 midlNod[n1], nextNod[n12], nextNod[n31]);
3944 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3946 if ( nbDouble != 4 ) break;
3947 // ---> hexahedron with 20 nodes
3948 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3949 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3950 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3951 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3952 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3954 else if(nbSame==1) {
3955 // ---> pyramid + pentahedron - can not be created since it is needed
3956 // additional middle node at the center of face
3957 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3960 else if( nbSame == 2 ) {
3961 if ( nbDouble != 2 ) break;
3962 // ---> 2d order Pentahedron with 15 nodes
3964 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3965 // iBeforeSame is same too
3972 // iAfterSame is same too
3982 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3983 prevNod[n4], prevNod[n5], nextNod[n5],
3984 prevNod[n12], midlNod[n2], nextNod[n12],
3985 prevNod[n45], midlNod[n5], nextNod[n45],
3986 prevNod[n14], prevNod[n25], nextNod[n25]);
3990 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3992 if( nbSame == 0 && nbDouble == 9 ) {
3993 // ---> tri-quadratic hexahedron with 27 nodes
3994 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3995 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3996 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3997 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3998 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3999 prevNod[8], // bottom center
4000 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4001 nextNod[8], // top center
4002 midlNod[8]);// elem center
4010 case SMDSEntity_Polygon: { // sweep POLYGON
4012 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4013 // ---> hexagonal prism
4014 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4015 prevNod[3], prevNod[4], prevNod[5],
4016 nextNod[0], nextNod[1], nextNod[2],
4017 nextNod[3], nextNod[4], nextNod[5]);
4021 case SMDSEntity_Ball:
4029 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4031 if ( baseType != SMDSEntity_Polygon )
4033 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4034 SMDS_MeshCell::applyInterlace( ind, prevNod );
4035 SMDS_MeshCell::applyInterlace( ind, nextNod );
4036 SMDS_MeshCell::applyInterlace( ind, midlNod );
4037 SMDS_MeshCell::applyInterlace( ind, itNN );
4038 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4039 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4041 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4042 vector<int> quantities (nbNodes + 2);
4043 polyedre_nodes.clear();
4047 for (int inode = 0; inode < nbNodes; inode++)
4048 polyedre_nodes.push_back( prevNod[inode] );
4049 quantities.push_back( nbNodes );
4052 polyedre_nodes.push_back( nextNod[0] );
4053 for (int inode = nbNodes; inode-1; --inode )
4054 polyedre_nodes.push_back( nextNod[inode-1] );
4055 quantities.push_back( nbNodes );
4058 for (int iface = 0; iface < nbNodes; iface++)
4060 const int prevNbNodes = polyedre_nodes.size();
4061 int inextface = (iface+1) % nbNodes;
4062 polyedre_nodes.push_back( prevNod[inextface] );
4063 polyedre_nodes.push_back( prevNod[iface] );
4064 if ( prevNod[iface] != nextNod[iface] )
4066 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4067 polyedre_nodes.push_back( nextNod[iface] );
4069 if ( prevNod[inextface] != nextNod[inextface] )
4071 polyedre_nodes.push_back( nextNod[inextface] );
4072 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4074 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4075 if ( nbFaceNodes > 2 )
4076 quantities.push_back( nbFaceNodes );
4077 else // degenerated face
4078 polyedre_nodes.resize( prevNbNodes );
4080 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4084 newElems.push_back( aNewElem );
4085 myLastCreatedElems.Append(aNewElem);
4086 srcElements.Append( elem );
4089 // set new prev nodes
4090 for ( iNode = 0; iNode < nbNodes; iNode++ )
4091 prevNod[ iNode ] = nextNod[ iNode ];
4096 //=======================================================================
4098 * \brief Create 1D and 2D elements around swept elements
4099 * \param mapNewNodes - source nodes and ones generated from them
4100 * \param newElemsMap - source elements and ones generated from them
4101 * \param elemNewNodesMap - nodes generated from each node of each element
4102 * \param elemSet - all swept elements
4103 * \param nbSteps - number of sweeping steps
4104 * \param srcElements - to append elem for each generated element
4106 //=======================================================================
4108 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4109 TElemOfElemListMap & newElemsMap,
4110 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4111 TIDSortedElemSet& elemSet,
4113 SMESH_SequenceOfElemPtr& srcElements)
4115 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4116 SMESHDS_Mesh* aMesh = GetMeshDS();
4118 // Find nodes belonging to only one initial element - sweep them to get edges.
4120 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4121 for ( ; nList != mapNewNodes.end(); nList++ )
4123 const SMDS_MeshNode* node =
4124 static_cast<const SMDS_MeshNode*>( nList->first );
4125 if ( newElemsMap.count( node ))
4126 continue; // node was extruded into edge
4127 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4128 int nbInitElems = 0;
4129 const SMDS_MeshElement* el = 0;
4130 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4131 while ( eIt->more() && nbInitElems < 2 ) {
4133 SMDSAbs_ElementType type = el->GetType();
4134 if ( type == SMDSAbs_Volume || type < highType ) continue;
4135 if ( type > highType ) {
4139 nbInitElems += elemSet.count(el);
4141 if ( nbInitElems < 2 ) {
4142 bool NotCreateEdge = el && el->IsMediumNode(node);
4143 if(!NotCreateEdge) {
4144 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4145 list<const SMDS_MeshElement*> newEdges;
4146 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4151 // Make a ceiling for each element ie an equal element of last new nodes.
4152 // Find free links of faces - make edges and sweep them into faces.
4154 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4155 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4156 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4158 const SMDS_MeshElement* elem = itElem->first;
4159 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4161 if(itElem->second.size()==0) continue;
4163 const bool isQuadratic = elem->IsQuadratic();
4165 if ( elem->GetType() == SMDSAbs_Edge ) {
4166 // create a ceiling edge
4167 if ( !isQuadratic ) {
4168 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4169 vecNewNodes[ 1 ]->second.back())) {
4170 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4171 vecNewNodes[ 1 ]->second.back()));
4172 srcElements.Append( myLastCreatedElems.Last() );
4176 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4177 vecNewNodes[ 1 ]->second.back(),
4178 vecNewNodes[ 2 ]->second.back())) {
4179 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4180 vecNewNodes[ 1 ]->second.back(),
4181 vecNewNodes[ 2 ]->second.back()));
4182 srcElements.Append( myLastCreatedElems.Last() );
4186 if ( elem->GetType() != SMDSAbs_Face )
4189 bool hasFreeLinks = false;
4191 TIDSortedElemSet avoidSet;
4192 avoidSet.insert( elem );
4194 set<const SMDS_MeshNode*> aFaceLastNodes;
4195 int iNode, nbNodes = vecNewNodes.size();
4196 if ( !isQuadratic ) {
4197 // loop on the face nodes
4198 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4199 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4200 // look for free links of the face
4201 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4202 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4203 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4204 // check if a link is free
4205 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4206 hasFreeLinks = true;
4207 // make an edge and a ceiling for a new edge
4208 if ( !aMesh->FindEdge( n1, n2 )) {
4209 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4210 srcElements.Append( myLastCreatedElems.Last() );
4212 n1 = vecNewNodes[ iNode ]->second.back();
4213 n2 = vecNewNodes[ iNext ]->second.back();
4214 if ( !aMesh->FindEdge( n1, n2 )) {
4215 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4216 srcElements.Append( myLastCreatedElems.Last() );
4221 else { // elem is quadratic face
4222 int nbn = nbNodes/2;
4223 for ( iNode = 0; iNode < nbn; iNode++ ) {
4224 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4225 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4226 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4227 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4228 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4229 // check if a link is free
4230 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4231 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4232 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4233 hasFreeLinks = true;
4234 // make an edge and a ceiling for a new edge
4236 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4237 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4238 srcElements.Append( myLastCreatedElems.Last() );
4240 n1 = vecNewNodes[ iNode ]->second.back();
4241 n2 = vecNewNodes[ iNext ]->second.back();
4242 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4243 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4244 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4245 srcElements.Append( myLastCreatedElems.Last() );
4249 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4250 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4254 // sweep free links into faces
4256 if ( hasFreeLinks ) {
4257 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4258 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4260 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4261 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4262 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4263 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4265 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4266 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4267 std::advance( v, volNb );
4268 // find indices of free faces of a volume and their source edges
4269 list< int > freeInd;
4270 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4271 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4272 int iF, nbF = vTool.NbFaces();
4273 for ( iF = 0; iF < nbF; iF ++ ) {
4274 if (vTool.IsFreeFace( iF ) &&
4275 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4276 initNodeSet != faceNodeSet) // except an initial face
4278 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4280 freeInd.push_back( iF );
4281 // find source edge of a free face iF
4282 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4283 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4284 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4285 initNodeSet.begin(), initNodeSet.end(),
4286 commonNodes.begin());
4287 if ( (*v)->IsQuadratic() )
4288 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4290 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4292 if ( !srcEdges.back() )
4294 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4295 << iF << " of volume #" << vTool.ID() << endl;
4300 if ( freeInd.empty() )
4303 // create faces for all steps;
4304 // if such a face has been already created by sweep of edge,
4305 // assure that its orientation is OK
4306 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4307 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4308 vTool.SetExternalNormal();
4309 const int nextShift = vTool.IsForward() ? +1 : -1;
4310 list< int >::iterator ind = freeInd.begin();
4311 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4312 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4314 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4315 int nbn = vTool.NbFaceNodes( *ind );
4316 const SMDS_MeshElement * f = 0;
4317 if ( nbn == 3 ) ///// triangle
4319 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4321 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4323 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4325 nodes[ 1 + nextShift ] };
4327 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4329 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4333 else if ( nbn == 4 ) ///// quadrangle
4335 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4337 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4339 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4340 nodes[ 2 ], nodes[ 2+nextShift ] };
4342 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4344 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4345 newOrder[ 2 ], newOrder[ 3 ]));
4348 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4350 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4352 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4354 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4356 nodes[2 + 2*nextShift],
4357 nodes[3 - 2*nextShift],
4359 nodes[3 + 2*nextShift]};
4361 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4363 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4371 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4373 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4374 nodes[1], nodes[3], nodes[5], nodes[7] );
4376 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4378 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4379 nodes[4 - 2*nextShift],
4381 nodes[4 + 2*nextShift],
4383 nodes[5 - 2*nextShift],
4385 nodes[5 + 2*nextShift] };
4387 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4389 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4390 newOrder[ 2 ], newOrder[ 3 ],
4391 newOrder[ 4 ], newOrder[ 5 ],
4392 newOrder[ 6 ], newOrder[ 7 ]));
4395 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4397 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4398 SMDSAbs_Face, /*noMedium=*/false);
4400 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4402 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4403 nodes[4 - 2*nextShift],
4405 nodes[4 + 2*nextShift],
4407 nodes[5 - 2*nextShift],
4409 nodes[5 + 2*nextShift],
4412 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4414 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4415 newOrder[ 2 ], newOrder[ 3 ],
4416 newOrder[ 4 ], newOrder[ 5 ],
4417 newOrder[ 6 ], newOrder[ 7 ],
4421 else //////// polygon
4423 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4424 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4426 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4428 if ( !vTool.IsForward() )
4429 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4431 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4433 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4437 while ( srcElements.Length() < myLastCreatedElems.Length() )
4438 srcElements.Append( *srcEdge );
4440 } // loop on free faces
4442 // go to the next volume
4444 while ( iVol++ < nbVolumesByStep ) v++;
4447 } // loop on volumes of one step
4448 } // sweep free links into faces
4450 // Make a ceiling face with a normal external to a volume
4452 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4454 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4456 lastVol.SetExternalNormal();
4457 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4458 int nbn = lastVol.NbFaceNodes( iF );
4460 if (!hasFreeLinks ||
4461 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4462 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4464 else if ( nbn == 4 )
4466 if (!hasFreeLinks ||
4467 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4468 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4470 else if ( nbn == 6 && isQuadratic )
4472 if (!hasFreeLinks ||
4473 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4474 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4475 nodes[1], nodes[3], nodes[5]));
4477 else if ( nbn == 8 && isQuadratic )
4479 if (!hasFreeLinks ||
4480 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4481 nodes[1], nodes[3], nodes[5], nodes[7]) )
4482 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4483 nodes[1], nodes[3], nodes[5], nodes[7]));
4485 else if ( nbn == 9 && isQuadratic )
4487 if (!hasFreeLinks ||
4488 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4489 SMDSAbs_Face, /*noMedium=*/false) )
4490 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4491 nodes[1], nodes[3], nodes[5], nodes[7],
4495 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4496 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4497 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4500 while ( srcElements.Length() < myLastCreatedElems.Length() )
4501 srcElements.Append( myLastCreatedElems.Last() );
4503 } // loop on swept elements
4506 //=======================================================================
4507 //function : RotationSweep
4509 //=======================================================================
4511 SMESH_MeshEditor::PGroupIDs
4512 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4513 const gp_Ax1& theAxis,
4514 const double theAngle,
4515 const int theNbSteps,
4516 const double theTol,
4517 const bool theMakeGroups,
4518 const bool theMakeWalls)
4520 myLastCreatedElems.Clear();
4521 myLastCreatedNodes.Clear();
4523 // source elements for each generated one
4524 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4526 MESSAGE( "RotationSweep()");
4528 aTrsf.SetRotation( theAxis, theAngle );
4530 aTrsf2.SetRotation( theAxis, theAngle/2. );
4532 gp_Lin aLine( theAxis );
4533 double aSqTol = theTol * theTol;
4535 SMESHDS_Mesh* aMesh = GetMeshDS();
4537 TNodeOfNodeListMap mapNewNodes;
4538 TElemOfVecOfNnlmiMap mapElemNewNodes;
4539 TElemOfElemListMap newElemsMap;
4541 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4542 myMesh->NbFaces(ORDER_QUADRATIC) +
4543 myMesh->NbVolumes(ORDER_QUADRATIC) );
4545 TIDSortedElemSet::iterator itElem;
4546 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4547 const SMDS_MeshElement* elem = *itElem;
4548 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4550 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4551 newNodesItVec.reserve( elem->NbNodes() );
4553 // loop on elem nodes
4554 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4555 while ( itN->more() )
4557 // check if a node has been already sweeped
4558 const SMDS_MeshNode* node = cast2Node( itN->next() );
4560 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4562 aXYZ.Coord( coord[0], coord[1], coord[2] );
4563 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4565 TNodeOfNodeListMapItr nIt =
4566 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4567 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4568 if ( listNewNodes.empty() )
4570 // check if we are to create medium nodes between corner ones
4571 bool needMediumNodes = false;
4572 if ( isQuadraticMesh )
4574 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4575 while (it->more() && !needMediumNodes )
4577 const SMDS_MeshElement* invElem = it->next();
4578 if ( invElem != elem && !theElems.count( invElem )) continue;
4579 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4580 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4581 needMediumNodes = true;
4586 const SMDS_MeshNode * newNode = node;
4587 for ( int i = 0; i < theNbSteps; i++ ) {
4589 if ( needMediumNodes ) // create a medium node
4591 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4592 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4593 myLastCreatedNodes.Append(newNode);
4594 srcNodes.Append( node );
4595 listNewNodes.push_back( newNode );
4596 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4599 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4601 // create a corner node
4602 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4603 myLastCreatedNodes.Append(newNode);
4604 srcNodes.Append( node );
4605 listNewNodes.push_back( newNode );
4608 listNewNodes.push_back( newNode );
4609 // if ( needMediumNodes )
4610 // listNewNodes.push_back( newNode );
4614 newNodesItVec.push_back( nIt );
4616 // make new elements
4617 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4621 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4623 PGroupIDs newGroupIDs;
4624 if ( theMakeGroups )
4625 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4631 //=======================================================================
4632 //function : CreateNode
4634 //=======================================================================
4635 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4638 const double tolnode,
4639 SMESH_SequenceOfNode& aNodes)
4641 // myLastCreatedElems.Clear();
4642 // myLastCreatedNodes.Clear();
4645 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4647 // try to search in sequence of existing nodes
4648 // if aNodes.Length()>0 we 'nave to use given sequence
4649 // else - use all nodes of mesh
4650 if(aNodes.Length()>0) {
4652 for(i=1; i<=aNodes.Length(); i++) {
4653 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4654 if(P1.Distance(P2)<tolnode)
4655 return aNodes.Value(i);
4659 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4660 while(itn->more()) {
4661 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4662 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4663 if(P1.Distance(P2)<tolnode)
4668 // create new node and return it
4669 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4670 //myLastCreatedNodes.Append(NewNode);
4675 //=======================================================================
4676 //function : ExtrusionSweep
4678 //=======================================================================
4680 SMESH_MeshEditor::PGroupIDs
4681 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4682 const gp_Vec& theStep,
4683 const int theNbSteps,
4684 TElemOfElemListMap& newElemsMap,
4685 const bool theMakeGroups,
4687 const double theTolerance)
4689 ExtrusParam aParams;
4690 aParams.myDir = gp_Dir(theStep);
4691 aParams.myNodes.Clear();
4692 aParams.mySteps = new TColStd_HSequenceOfReal;
4694 for(i=1; i<=theNbSteps; i++)
4695 aParams.mySteps->Append(theStep.Magnitude());
4698 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4702 //=======================================================================
4703 //function : ExtrusionSweep
4705 //=======================================================================
4707 SMESH_MeshEditor::PGroupIDs
4708 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4709 ExtrusParam& theParams,
4710 TElemOfElemListMap& newElemsMap,
4711 const bool theMakeGroups,
4713 const double theTolerance)
4715 myLastCreatedElems.Clear();
4716 myLastCreatedNodes.Clear();
4718 // source elements for each generated one
4719 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4721 SMESHDS_Mesh* aMesh = GetMeshDS();
4723 int nbsteps = theParams.mySteps->Length();
4725 TNodeOfNodeListMap mapNewNodes;
4726 //TNodeOfNodeVecMap mapNewNodes;
4727 TElemOfVecOfNnlmiMap mapElemNewNodes;
4728 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4730 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4731 myMesh->NbFaces(ORDER_QUADRATIC) +
4732 myMesh->NbVolumes(ORDER_QUADRATIC) );
4734 TIDSortedElemSet::iterator itElem;
4735 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4736 // check element type
4737 const SMDS_MeshElement* elem = *itElem;
4738 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4741 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4742 newNodesItVec.reserve( elem->NbNodes() );
4744 // loop on elem nodes
4745 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4746 while ( itN->more() )
4748 // check if a node has been already sweeped
4749 const SMDS_MeshNode* node = cast2Node( itN->next() );
4750 TNodeOfNodeListMap::iterator nIt =
4751 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4752 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4753 if ( listNewNodes.empty() )
4757 // check if we are to create medium nodes between corner ones
4758 bool needMediumNodes = false;
4759 if ( isQuadraticMesh )
4761 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4762 while (it->more() && !needMediumNodes )
4764 const SMDS_MeshElement* invElem = it->next();
4765 if ( invElem != elem && !theElems.count( invElem )) continue;
4766 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4767 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4768 needMediumNodes = true;
4772 double coord[] = { node->X(), node->Y(), node->Z() };
4773 for ( int i = 0; i < nbsteps; i++ )
4775 if ( needMediumNodes ) // create a medium node
4777 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4778 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4779 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4780 if( theFlags & EXTRUSION_FLAG_SEW ) {
4781 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4782 theTolerance, theParams.myNodes);
4783 listNewNodes.push_back( newNode );
4786 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4787 myLastCreatedNodes.Append(newNode);
4788 srcNodes.Append( node );
4789 listNewNodes.push_back( newNode );
4792 // create a corner node
4793 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4794 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4795 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4796 if( theFlags & EXTRUSION_FLAG_SEW ) {
4797 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4798 theTolerance, theParams.myNodes);
4799 listNewNodes.push_back( newNode );
4802 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4803 myLastCreatedNodes.Append(newNode);
4804 srcNodes.Append( node );
4805 listNewNodes.push_back( newNode );
4809 newNodesItVec.push_back( nIt );
4811 // make new elements
4812 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4815 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4816 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4818 PGroupIDs newGroupIDs;
4819 if ( theMakeGroups )
4820 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4825 //=======================================================================
4826 //function : ExtrusionAlongTrack
4828 //=======================================================================
4829 SMESH_MeshEditor::Extrusion_Error
4830 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4831 SMESH_subMesh* theTrack,
4832 const SMDS_MeshNode* theN1,
4833 const bool theHasAngles,
4834 list<double>& theAngles,
4835 const bool theLinearVariation,
4836 const bool theHasRefPoint,
4837 const gp_Pnt& theRefPoint,
4838 const bool theMakeGroups)
4840 MESSAGE("ExtrusionAlongTrack");
4841 myLastCreatedElems.Clear();
4842 myLastCreatedNodes.Clear();
4845 std::list<double> aPrms;
4846 TIDSortedElemSet::iterator itElem;
4849 TopoDS_Edge aTrackEdge;
4850 TopoDS_Vertex aV1, aV2;
4852 SMDS_ElemIteratorPtr aItE;
4853 SMDS_NodeIteratorPtr aItN;
4854 SMDSAbs_ElementType aTypeE;
4856 TNodeOfNodeListMap mapNewNodes;
4859 aNbE = theElements.size();
4862 return EXTR_NO_ELEMENTS;
4864 // 1.1 Track Pattern
4867 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4869 aItE = pSubMeshDS->GetElements();
4870 while ( aItE->more() ) {
4871 const SMDS_MeshElement* pE = aItE->next();
4872 aTypeE = pE->GetType();
4873 // Pattern must contain links only
4874 if ( aTypeE != SMDSAbs_Edge )
4875 return EXTR_PATH_NOT_EDGE;
4878 list<SMESH_MeshEditor_PathPoint> fullList;
4880 const TopoDS_Shape& aS = theTrack->GetSubShape();
4881 // Sub-shape for the Pattern must be an Edge or Wire
4882 if( aS.ShapeType() == TopAbs_EDGE ) {
4883 aTrackEdge = TopoDS::Edge( aS );
4884 // the Edge must not be degenerated
4885 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4886 return EXTR_BAD_PATH_SHAPE;
4887 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4888 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4889 const SMDS_MeshNode* aN1 = aItN->next();
4890 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4891 const SMDS_MeshNode* aN2 = aItN->next();
4892 // starting node must be aN1 or aN2
4893 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4894 return EXTR_BAD_STARTING_NODE;
4895 aItN = pSubMeshDS->GetNodes();
4896 while ( aItN->more() ) {
4897 const SMDS_MeshNode* pNode = aItN->next();
4898 const SMDS_EdgePosition* pEPos =
4899 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4900 double aT = pEPos->GetUParameter();
4901 aPrms.push_back( aT );
4903 //Extrusion_Error err =
4904 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4905 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4906 list< SMESH_subMesh* > LSM;
4907 TopTools_SequenceOfShape Edges;
4908 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4909 while(itSM->more()) {
4910 SMESH_subMesh* SM = itSM->next();
4912 const TopoDS_Shape& aS = SM->GetSubShape();
4915 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4916 int startNid = theN1->GetID();
4917 TColStd_MapOfInteger UsedNums;
4919 int NbEdges = Edges.Length();
4921 for(; i<=NbEdges; i++) {
4923 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4924 for(; itLSM!=LSM.end(); itLSM++) {
4926 if(UsedNums.Contains(k)) continue;
4927 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4928 SMESH_subMesh* locTrack = *itLSM;
4929 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4930 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4931 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4932 const SMDS_MeshNode* aN1 = aItN->next();
4933 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4934 const SMDS_MeshNode* aN2 = aItN->next();
4935 // starting node must be aN1 or aN2
4936 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4937 // 2. Collect parameters on the track edge
4939 aItN = locMeshDS->GetNodes();
4940 while ( aItN->more() ) {
4941 const SMDS_MeshNode* pNode = aItN->next();
4942 const SMDS_EdgePosition* pEPos =
4943 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4944 double aT = pEPos->GetUParameter();
4945 aPrms.push_back( aT );
4947 list<SMESH_MeshEditor_PathPoint> LPP;
4948 //Extrusion_Error err =
4949 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4950 LLPPs.push_back(LPP);
4952 // update startN for search following egde
4953 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4954 else startNid = aN1->GetID();
4958 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4959 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4960 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4961 for(; itPP!=firstList.end(); itPP++) {
4962 fullList.push_back( *itPP );
4964 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4965 fullList.pop_back();
4967 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4968 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4969 itPP = currList.begin();
4970 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4971 gp_Dir D1 = PP1.Tangent();
4972 gp_Dir D2 = PP2.Tangent();
4973 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4974 (D1.Z()+D2.Z())/2 ) );
4975 PP1.SetTangent(Dnew);
4976 fullList.push_back(PP1);
4978 for(; itPP!=firstList.end(); itPP++) {
4979 fullList.push_back( *itPP );
4981 PP1 = fullList.back();
4982 fullList.pop_back();
4984 // if wire not closed
4985 fullList.push_back(PP1);
4989 return EXTR_BAD_PATH_SHAPE;
4992 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4993 theHasRefPoint, theRefPoint, theMakeGroups);
4997 //=======================================================================
4998 //function : ExtrusionAlongTrack
5000 //=======================================================================
5001 SMESH_MeshEditor::Extrusion_Error
5002 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5003 SMESH_Mesh* theTrack,
5004 const SMDS_MeshNode* theN1,
5005 const bool theHasAngles,
5006 list<double>& theAngles,
5007 const bool theLinearVariation,
5008 const bool theHasRefPoint,
5009 const gp_Pnt& theRefPoint,
5010 const bool theMakeGroups)
5012 myLastCreatedElems.Clear();
5013 myLastCreatedNodes.Clear();
5016 std::list<double> aPrms;
5017 TIDSortedElemSet::iterator itElem;
5020 TopoDS_Edge aTrackEdge;
5021 TopoDS_Vertex aV1, aV2;
5023 SMDS_ElemIteratorPtr aItE;
5024 SMDS_NodeIteratorPtr aItN;
5025 SMDSAbs_ElementType aTypeE;
5027 TNodeOfNodeListMap mapNewNodes;
5030 aNbE = theElements.size();
5033 return EXTR_NO_ELEMENTS;
5035 // 1.1 Track Pattern
5038 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5040 aItE = pMeshDS->elementsIterator();
5041 while ( aItE->more() ) {
5042 const SMDS_MeshElement* pE = aItE->next();
5043 aTypeE = pE->GetType();
5044 // Pattern must contain links only
5045 if ( aTypeE != SMDSAbs_Edge )
5046 return EXTR_PATH_NOT_EDGE;
5049 list<SMESH_MeshEditor_PathPoint> fullList;
5051 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5053 if( aS == SMESH_Mesh::PseudoShape() ) {
5054 //Mesh without shape
5055 const SMDS_MeshNode* currentNode = NULL;
5056 const SMDS_MeshNode* prevNode = theN1;
5057 std::vector<const SMDS_MeshNode*> aNodesList;
5058 aNodesList.push_back(theN1);
5059 int nbEdges = 0, conn=0;
5060 const SMDS_MeshElement* prevElem = NULL;
5061 const SMDS_MeshElement* currentElem = NULL;
5062 int totalNbEdges = theTrack->NbEdges();
5063 SMDS_ElemIteratorPtr nIt;
5066 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5067 return EXTR_BAD_STARTING_NODE;
5070 conn = nbEdgeConnectivity(theN1);
5072 return EXTR_PATH_NOT_EDGE;
5074 aItE = theN1->GetInverseElementIterator();
5075 prevElem = aItE->next();
5076 currentElem = prevElem;
5078 if(totalNbEdges == 1 ) {
5079 nIt = currentElem->nodesIterator();
5080 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5081 if(currentNode == prevNode)
5082 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5083 aNodesList.push_back(currentNode);
5085 nIt = currentElem->nodesIterator();
5086 while( nIt->more() ) {
5087 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5088 if(currentNode == prevNode)
5089 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5090 aNodesList.push_back(currentNode);
5092 //case of the closed mesh
5093 if(currentNode == theN1) {
5098 conn = nbEdgeConnectivity(currentNode);
5100 return EXTR_PATH_NOT_EDGE;
5101 }else if( conn == 1 && nbEdges > 0 ) {
5106 prevNode = currentNode;
5107 aItE = currentNode->GetInverseElementIterator();
5108 currentElem = aItE->next();
5109 if( currentElem == prevElem)
5110 currentElem = aItE->next();
5111 nIt = currentElem->nodesIterator();
5112 prevElem = currentElem;
5118 if(nbEdges != totalNbEdges)
5119 return EXTR_PATH_NOT_EDGE;
5121 TopTools_SequenceOfShape Edges;
5122 double x1,x2,y1,y2,z1,z2;
5123 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5124 int startNid = theN1->GetID();
5125 for(int i = 1; i < aNodesList.size(); i++) {
5126 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5127 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5128 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5129 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5130 list<SMESH_MeshEditor_PathPoint> LPP;
5132 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5133 LLPPs.push_back(LPP);
5134 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5135 else startNid = aNodesList[i-1]->GetID();
5139 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5140 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5141 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5142 for(; itPP!=firstList.end(); itPP++) {
5143 fullList.push_back( *itPP );
5146 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5147 SMESH_MeshEditor_PathPoint PP2;
5148 fullList.pop_back();
5150 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5151 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5152 itPP = currList.begin();
5153 PP2 = currList.front();
5154 gp_Dir D1 = PP1.Tangent();
5155 gp_Dir D2 = PP2.Tangent();
5156 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5157 (D1.Z()+D2.Z())/2 ) );
5158 PP1.SetTangent(Dnew);
5159 fullList.push_back(PP1);
5161 for(; itPP!=currList.end(); itPP++) {
5162 fullList.push_back( *itPP );
5164 PP1 = fullList.back();
5165 fullList.pop_back();
5167 fullList.push_back(PP1);
5169 } // Sub-shape for the Pattern must be an Edge or Wire
5170 else if( aS.ShapeType() == TopAbs_EDGE ) {
5171 aTrackEdge = TopoDS::Edge( aS );
5172 // the Edge must not be degenerated
5173 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5174 return EXTR_BAD_PATH_SHAPE;
5175 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5176 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5177 const SMDS_MeshNode* aN1 = aItN->next();
5178 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5179 const SMDS_MeshNode* aN2 = aItN->next();
5180 // starting node must be aN1 or aN2
5181 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5182 return EXTR_BAD_STARTING_NODE;
5183 aItN = pMeshDS->nodesIterator();
5184 while ( aItN->more() ) {
5185 const SMDS_MeshNode* pNode = aItN->next();
5186 if( pNode==aN1 || pNode==aN2 ) continue;
5187 const SMDS_EdgePosition* pEPos =
5188 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5189 double aT = pEPos->GetUParameter();
5190 aPrms.push_back( aT );
5192 //Extrusion_Error err =
5193 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5195 else if( aS.ShapeType() == TopAbs_WIRE ) {
5196 list< SMESH_subMesh* > LSM;
5197 TopTools_SequenceOfShape Edges;
5198 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5199 for(; eExp.More(); eExp.Next()) {
5200 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5201 if( BRep_Tool::Degenerated(E) ) continue;
5202 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5208 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5209 int startNid = theN1->GetID();
5210 TColStd_MapOfInteger UsedNums;
5211 int NbEdges = Edges.Length();
5213 for(; i<=NbEdges; i++) {
5215 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5216 for(; itLSM!=LSM.end(); itLSM++) {
5218 if(UsedNums.Contains(k)) continue;
5219 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5220 SMESH_subMesh* locTrack = *itLSM;
5221 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5222 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5223 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5224 const SMDS_MeshNode* aN1 = aItN->next();
5225 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5226 const SMDS_MeshNode* aN2 = aItN->next();
5227 // starting node must be aN1 or aN2
5228 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5229 // 2. Collect parameters on the track edge
5231 aItN = locMeshDS->GetNodes();
5232 while ( aItN->more() ) {
5233 const SMDS_MeshNode* pNode = aItN->next();
5234 const SMDS_EdgePosition* pEPos =
5235 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5236 double aT = pEPos->GetUParameter();
5237 aPrms.push_back( aT );
5239 list<SMESH_MeshEditor_PathPoint> LPP;
5240 //Extrusion_Error err =
5241 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5242 LLPPs.push_back(LPP);
5244 // update startN for search following egde
5245 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5246 else startNid = aN1->GetID();
5250 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5251 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5252 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5253 for(; itPP!=firstList.end(); itPP++) {
5254 fullList.push_back( *itPP );
5256 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5257 fullList.pop_back();
5259 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5260 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5261 itPP = currList.begin();
5262 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5263 gp_Dir D1 = PP1.Tangent();
5264 gp_Dir D2 = PP2.Tangent();
5265 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5266 (D1.Z()+D2.Z())/2 ) );
5267 PP1.SetTangent(Dnew);
5268 fullList.push_back(PP1);
5270 for(; itPP!=currList.end(); itPP++) {
5271 fullList.push_back( *itPP );
5273 PP1 = fullList.back();
5274 fullList.pop_back();
5276 // if wire not closed
5277 fullList.push_back(PP1);
5281 return EXTR_BAD_PATH_SHAPE;
5284 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5285 theHasRefPoint, theRefPoint, theMakeGroups);
5289 //=======================================================================
5290 //function : MakeEdgePathPoints
5291 //purpose : auxilary for ExtrusionAlongTrack
5292 //=======================================================================
5293 SMESH_MeshEditor::Extrusion_Error
5294 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5295 const TopoDS_Edge& aTrackEdge,
5297 list<SMESH_MeshEditor_PathPoint>& LPP)
5299 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5301 aTolVec2=aTolVec*aTolVec;
5303 TopoDS_Vertex aV1, aV2;
5304 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5305 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5306 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5307 // 2. Collect parameters on the track edge
5308 aPrms.push_front( aT1 );
5309 aPrms.push_back( aT2 );
5312 if( FirstIsStart ) {
5323 SMESH_MeshEditor_PathPoint aPP;
5324 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5325 std::list<double>::iterator aItD = aPrms.begin();
5326 for(; aItD != aPrms.end(); ++aItD) {
5330 aC3D->D1( aT, aP3D, aVec );
5331 aL2 = aVec.SquareMagnitude();
5332 if ( aL2 < aTolVec2 )
5333 return EXTR_CANT_GET_TANGENT;
5334 gp_Dir aTgt( aVec );
5336 aPP.SetTangent( aTgt );
5337 aPP.SetParameter( aT );
5344 //=======================================================================
5345 //function : MakeExtrElements
5346 //purpose : auxilary for ExtrusionAlongTrack
5347 //=======================================================================
5348 SMESH_MeshEditor::Extrusion_Error
5349 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5350 list<SMESH_MeshEditor_PathPoint>& fullList,
5351 const bool theHasAngles,
5352 list<double>& theAngles,
5353 const bool theLinearVariation,
5354 const bool theHasRefPoint,
5355 const gp_Pnt& theRefPoint,
5356 const bool theMakeGroups)
5358 MESSAGE("MakeExtrElements");
5359 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5360 int aNbTP = fullList.size();
5361 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5363 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5364 LinearAngleVariation(aNbTP-1, theAngles);
5366 vector<double> aAngles( aNbTP );
5368 for(; j<aNbTP; ++j) {
5371 if ( theHasAngles ) {
5373 std::list<double>::iterator aItD = theAngles.begin();
5374 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5376 aAngles[j] = anAngle;
5379 // fill vector of path points with angles
5380 //aPPs.resize(fullList.size());
5382 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5383 for(; itPP!=fullList.end(); itPP++) {
5385 SMESH_MeshEditor_PathPoint PP = *itPP;
5386 PP.SetAngle(aAngles[j]);
5390 TNodeOfNodeListMap mapNewNodes;
5391 TElemOfVecOfNnlmiMap mapElemNewNodes;
5392 TElemOfElemListMap newElemsMap;
5393 TIDSortedElemSet::iterator itElem;
5396 SMDSAbs_ElementType aTypeE;
5397 // source elements for each generated one
5398 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5400 // 3. Center of rotation aV0
5401 gp_Pnt aV0 = theRefPoint;
5403 if ( !theHasRefPoint ) {
5405 aGC.SetCoord( 0.,0.,0. );
5407 itElem = theElements.begin();
5408 for ( ; itElem != theElements.end(); itElem++ ) {
5409 const SMDS_MeshElement* elem = *itElem;
5411 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5412 while ( itN->more() ) {
5413 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5418 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5419 list<const SMDS_MeshNode*> aLNx;
5420 mapNewNodes[node] = aLNx;
5422 gp_XYZ aXYZ( aX, aY, aZ );
5430 } // if (!theHasRefPoint) {
5431 mapNewNodes.clear();
5433 // 4. Processing the elements
5434 SMESHDS_Mesh* aMesh = GetMeshDS();
5436 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5437 // check element type
5438 const SMDS_MeshElement* elem = *itElem;
5439 aTypeE = elem->GetType();
5440 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5443 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5444 newNodesItVec.reserve( elem->NbNodes() );
5446 // loop on elem nodes
5448 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5449 while ( itN->more() )
5452 // check if a node has been already processed
5453 const SMDS_MeshNode* node =
5454 static_cast<const SMDS_MeshNode*>( itN->next() );
5455 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5456 if ( nIt == mapNewNodes.end() ) {
5457 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5458 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5461 aX = node->X(); aY = node->Y(); aZ = node->Z();
5463 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5464 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5465 gp_Ax1 anAx1, anAxT1T0;
5466 gp_Dir aDT1x, aDT0x, aDT1T0;
5471 aPN0.SetCoord(aX, aY, aZ);
5473 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5475 aDT0x= aPP0.Tangent();
5476 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5478 for ( j = 1; j < aNbTP; ++j ) {
5479 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5481 aDT1x = aPP1.Tangent();
5482 aAngle1x = aPP1.Angle();
5484 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5486 gp_Vec aV01x( aP0x, aP1x );
5487 aTrsf.SetTranslation( aV01x );
5490 aV1x = aV0x.Transformed( aTrsf );
5491 aPN1 = aPN0.Transformed( aTrsf );
5493 // rotation 1 [ T1,T0 ]
5494 aAngleT1T0=-aDT1x.Angle( aDT0x );
5495 if (fabs(aAngleT1T0) > aTolAng) {
5497 anAxT1T0.SetLocation( aV1x );
5498 anAxT1T0.SetDirection( aDT1T0 );
5499 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5501 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5505 if ( theHasAngles ) {
5506 anAx1.SetLocation( aV1x );
5507 anAx1.SetDirection( aDT1x );
5508 aTrsfRot.SetRotation( anAx1, aAngle1x );
5510 aPN1 = aPN1.Transformed( aTrsfRot );
5514 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5515 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5516 // create additional node
5517 double x = ( aPN1.X() + aPN0.X() )/2.;
5518 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5519 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5520 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5521 myLastCreatedNodes.Append(newNode);
5522 srcNodes.Append( node );
5523 listNewNodes.push_back( newNode );
5528 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5529 myLastCreatedNodes.Append(newNode);
5530 srcNodes.Append( node );
5531 listNewNodes.push_back( newNode );
5541 // if current elem is quadratic and current node is not medium
5542 // we have to check - may be it is needed to insert additional nodes
5543 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5544 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5545 if(listNewNodes.size()==aNbTP-1) {
5546 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5547 gp_XYZ P(node->X(), node->Y(), node->Z());
5548 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5550 for(i=0; i<aNbTP-1; i++) {
5551 const SMDS_MeshNode* N = *it;
5552 double x = ( N->X() + P.X() )/2.;
5553 double y = ( N->Y() + P.Y() )/2.;
5554 double z = ( N->Z() + P.Z() )/2.;
5555 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5556 srcNodes.Append( node );
5557 myLastCreatedNodes.Append(newN);
5560 P = gp_XYZ(N->X(),N->Y(),N->Z());
5562 listNewNodes.clear();
5563 for(i=0; i<2*(aNbTP-1); i++) {
5564 listNewNodes.push_back(aNodes[i]);
5570 newNodesItVec.push_back( nIt );
5572 // make new elements
5573 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5574 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5575 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5578 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5580 if ( theMakeGroups )
5581 generateGroups( srcNodes, srcElems, "extruded");
5587 //=======================================================================
5588 //function : LinearAngleVariation
5589 //purpose : auxilary for ExtrusionAlongTrack
5590 //=======================================================================
5591 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5592 list<double>& Angles)
5594 int nbAngles = Angles.size();
5595 if( nbSteps > nbAngles ) {
5596 vector<double> theAngles(nbAngles);
5597 list<double>::iterator it = Angles.begin();
5599 for(; it!=Angles.end(); it++) {
5601 theAngles[i] = (*it);
5604 double rAn2St = double( nbAngles ) / double( nbSteps );
5605 double angPrev = 0, angle;
5606 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5607 double angCur = rAn2St * ( iSt+1 );
5608 double angCurFloor = floor( angCur );
5609 double angPrevFloor = floor( angPrev );
5610 if ( angPrevFloor == angCurFloor )
5611 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5613 int iP = int( angPrevFloor );
5614 double angPrevCeil = ceil(angPrev);
5615 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5617 int iC = int( angCurFloor );
5618 if ( iC < nbAngles )
5619 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5621 iP = int( angPrevCeil );
5623 angle += theAngles[ iC ];
5625 res.push_back(angle);
5630 for(; it!=res.end(); it++)
5631 Angles.push_back( *it );
5636 //================================================================================
5638 * \brief Move or copy theElements applying theTrsf to their nodes
5639 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5640 * \param theTrsf - transformation to apply
5641 * \param theCopy - if true, create translated copies of theElems
5642 * \param theMakeGroups - if true and theCopy, create translated groups
5643 * \param theTargetMesh - mesh to copy translated elements into
5644 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5646 //================================================================================
5648 SMESH_MeshEditor::PGroupIDs
5649 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5650 const gp_Trsf& theTrsf,
5652 const bool theMakeGroups,
5653 SMESH_Mesh* theTargetMesh)
5655 myLastCreatedElems.Clear();
5656 myLastCreatedNodes.Clear();
5658 bool needReverse = false;
5659 string groupPostfix;
5660 switch ( theTrsf.Form() ) {
5662 MESSAGE("gp_PntMirror");
5664 groupPostfix = "mirrored";
5667 MESSAGE("gp_Ax1Mirror");
5668 groupPostfix = "mirrored";
5671 MESSAGE("gp_Ax2Mirror");
5673 groupPostfix = "mirrored";
5676 MESSAGE("gp_Rotation");
5677 groupPostfix = "rotated";
5679 case gp_Translation:
5680 MESSAGE("gp_Translation");
5681 groupPostfix = "translated";
5684 MESSAGE("gp_Scale");
5685 groupPostfix = "scaled";
5687 case gp_CompoundTrsf: // different scale by axis
5688 MESSAGE("gp_CompoundTrsf");
5689 groupPostfix = "scaled";
5693 needReverse = false;
5694 groupPostfix = "transformed";
5697 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5698 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5699 SMESHDS_Mesh* aMesh = GetMeshDS();
5702 // map old node to new one
5703 TNodeNodeMap nodeMap;
5705 // elements sharing moved nodes; those of them which have all
5706 // nodes mirrored but are not in theElems are to be reversed
5707 TIDSortedElemSet inverseElemSet;
5709 // source elements for each generated one
5710 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5712 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5713 TIDSortedElemSet orphanNode;
5715 if ( theElems.empty() ) // transform the whole mesh
5718 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5719 while ( eIt->more() ) theElems.insert( eIt->next() );
5721 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5722 while ( nIt->more() )
5724 const SMDS_MeshNode* node = nIt->next();
5725 if ( node->NbInverseElements() == 0)
5726 orphanNode.insert( node );
5730 // loop on elements to transform nodes : first orphan nodes then elems
5731 TIDSortedElemSet::iterator itElem;
5732 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5733 for (int i=0; i<2; i++)
5734 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5735 const SMDS_MeshElement* elem = *itElem;
5739 // loop on elem nodes
5740 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5741 while ( itN->more() ) {
5743 const SMDS_MeshNode* node = cast2Node( itN->next() );
5744 // check if a node has been already transformed
5745 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5746 nodeMap.insert( make_pair ( node, node ));
5747 if ( !n2n_isnew.second )
5751 coord[0] = node->X();
5752 coord[1] = node->Y();
5753 coord[2] = node->Z();
5754 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5755 if ( theTargetMesh ) {
5756 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5757 n2n_isnew.first->second = newNode;
5758 myLastCreatedNodes.Append(newNode);
5759 srcNodes.Append( node );
5761 else if ( theCopy ) {
5762 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5763 n2n_isnew.first->second = newNode;
5764 myLastCreatedNodes.Append(newNode);
5765 srcNodes.Append( node );
5768 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5769 // node position on shape becomes invalid
5770 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5771 ( SMDS_SpacePosition::originSpacePosition() );
5774 // keep inverse elements
5775 if ( !theCopy && !theTargetMesh && needReverse ) {
5776 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5777 while ( invElemIt->more() ) {
5778 const SMDS_MeshElement* iel = invElemIt->next();
5779 inverseElemSet.insert( iel );
5785 // either create new elements or reverse mirrored ones
5786 if ( !theCopy && !needReverse && !theTargetMesh )
5789 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5790 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5791 theElems.insert( *invElemIt );
5793 // Replicate or reverse elements
5795 std::vector<int> iForw;
5796 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5798 const SMDS_MeshElement* elem = *itElem;
5799 if ( !elem ) continue;
5801 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5802 int nbNodes = elem->NbNodes();
5803 if ( geomType == SMDSGeom_NONE ) continue; // node
5805 switch ( geomType ) {
5807 case SMDSGeom_POLYGON: // ---------------------- polygon
5809 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5811 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5812 while (itN->more()) {
5813 const SMDS_MeshNode* node =
5814 static_cast<const SMDS_MeshNode*>(itN->next());
5815 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5816 if (nodeMapIt == nodeMap.end())
5817 break; // not all nodes transformed
5819 // reverse mirrored faces and volumes
5820 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5822 poly_nodes[iNode] = (*nodeMapIt).second;
5826 if ( iNode != nbNodes )
5827 continue; // not all nodes transformed
5829 if ( theTargetMesh ) {
5830 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5831 srcElems.Append( elem );
5833 else if ( theCopy ) {
5834 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5835 srcElems.Append( elem );
5838 aMesh->ChangePolygonNodes(elem, poly_nodes);
5843 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5845 const SMDS_VtkVolume* aPolyedre =
5846 dynamic_cast<const SMDS_VtkVolume*>( elem );
5848 MESSAGE("Warning: bad volumic element");
5852 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5853 vector<int> quantities; quantities.reserve( nbNodes );
5855 bool allTransformed = true;
5856 int nbFaces = aPolyedre->NbFaces();
5857 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5858 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5859 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5860 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5861 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5862 if (nodeMapIt == nodeMap.end()) {
5863 allTransformed = false; // not all nodes transformed
5865 poly_nodes.push_back((*nodeMapIt).second);
5867 if ( needReverse && allTransformed )
5868 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5870 quantities.push_back(nbFaceNodes);
5872 if ( !allTransformed )
5873 continue; // not all nodes transformed
5875 if ( theTargetMesh ) {
5876 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5877 srcElems.Append( elem );
5879 else if ( theCopy ) {
5880 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5881 srcElems.Append( elem );
5884 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5889 case SMDSGeom_BALL: // -------------------- Ball
5891 if ( !theCopy && !theTargetMesh ) continue;
5893 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5894 if (nodeMapIt == nodeMap.end())
5895 continue; // not all nodes transformed
5897 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5898 if ( theTargetMesh ) {
5899 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5900 srcElems.Append( elem );
5903 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5904 srcElems.Append( elem );
5909 default: // ----------------------- Regular elements
5911 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5912 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5913 const std::vector<int>& i = needReverse ? iRev : iForw;
5915 // find transformed nodes
5916 vector<const SMDS_MeshNode*> nodes(nbNodes);
5918 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5919 while ( itN->more() ) {
5920 const SMDS_MeshNode* node =
5921 static_cast<const SMDS_MeshNode*>( itN->next() );
5922 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5923 if ( nodeMapIt == nodeMap.end() )
5924 break; // not all nodes transformed
5925 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5927 if ( iNode != nbNodes )
5928 continue; // not all nodes transformed
5930 if ( theTargetMesh ) {
5931 if ( SMDS_MeshElement* copy =
5932 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5933 myLastCreatedElems.Append( copy );
5934 srcElems.Append( elem );
5937 else if ( theCopy ) {
5938 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5939 srcElems.Append( elem );
5942 // reverse element as it was reversed by transformation
5944 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5946 } // switch ( geomType )
5948 } // loop on elements
5950 PGroupIDs newGroupIDs;
5952 if ( ( theMakeGroups && theCopy ) ||
5953 ( theMakeGroups && theTargetMesh ) )
5954 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5959 //=======================================================================
5961 * \brief Create groups of elements made during transformation
5962 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5963 * \param elemGens - elements making corresponding myLastCreatedElems
5964 * \param postfix - to append to names of new groups
5966 //=======================================================================
5968 SMESH_MeshEditor::PGroupIDs
5969 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5970 const SMESH_SequenceOfElemPtr& elemGens,
5971 const std::string& postfix,
5972 SMESH_Mesh* targetMesh)
5974 PGroupIDs newGroupIDs( new list<int> );
5975 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5977 // Sort existing groups by types and collect their names
5979 // to store an old group and a generated new one
5980 typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5981 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5982 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5984 set< string > groupNames;
5986 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5987 if ( !groupIt->more() ) return newGroupIDs;
5989 int newGroupID = mesh->GetGroupIds().back()+1;
5990 while ( groupIt->more() )
5992 SMESH_Group * group = groupIt->next();
5993 if ( !group ) continue;
5994 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5995 if ( !groupDS || groupDS->IsEmpty() ) continue;
5996 groupNames.insert( group->GetName() );
5997 groupDS->SetStoreName( group->GetName() );
5998 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5999 groupDS->GetType() );
6000 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
6001 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6004 // Loop on nodes and elements to add them in new groups
6006 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6008 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6009 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6010 if ( gens.Length() != elems.Length() )
6011 throw SALOME_Exception(LOCALIZED("invalid args"));
6013 // loop on created elements
6014 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6016 const SMDS_MeshElement* sourceElem = gens( iElem );
6017 if ( !sourceElem ) {
6018 MESSAGE("generateGroups(): NULL source element");
6021 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6022 if ( groupsOldNew.empty() ) { // no groups of this type at all
6023 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6024 ++iElem; // skip all elements made by sourceElem
6027 // collect all elements made by sourceElem
6028 list< const SMDS_MeshElement* > resultElems;
6029 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6030 if ( resElem != sourceElem )
6031 resultElems.push_back( resElem );
6032 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6033 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6034 if ( resElem != sourceElem )
6035 resultElems.push_back( resElem );
6037 // add resultElems to groups made by ones the sourceElem belongs to
6038 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6039 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6041 SMESHDS_GroupBase* oldGroup = gOldNew->first;
6042 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6044 // fill in a new group
6045 SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6046 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6047 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6048 newGroup.Add( *resElemIt );
6051 } // loop on created elements
6052 }// loop on nodes and elements
6054 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6056 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6058 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6059 SMESHDS_Group* newGroupDS = orderedOldNewGroups[i]->second;
6060 if ( newGroupDS->IsEmpty() )
6062 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6067 string name = oldGroupDS->GetStoreName();
6068 if ( !targetMesh ) {
6072 while ( !groupNames.insert( name ).second ) // name exists
6073 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6075 newGroupDS->SetStoreName( name.c_str() );
6077 // make a SMESH_Groups
6078 mesh->AddGroup( newGroupDS );
6079 newGroupIDs->push_back( newGroupDS->GetID() );
6082 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6089 //================================================================================
6091 * \brief Return list of group of nodes close to each other within theTolerance
6092 * Search among theNodes or in the whole mesh if theNodes is empty using
6093 * an Octree algorithm
6095 //================================================================================
6097 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6098 const double theTolerance,
6099 TListOfListOfNodes & theGroupsOfNodes)
6101 myLastCreatedElems.Clear();
6102 myLastCreatedNodes.Clear();
6104 if ( theNodes.empty() )
6105 { // get all nodes in the mesh
6106 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6107 while ( nIt->more() )
6108 theNodes.insert( theNodes.end(),nIt->next());
6111 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6115 //=======================================================================
6117 * \brief Implementation of search for the node closest to point
6119 //=======================================================================
6121 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6123 //---------------------------------------------------------------------
6125 * \brief Constructor
6127 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6129 myMesh = ( SMESHDS_Mesh* ) theMesh;
6131 TIDSortedNodeSet nodes;
6133 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6134 while ( nIt->more() )
6135 nodes.insert( nodes.end(), nIt->next() );
6137 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6139 // get max size of a leaf box
6140 SMESH_OctreeNode* tree = myOctreeNode;
6141 while ( !tree->isLeaf() )
6143 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6147 myHalfLeafSize = tree->maxSize() / 2.;
6150 //---------------------------------------------------------------------
6152 * \brief Move node and update myOctreeNode accordingly
6154 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6156 myOctreeNode->UpdateByMoveNode( node, toPnt );
6157 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6160 //---------------------------------------------------------------------
6162 * \brief Do it's job
6164 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6166 map<double, const SMDS_MeshNode*> dist2Nodes;
6167 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6168 if ( !dist2Nodes.empty() )
6169 return dist2Nodes.begin()->second;
6170 list<const SMDS_MeshNode*> nodes;
6171 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6173 double minSqDist = DBL_MAX;
6174 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6176 // sort leafs by their distance from thePnt
6177 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6178 TDistTreeMap treeMap;
6179 list< SMESH_OctreeNode* > treeList;
6180 list< SMESH_OctreeNode* >::iterator trIt;
6181 treeList.push_back( myOctreeNode );
6183 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6184 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6185 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6187 SMESH_OctreeNode* tree = *trIt;
6188 if ( !tree->isLeaf() ) // put children to the queue
6190 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6191 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6192 while ( cIt->more() )
6193 treeList.push_back( cIt->next() );
6195 else if ( tree->NbNodes() ) // put a tree to the treeMap
6197 const Bnd_B3d& box = *tree->getBox();
6198 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6199 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6200 if ( !it_in.second ) // not unique distance to box center
6201 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6204 // find distance after which there is no sense to check tree's
6205 double sqLimit = DBL_MAX;
6206 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6207 if ( treeMap.size() > 5 ) {
6208 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6209 const Bnd_B3d& box = *closestTree->getBox();
6210 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6211 sqLimit = limit * limit;
6213 // get all nodes from trees
6214 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6215 if ( sqDist_tree->first > sqLimit )
6217 SMESH_OctreeNode* tree = sqDist_tree->second;
6218 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6221 // find closest among nodes
6222 minSqDist = DBL_MAX;
6223 const SMDS_MeshNode* closestNode = 0;
6224 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6225 for ( ; nIt != nodes.end(); ++nIt ) {
6226 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6227 if ( minSqDist > sqDist ) {
6235 //---------------------------------------------------------------------
6239 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6241 //---------------------------------------------------------------------
6243 * \brief Return the node tree
6245 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6248 SMESH_OctreeNode* myOctreeNode;
6249 SMESHDS_Mesh* myMesh;
6250 double myHalfLeafSize; // max size of a leaf box
6253 //=======================================================================
6255 * \brief Return SMESH_NodeSearcher
6257 //=======================================================================
6259 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6261 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6264 // ========================================================================
6265 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6267 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6268 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6269 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6271 //=======================================================================
6273 * \brief Octal tree of bounding boxes of elements
6275 //=======================================================================
6277 class ElementBndBoxTree : public SMESH_Octree
6281 ElementBndBoxTree(const SMDS_Mesh& mesh,
6282 SMDSAbs_ElementType elemType,
6283 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6284 double tolerance = NodeRadius );
6285 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6286 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6287 void getElementsInSphere ( const gp_XYZ& center,
6288 const double radius, TIDSortedElemSet& foundElems);
6289 size_t getSize() { return std::max( _size, _elements.size() ); }
6290 ~ElementBndBoxTree();
6293 ElementBndBoxTree():_size(0) {}
6294 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6295 void buildChildrenData();
6296 Bnd_B3d* buildRootBox();
6298 //!< Bounding box of element
6299 struct ElementBox : public Bnd_B3d
6301 const SMDS_MeshElement* _element;
6302 int _refCount; // an ElementBox can be included in several tree branches
6303 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6305 vector< ElementBox* > _elements;
6309 //================================================================================
6311 * \brief ElementBndBoxTree creation
6313 //================================================================================
6315 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6316 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6318 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6319 _elements.reserve( nbElems );
6321 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6322 while ( elemIt->more() )
6323 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6328 //================================================================================
6332 //================================================================================
6334 ElementBndBoxTree::~ElementBndBoxTree()
6336 for ( int i = 0; i < _elements.size(); ++i )
6337 if ( --_elements[i]->_refCount <= 0 )
6338 delete _elements[i];
6341 //================================================================================
6343 * \brief Return the maximal box
6345 //================================================================================
6347 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6349 Bnd_B3d* box = new Bnd_B3d;
6350 for ( int i = 0; i < _elements.size(); ++i )
6351 box->Add( *_elements[i] );
6355 //================================================================================
6357 * \brief Redistrubute element boxes among children
6359 //================================================================================
6361 void ElementBndBoxTree::buildChildrenData()
6363 for ( int i = 0; i < _elements.size(); ++i )
6365 for (int j = 0; j < 8; j++)
6367 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6369 _elements[i]->_refCount++;
6370 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6373 _elements[i]->_refCount--;
6375 _size = _elements.size();
6376 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6378 for (int j = 0; j < 8; j++)
6380 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6381 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6382 child->myIsLeaf = true;
6384 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6385 SMESHUtils::CompactVector( child->_elements );
6389 //================================================================================
6391 * \brief Return elements which can include the point
6393 //================================================================================
6395 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6396 TIDSortedElemSet& foundElems)
6398 if ( getBox()->IsOut( point.XYZ() ))
6403 for ( int i = 0; i < _elements.size(); ++i )
6404 if ( !_elements[i]->IsOut( point.XYZ() ))
6405 foundElems.insert( _elements[i]->_element );
6409 for (int i = 0; i < 8; i++)
6410 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6414 //================================================================================
6416 * \brief Return elements which can be intersected by the line
6418 //================================================================================
6420 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6421 TIDSortedElemSet& foundElems)
6423 if ( getBox()->IsOut( line ))
6428 for ( int i = 0; i < _elements.size(); ++i )
6429 if ( !_elements[i]->IsOut( line ))
6430 foundElems.insert( _elements[i]->_element );
6434 for (int i = 0; i < 8; i++)
6435 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6439 //================================================================================
6441 * \brief Return elements from leaves intersecting the sphere
6443 //================================================================================
6445 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6446 const double radius,
6447 TIDSortedElemSet& foundElems)
6449 if ( getBox()->IsOut( center, radius ))
6454 for ( int i = 0; i < _elements.size(); ++i )
6455 if ( !_elements[i]->IsOut( center, radius ))
6456 foundElems.insert( _elements[i]->_element );
6460 for (int i = 0; i < 8; i++)
6461 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6465 //================================================================================
6467 * \brief Construct the element box
6469 //================================================================================
6471 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6475 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6476 while ( nIt->more() )
6477 Add( SMESH_TNodeXYZ( nIt->next() ));
6478 Enlarge( tolerance );
6483 //=======================================================================
6485 * \brief Implementation of search for the elements by point and
6486 * of classification of point in 2D mesh
6488 //=======================================================================
6490 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6492 SMESHDS_Mesh* _mesh;
6493 SMDS_ElemIteratorPtr _meshPartIt;
6494 ElementBndBoxTree* _ebbTree;
6495 SMESH_NodeSearcherImpl* _nodeSearcher;
6496 SMDSAbs_ElementType _elementType;
6498 bool _outerFacesFound;
6499 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6501 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6502 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6503 ~SMESH_ElementSearcherImpl()
6505 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6506 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6508 virtual int FindElementsByPoint(const gp_Pnt& point,
6509 SMDSAbs_ElementType type,
6510 vector< const SMDS_MeshElement* >& foundElements);
6511 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6512 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6513 SMDSAbs_ElementType type );
6515 void GetElementsNearLine( const gp_Ax1& line,
6516 SMDSAbs_ElementType type,
6517 vector< const SMDS_MeshElement* >& foundElems);
6518 double getTolerance();
6519 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6520 const double tolerance, double & param);
6521 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6522 bool isOuterBoundary(const SMDS_MeshElement* face) const
6524 return _outerFaces.empty() || _outerFaces.count(face);
6526 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6528 const SMDS_MeshElement* _face;
6530 bool _coincides; //!< the line lays in face plane
6531 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6532 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6534 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6537 TIDSortedElemSet _faces;
6538 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6539 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6543 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6545 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6546 << ", _coincides="<<i._coincides << ")";
6549 //=======================================================================
6551 * \brief define tolerance for search
6553 //=======================================================================
6555 double SMESH_ElementSearcherImpl::getTolerance()
6557 if ( _tolerance < 0 )
6559 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6562 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6564 double boxSize = _nodeSearcher->getTree()->maxSize();
6565 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6567 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6569 double boxSize = _ebbTree->maxSize();
6570 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6572 if ( _tolerance == 0 )
6574 // define tolerance by size of a most complex element
6575 int complexType = SMDSAbs_Volume;
6576 while ( complexType > SMDSAbs_All &&
6577 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6579 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6581 if ( complexType == int( SMDSAbs_Node ))
6583 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6585 if ( meshInfo.NbNodes() > 2 )
6586 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6590 SMDS_ElemIteratorPtr elemIt =
6591 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6592 const SMDS_MeshElement* elem = elemIt->next();
6593 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6594 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6596 while ( nodeIt->more() )
6598 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6599 elemSize = max( dist, elemSize );
6602 _tolerance = 1e-4 * elemSize;
6608 //================================================================================
6610 * \brief Find intersection of the line and an edge of face and return parameter on line
6612 //================================================================================
6614 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6615 const SMDS_MeshElement* face,
6622 GeomAPI_ExtremaCurveCurve anExtCC;
6623 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6625 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6626 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6628 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6629 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6630 anExtCC.Init( lineCurve, edge);
6631 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6633 Quantity_Parameter pl, pe;
6634 anExtCC.LowerDistanceParameters( pl, pe );
6636 if ( ++nbInts == 2 )
6640 if ( nbInts > 0 ) param /= nbInts;
6643 //================================================================================
6645 * \brief Find all faces belonging to the outer boundary of mesh
6647 //================================================================================
6649 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6651 if ( _outerFacesFound ) return;
6653 // Collect all outer faces by passing from one outer face to another via their links
6654 // and BTW find out if there are internal faces at all.
6656 // checked links and links where outer boundary meets internal one
6657 set< SMESH_TLink > visitedLinks, seamLinks;
6659 // links to treat with already visited faces sharing them
6660 list < TFaceLink > startLinks;
6662 // load startLinks with the first outerFace
6663 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6664 _outerFaces.insert( outerFace );
6666 TIDSortedElemSet emptySet;
6667 while ( !startLinks.empty() )
6669 const SMESH_TLink& link = startLinks.front()._link;
6670 TIDSortedElemSet& faces = startLinks.front()._faces;
6672 outerFace = *faces.begin();
6673 // find other faces sharing the link
6674 const SMDS_MeshElement* f;
6675 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6678 // select another outer face among the found
6679 const SMDS_MeshElement* outerFace2 = 0;
6680 if ( faces.size() == 2 )
6682 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6684 else if ( faces.size() > 2 )
6686 seamLinks.insert( link );
6688 // link direction within the outerFace
6689 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6690 SMESH_TNodeXYZ( link.node2()));
6691 int i1 = outerFace->GetNodeIndex( link.node1() );
6692 int i2 = outerFace->GetNodeIndex( link.node2() );
6693 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6694 if ( rev ) n1n2.Reverse();
6696 gp_XYZ ofNorm, fNorm;
6697 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6699 // direction from the link inside outerFace
6700 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6701 // sort all other faces by angle with the dirInOF
6702 map< double, const SMDS_MeshElement* > angle2Face;
6703 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6704 for ( ; face != faces.end(); ++face )
6706 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6708 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6709 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6710 if ( angle < 0 ) angle += 2. * M_PI;
6711 angle2Face.insert( make_pair( angle, *face ));
6713 if ( !angle2Face.empty() )
6714 outerFace2 = angle2Face.begin()->second;
6717 // store the found outer face and add its links to continue seaching from
6720 _outerFaces.insert( outerFace );
6721 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6722 for ( int i = 0; i < nbNodes; ++i )
6724 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6725 if ( visitedLinks.insert( link2 ).second )
6726 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6729 startLinks.pop_front();
6731 _outerFacesFound = true;
6733 if ( !seamLinks.empty() )
6735 // There are internal boundaries touching the outher one,
6736 // find all faces of internal boundaries in order to find
6737 // faces of boundaries of holes, if any.
6742 _outerFaces.clear();
6746 //=======================================================================
6748 * \brief Find elements of given type where the given point is IN or ON.
6749 * Returns nb of found elements and elements them-selves.
6751 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6753 //=======================================================================
6755 int SMESH_ElementSearcherImpl::
6756 FindElementsByPoint(const gp_Pnt& point,
6757 SMDSAbs_ElementType type,
6758 vector< const SMDS_MeshElement* >& foundElements)
6760 foundElements.clear();
6762 double tolerance = getTolerance();
6764 // =================================================================================
6765 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6767 if ( !_nodeSearcher )
6768 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6770 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6771 if ( !closeNode ) return foundElements.size();
6773 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6774 return foundElements.size(); // to far from any node
6776 if ( type == SMDSAbs_Node )
6778 foundElements.push_back( closeNode );
6782 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6783 while ( elemIt->more() )
6784 foundElements.push_back( elemIt->next() );
6787 // =================================================================================
6788 else // elements more complex than 0D
6790 if ( !_ebbTree || _elementType != type )
6792 if ( _ebbTree ) delete _ebbTree;
6793 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6795 TIDSortedElemSet suspectElems;
6796 _ebbTree->getElementsNearPoint( point, suspectElems );
6797 TIDSortedElemSet::iterator elem = suspectElems.begin();
6798 for ( ; elem != suspectElems.end(); ++elem )
6799 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6800 foundElements.push_back( *elem );
6802 return foundElements.size();
6805 //=======================================================================
6807 * \brief Find an element of given type most close to the given point
6809 * WARNING: Only face search is implemeneted so far
6811 //=======================================================================
6813 const SMDS_MeshElement*
6814 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6815 SMDSAbs_ElementType type )
6817 const SMDS_MeshElement* closestElem = 0;
6819 if ( type == SMDSAbs_Face )
6821 if ( !_ebbTree || _elementType != type )
6823 if ( _ebbTree ) delete _ebbTree;
6824 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6826 TIDSortedElemSet suspectElems;
6827 _ebbTree->getElementsNearPoint( point, suspectElems );
6829 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6831 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6832 _ebbTree->getBox()->CornerMax() );
6834 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6835 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6837 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6838 while ( suspectElems.empty() )
6840 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6844 double minDist = std::numeric_limits<double>::max();
6845 multimap< double, const SMDS_MeshElement* > dist2face;
6846 TIDSortedElemSet::iterator elem = suspectElems.begin();
6847 for ( ; elem != suspectElems.end(); ++elem )
6849 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6851 if ( dist < minDist + 1e-10)
6854 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6857 if ( !dist2face.empty() )
6859 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6860 closestElem = d2f->second;
6861 // if there are several elements at the same distance, select one
6862 // with GC closest to the point
6863 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6864 double minDistToGC = 0;
6865 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6867 if ( minDistToGC == 0 )
6870 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6871 TXyzIterator(), gc ) / closestElem->NbNodes();
6872 minDistToGC = point.SquareDistance( gc );
6875 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6876 TXyzIterator(), gc ) / d2f->second->NbNodes();
6877 double d = point.SquareDistance( gc );
6878 if ( d < minDistToGC )
6881 closestElem = d2f->second;
6884 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6885 // <<closestElem->GetID() << " DIST " << minDist << endl;
6890 // NOT IMPLEMENTED SO FAR
6896 //================================================================================
6898 * \brief Classify the given point in the closed 2D mesh
6900 //================================================================================
6902 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6904 double tolerance = getTolerance();
6905 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6907 if ( _ebbTree ) delete _ebbTree;
6908 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6910 // Algo: analyse transition of a line starting at the point through mesh boundary;
6911 // try three lines parallel to axis of the coordinate system and perform rough
6912 // analysis. If solution is not clear perform thorough analysis.
6914 const int nbAxes = 3;
6915 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6916 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6917 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6918 multimap< int, int > nbInt2Axis; // to find the simplest case
6919 for ( int axis = 0; axis < nbAxes; ++axis )
6921 gp_Ax1 lineAxis( point, axisDir[axis]);
6922 gp_Lin line ( lineAxis );
6924 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6925 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6927 // Intersect faces with the line
6929 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6930 TIDSortedElemSet::iterator face = suspectFaces.begin();
6931 for ( ; face != suspectFaces.end(); ++face )
6935 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6936 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6938 // perform intersection
6939 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6940 if ( !intersection.IsDone() )
6942 if ( intersection.IsInQuadric() )
6944 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6946 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6948 gp_Pnt intersectionPoint = intersection.Point(1);
6949 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6950 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6953 // Analyse intersections roughly
6955 int nbInter = u2inters.size();
6959 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6960 if ( nbInter == 1 ) // not closed mesh
6961 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6963 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6966 if ( (f<0) == (l<0) )
6969 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6970 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6971 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6974 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6976 if ( _outerFacesFound ) break; // pass to thorough analysis
6978 } // three attempts - loop on CS axes
6980 // Analyse intersections thoroughly.
6981 // We make two loops maximum, on the first one we only exclude touching intersections,
6982 // on the second, if situation is still unclear, we gather and use information on
6983 // position of faces (internal or outer). If faces position is already gathered,
6984 // we make the second loop right away.
6986 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6988 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6989 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6991 int axis = nb_axis->second;
6992 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6994 gp_Ax1 lineAxis( point, axisDir[axis]);
6995 gp_Lin line ( lineAxis );
6997 // add tangent intersections to u2inters
6999 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7000 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7001 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7002 u2inters.insert(make_pair( param, *tgtInt ));
7003 tangentInters[ axis ].clear();
7005 // Count intersections before and after the point excluding touching ones.
7006 // If hasPositionInfo we count intersections of outer boundary only
7008 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7009 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7010 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7011 bool ok = ! u_int1->second._coincides;
7012 while ( ok && u_int1 != u2inters.end() )
7014 double u = u_int1->first;
7015 bool touchingInt = false;
7016 if ( ++u_int2 != u2inters.end() )
7018 // skip intersections at the same point (if the line passes through edge or node)
7020 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7026 // skip tangent intersections
7028 const SMDS_MeshElement* prevFace = u_int1->second._face;
7029 while ( ok && u_int2->second._coincides )
7031 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7037 ok = ( u_int2 != u2inters.end() );
7042 // skip intersections at the same point after tangent intersections
7045 double u2 = u_int2->first;
7047 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7053 // decide if we skipped a touching intersection
7054 if ( nbSamePnt + nbTgt > 0 )
7056 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7057 map< double, TInters >::iterator u_int = u_int1;
7058 for ( ; u_int != u_int2; ++u_int )
7060 if ( u_int->second._coincides ) continue;
7061 double dot = u_int->second._faceNorm * line.Direction();
7062 if ( dot > maxDot ) maxDot = dot;
7063 if ( dot < minDot ) minDot = dot;
7065 touchingInt = ( minDot*maxDot < 0 );
7070 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7081 u_int1 = u_int2; // to next intersection
7083 } // loop on intersections with one line
7087 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7090 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7093 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7094 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7096 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7099 if ( (f<0) == (l<0) )
7102 if ( hasPositionInfo )
7103 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7105 } // loop on intersections of the tree lines - thorough analysis
7107 if ( !hasPositionInfo )
7109 // gather info on faces position - is face in the outer boundary or not
7110 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7111 findOuterBoundary( u2inters.begin()->second._face );
7114 } // two attempts - with and w/o faces position info in the mesh
7116 return TopAbs_UNKNOWN;
7119 //=======================================================================
7121 * \brief Return elements possibly intersecting the line
7123 //=======================================================================
7125 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7126 SMDSAbs_ElementType type,
7127 vector< const SMDS_MeshElement* >& foundElems)
7129 if ( !_ebbTree || _elementType != type )
7131 if ( _ebbTree ) delete _ebbTree;
7132 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7134 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7135 _ebbTree->getElementsNearLine( line, suspectFaces );
7136 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7139 //=======================================================================
7141 * \brief Return SMESH_ElementSearcher
7143 //=======================================================================
7145 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7147 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7150 //=======================================================================
7152 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7154 //=======================================================================
7156 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7158 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7161 //=======================================================================
7163 * \brief Return true if the point is IN or ON of the element
7165 //=======================================================================
7167 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7169 if ( element->GetType() == SMDSAbs_Volume)
7171 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7174 // get ordered nodes
7176 vector< gp_XYZ > xyz;
7177 vector<const SMDS_MeshNode*> nodeList;
7179 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7180 if ( element->IsQuadratic() ) {
7181 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7182 nodeIt = f->interlacedNodesElemIterator();
7183 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7184 nodeIt = e->interlacedNodesElemIterator();
7186 while ( nodeIt->more() )
7188 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7189 xyz.push_back( SMESH_TNodeXYZ(node) );
7190 nodeList.push_back(node);
7193 int i, nbNodes = element->NbNodes();
7195 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7197 // compute face normal
7198 gp_Vec faceNorm(0,0,0);
7199 xyz.push_back( xyz.front() );
7200 nodeList.push_back( nodeList.front() );
7201 for ( i = 0; i < nbNodes; ++i )
7203 gp_Vec edge1( xyz[i+1], xyz[i]);
7204 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7205 faceNorm += edge1 ^ edge2;
7207 double normSize = faceNorm.Magnitude();
7208 if ( normSize <= tol )
7210 // degenerated face: point is out if it is out of all face edges
7211 for ( i = 0; i < nbNodes; ++i )
7213 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7214 if ( !IsOut( &edge, point, tol ))
7219 faceNorm /= normSize;
7221 // check if the point lays on face plane
7222 gp_Vec n2p( xyz[0], point );
7223 if ( fabs( n2p * faceNorm ) > tol )
7224 return true; // not on face plane
7226 // check if point is out of face boundary:
7227 // define it by closest transition of a ray point->infinity through face boundary
7228 // on the face plane.
7229 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7230 // to find intersections of the ray with the boundary.
7232 gp_Vec plnNorm = ray ^ faceNorm;
7233 normSize = plnNorm.Magnitude();
7234 if ( normSize <= tol ) return false; // point coincides with the first node
7235 plnNorm /= normSize;
7236 // for each node of the face, compute its signed distance to the plane
7237 vector<double> dist( nbNodes + 1);
7238 for ( i = 0; i < nbNodes; ++i )
7240 gp_Vec n2p( xyz[i], point );
7241 dist[i] = n2p * plnNorm;
7243 dist.back() = dist.front();
7244 // find the closest intersection
7246 double rClosest, distClosest = 1e100;;
7248 for ( i = 0; i < nbNodes; ++i )
7251 if ( fabs( dist[i]) < tol )
7253 else if ( fabs( dist[i+1]) < tol )
7255 else if ( dist[i] * dist[i+1] < 0 )
7256 r = dist[i] / ( dist[i] - dist[i+1] );
7258 continue; // no intersection
7259 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7260 gp_Vec p2int ( point, pInt);
7261 if ( p2int * ray > -tol ) // right half-space
7263 double intDist = p2int.SquareMagnitude();
7264 if ( intDist < distClosest )
7269 distClosest = intDist;
7274 return true; // no intesections - out
7276 // analyse transition
7277 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7278 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7279 gp_Vec p2int ( point, pClosest );
7280 bool out = (edgeNorm * p2int) < -tol;
7281 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7284 // ray pass through a face node; analyze transition through an adjacent edge
7285 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7286 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7287 gp_Vec edgeAdjacent( p1, p2 );
7288 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7289 bool out2 = (edgeNorm2 * p2int) < -tol;
7291 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7292 return covexCorner ? (out || out2) : (out && out2);
7294 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7296 // point is out of edge if it is NOT ON any straight part of edge
7297 // (we consider quadratic edge as being composed of two straight parts)
7298 for ( i = 1; i < nbNodes; ++i )
7300 gp_Vec edge( xyz[i-1], xyz[i]);
7301 gp_Vec n1p ( xyz[i-1], point);
7302 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7305 gp_Vec n2p( xyz[i], point );
7306 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7308 return false; // point is ON this part
7312 // Node or 0D element -------------------------------------------------------------------------
7314 gp_Vec n2p ( xyz[0], point );
7315 return n2p.Magnitude() <= tol;
7320 //=======================================================================
7324 // Position of a point relative to a segment
7328 // VERTEX 1 o----ON-----> VERTEX 2
7332 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7333 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7337 int _index; // index of vertex or segment
7339 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7340 bool operator < (const PointPos& other ) const
7342 if ( _name == other._name )
7343 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7344 return _name < other._name;
7348 //================================================================================
7350 * \brief Return of a point relative to a segment
7351 * \param point2D - the point to analyze position of
7352 * \param xyVec - end points of segments
7353 * \param index0 - 0-based index of the first point of segment
7354 * \param posToFindOut - flags of positions to detect
7355 * \retval PointPos - point position
7357 //================================================================================
7359 PointPos getPointPosition( const gp_XY& point2D,
7360 const gp_XY* segEnds,
7361 const int index0 = 0,
7362 const int posToFindOut = POS_ALL)
7364 const gp_XY& p1 = segEnds[ index0 ];
7365 const gp_XY& p2 = segEnds[ index0+1 ];
7366 const gp_XY grad = p2 - p1;
7368 if ( posToFindOut & POS_VERTEX )
7370 // check if the point2D is at "vertex 1" zone
7371 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7372 p1.Y() + grad.X() ) };
7373 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7374 return PointPos( POS_VERTEX, index0 );
7376 // check if the point2D is at "vertex 2" zone
7377 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7378 p2.Y() + grad.X() ) };
7379 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7380 return PointPos( POS_VERTEX, index0 + 1);
7382 double edgeEquation =
7383 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7384 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7388 //=======================================================================
7390 * \brief Return minimal distance from a point to a face
7392 * Currently we ignore non-planarity and 2nd order of face
7394 //=======================================================================
7396 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7397 const gp_Pnt& point )
7399 double badDistance = -1;
7400 if ( !face ) return badDistance;
7402 // coordinates of nodes (medium nodes, if any, ignored)
7403 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7404 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7405 xyz.resize( face->NbCornerNodes()+1 );
7407 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7408 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7410 gp_Vec OZ ( xyz[0], xyz[1] );
7411 gp_Vec OX ( xyz[0], xyz[2] );
7412 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7414 if ( xyz.size() < 4 ) return badDistance;
7415 OZ = gp_Vec ( xyz[0], xyz[2] );
7416 OX = gp_Vec ( xyz[0], xyz[3] );
7420 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7422 catch ( Standard_Failure ) {
7425 trsf.SetTransformation( tgtCS );
7427 // move all the nodes to 2D
7428 vector<gp_XY> xy( xyz.size() );
7429 for ( size_t i = 0;i < xyz.size()-1; ++i )
7431 gp_XYZ p3d = xyz[i];
7432 trsf.Transforms( p3d );
7433 xy[i].SetCoord( p3d.X(), p3d.Z() );
7435 xyz.back() = xyz.front();
7436 xy.back() = xy.front();
7438 // // move the point in 2D
7439 gp_XYZ tmpPnt = point.XYZ();
7440 trsf.Transforms( tmpPnt );
7441 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7443 // loop on segments of the face to analyze point position ralative to the face
7444 set< PointPos > pntPosSet;
7445 for ( size_t i = 1; i < xy.size(); ++i )
7447 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7448 pntPosSet.insert( pos );
7452 PointPos pos = *pntPosSet.begin();
7453 // cout << "Face " << face->GetID() << " DIST: ";
7454 switch ( pos._name )
7457 // point is most close to a segment
7458 gp_Vec p0p1( point, xyz[ pos._index ] );
7459 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7461 double projDist = p0p1 * p1p2; // distance projected to the segment
7462 gp_Vec projVec = p1p2 * projDist;
7463 gp_Vec distVec = p0p1 - projVec;
7464 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7465 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7466 return distVec.Magnitude();
7469 // point is inside the face
7470 double distToFacePlane = tmpPnt.Y();
7471 // cout << distToFacePlane << ", INSIDE " << endl;
7472 return Abs( distToFacePlane );
7475 // point is most close to a node
7476 gp_Vec distVec( point, xyz[ pos._index ]);
7477 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7478 return distVec.Magnitude();
7484 //=======================================================================
7485 //function : SimplifyFace
7487 //=======================================================================
7488 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7489 vector<const SMDS_MeshNode *>& poly_nodes,
7490 vector<int>& quantities) const
7492 int nbNodes = faceNodes.size();
7497 set<const SMDS_MeshNode*> nodeSet;
7499 // get simple seq of nodes
7500 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7501 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7502 int iSimple = 0, nbUnique = 0;
7504 simpleNodes[iSimple++] = faceNodes[0];
7506 for (int iCur = 1; iCur < nbNodes; iCur++) {
7507 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7508 simpleNodes[iSimple++] = faceNodes[iCur];
7509 if (nodeSet.insert( faceNodes[iCur] ).second)
7513 int nbSimple = iSimple;
7514 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7524 bool foundLoop = (nbSimple > nbUnique);
7527 set<const SMDS_MeshNode*> loopSet;
7528 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7529 const SMDS_MeshNode* n = simpleNodes[iSimple];
7530 if (!loopSet.insert( n ).second) {
7534 int iC = 0, curLast = iSimple;
7535 for (; iC < curLast; iC++) {
7536 if (simpleNodes[iC] == n) break;
7538 int loopLen = curLast - iC;
7540 // create sub-element
7542 quantities.push_back(loopLen);
7543 for (; iC < curLast; iC++) {
7544 poly_nodes.push_back(simpleNodes[iC]);
7547 // shift the rest nodes (place from the first loop position)
7548 for (iC = curLast + 1; iC < nbSimple; iC++) {
7549 simpleNodes[iC - loopLen] = simpleNodes[iC];
7551 nbSimple -= loopLen;
7554 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7555 } // while (foundLoop)
7559 quantities.push_back(iSimple);
7560 for (int i = 0; i < iSimple; i++)
7561 poly_nodes.push_back(simpleNodes[i]);
7567 //=======================================================================
7568 //function : MergeNodes
7569 //purpose : In each group, the cdr of nodes are substituted by the first one
7571 //=======================================================================
7573 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7575 MESSAGE("MergeNodes");
7576 myLastCreatedElems.Clear();
7577 myLastCreatedNodes.Clear();
7579 SMESHDS_Mesh* aMesh = GetMeshDS();
7581 TNodeNodeMap nodeNodeMap; // node to replace - new node
7582 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7583 list< int > rmElemIds, rmNodeIds;
7585 // Fill nodeNodeMap and elems
7587 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7588 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7589 list<const SMDS_MeshNode*>& nodes = *grIt;
7590 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7591 const SMDS_MeshNode* nToKeep = *nIt;
7592 //MESSAGE("node to keep " << nToKeep->GetID());
7593 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7594 const SMDS_MeshNode* nToRemove = *nIt;
7595 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7596 if ( nToRemove != nToKeep ) {
7597 //MESSAGE(" node to remove " << nToRemove->GetID());
7598 rmNodeIds.push_back( nToRemove->GetID() );
7599 AddToSameGroups( nToKeep, nToRemove, aMesh );
7602 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7603 while ( invElemIt->more() ) {
7604 const SMDS_MeshElement* elem = invElemIt->next();
7609 // Change element nodes or remove an element
7611 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7612 for ( ; eIt != elems.end(); eIt++ ) {
7613 const SMDS_MeshElement* elem = *eIt;
7614 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7615 int nbNodes = elem->NbNodes();
7616 int aShapeId = FindShape( elem );
7618 set<const SMDS_MeshNode*> nodeSet;
7619 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7620 int iUnique = 0, iCur = 0, nbRepl = 0;
7621 vector<int> iRepl( nbNodes );
7623 // get new seq of nodes
7624 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7625 while ( itN->more() ) {
7626 const SMDS_MeshNode* n =
7627 static_cast<const SMDS_MeshNode*>( itN->next() );
7629 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7630 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7632 // BUG 0020185: begin
7634 bool stopRecur = false;
7635 set<const SMDS_MeshNode*> nodesRecur;
7636 nodesRecur.insert(n);
7637 while (!stopRecur) {
7638 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7639 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7640 n = (*nnIt_i).second;
7641 if (!nodesRecur.insert(n).second) {
7642 // error: recursive dependancy
7652 curNodes[ iCur ] = n;
7653 bool isUnique = nodeSet.insert( n ).second;
7655 uniqueNodes[ iUnique++ ] = n;
7657 iRepl[ nbRepl++ ] = iCur;
7661 // Analyse element topology after replacement
7664 int nbUniqueNodes = nodeSet.size();
7665 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7666 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7667 // Polygons and Polyhedral volumes
7668 if (elem->IsPoly()) {
7670 if (elem->GetType() == SMDSAbs_Face) {
7672 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7674 for (; inode < nbNodes; inode++) {
7675 face_nodes[inode] = curNodes[inode];
7678 vector<const SMDS_MeshNode *> polygons_nodes;
7679 vector<int> quantities;
7680 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7683 for (int iface = 0; iface < nbNew; iface++) {
7684 int nbNodes = quantities[iface];
7685 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7686 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7687 poly_nodes[ii] = polygons_nodes[inode];
7689 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7690 myLastCreatedElems.Append(newElem);
7692 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7695 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7696 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7697 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7699 if (nbNew > 0) quid = nbNew - 1;
7700 vector<int> newquant(quantities.begin()+quid, quantities.end());
7701 const SMDS_MeshElement* newElem = 0;
7702 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7703 myLastCreatedElems.Append(newElem);
7704 if ( aShapeId && newElem )
7705 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7706 rmElemIds.push_back(elem->GetID());
7709 rmElemIds.push_back(elem->GetID());
7713 else if (elem->GetType() == SMDSAbs_Volume) {
7714 // Polyhedral volume
7715 if (nbUniqueNodes < 4) {
7716 rmElemIds.push_back(elem->GetID());
7719 // each face has to be analyzed in order to check volume validity
7720 const SMDS_VtkVolume* aPolyedre =
7721 dynamic_cast<const SMDS_VtkVolume*>( elem );
7723 int nbFaces = aPolyedre->NbFaces();
7725 vector<const SMDS_MeshNode *> poly_nodes;
7726 vector<int> quantities;
7728 for (int iface = 1; iface <= nbFaces; iface++) {
7729 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7730 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7732 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7733 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7734 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7735 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7736 faceNode = (*nnIt).second;
7738 faceNodes[inode - 1] = faceNode;
7741 SimplifyFace(faceNodes, poly_nodes, quantities);
7744 if (quantities.size() > 3) {
7745 // to be done: remove coincident faces
7748 if (quantities.size() > 3)
7750 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7751 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7752 const SMDS_MeshElement* newElem = 0;
7753 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7754 myLastCreatedElems.Append(newElem);
7755 if ( aShapeId && newElem )
7756 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7757 rmElemIds.push_back(elem->GetID());
7761 rmElemIds.push_back(elem->GetID());
7772 // TODO not all the possible cases are solved. Find something more generic?
7773 switch ( nbNodes ) {
7774 case 2: ///////////////////////////////////// EDGE
7775 isOk = false; break;
7776 case 3: ///////////////////////////////////// TRIANGLE
7777 isOk = false; break;
7779 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7781 else { //////////////////////////////////// QUADRANGLE
7782 if ( nbUniqueNodes < 3 )
7784 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7785 isOk = false; // opposite nodes stick
7786 //MESSAGE("isOk " << isOk);
7789 case 6: ///////////////////////////////////// PENTAHEDRON
7790 if ( nbUniqueNodes == 4 ) {
7791 // ---------------------------------> tetrahedron
7793 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7794 // all top nodes stick: reverse a bottom
7795 uniqueNodes[ 0 ] = curNodes [ 1 ];
7796 uniqueNodes[ 1 ] = curNodes [ 0 ];
7798 else if (nbRepl == 3 &&
7799 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7800 // all bottom nodes stick: set a top before
7801 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7802 uniqueNodes[ 0 ] = curNodes [ 3 ];
7803 uniqueNodes[ 1 ] = curNodes [ 4 ];
7804 uniqueNodes[ 2 ] = curNodes [ 5 ];
7806 else if (nbRepl == 4 &&
7807 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7808 // a lateral face turns into a line: reverse a bottom
7809 uniqueNodes[ 0 ] = curNodes [ 1 ];
7810 uniqueNodes[ 1 ] = curNodes [ 0 ];
7815 else if ( nbUniqueNodes == 5 ) {
7816 // PENTAHEDRON --------------------> 2 tetrahedrons
7817 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7818 // a bottom node sticks with a linked top one
7820 SMDS_MeshElement* newElem =
7821 aMesh->AddVolume(curNodes[ 3 ],
7824 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7825 myLastCreatedElems.Append(newElem);
7827 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7828 // 2. : reverse a bottom
7829 uniqueNodes[ 0 ] = curNodes [ 1 ];
7830 uniqueNodes[ 1 ] = curNodes [ 0 ];
7840 if(elem->IsQuadratic()) { // Quadratic quadrangle
7852 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7855 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7857 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7858 uniqueNodes[0] = curNodes[0];
7859 uniqueNodes[1] = curNodes[2];
7860 uniqueNodes[2] = curNodes[3];
7861 uniqueNodes[3] = curNodes[5];
7862 uniqueNodes[4] = curNodes[6];
7863 uniqueNodes[5] = curNodes[7];
7866 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7867 uniqueNodes[0] = curNodes[0];
7868 uniqueNodes[1] = curNodes[1];
7869 uniqueNodes[2] = curNodes[2];
7870 uniqueNodes[3] = curNodes[4];
7871 uniqueNodes[4] = curNodes[5];
7872 uniqueNodes[5] = curNodes[6];
7875 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7876 uniqueNodes[0] = curNodes[1];
7877 uniqueNodes[1] = curNodes[2];
7878 uniqueNodes[2] = curNodes[3];
7879 uniqueNodes[3] = curNodes[5];
7880 uniqueNodes[4] = curNodes[6];
7881 uniqueNodes[5] = curNodes[0];
7884 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7885 uniqueNodes[0] = curNodes[0];
7886 uniqueNodes[1] = curNodes[1];
7887 uniqueNodes[2] = curNodes[3];
7888 uniqueNodes[3] = curNodes[4];
7889 uniqueNodes[4] = curNodes[6];
7890 uniqueNodes[5] = curNodes[7];
7893 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7894 uniqueNodes[0] = curNodes[0];
7895 uniqueNodes[1] = curNodes[2];
7896 uniqueNodes[2] = curNodes[3];
7897 uniqueNodes[3] = curNodes[1];
7898 uniqueNodes[4] = curNodes[6];
7899 uniqueNodes[5] = curNodes[7];
7902 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7903 uniqueNodes[0] = curNodes[0];
7904 uniqueNodes[1] = curNodes[1];
7905 uniqueNodes[2] = curNodes[2];
7906 uniqueNodes[3] = curNodes[4];
7907 uniqueNodes[4] = curNodes[5];
7908 uniqueNodes[5] = curNodes[7];
7911 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7912 uniqueNodes[0] = curNodes[0];
7913 uniqueNodes[1] = curNodes[1];
7914 uniqueNodes[2] = curNodes[3];
7915 uniqueNodes[3] = curNodes[4];
7916 uniqueNodes[4] = curNodes[2];
7917 uniqueNodes[5] = curNodes[7];
7920 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7921 uniqueNodes[0] = curNodes[0];
7922 uniqueNodes[1] = curNodes[1];
7923 uniqueNodes[2] = curNodes[2];
7924 uniqueNodes[3] = curNodes[4];
7925 uniqueNodes[4] = curNodes[5];
7926 uniqueNodes[5] = curNodes[3];
7931 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7934 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7938 //////////////////////////////////// HEXAHEDRON
7940 SMDS_VolumeTool hexa (elem);
7941 hexa.SetExternalNormal();
7942 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7943 //////////////////////// HEX ---> 1 tetrahedron
7944 for ( int iFace = 0; iFace < 6; iFace++ ) {
7945 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7946 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7947 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7948 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7949 // one face turns into a point ...
7950 int iOppFace = hexa.GetOppFaceIndex( iFace );
7951 ind = hexa.GetFaceNodesIndices( iOppFace );
7953 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7954 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7957 if ( nbStick == 1 ) {
7958 // ... and the opposite one - into a triangle.
7960 ind = hexa.GetFaceNodesIndices( iFace );
7961 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7968 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7969 //////////////////////// HEX ---> 1 prism
7970 int nbTria = 0, iTria[3];
7971 const int *ind; // indices of face nodes
7972 // look for triangular faces
7973 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7974 ind = hexa.GetFaceNodesIndices( iFace );
7975 TIDSortedNodeSet faceNodes;
7976 for ( iCur = 0; iCur < 4; iCur++ )
7977 faceNodes.insert( curNodes[ind[iCur]] );
7978 if ( faceNodes.size() == 3 )
7979 iTria[ nbTria++ ] = iFace;
7981 // check if triangles are opposite
7982 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7985 // set nodes of the bottom triangle
7986 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7988 for ( iCur = 0; iCur < 4; iCur++ )
7989 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7990 indB.push_back( ind[iCur] );
7991 if ( !hexa.IsForward() )
7992 std::swap( indB[0], indB[2] );
7993 for ( iCur = 0; iCur < 3; iCur++ )
7994 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7995 // set nodes of the top triangle
7996 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7997 for ( iCur = 0; iCur < 3; ++iCur )
7998 for ( int j = 0; j < 4; ++j )
7999 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8001 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8007 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8008 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8009 for ( int iFace = 0; iFace < 6; iFace++ ) {
8010 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8011 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8012 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8013 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8014 // one face turns into a point ...
8015 int iOppFace = hexa.GetOppFaceIndex( iFace );
8016 ind = hexa.GetFaceNodesIndices( iOppFace );
8018 iUnique = 2; // reverse a tetrahedron 1 bottom
8019 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8020 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8022 else if ( iUnique >= 0 )
8023 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8025 if ( nbStick == 0 ) {
8026 // ... and the opposite one is a quadrangle
8028 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8029 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8032 SMDS_MeshElement* newElem =
8033 aMesh->AddVolume(curNodes[ind[ 0 ]],
8036 curNodes[indTop[ 0 ]]);
8037 myLastCreatedElems.Append(newElem);
8039 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8046 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8047 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8048 // find indices of quad and tri faces
8049 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8050 for ( iFace = 0; iFace < 6; iFace++ ) {
8051 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8053 for ( iCur = 0; iCur < 4; iCur++ )
8054 nodeSet.insert( curNodes[ind[ iCur ]] );
8055 nbUniqueNodes = nodeSet.size();
8056 if ( nbUniqueNodes == 3 )
8057 iTriFace[ nbTri++ ] = iFace;
8058 else if ( nbUniqueNodes == 4 )
8059 iQuadFace[ nbQuad++ ] = iFace;
8061 if (nbQuad == 2 && nbTri == 4 &&
8062 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8063 // 2 opposite quadrangles stuck with a diagonal;
8064 // sample groups of merged indices: (0-4)(2-6)
8065 // --------------------------------------------> 2 tetrahedrons
8066 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8067 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8068 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8069 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8070 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8071 // stuck with 0-2 diagonal
8079 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8080 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8081 // stuck with 1-3 diagonal
8093 uniqueNodes[ 0 ] = curNodes [ i0 ];
8094 uniqueNodes[ 1 ] = curNodes [ i1d ];
8095 uniqueNodes[ 2 ] = curNodes [ i3d ];
8096 uniqueNodes[ 3 ] = curNodes [ i0t ];
8099 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8103 myLastCreatedElems.Append(newElem);
8105 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8108 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8109 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8110 // --------------------------------------------> prism
8111 // find 2 opposite triangles
8113 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8114 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8115 // find indices of kept and replaced nodes
8116 // and fill unique nodes of 2 opposite triangles
8117 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8118 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8119 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8120 // fill unique nodes
8123 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8124 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8125 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8127 // iCur of a linked node of the opposite face (make normals co-directed):
8128 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8129 // check that correspondent corners of triangles are linked
8130 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8133 uniqueNodes[ iUnique ] = n;
8134 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8143 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8146 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8153 } // switch ( nbNodes )
8155 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8157 if ( isOk ) { // the elem remains valid after sticking nodes
8158 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8160 // Change nodes of polyedre
8161 const SMDS_VtkVolume* aPolyedre =
8162 dynamic_cast<const SMDS_VtkVolume*>( elem );
8164 int nbFaces = aPolyedre->NbFaces();
8166 vector<const SMDS_MeshNode *> poly_nodes;
8167 vector<int> quantities (nbFaces);
8169 for (int iface = 1; iface <= nbFaces; iface++) {
8170 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8171 quantities[iface - 1] = nbFaceNodes;
8173 for (inode = 1; inode <= nbFaceNodes; inode++) {
8174 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8176 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8177 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8178 curNode = (*nnIt).second;
8180 poly_nodes.push_back(curNode);
8183 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8186 else // replace non-polyhedron elements
8188 const SMDSAbs_ElementType etyp = elem->GetType();
8189 const int elemId = elem->GetID();
8190 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8191 uniqueNodes.resize(nbUniqueNodes);
8193 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8195 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8196 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8197 if ( sm && newElem )
8198 sm->AddElement( newElem );
8199 if ( elem != newElem )
8200 ReplaceElemInGroups( elem, newElem, aMesh );
8204 // Remove invalid regular element or invalid polygon
8205 rmElemIds.push_back( elem->GetID() );
8208 } // loop on elements
8210 // Remove bad elements, then equal nodes (order important)
8212 Remove( rmElemIds, false );
8213 Remove( rmNodeIds, true );
8218 // ========================================================
8219 // class : SortableElement
8220 // purpose : allow sorting elements basing on their nodes
8221 // ========================================================
8222 class SortableElement : public set <const SMDS_MeshElement*>
8226 SortableElement( const SMDS_MeshElement* theElem )
8229 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8230 while ( nodeIt->more() )
8231 this->insert( nodeIt->next() );
8234 const SMDS_MeshElement* Get() const
8237 void Set(const SMDS_MeshElement* e) const
8242 mutable const SMDS_MeshElement* myElem;
8245 //=======================================================================
8246 //function : FindEqualElements
8247 //purpose : Return list of group of elements built on the same nodes.
8248 // Search among theElements or in the whole mesh if theElements is empty
8249 //=======================================================================
8251 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8252 TListOfListOfElementsID & theGroupsOfElementsID)
8254 myLastCreatedElems.Clear();
8255 myLastCreatedNodes.Clear();
8257 typedef map< SortableElement, int > TMapOfNodeSet;
8258 typedef list<int> TGroupOfElems;
8260 if ( theElements.empty() )
8261 { // get all elements in the mesh
8262 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8263 while ( eIt->more() )
8264 theElements.insert( theElements.end(), eIt->next());
8267 vector< TGroupOfElems > arrayOfGroups;
8268 TGroupOfElems groupOfElems;
8269 TMapOfNodeSet mapOfNodeSet;
8271 TIDSortedElemSet::iterator elemIt = theElements.begin();
8272 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8273 const SMDS_MeshElement* curElem = *elemIt;
8274 SortableElement SE(curElem);
8277 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8278 if( !(pp.second) ) {
8279 TMapOfNodeSet::iterator& itSE = pp.first;
8280 ind = (*itSE).second;
8281 arrayOfGroups[ind].push_back(curElem->GetID());
8284 groupOfElems.clear();
8285 groupOfElems.push_back(curElem->GetID());
8286 arrayOfGroups.push_back(groupOfElems);
8291 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8292 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8293 groupOfElems = *groupIt;
8294 if ( groupOfElems.size() > 1 ) {
8295 groupOfElems.sort();
8296 theGroupsOfElementsID.push_back(groupOfElems);
8301 //=======================================================================
8302 //function : MergeElements
8303 //purpose : In each given group, substitute all elements by the first one.
8304 //=======================================================================
8306 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8308 myLastCreatedElems.Clear();
8309 myLastCreatedNodes.Clear();
8311 typedef list<int> TListOfIDs;
8312 TListOfIDs rmElemIds; // IDs of elems to remove
8314 SMESHDS_Mesh* aMesh = GetMeshDS();
8316 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8317 while ( groupsIt != theGroupsOfElementsID.end() ) {
8318 TListOfIDs& aGroupOfElemID = *groupsIt;
8319 aGroupOfElemID.sort();
8320 int elemIDToKeep = aGroupOfElemID.front();
8321 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8322 aGroupOfElemID.pop_front();
8323 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8324 while ( idIt != aGroupOfElemID.end() ) {
8325 int elemIDToRemove = *idIt;
8326 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8327 // add the kept element in groups of removed one (PAL15188)
8328 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8329 rmElemIds.push_back( elemIDToRemove );
8335 Remove( rmElemIds, false );
8338 //=======================================================================
8339 //function : MergeEqualElements
8340 //purpose : Remove all but one of elements built on the same nodes.
8341 //=======================================================================
8343 void SMESH_MeshEditor::MergeEqualElements()
8345 TIDSortedElemSet aMeshElements; /* empty input ==
8346 to merge equal elements in the whole mesh */
8347 TListOfListOfElementsID aGroupsOfElementsID;
8348 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8349 MergeElements(aGroupsOfElementsID);
8352 //=======================================================================
8353 //function : FindFaceInSet
8354 //purpose : Return a face having linked nodes n1 and n2 and which is
8355 // - not in avoidSet,
8356 // - in elemSet provided that !elemSet.empty()
8357 // i1 and i2 optionally returns indices of n1 and n2
8358 //=======================================================================
8360 const SMDS_MeshElement*
8361 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8362 const SMDS_MeshNode* n2,
8363 const TIDSortedElemSet& elemSet,
8364 const TIDSortedElemSet& avoidSet,
8370 const SMDS_MeshElement* face = 0;
8372 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8373 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8374 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8376 //MESSAGE("in while ( invElemIt->more() && !face )");
8377 const SMDS_MeshElement* elem = invElemIt->next();
8378 if (avoidSet.count( elem ))
8380 if ( !elemSet.empty() && !elemSet.count( elem ))
8383 i1 = elem->GetNodeIndex( n1 );
8384 // find a n2 linked to n1
8385 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8386 for ( int di = -1; di < 2 && !face; di += 2 )
8388 i2 = (i1+di+nbN) % nbN;
8389 if ( elem->GetNode( i2 ) == n2 )
8392 if ( !face && elem->IsQuadratic())
8394 // analysis for quadratic elements using all nodes
8395 const SMDS_VtkFace* F =
8396 dynamic_cast<const SMDS_VtkFace*>(elem);
8397 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8398 // use special nodes iterator
8399 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8400 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8401 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8403 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8404 if ( n1 == prevN && n2 == n )
8408 else if ( n2 == prevN && n1 == n )
8410 face = elem; swap( i1, i2 );
8416 if ( n1ind ) *n1ind = i1;
8417 if ( n2ind ) *n2ind = i2;
8421 //=======================================================================
8422 //function : findAdjacentFace
8424 //=======================================================================
8426 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8427 const SMDS_MeshNode* n2,
8428 const SMDS_MeshElement* elem)
8430 TIDSortedElemSet elemSet, avoidSet;
8432 avoidSet.insert ( elem );
8433 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8436 //=======================================================================
8437 //function : FindFreeBorder
8439 //=======================================================================
8441 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8443 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8444 const SMDS_MeshNode* theSecondNode,
8445 const SMDS_MeshNode* theLastNode,
8446 list< const SMDS_MeshNode* > & theNodes,
8447 list< const SMDS_MeshElement* >& theFaces)
8449 if ( !theFirstNode || !theSecondNode )
8451 // find border face between theFirstNode and theSecondNode
8452 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8456 theFaces.push_back( curElem );
8457 theNodes.push_back( theFirstNode );
8458 theNodes.push_back( theSecondNode );
8460 //vector<const SMDS_MeshNode*> nodes;
8461 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8462 TIDSortedElemSet foundElems;
8463 bool needTheLast = ( theLastNode != 0 );
8465 while ( nStart != theLastNode ) {
8466 if ( nStart == theFirstNode )
8467 return !needTheLast;
8469 // find all free border faces sharing form nStart
8471 list< const SMDS_MeshElement* > curElemList;
8472 list< const SMDS_MeshNode* > nStartList;
8473 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8474 while ( invElemIt->more() ) {
8475 const SMDS_MeshElement* e = invElemIt->next();
8476 if ( e == curElem || foundElems.insert( e ).second ) {
8478 int iNode = 0, nbNodes = e->NbNodes();
8479 //const SMDS_MeshNode* nodes[nbNodes+1];
8480 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8482 if(e->IsQuadratic()) {
8483 const SMDS_VtkFace* F =
8484 dynamic_cast<const SMDS_VtkFace*>(e);
8485 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8486 // use special nodes iterator
8487 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8488 while( anIter->more() ) {
8489 nodes[ iNode++ ] = cast2Node(anIter->next());
8493 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8494 while ( nIt->more() )
8495 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8497 nodes[ iNode ] = nodes[ 0 ];
8499 for ( iNode = 0; iNode < nbNodes; iNode++ )
8500 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8501 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8502 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8504 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8505 curElemList.push_back( e );
8509 // analyse the found
8511 int nbNewBorders = curElemList.size();
8512 if ( nbNewBorders == 0 ) {
8513 // no free border furthermore
8514 return !needTheLast;
8516 else if ( nbNewBorders == 1 ) {
8517 // one more element found
8519 nStart = nStartList.front();
8520 curElem = curElemList.front();
8521 theFaces.push_back( curElem );
8522 theNodes.push_back( nStart );
8525 // several continuations found
8526 list< const SMDS_MeshElement* >::iterator curElemIt;
8527 list< const SMDS_MeshNode* >::iterator nStartIt;
8528 // check if one of them reached the last node
8529 if ( needTheLast ) {
8530 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8531 curElemIt!= curElemList.end();
8532 curElemIt++, nStartIt++ )
8533 if ( *nStartIt == theLastNode ) {
8534 theFaces.push_back( *curElemIt );
8535 theNodes.push_back( *nStartIt );
8539 // find the best free border by the continuations
8540 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8541 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8542 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8543 curElemIt!= curElemList.end();
8544 curElemIt++, nStartIt++ )
8546 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8547 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8548 // find one more free border
8549 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8553 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8554 // choice: clear a worse one
8555 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8556 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8557 contNodes[ iWorse ].clear();
8558 contFaces[ iWorse ].clear();
8561 if ( contNodes[0].empty() && contNodes[1].empty() )
8564 // append the best free border
8565 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8566 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8567 theNodes.pop_back(); // remove nIgnore
8568 theNodes.pop_back(); // remove nStart
8569 theFaces.pop_back(); // remove curElem
8570 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8571 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8572 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8573 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8576 } // several continuations found
8577 } // while ( nStart != theLastNode )
8582 //=======================================================================
8583 //function : CheckFreeBorderNodes
8584 //purpose : Return true if the tree nodes are on a free border
8585 //=======================================================================
8587 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8588 const SMDS_MeshNode* theNode2,
8589 const SMDS_MeshNode* theNode3)
8591 list< const SMDS_MeshNode* > nodes;
8592 list< const SMDS_MeshElement* > faces;
8593 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8596 //=======================================================================
8597 //function : SewFreeBorder
8599 //=======================================================================
8601 SMESH_MeshEditor::Sew_Error
8602 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8603 const SMDS_MeshNode* theBordSecondNode,
8604 const SMDS_MeshNode* theBordLastNode,
8605 const SMDS_MeshNode* theSideFirstNode,
8606 const SMDS_MeshNode* theSideSecondNode,
8607 const SMDS_MeshNode* theSideThirdNode,
8608 const bool theSideIsFreeBorder,
8609 const bool toCreatePolygons,
8610 const bool toCreatePolyedrs)
8612 myLastCreatedElems.Clear();
8613 myLastCreatedNodes.Clear();
8615 MESSAGE("::SewFreeBorder()");
8616 Sew_Error aResult = SEW_OK;
8618 // ====================================
8619 // find side nodes and elements
8620 // ====================================
8622 list< const SMDS_MeshNode* > nSide[ 2 ];
8623 list< const SMDS_MeshElement* > eSide[ 2 ];
8624 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8625 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8629 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8630 nSide[0], eSide[0])) {
8631 MESSAGE(" Free Border 1 not found " );
8632 aResult = SEW_BORDER1_NOT_FOUND;
8634 if (theSideIsFreeBorder) {
8637 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8638 nSide[1], eSide[1])) {
8639 MESSAGE(" Free Border 2 not found " );
8640 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8643 if ( aResult != SEW_OK )
8646 if (!theSideIsFreeBorder) {
8650 // -------------------------------------------------------------------------
8652 // 1. If nodes to merge are not coincident, move nodes of the free border
8653 // from the coord sys defined by the direction from the first to last
8654 // nodes of the border to the correspondent sys of the side 2
8655 // 2. On the side 2, find the links most co-directed with the correspondent
8656 // links of the free border
8657 // -------------------------------------------------------------------------
8659 // 1. Since sewing may break if there are volumes to split on the side 2,
8660 // we wont move nodes but just compute new coordinates for them
8661 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8662 TNodeXYZMap nBordXYZ;
8663 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8664 list< const SMDS_MeshNode* >::iterator nBordIt;
8666 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8667 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8668 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8669 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8670 double tol2 = 1.e-8;
8671 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8672 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8673 // Need node movement.
8675 // find X and Z axes to create trsf
8676 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8678 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8680 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8683 gp_Ax3 toBordAx( Pb1, Zb, X );
8684 gp_Ax3 fromSideAx( Ps1, Zs, X );
8685 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8687 gp_Trsf toBordSys, fromSide2Sys;
8688 toBordSys.SetTransformation( toBordAx );
8689 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8690 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8693 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8694 const SMDS_MeshNode* n = *nBordIt;
8695 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8696 toBordSys.Transforms( xyz );
8697 fromSide2Sys.Transforms( xyz );
8698 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8702 // just insert nodes XYZ in the nBordXYZ map
8703 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8704 const SMDS_MeshNode* n = *nBordIt;
8705 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8709 // 2. On the side 2, find the links most co-directed with the correspondent
8710 // links of the free border
8712 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8713 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8714 sideNodes.push_back( theSideFirstNode );
8716 bool hasVolumes = false;
8717 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8718 set<long> foundSideLinkIDs, checkedLinkIDs;
8719 SMDS_VolumeTool volume;
8720 //const SMDS_MeshNode* faceNodes[ 4 ];
8722 const SMDS_MeshNode* sideNode;
8723 const SMDS_MeshElement* sideElem;
8724 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8725 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8726 nBordIt = bordNodes.begin();
8728 // border node position and border link direction to compare with
8729 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8730 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8731 // choose next side node by link direction or by closeness to
8732 // the current border node:
8733 bool searchByDir = ( *nBordIt != theBordLastNode );
8735 // find the next node on the Side 2
8737 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8739 checkedLinkIDs.clear();
8740 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8742 // loop on inverse elements of current node (prevSideNode) on the Side 2
8743 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8744 while ( invElemIt->more() )
8746 const SMDS_MeshElement* elem = invElemIt->next();
8747 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8748 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8749 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8750 bool isVolume = volume.Set( elem );
8751 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8752 if ( isVolume ) // --volume
8754 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8755 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8756 if(elem->IsQuadratic()) {
8757 const SMDS_VtkFace* F =
8758 dynamic_cast<const SMDS_VtkFace*>(elem);
8759 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8760 // use special nodes iterator
8761 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8762 while( anIter->more() ) {
8763 nodes[ iNode ] = cast2Node(anIter->next());
8764 if ( nodes[ iNode++ ] == prevSideNode )
8765 iPrevNode = iNode - 1;
8769 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8770 while ( nIt->more() ) {
8771 nodes[ iNode ] = cast2Node( nIt->next() );
8772 if ( nodes[ iNode++ ] == prevSideNode )
8773 iPrevNode = iNode - 1;
8776 // there are 2 links to check
8781 // loop on links, to be precise, on the second node of links
8782 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8783 const SMDS_MeshNode* n = nodes[ iNode ];
8785 if ( !volume.IsLinked( n, prevSideNode ))
8789 if ( iNode ) // a node before prevSideNode
8790 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8791 else // a node after prevSideNode
8792 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8794 // check if this link was already used
8795 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8796 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8797 if (!isJustChecked &&
8798 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8800 // test a link geometrically
8801 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8802 bool linkIsBetter = false;
8803 double dot = 0.0, dist = 0.0;
8804 if ( searchByDir ) { // choose most co-directed link
8805 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8806 linkIsBetter = ( dot > maxDot );
8808 else { // choose link with the node closest to bordPos
8809 dist = ( nextXYZ - bordPos ).SquareModulus();
8810 linkIsBetter = ( dist < minDist );
8812 if ( linkIsBetter ) {
8821 } // loop on inverse elements of prevSideNode
8824 MESSAGE(" Cant find path by links of the Side 2 ");
8825 return SEW_BAD_SIDE_NODES;
8827 sideNodes.push_back( sideNode );
8828 sideElems.push_back( sideElem );
8829 foundSideLinkIDs.insert ( linkID );
8830 prevSideNode = sideNode;
8832 if ( *nBordIt == theBordLastNode )
8833 searchByDir = false;
8835 // find the next border link to compare with
8836 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8837 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8838 // move to next border node if sideNode is before forward border node (bordPos)
8839 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8840 prevBordNode = *nBordIt;
8842 bordPos = nBordXYZ[ *nBordIt ];
8843 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8844 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8848 while ( sideNode != theSideSecondNode );
8850 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8851 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8852 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8854 } // end nodes search on the side 2
8856 // ============================
8857 // sew the border to the side 2
8858 // ============================
8860 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8861 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8863 TListOfListOfNodes nodeGroupsToMerge;
8864 if ( nbNodes[0] == nbNodes[1] ||
8865 ( theSideIsFreeBorder && !theSideThirdNode)) {
8867 // all nodes are to be merged
8869 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8870 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8871 nIt[0]++, nIt[1]++ )
8873 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8874 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8875 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8880 // insert new nodes into the border and the side to get equal nb of segments
8882 // get normalized parameters of nodes on the borders
8883 //double param[ 2 ][ maxNbNodes ];
8885 param[0] = new double [ maxNbNodes ];
8886 param[1] = new double [ maxNbNodes ];
8888 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8889 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8890 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8891 const SMDS_MeshNode* nPrev = *nIt;
8892 double bordLength = 0;
8893 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8894 const SMDS_MeshNode* nCur = *nIt;
8895 gp_XYZ segment (nCur->X() - nPrev->X(),
8896 nCur->Y() - nPrev->Y(),
8897 nCur->Z() - nPrev->Z());
8898 double segmentLen = segment.Modulus();
8899 bordLength += segmentLen;
8900 param[ iBord ][ iNode ] = bordLength;
8903 // normalize within [0,1]
8904 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8905 param[ iBord ][ iNode ] /= bordLength;
8909 // loop on border segments
8910 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8911 int i[ 2 ] = { 0, 0 };
8912 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8913 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8915 TElemOfNodeListMap insertMap;
8916 TElemOfNodeListMap::iterator insertMapIt;
8918 // key: elem to insert nodes into
8919 // value: 2 nodes to insert between + nodes to be inserted
8921 bool next[ 2 ] = { false, false };
8923 // find min adjacent segment length after sewing
8924 double nextParam = 10., prevParam = 0;
8925 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8926 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8927 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8928 if ( i[ iBord ] > 0 )
8929 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8931 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8932 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8933 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8935 // choose to insert or to merge nodes
8936 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8937 if ( Abs( du ) <= minSegLen * 0.2 ) {
8940 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8941 const SMDS_MeshNode* n0 = *nIt[0];
8942 const SMDS_MeshNode* n1 = *nIt[1];
8943 nodeGroupsToMerge.back().push_back( n1 );
8944 nodeGroupsToMerge.back().push_back( n0 );
8945 // position of node of the border changes due to merge
8946 param[ 0 ][ i[0] ] += du;
8947 // move n1 for the sake of elem shape evaluation during insertion.
8948 // n1 will be removed by MergeNodes() anyway
8949 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8950 next[0] = next[1] = true;
8955 int intoBord = ( du < 0 ) ? 0 : 1;
8956 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8957 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8958 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8959 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8960 if ( intoBord == 1 ) {
8961 // move node of the border to be on a link of elem of the side
8962 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8963 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8964 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8965 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8966 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8968 insertMapIt = insertMap.find( elem );
8969 bool notFound = ( insertMapIt == insertMap.end() );
8970 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8972 // insert into another link of the same element:
8973 // 1. perform insertion into the other link of the elem
8974 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8975 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8976 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8977 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8978 // 2. perform insertion into the link of adjacent faces
8980 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8982 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8986 if (toCreatePolyedrs) {
8987 // perform insertion into the links of adjacent volumes
8988 UpdateVolumes(n12, n22, nodeList);
8990 // 3. find an element appeared on n1 and n2 after the insertion
8991 insertMap.erase( elem );
8992 elem = findAdjacentFace( n1, n2, 0 );
8994 if ( notFound || otherLink ) {
8995 // add element and nodes of the side into the insertMap
8996 insertMapIt = insertMap.insert
8997 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8998 (*insertMapIt).second.push_back( n1 );
8999 (*insertMapIt).second.push_back( n2 );
9001 // add node to be inserted into elem
9002 (*insertMapIt).second.push_back( nIns );
9003 next[ 1 - intoBord ] = true;
9006 // go to the next segment
9007 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9008 if ( next[ iBord ] ) {
9009 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9011 nPrev[ iBord ] = *nIt[ iBord ];
9012 nIt[ iBord ]++; i[ iBord ]++;
9016 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9018 // perform insertion of nodes into elements
9020 for (insertMapIt = insertMap.begin();
9021 insertMapIt != insertMap.end();
9024 const SMDS_MeshElement* elem = (*insertMapIt).first;
9025 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9026 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9027 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9029 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9031 if ( !theSideIsFreeBorder ) {
9032 // look for and insert nodes into the faces adjacent to elem
9034 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9036 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9041 if (toCreatePolyedrs) {
9042 // perform insertion into the links of adjacent volumes
9043 UpdateVolumes(n1, n2, nodeList);
9049 } // end: insert new nodes
9051 MergeNodes ( nodeGroupsToMerge );
9056 //=======================================================================
9057 //function : InsertNodesIntoLink
9058 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9059 // and theBetweenNode2 and split theElement
9060 //=======================================================================
9062 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9063 const SMDS_MeshNode* theBetweenNode1,
9064 const SMDS_MeshNode* theBetweenNode2,
9065 list<const SMDS_MeshNode*>& theNodesToInsert,
9066 const bool toCreatePoly)
9068 if ( theFace->GetType() != SMDSAbs_Face ) return;
9070 // find indices of 2 link nodes and of the rest nodes
9071 int iNode = 0, il1, il2, i3, i4;
9072 il1 = il2 = i3 = i4 = -1;
9073 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9074 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9076 if(theFace->IsQuadratic()) {
9077 const SMDS_VtkFace* F =
9078 dynamic_cast<const SMDS_VtkFace*>(theFace);
9079 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9080 // use special nodes iterator
9081 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9082 while( anIter->more() ) {
9083 const SMDS_MeshNode* n = cast2Node(anIter->next());
9084 if ( n == theBetweenNode1 )
9086 else if ( n == theBetweenNode2 )
9092 nodes[ iNode++ ] = n;
9096 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9097 while ( nodeIt->more() ) {
9098 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9099 if ( n == theBetweenNode1 )
9101 else if ( n == theBetweenNode2 )
9107 nodes[ iNode++ ] = n;
9110 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9113 // arrange link nodes to go one after another regarding the face orientation
9114 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9115 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9120 aNodesToInsert.reverse();
9122 // check that not link nodes of a quadrangles are in good order
9123 int nbFaceNodes = theFace->NbNodes();
9124 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9130 if (toCreatePoly || theFace->IsPoly()) {
9133 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9135 // add nodes of face up to first node of link
9138 if(theFace->IsQuadratic()) {
9139 const SMDS_VtkFace* F =
9140 dynamic_cast<const SMDS_VtkFace*>(theFace);
9141 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9142 // use special nodes iterator
9143 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9144 while( anIter->more() && !isFLN ) {
9145 const SMDS_MeshNode* n = cast2Node(anIter->next());
9146 poly_nodes[iNode++] = n;
9147 if (n == nodes[il1]) {
9151 // add nodes to insert
9152 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9153 for (; nIt != aNodesToInsert.end(); nIt++) {
9154 poly_nodes[iNode++] = *nIt;
9156 // add nodes of face starting from last node of link
9157 while ( anIter->more() ) {
9158 poly_nodes[iNode++] = cast2Node(anIter->next());
9162 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9163 while ( nodeIt->more() && !isFLN ) {
9164 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9165 poly_nodes[iNode++] = n;
9166 if (n == nodes[il1]) {
9170 // add nodes to insert
9171 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9172 for (; nIt != aNodesToInsert.end(); nIt++) {
9173 poly_nodes[iNode++] = *nIt;
9175 // add nodes of face starting from last node of link
9176 while ( nodeIt->more() ) {
9177 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9178 poly_nodes[iNode++] = n;
9182 // edit or replace the face
9183 SMESHDS_Mesh *aMesh = GetMeshDS();
9185 if (theFace->IsPoly()) {
9186 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9189 int aShapeId = FindShape( theFace );
9191 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9192 myLastCreatedElems.Append(newElem);
9193 if ( aShapeId && newElem )
9194 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9196 aMesh->RemoveElement(theFace);
9201 SMESHDS_Mesh *aMesh = GetMeshDS();
9202 if( !theFace->IsQuadratic() ) {
9204 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9205 int nbLinkNodes = 2 + aNodesToInsert.size();
9206 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9207 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9208 linkNodes[ 0 ] = nodes[ il1 ];
9209 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9210 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9211 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9212 linkNodes[ iNode++ ] = *nIt;
9214 // decide how to split a quadrangle: compare possible variants
9215 // and choose which of splits to be a quadrangle
9216 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9217 if ( nbFaceNodes == 3 ) {
9218 iBestQuad = nbSplits;
9221 else if ( nbFaceNodes == 4 ) {
9222 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9223 double aBestRate = DBL_MAX;
9224 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9226 double aBadRate = 0;
9227 // evaluate elements quality
9228 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9229 if ( iSplit == iQuad ) {
9230 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9234 aBadRate += getBadRate( &quad, aCrit );
9237 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9239 nodes[ iSplit < iQuad ? i4 : i3 ]);
9240 aBadRate += getBadRate( &tria, aCrit );
9244 if ( aBadRate < aBestRate ) {
9246 aBestRate = aBadRate;
9251 // create new elements
9252 int aShapeId = FindShape( theFace );
9255 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9256 SMDS_MeshElement* newElem = 0;
9257 if ( iSplit == iBestQuad )
9258 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9263 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9265 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9266 myLastCreatedElems.Append(newElem);
9267 if ( aShapeId && newElem )
9268 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9271 // change nodes of theFace
9272 const SMDS_MeshNode* newNodes[ 4 ];
9273 newNodes[ 0 ] = linkNodes[ i1 ];
9274 newNodes[ 1 ] = linkNodes[ i2 ];
9275 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9276 newNodes[ 3 ] = nodes[ i4 ];
9277 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9278 const SMDS_MeshElement* newElem = 0;
9279 if (iSplit == iBestQuad)
9280 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9282 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9283 myLastCreatedElems.Append(newElem);
9284 if ( aShapeId && newElem )
9285 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9286 } // end if(!theFace->IsQuadratic())
9287 else { // theFace is quadratic
9288 // we have to split theFace on simple triangles and one simple quadrangle
9290 int nbshift = tmp*2;
9291 // shift nodes in nodes[] by nbshift
9293 for(i=0; i<nbshift; i++) {
9294 const SMDS_MeshNode* n = nodes[0];
9295 for(j=0; j<nbFaceNodes-1; j++) {
9296 nodes[j] = nodes[j+1];
9298 nodes[nbFaceNodes-1] = n;
9300 il1 = il1 - nbshift;
9301 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9302 // n0 n1 n2 n0 n1 n2
9303 // +-----+-----+ +-----+-----+
9312 // create new elements
9313 int aShapeId = FindShape( theFace );
9316 if(nbFaceNodes==6) { // quadratic triangle
9317 SMDS_MeshElement* newElem =
9318 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9319 myLastCreatedElems.Append(newElem);
9320 if ( aShapeId && newElem )
9321 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9322 if(theFace->IsMediumNode(nodes[il1])) {
9323 // create quadrangle
9324 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9325 myLastCreatedElems.Append(newElem);
9326 if ( aShapeId && newElem )
9327 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9333 // create quadrangle
9334 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9335 myLastCreatedElems.Append(newElem);
9336 if ( aShapeId && newElem )
9337 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9343 else { // nbFaceNodes==8 - quadratic quadrangle
9344 SMDS_MeshElement* newElem =
9345 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9346 myLastCreatedElems.Append(newElem);
9347 if ( aShapeId && newElem )
9348 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9349 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9350 myLastCreatedElems.Append(newElem);
9351 if ( aShapeId && newElem )
9352 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9353 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9354 myLastCreatedElems.Append(newElem);
9355 if ( aShapeId && newElem )
9356 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9357 if(theFace->IsMediumNode(nodes[il1])) {
9358 // create quadrangle
9359 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9360 myLastCreatedElems.Append(newElem);
9361 if ( aShapeId && newElem )
9362 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9368 // create quadrangle
9369 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9370 myLastCreatedElems.Append(newElem);
9371 if ( aShapeId && newElem )
9372 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9378 // create needed triangles using n1,n2,n3 and inserted nodes
9379 int nbn = 2 + aNodesToInsert.size();
9380 //const SMDS_MeshNode* aNodes[nbn];
9381 vector<const SMDS_MeshNode*> aNodes(nbn);
9382 aNodes[0] = nodes[n1];
9383 aNodes[nbn-1] = nodes[n2];
9384 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9385 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9386 aNodes[iNode++] = *nIt;
9388 for(i=1; i<nbn; i++) {
9389 SMDS_MeshElement* newElem =
9390 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9391 myLastCreatedElems.Append(newElem);
9392 if ( aShapeId && newElem )
9393 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9397 aMesh->RemoveElement(theFace);
9400 //=======================================================================
9401 //function : UpdateVolumes
9403 //=======================================================================
9404 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9405 const SMDS_MeshNode* theBetweenNode2,
9406 list<const SMDS_MeshNode*>& theNodesToInsert)
9408 myLastCreatedElems.Clear();
9409 myLastCreatedNodes.Clear();
9411 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9412 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9413 const SMDS_MeshElement* elem = invElemIt->next();
9415 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9416 SMDS_VolumeTool aVolume (elem);
9417 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9420 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9421 int iface, nbFaces = aVolume.NbFaces();
9422 vector<const SMDS_MeshNode *> poly_nodes;
9423 vector<int> quantities (nbFaces);
9425 for (iface = 0; iface < nbFaces; iface++) {
9426 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9427 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9428 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9430 for (int inode = 0; inode < nbFaceNodes; inode++) {
9431 poly_nodes.push_back(faceNodes[inode]);
9433 if (nbInserted == 0) {
9434 if (faceNodes[inode] == theBetweenNode1) {
9435 if (faceNodes[inode + 1] == theBetweenNode2) {
9436 nbInserted = theNodesToInsert.size();
9438 // add nodes to insert
9439 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9440 for (; nIt != theNodesToInsert.end(); nIt++) {
9441 poly_nodes.push_back(*nIt);
9445 else if (faceNodes[inode] == theBetweenNode2) {
9446 if (faceNodes[inode + 1] == theBetweenNode1) {
9447 nbInserted = theNodesToInsert.size();
9449 // add nodes to insert in reversed order
9450 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9452 for (; nIt != theNodesToInsert.begin(); nIt--) {
9453 poly_nodes.push_back(*nIt);
9455 poly_nodes.push_back(*nIt);
9462 quantities[iface] = nbFaceNodes + nbInserted;
9465 // Replace or update the volume
9466 SMESHDS_Mesh *aMesh = GetMeshDS();
9468 if (elem->IsPoly()) {
9469 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9473 int aShapeId = FindShape( elem );
9475 SMDS_MeshElement* newElem =
9476 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9477 myLastCreatedElems.Append(newElem);
9478 if (aShapeId && newElem)
9479 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9481 aMesh->RemoveElement(elem);
9488 //================================================================================
9490 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9492 //================================================================================
9494 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9495 vector<const SMDS_MeshNode *> & nodes,
9496 vector<int> & nbNodeInFaces )
9499 nbNodeInFaces.clear();
9500 SMDS_VolumeTool vTool ( elem );
9501 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9503 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9504 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9505 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9510 //=======================================================================
9512 * \brief Convert elements contained in a submesh to quadratic
9513 * \return int - nb of checked elements
9515 //=======================================================================
9517 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9518 SMESH_MesherHelper& theHelper,
9519 const bool theForce3d)
9522 if( !theSm ) return nbElem;
9524 vector<int> nbNodeInFaces;
9525 vector<const SMDS_MeshNode *> nodes;
9526 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9527 while(ElemItr->more())
9530 const SMDS_MeshElement* elem = ElemItr->next();
9531 if( !elem || elem->IsQuadratic() ) continue;
9533 // get elem data needed to re-create it
9535 const int id = elem->GetID();
9536 const int nbNodes = elem->NbNodes();
9537 const SMDSAbs_ElementType aType = elem->GetType();
9538 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9539 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9540 if ( aGeomType == SMDSEntity_Polyhedra )
9541 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9542 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9543 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9545 // remove a linear element
9546 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9548 const SMDS_MeshElement* NewElem = 0;
9554 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9562 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9565 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9568 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9573 case SMDSAbs_Volume :
9577 case SMDSEntity_Tetra:
9578 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9580 case SMDSEntity_Pyramid:
9581 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9583 case SMDSEntity_Penta:
9584 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9586 case SMDSEntity_Hexa:
9587 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9588 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9590 case SMDSEntity_Hexagonal_Prism:
9592 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9599 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9601 theSm->AddElement( NewElem );
9606 //=======================================================================
9607 //function : ConvertToQuadratic
9609 //=======================================================================
9611 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9613 SMESHDS_Mesh* meshDS = GetMeshDS();
9615 SMESH_MesherHelper aHelper(*myMesh);
9616 aHelper.SetIsQuadratic( true );
9618 int nbCheckedElems = 0;
9619 if ( myMesh->HasShapeToMesh() )
9621 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9623 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9624 while ( smIt->more() ) {
9625 SMESH_subMesh* sm = smIt->next();
9626 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9627 aHelper.SetSubShape( sm->GetSubShape() );
9628 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9633 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9634 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9636 SMESHDS_SubMesh *smDS = 0;
9637 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9638 while(aEdgeItr->more())
9640 const SMDS_MeshEdge* edge = aEdgeItr->next();
9641 if(edge && !edge->IsQuadratic())
9643 int id = edge->GetID();
9644 //MESSAGE("edge->GetID() " << id);
9645 const SMDS_MeshNode* n1 = edge->GetNode(0);
9646 const SMDS_MeshNode* n2 = edge->GetNode(1);
9648 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9650 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9651 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9654 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9655 while(aFaceItr->more())
9657 const SMDS_MeshFace* face = aFaceItr->next();
9658 if(!face || face->IsQuadratic() ) continue;
9660 const int id = face->GetID();
9661 const SMDSAbs_EntityType type = face->GetEntityType();
9662 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9664 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9666 SMDS_MeshFace * NewFace = 0;
9669 case SMDSEntity_Triangle:
9670 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9672 case SMDSEntity_Quadrangle:
9673 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9676 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9678 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9680 vector<int> nbNodeInFaces;
9681 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9682 while(aVolumeItr->more())
9684 const SMDS_MeshVolume* volume = aVolumeItr->next();
9685 if(!volume || volume->IsQuadratic() ) continue;
9687 const int id = volume->GetID();
9688 const SMDSAbs_EntityType type = volume->GetEntityType();
9689 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9690 if ( type == SMDSEntity_Polyhedra )
9691 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9692 else if ( type == SMDSEntity_Hexagonal_Prism )
9693 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9695 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9697 SMDS_MeshVolume * NewVolume = 0;
9700 case SMDSEntity_Tetra:
9701 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9703 case SMDSEntity_Hexa:
9704 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9705 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9707 case SMDSEntity_Pyramid:
9708 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9709 nodes[3], nodes[4], id, theForce3d);
9711 case SMDSEntity_Penta:
9712 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9713 nodes[3], nodes[4], nodes[5], id, theForce3d);
9715 case SMDSEntity_Hexagonal_Prism:
9717 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9719 ReplaceElemInGroups(volume, NewVolume, meshDS);
9724 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9725 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9726 aHelper.FixQuadraticElements(myError);
9730 //================================================================================
9732 * \brief Makes given elements quadratic
9733 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9734 * \param theElements - elements to make quadratic
9736 //================================================================================
9738 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9739 TIDSortedElemSet& theElements)
9741 if ( theElements.empty() ) return;
9743 // we believe that all theElements are of the same type
9744 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9746 // get all nodes shared by theElements
9747 TIDSortedNodeSet allNodes;
9748 TIDSortedElemSet::iterator eIt = theElements.begin();
9749 for ( ; eIt != theElements.end(); ++eIt )
9750 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9752 // complete theElements with elements of lower dim whose all nodes are in allNodes
9754 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9755 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9756 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9757 for ( ; nIt != allNodes.end(); ++nIt )
9759 const SMDS_MeshNode* n = *nIt;
9760 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9761 while ( invIt->more() )
9763 const SMDS_MeshElement* e = invIt->next();
9764 if ( e->IsQuadratic() )
9766 quadAdjacentElems[ e->GetType() ].insert( e );
9769 if ( e->GetType() >= elemType )
9771 continue; // same type of more complex linear element
9774 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9775 continue; // e is already checked
9779 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9780 while ( nodeIt->more() && allIn )
9781 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9783 theElements.insert(e );
9787 SMESH_MesherHelper helper(*myMesh);
9788 helper.SetIsQuadratic( true );
9790 // add links of quadratic adjacent elements to the helper
9792 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9793 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9794 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9796 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9798 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9799 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9800 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9802 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9804 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9805 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9806 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9808 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9811 // make quadratic elements instead of linear ones
9813 SMESHDS_Mesh* meshDS = GetMeshDS();
9814 SMESHDS_SubMesh* smDS = 0;
9815 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9817 const SMDS_MeshElement* elem = *eIt;
9818 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9821 const int id = elem->GetID();
9822 const SMDSAbs_ElementType type = elem->GetType();
9823 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9825 if ( !smDS || !smDS->Contains( elem ))
9826 smDS = meshDS->MeshElements( elem->getshapeId() );
9827 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9829 SMDS_MeshElement * newElem = 0;
9830 switch( nodes.size() )
9832 case 4: // cases for most frequently used element types go first (for optimization)
9833 if ( type == SMDSAbs_Volume )
9834 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9836 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9839 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9840 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9843 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9846 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9849 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9850 nodes[4], id, theForce3d);
9853 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9854 nodes[4], nodes[5], id, theForce3d);
9858 ReplaceElemInGroups( elem, newElem, meshDS);
9859 if( newElem && smDS )
9860 smDS->AddElement( newElem );
9863 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9864 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9865 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9866 helper.FixQuadraticElements( myError );
9870 //=======================================================================
9872 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9873 * \return int - nb of checked elements
9875 //=======================================================================
9877 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9878 SMDS_ElemIteratorPtr theItr,
9879 const int theShapeID)
9882 SMESHDS_Mesh* meshDS = GetMeshDS();
9884 while( theItr->more() )
9886 const SMDS_MeshElement* elem = theItr->next();
9888 if( elem && elem->IsQuadratic())
9890 int id = elem->GetID();
9891 int nbCornerNodes = elem->NbCornerNodes();
9892 SMDSAbs_ElementType aType = elem->GetType();
9894 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9896 //remove a quadratic element
9897 if ( !theSm || !theSm->Contains( elem ))
9898 theSm = meshDS->MeshElements( elem->getshapeId() );
9899 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9901 // remove medium nodes
9902 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9903 if ( nodes[i]->NbInverseElements() == 0 )
9904 meshDS->RemoveFreeNode( nodes[i], theSm );
9906 // add a linear element
9907 nodes.resize( nbCornerNodes );
9908 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9909 ReplaceElemInGroups(elem, newElem, meshDS);
9910 if( theSm && newElem )
9911 theSm->AddElement( newElem );
9917 //=======================================================================
9918 //function : ConvertFromQuadratic
9920 //=======================================================================
9922 bool SMESH_MeshEditor::ConvertFromQuadratic()
9924 int nbCheckedElems = 0;
9925 if ( myMesh->HasShapeToMesh() )
9927 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9929 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9930 while ( smIt->more() ) {
9931 SMESH_subMesh* sm = smIt->next();
9932 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9933 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9939 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9940 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9942 SMESHDS_SubMesh *aSM = 0;
9943 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9951 //================================================================================
9953 * \brief Return true if all medium nodes of the element are in the node set
9955 //================================================================================
9957 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9959 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9960 if ( !nodeSet.count( elem->GetNode(i) ))
9966 //================================================================================
9968 * \brief Makes given elements linear
9970 //================================================================================
9972 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9974 if ( theElements.empty() ) return;
9976 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9977 set<int> mediumNodeIDs;
9978 TIDSortedElemSet::iterator eIt = theElements.begin();
9979 for ( ; eIt != theElements.end(); ++eIt )
9981 const SMDS_MeshElement* e = *eIt;
9982 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9983 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9986 // replace given elements by linear ones
9987 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9988 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9989 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9991 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9992 // except those elements sharing medium nodes of quadratic element whose medium nodes
9993 // are not all in mediumNodeIDs
9995 // get remaining medium nodes
9996 TIDSortedNodeSet mediumNodes;
9997 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9998 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9999 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10000 mediumNodes.insert( mediumNodes.end(), n );
10002 // find more quadratic elements to convert
10003 TIDSortedElemSet moreElemsToConvert;
10004 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10005 for ( ; nIt != mediumNodes.end(); ++nIt )
10007 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10008 while ( invIt->more() )
10010 const SMDS_MeshElement* e = invIt->next();
10011 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10013 // find a more complex element including e and
10014 // whose medium nodes are not in mediumNodes
10015 bool complexFound = false;
10016 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10018 SMDS_ElemIteratorPtr invIt2 =
10019 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10020 while ( invIt2->more() )
10022 const SMDS_MeshElement* eComplex = invIt2->next();
10023 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10025 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10026 if ( nbCommonNodes == e->NbNodes())
10028 complexFound = true;
10029 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10035 if ( !complexFound )
10036 moreElemsToConvert.insert( e );
10040 elemIt = SMDS_ElemIteratorPtr
10041 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10042 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10045 //=======================================================================
10046 //function : SewSideElements
10048 //=======================================================================
10050 SMESH_MeshEditor::Sew_Error
10051 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10052 TIDSortedElemSet& theSide2,
10053 const SMDS_MeshNode* theFirstNode1,
10054 const SMDS_MeshNode* theFirstNode2,
10055 const SMDS_MeshNode* theSecondNode1,
10056 const SMDS_MeshNode* theSecondNode2)
10058 myLastCreatedElems.Clear();
10059 myLastCreatedNodes.Clear();
10061 MESSAGE ("::::SewSideElements()");
10062 if ( theSide1.size() != theSide2.size() )
10063 return SEW_DIFF_NB_OF_ELEMENTS;
10065 Sew_Error aResult = SEW_OK;
10067 // 1. Build set of faces representing each side
10068 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10069 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10071 // =======================================================================
10072 // 1. Build set of faces representing each side:
10073 // =======================================================================
10074 // a. build set of nodes belonging to faces
10075 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10076 // c. create temporary faces representing side of volumes if correspondent
10077 // face does not exist
10079 SMESHDS_Mesh* aMesh = GetMeshDS();
10080 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10081 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10082 TIDSortedElemSet faceSet1, faceSet2;
10083 set<const SMDS_MeshElement*> volSet1, volSet2;
10084 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10085 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10086 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10087 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10088 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10089 int iSide, iFace, iNode;
10091 list<const SMDS_MeshElement* > tempFaceList;
10092 for ( iSide = 0; iSide < 2; iSide++ ) {
10093 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10094 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10095 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10096 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10097 set<const SMDS_MeshElement*>::iterator vIt;
10098 TIDSortedElemSet::iterator eIt;
10099 set<const SMDS_MeshNode*>::iterator nIt;
10101 // check that given nodes belong to given elements
10102 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10103 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10104 int firstIndex = -1, secondIndex = -1;
10105 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10106 const SMDS_MeshElement* elem = *eIt;
10107 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10108 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10109 if ( firstIndex > -1 && secondIndex > -1 ) break;
10111 if ( firstIndex < 0 || secondIndex < 0 ) {
10112 // we can simply return until temporary faces created
10113 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10116 // -----------------------------------------------------------
10117 // 1a. Collect nodes of existing faces
10118 // and build set of face nodes in order to detect missing
10119 // faces corresponding to sides of volumes
10120 // -----------------------------------------------------------
10122 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10124 // loop on the given element of a side
10125 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10126 //const SMDS_MeshElement* elem = *eIt;
10127 const SMDS_MeshElement* elem = *eIt;
10128 if ( elem->GetType() == SMDSAbs_Face ) {
10129 faceSet->insert( elem );
10130 set <const SMDS_MeshNode*> faceNodeSet;
10131 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10132 while ( nodeIt->more() ) {
10133 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10134 nodeSet->insert( n );
10135 faceNodeSet.insert( n );
10137 setOfFaceNodeSet.insert( faceNodeSet );
10139 else if ( elem->GetType() == SMDSAbs_Volume )
10140 volSet->insert( elem );
10142 // ------------------------------------------------------------------------------
10143 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10144 // ------------------------------------------------------------------------------
10146 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10147 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10148 while ( fIt->more() ) { // loop on faces sharing a node
10149 const SMDS_MeshElement* f = fIt->next();
10150 if ( faceSet->find( f ) == faceSet->end() ) {
10151 // check if all nodes are in nodeSet and
10152 // complete setOfFaceNodeSet if they are
10153 set <const SMDS_MeshNode*> faceNodeSet;
10154 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10155 bool allInSet = true;
10156 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10157 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10158 if ( nodeSet->find( n ) == nodeSet->end() )
10161 faceNodeSet.insert( n );
10164 faceSet->insert( f );
10165 setOfFaceNodeSet.insert( faceNodeSet );
10171 // -------------------------------------------------------------------------
10172 // 1c. Create temporary faces representing sides of volumes if correspondent
10173 // face does not exist
10174 // -------------------------------------------------------------------------
10176 if ( !volSet->empty() ) {
10177 //int nodeSetSize = nodeSet->size();
10179 // loop on given volumes
10180 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10181 SMDS_VolumeTool vol (*vIt);
10182 // loop on volume faces: find free faces
10183 // --------------------------------------
10184 list<const SMDS_MeshElement* > freeFaceList;
10185 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10186 if ( !vol.IsFreeFace( iFace ))
10188 // check if there is already a face with same nodes in a face set
10189 const SMDS_MeshElement* aFreeFace = 0;
10190 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10191 int nbNodes = vol.NbFaceNodes( iFace );
10192 set <const SMDS_MeshNode*> faceNodeSet;
10193 vol.GetFaceNodes( iFace, faceNodeSet );
10194 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10196 // no such a face is given but it still can exist, check it
10197 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10198 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10200 if ( !aFreeFace ) {
10201 // create a temporary face
10202 if ( nbNodes == 3 ) {
10203 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10204 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10206 else if ( nbNodes == 4 ) {
10207 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10208 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10211 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10212 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10213 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10216 tempFaceList.push_back( aFreeFace );
10220 freeFaceList.push_back( aFreeFace );
10222 } // loop on faces of a volume
10224 // choose one of several free faces of a volume
10225 // --------------------------------------------
10226 if ( freeFaceList.size() > 1 ) {
10227 // choose a face having max nb of nodes shared by other elems of a side
10228 int maxNbNodes = -1;
10229 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10230 while ( fIt != freeFaceList.end() ) { // loop on free faces
10231 int nbSharedNodes = 0;
10232 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10233 while ( nodeIt->more() ) { // loop on free face nodes
10234 const SMDS_MeshNode* n =
10235 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10236 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10237 while ( invElemIt->more() ) {
10238 const SMDS_MeshElement* e = invElemIt->next();
10239 nbSharedNodes += faceSet->count( e );
10240 nbSharedNodes += elemSet->count( e );
10243 if ( nbSharedNodes > maxNbNodes ) {
10244 maxNbNodes = nbSharedNodes;
10245 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10247 else if ( nbSharedNodes == maxNbNodes ) {
10251 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10254 if ( freeFaceList.size() > 1 )
10256 // could not choose one face, use another way
10257 // choose a face most close to the bary center of the opposite side
10258 gp_XYZ aBC( 0., 0., 0. );
10259 set <const SMDS_MeshNode*> addedNodes;
10260 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10261 eIt = elemSet2->begin();
10262 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10263 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10264 while ( nodeIt->more() ) { // loop on free face nodes
10265 const SMDS_MeshNode* n =
10266 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10267 if ( addedNodes.insert( n ).second )
10268 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10271 aBC /= addedNodes.size();
10272 double minDist = DBL_MAX;
10273 fIt = freeFaceList.begin();
10274 while ( fIt != freeFaceList.end() ) { // loop on free faces
10276 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10277 while ( nodeIt->more() ) { // loop on free face nodes
10278 const SMDS_MeshNode* n =
10279 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10280 gp_XYZ p( n->X(),n->Y(),n->Z() );
10281 dist += ( aBC - p ).SquareModulus();
10283 if ( dist < minDist ) {
10285 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10288 fIt = freeFaceList.erase( fIt++ );
10291 } // choose one of several free faces of a volume
10293 if ( freeFaceList.size() == 1 ) {
10294 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10295 faceSet->insert( aFreeFace );
10296 // complete a node set with nodes of a found free face
10297 // for ( iNode = 0; iNode < ; iNode++ )
10298 // nodeSet->insert( fNodes[ iNode ] );
10301 } // loop on volumes of a side
10303 // // complete a set of faces if new nodes in a nodeSet appeared
10304 // // ----------------------------------------------------------
10305 // if ( nodeSetSize != nodeSet->size() ) {
10306 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10307 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10308 // while ( fIt->more() ) { // loop on faces sharing a node
10309 // const SMDS_MeshElement* f = fIt->next();
10310 // if ( faceSet->find( f ) == faceSet->end() ) {
10311 // // check if all nodes are in nodeSet and
10312 // // complete setOfFaceNodeSet if they are
10313 // set <const SMDS_MeshNode*> faceNodeSet;
10314 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10315 // bool allInSet = true;
10316 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10317 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10318 // if ( nodeSet->find( n ) == nodeSet->end() )
10319 // allInSet = false;
10321 // faceNodeSet.insert( n );
10323 // if ( allInSet ) {
10324 // faceSet->insert( f );
10325 // setOfFaceNodeSet.insert( faceNodeSet );
10331 } // Create temporary faces, if there are volumes given
10334 if ( faceSet1.size() != faceSet2.size() ) {
10335 // delete temporary faces: they are in reverseElements of actual nodes
10336 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10337 // while ( tmpFaceIt->more() )
10338 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10339 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10340 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10341 // aMesh->RemoveElement(*tmpFaceIt);
10342 MESSAGE("Diff nb of faces");
10343 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10346 // ============================================================
10347 // 2. Find nodes to merge:
10348 // bind a node to remove to a node to put instead
10349 // ============================================================
10351 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10352 if ( theFirstNode1 != theFirstNode2 )
10353 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10354 if ( theSecondNode1 != theSecondNode2 )
10355 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10357 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10358 set< long > linkIdSet; // links to process
10359 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10361 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10362 list< NLink > linkList[2];
10363 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10364 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10365 // loop on links in linkList; find faces by links and append links
10366 // of the found faces to linkList
10367 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10368 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10370 NLink link[] = { *linkIt[0], *linkIt[1] };
10371 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10372 if ( !linkIdSet.count( linkID ) )
10375 // by links, find faces in the face sets,
10376 // and find indices of link nodes in the found faces;
10377 // in a face set, there is only one or no face sharing a link
10378 // ---------------------------------------------------------------
10380 const SMDS_MeshElement* face[] = { 0, 0 };
10381 vector<const SMDS_MeshNode*> fnodes[2];
10382 int iLinkNode[2][2];
10383 TIDSortedElemSet avoidSet;
10384 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10385 const SMDS_MeshNode* n1 = link[iSide].first;
10386 const SMDS_MeshNode* n2 = link[iSide].second;
10387 //cout << "Side " << iSide << " ";
10388 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10389 // find a face by two link nodes
10390 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10391 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10392 if ( face[ iSide ])
10394 //cout << " F " << face[ iSide]->GetID() <<endl;
10395 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10396 // put face nodes to fnodes
10397 if ( face[ iSide ]->IsQuadratic() )
10399 // use interlaced nodes iterator
10400 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10401 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10402 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10403 while ( nIter->more() )
10404 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10408 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10409 face[ iSide ]->end_nodes() );
10411 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10415 // check similarity of elements of the sides
10416 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10417 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10418 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10419 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10422 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10424 break; // do not return because it's necessary to remove tmp faces
10427 // set nodes to merge
10428 // -------------------
10430 if ( face[0] && face[1] ) {
10431 const int nbNodes = face[0]->NbNodes();
10432 if ( nbNodes != face[1]->NbNodes() ) {
10433 MESSAGE("Diff nb of face nodes");
10434 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10435 break; // do not return because it s necessary to remove tmp faces
10437 bool reverse[] = { false, false }; // order of nodes in the link
10438 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10439 // analyse link orientation in faces
10440 int i1 = iLinkNode[ iSide ][ 0 ];
10441 int i2 = iLinkNode[ iSide ][ 1 ];
10442 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10444 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10445 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10446 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10448 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10449 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10452 // add other links of the faces to linkList
10453 // -----------------------------------------
10455 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10456 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10457 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10458 if ( !iter_isnew.second ) { // already in a set: no need to process
10459 linkIdSet.erase( iter_isnew.first );
10461 else // new in set == encountered for the first time: add
10463 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10464 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10465 linkList[0].push_back ( NLink( n1, n2 ));
10466 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10471 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10474 } // loop on link lists
10476 if ( aResult == SEW_OK &&
10477 ( //linkIt[0] != linkList[0].end() ||
10478 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10479 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10480 " " << (faceSetPtr[1]->empty()));
10481 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10484 // ====================================================================
10485 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10486 // ====================================================================
10488 // delete temporary faces
10489 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10490 // while ( tmpFaceIt->more() )
10491 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10492 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10493 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10494 aMesh->RemoveElement(*tmpFaceIt);
10496 if ( aResult != SEW_OK)
10499 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10500 // loop on nodes replacement map
10501 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10502 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10503 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10504 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10505 nodeIDsToRemove.push_back( nToRemove->GetID() );
10506 // loop on elements sharing nToRemove
10507 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10508 while ( invElemIt->more() ) {
10509 const SMDS_MeshElement* e = invElemIt->next();
10510 // get a new suite of nodes: make replacement
10511 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10512 vector< const SMDS_MeshNode*> nodes( nbNodes );
10513 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10514 while ( nIt->more() ) {
10515 const SMDS_MeshNode* n =
10516 static_cast<const SMDS_MeshNode*>( nIt->next() );
10517 nnIt = nReplaceMap.find( n );
10518 if ( nnIt != nReplaceMap.end() ) {
10520 n = (*nnIt).second;
10524 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10525 // elemIDsToRemove.push_back( e->GetID() );
10529 SMDSAbs_ElementType etyp = e->GetType();
10530 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10533 myLastCreatedElems.Append(newElem);
10534 AddToSameGroups(newElem, e, aMesh);
10535 int aShapeId = e->getshapeId();
10538 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10541 aMesh->RemoveElement(e);
10546 Remove( nodeIDsToRemove, true );
10551 //================================================================================
10553 * \brief Find corresponding nodes in two sets of faces
10554 * \param theSide1 - first face set
10555 * \param theSide2 - second first face
10556 * \param theFirstNode1 - a boundary node of set 1
10557 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10558 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10559 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10560 * \param nReplaceMap - output map of corresponding nodes
10561 * \return bool - is a success or not
10563 //================================================================================
10566 //#define DEBUG_MATCHING_NODES
10569 SMESH_MeshEditor::Sew_Error
10570 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10571 set<const SMDS_MeshElement*>& theSide2,
10572 const SMDS_MeshNode* theFirstNode1,
10573 const SMDS_MeshNode* theFirstNode2,
10574 const SMDS_MeshNode* theSecondNode1,
10575 const SMDS_MeshNode* theSecondNode2,
10576 TNodeNodeMap & nReplaceMap)
10578 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10580 nReplaceMap.clear();
10581 if ( theFirstNode1 != theFirstNode2 )
10582 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10583 if ( theSecondNode1 != theSecondNode2 )
10584 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10586 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10587 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10589 list< NLink > linkList[2];
10590 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10591 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10593 // loop on links in linkList; find faces by links and append links
10594 // of the found faces to linkList
10595 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10596 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10597 NLink link[] = { *linkIt[0], *linkIt[1] };
10598 if ( linkSet.find( link[0] ) == linkSet.end() )
10601 // by links, find faces in the face sets,
10602 // and find indices of link nodes in the found faces;
10603 // in a face set, there is only one or no face sharing a link
10604 // ---------------------------------------------------------------
10606 const SMDS_MeshElement* face[] = { 0, 0 };
10607 list<const SMDS_MeshNode*> notLinkNodes[2];
10608 //bool reverse[] = { false, false }; // order of notLinkNodes
10610 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10612 const SMDS_MeshNode* n1 = link[iSide].first;
10613 const SMDS_MeshNode* n2 = link[iSide].second;
10614 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10615 set< const SMDS_MeshElement* > facesOfNode1;
10616 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10618 // during a loop of the first node, we find all faces around n1,
10619 // during a loop of the second node, we find one face sharing both n1 and n2
10620 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10621 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10622 while ( fIt->more() ) { // loop on faces sharing a node
10623 const SMDS_MeshElement* f = fIt->next();
10624 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10625 ! facesOfNode1.insert( f ).second ) // f encounters twice
10627 if ( face[ iSide ] ) {
10628 MESSAGE( "2 faces per link " );
10629 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10632 faceSet->erase( f );
10634 // get not link nodes
10635 int nbN = f->NbNodes();
10636 if ( f->IsQuadratic() )
10638 nbNodes[ iSide ] = nbN;
10639 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10640 int i1 = f->GetNodeIndex( n1 );
10641 int i2 = f->GetNodeIndex( n2 );
10642 int iEnd = nbN, iBeg = -1, iDelta = 1;
10643 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10645 std::swap( iEnd, iBeg ); iDelta = -1;
10650 if ( i == iEnd ) i = iBeg + iDelta;
10651 if ( i == i1 ) break;
10652 nodes.push_back ( f->GetNode( i ) );
10658 // check similarity of elements of the sides
10659 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10660 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10661 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10662 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10665 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10669 // set nodes to merge
10670 // -------------------
10672 if ( face[0] && face[1] ) {
10673 if ( nbNodes[0] != nbNodes[1] ) {
10674 MESSAGE("Diff nb of face nodes");
10675 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10677 #ifdef DEBUG_MATCHING_NODES
10678 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10679 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10680 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10682 int nbN = nbNodes[0];
10684 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10685 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10686 for ( int i = 0 ; i < nbN - 2; ++i ) {
10687 #ifdef DEBUG_MATCHING_NODES
10688 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10690 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10694 // add other links of the face 1 to linkList
10695 // -----------------------------------------
10697 const SMDS_MeshElement* f0 = face[0];
10698 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10699 for ( int i = 0; i < nbN; i++ )
10701 const SMDS_MeshNode* n2 = f0->GetNode( i );
10702 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10703 linkSet.insert( SMESH_TLink( n1, n2 ));
10704 if ( !iter_isnew.second ) { // already in a set: no need to process
10705 linkSet.erase( iter_isnew.first );
10707 else // new in set == encountered for the first time: add
10709 #ifdef DEBUG_MATCHING_NODES
10710 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10711 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10713 linkList[0].push_back ( NLink( n1, n2 ));
10714 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10719 } // loop on link lists
10724 //================================================================================
10726 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10727 \param theElems - the list of elements (edges or faces) to be replicated
10728 The nodes for duplication could be found from these elements
10729 \param theNodesNot - list of nodes to NOT replicate
10730 \param theAffectedElems - the list of elements (cells and edges) to which the
10731 replicated nodes should be associated to.
10732 \return TRUE if operation has been completed successfully, FALSE otherwise
10734 //================================================================================
10736 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10737 const TIDSortedElemSet& theNodesNot,
10738 const TIDSortedElemSet& theAffectedElems )
10740 myLastCreatedElems.Clear();
10741 myLastCreatedNodes.Clear();
10743 if ( theElems.size() == 0 )
10746 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10751 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10752 // duplicate elements and nodes
10753 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10754 // replce nodes by duplications
10755 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10759 //================================================================================
10761 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10762 \param theMeshDS - mesh instance
10763 \param theElems - the elements replicated or modified (nodes should be changed)
10764 \param theNodesNot - nodes to NOT replicate
10765 \param theNodeNodeMap - relation of old node to new created node
10766 \param theIsDoubleElem - flag os to replicate element or modify
10767 \return TRUE if operation has been completed successfully, FALSE otherwise
10769 //================================================================================
10771 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10772 const TIDSortedElemSet& theElems,
10773 const TIDSortedElemSet& theNodesNot,
10774 std::map< const SMDS_MeshNode*,
10775 const SMDS_MeshNode* >& theNodeNodeMap,
10776 const bool theIsDoubleElem )
10778 MESSAGE("doubleNodes");
10779 // iterate on through element and duplicate them (by nodes duplication)
10781 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10782 for ( ; elemItr != theElems.end(); ++elemItr )
10784 const SMDS_MeshElement* anElem = *elemItr;
10788 bool isDuplicate = false;
10789 // duplicate nodes to duplicate element
10790 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10791 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10793 while ( anIter->more() )
10796 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10797 SMDS_MeshNode* aNewNode = aCurrNode;
10798 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10799 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10800 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10803 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10804 theNodeNodeMap[ aCurrNode ] = aNewNode;
10805 myLastCreatedNodes.Append( aNewNode );
10807 isDuplicate |= (aCurrNode != aNewNode);
10808 newNodes[ ind++ ] = aNewNode;
10810 if ( !isDuplicate )
10813 if ( theIsDoubleElem )
10814 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10817 MESSAGE("ChangeElementNodes");
10818 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10825 //================================================================================
10827 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10828 \param theNodes - identifiers of nodes to be doubled
10829 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10830 nodes. If list of element identifiers is empty then nodes are doubled but
10831 they not assigned to elements
10832 \return TRUE if operation has been completed successfully, FALSE otherwise
10834 //================================================================================
10836 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10837 const std::list< int >& theListOfModifiedElems )
10839 MESSAGE("DoubleNodes");
10840 myLastCreatedElems.Clear();
10841 myLastCreatedNodes.Clear();
10843 if ( theListOfNodes.size() == 0 )
10846 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10850 // iterate through nodes and duplicate them
10852 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10854 std::list< int >::const_iterator aNodeIter;
10855 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10857 int aCurr = *aNodeIter;
10858 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10864 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10867 anOldNodeToNewNode[ aNode ] = aNewNode;
10868 myLastCreatedNodes.Append( aNewNode );
10872 // Create map of new nodes for modified elements
10874 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10876 std::list< int >::const_iterator anElemIter;
10877 for ( anElemIter = theListOfModifiedElems.begin();
10878 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10880 int aCurr = *anElemIter;
10881 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10885 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10887 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10889 while ( anIter->more() )
10891 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10892 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10894 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10895 aNodeArr[ ind++ ] = aNewNode;
10898 aNodeArr[ ind++ ] = aCurrNode;
10900 anElemToNodes[ anElem ] = aNodeArr;
10903 // Change nodes of elements
10905 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10906 anElemToNodesIter = anElemToNodes.begin();
10907 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10909 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10910 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10913 MESSAGE("ChangeElementNodes");
10914 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10923 //================================================================================
10925 \brief Check if element located inside shape
10926 \return TRUE if IN or ON shape, FALSE otherwise
10928 //================================================================================
10930 template<class Classifier>
10931 bool isInside(const SMDS_MeshElement* theElem,
10932 Classifier& theClassifier,
10933 const double theTol)
10935 gp_XYZ centerXYZ (0, 0, 0);
10936 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10937 while (aNodeItr->more())
10938 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10940 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10941 theClassifier.Perform(aPnt, theTol);
10942 TopAbs_State aState = theClassifier.State();
10943 return (aState == TopAbs_IN || aState == TopAbs_ON );
10946 //================================================================================
10948 * \brief Classifier of the 3D point on the TopoDS_Face
10949 * with interaface suitable for isInside()
10951 //================================================================================
10953 struct _FaceClassifier
10955 Extrema_ExtPS _extremum;
10956 BRepAdaptor_Surface _surface;
10957 TopAbs_State _state;
10959 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10961 _extremum.Initialize( _surface,
10962 _surface.FirstUParameter(), _surface.LastUParameter(),
10963 _surface.FirstVParameter(), _surface.LastVParameter(),
10964 _surface.Tolerance(), _surface.Tolerance() );
10966 void Perform(const gp_Pnt& aPnt, double theTol)
10968 _state = TopAbs_OUT;
10969 _extremum.Perform(aPnt);
10970 if ( _extremum.IsDone() )
10971 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10972 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10973 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10975 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10978 TopAbs_State State() const
10985 //================================================================================
10987 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10988 This method is the first step of DoubleNodeElemGroupsInRegion.
10989 \param theElems - list of groups of elements (edges or faces) to be replicated
10990 \param theNodesNot - list of groups of nodes not to replicated
10991 \param theShape - shape to detect affected elements (element which geometric center
10992 located on or inside shape).
10993 The replicated nodes should be associated to affected elements.
10994 \return groups of affected elements
10995 \sa DoubleNodeElemGroupsInRegion()
10997 //================================================================================
10999 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11000 const TIDSortedElemSet& theNodesNot,
11001 const TopoDS_Shape& theShape,
11002 TIDSortedElemSet& theAffectedElems)
11004 if ( theShape.IsNull() )
11007 const double aTol = Precision::Confusion();
11008 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11009 auto_ptr<_FaceClassifier> aFaceClassifier;
11010 if ( theShape.ShapeType() == TopAbs_SOLID )
11012 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11013 bsc3d->PerformInfinitePoint(aTol);
11015 else if (theShape.ShapeType() == TopAbs_FACE )
11017 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11020 // iterates on indicated elements and get elements by back references from their nodes
11021 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11022 for ( ; elemItr != theElems.end(); ++elemItr )
11024 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11028 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11029 while ( nodeItr->more() )
11031 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11032 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11034 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11035 while ( backElemItr->more() )
11037 const SMDS_MeshElement* curElem = backElemItr->next();
11038 if ( curElem && theElems.find(curElem) == theElems.end() &&
11040 isInside( curElem, *bsc3d, aTol ) :
11041 isInside( curElem, *aFaceClassifier, aTol )))
11042 theAffectedElems.insert( curElem );
11049 //================================================================================
11051 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11052 \param theElems - group of of elements (edges or faces) to be replicated
11053 \param theNodesNot - group of nodes not to replicate
11054 \param theShape - shape to detect affected elements (element which geometric center
11055 located on or inside shape).
11056 The replicated nodes should be associated to affected elements.
11057 \return TRUE if operation has been completed successfully, FALSE otherwise
11059 //================================================================================
11061 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11062 const TIDSortedElemSet& theNodesNot,
11063 const TopoDS_Shape& theShape )
11065 if ( theShape.IsNull() )
11068 const double aTol = Precision::Confusion();
11069 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11070 auto_ptr<_FaceClassifier> aFaceClassifier;
11071 if ( theShape.ShapeType() == TopAbs_SOLID )
11073 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11074 bsc3d->PerformInfinitePoint(aTol);
11076 else if (theShape.ShapeType() == TopAbs_FACE )
11078 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11081 // iterates on indicated elements and get elements by back references from their nodes
11082 TIDSortedElemSet anAffected;
11083 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11084 for ( ; elemItr != theElems.end(); ++elemItr )
11086 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11090 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11091 while ( nodeItr->more() )
11093 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11094 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11096 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11097 while ( backElemItr->more() )
11099 const SMDS_MeshElement* curElem = backElemItr->next();
11100 if ( curElem && theElems.find(curElem) == theElems.end() &&
11102 isInside( curElem, *bsc3d, aTol ) :
11103 isInside( curElem, *aFaceClassifier, aTol )))
11104 anAffected.insert( curElem );
11108 return DoubleNodes( theElems, theNodesNot, anAffected );
11112 * \brief compute an oriented angle between two planes defined by four points.
11113 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11114 * @param p0 base of the rotation axe
11115 * @param p1 extremity of the rotation axe
11116 * @param g1 belongs to the first plane
11117 * @param g2 belongs to the second plane
11119 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11121 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11122 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11123 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11124 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11125 gp_Vec vref(p0, p1);
11128 gp_Vec n1 = vref.Crossed(v1);
11129 gp_Vec n2 = vref.Crossed(v2);
11130 return n2.AngleWithRef(n1, vref);
11134 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11135 * The list of groups must describe a partition of the mesh volumes.
11136 * The nodes of the internal faces at the boundaries of the groups are doubled.
11137 * In option, the internal faces are replaced by flat elements.
11138 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11139 * The flat elements are stored in groups of volumes.
11140 * @param theElems - list of groups of volumes, where a group of volume is a set of
11141 * SMDS_MeshElements sorted by Id.
11142 * @param createJointElems - if TRUE, create the elements
11143 * @return TRUE if operation has been completed successfully, FALSE otherwise
11145 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11146 bool createJointElems)
11148 MESSAGE("----------------------------------------------");
11149 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11150 MESSAGE("----------------------------------------------");
11152 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11153 meshDS->BuildDownWardConnectivity(true);
11155 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11157 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11158 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11159 // build the list of nodes shared by 2 or more domains, with their domain indexes
11161 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11162 std::map<int,int>celldom; // cell vtkId --> domain
11163 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11164 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11165 faceDomains.clear();
11167 cellDomains.clear();
11168 nodeDomains.clear();
11169 std::map<int,int> emptyMap;
11170 std::set<int> emptySet;
11173 for (int idom = 0; idom < theElems.size(); idom++)
11176 // --- build a map (face to duplicate --> volume to modify)
11177 // with all the faces shared by 2 domains (group of elements)
11178 // and corresponding volume of this domain, for each shared face.
11179 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11181 //MESSAGE("Domain " << idom);
11182 const TIDSortedElemSet& domain = theElems[idom];
11183 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11184 for (; elemItr != domain.end(); ++elemItr)
11186 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11189 int vtkId = anElem->getVtkId();
11190 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11191 int neighborsVtkIds[NBMAXNEIGHBORS];
11192 int downIds[NBMAXNEIGHBORS];
11193 unsigned char downTypes[NBMAXNEIGHBORS];
11194 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11195 for (int n = 0; n < nbNeighbors; n++)
11197 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11198 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11199 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11201 DownIdType face(downIds[n], downTypes[n]);
11202 if (!faceDomains.count(face))
11203 faceDomains[face] = emptyMap; // create an empty entry for face
11204 if (!faceDomains[face].count(idom))
11206 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11207 celldom[vtkId] = idom;
11208 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11215 //MESSAGE("Number of shared faces " << faceDomains.size());
11216 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11218 // --- explore the shared faces domain by domain,
11219 // explore the nodes of the face and see if they belong to a cell in the domain,
11220 // which has only a node or an edge on the border (not a shared face)
11222 for (int idomain = 0; idomain < theElems.size(); idomain++)
11224 //MESSAGE("Domain " << idomain);
11225 const TIDSortedElemSet& domain = theElems[idomain];
11226 itface = faceDomains.begin();
11227 for (; itface != faceDomains.end(); ++itface)
11229 std::map<int, int> domvol = itface->second;
11230 if (!domvol.count(idomain))
11232 DownIdType face = itface->first;
11233 //MESSAGE(" --- face " << face.cellId);
11234 std::set<int> oldNodes;
11236 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11237 std::set<int>::iterator itn = oldNodes.begin();
11238 for (; itn != oldNodes.end(); ++itn)
11241 //MESSAGE(" node " << oldId);
11242 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11243 for (int i=0; i<l.ncells; i++)
11245 int vtkId = l.cells[i];
11246 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11247 if (!domain.count(anElem))
11249 int vtkType = grid->GetCellType(vtkId);
11250 int downId = grid->CellIdToDownId(vtkId);
11253 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11254 continue; // not OK at this stage of the algorithm:
11255 //no cells created after BuildDownWardConnectivity
11257 DownIdType aCell(downId, vtkType);
11258 if (!cellDomains.count(aCell))
11259 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11260 cellDomains[aCell][idomain] = vtkId;
11261 celldom[vtkId] = idomain;
11262 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11268 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11269 // for each shared face, get the nodes
11270 // for each node, for each domain of the face, create a clone of the node
11272 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11273 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11274 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11276 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11277 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11278 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11280 for (int idomain = 0; idomain < theElems.size(); idomain++)
11282 itface = faceDomains.begin();
11283 for (; itface != faceDomains.end(); ++itface)
11285 std::map<int, int> domvol = itface->second;
11286 if (!domvol.count(idomain))
11288 DownIdType face = itface->first;
11289 //MESSAGE(" --- face " << face.cellId);
11290 std::set<int> oldNodes;
11292 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11293 std::set<int>::iterator itn = oldNodes.begin();
11294 for (; itn != oldNodes.end(); ++itn)
11297 //MESSAGE("-+-+-a node " << oldId);
11298 if (!nodeDomains.count(oldId))
11299 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11300 if (nodeDomains[oldId].empty())
11302 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11303 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11305 std::map<int, int>::iterator itdom = domvol.begin();
11306 for (; itdom != domvol.end(); ++itdom)
11308 int idom = itdom->first;
11309 //MESSAGE(" domain " << idom);
11310 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11312 if (nodeDomains[oldId].size() >= 2) // a multiple node
11314 vector<int> orderedDoms;
11315 //MESSAGE("multiple node " << oldId);
11316 if (mutipleNodes.count(oldId))
11317 orderedDoms = mutipleNodes[oldId];
11320 map<int,int>::iterator it = nodeDomains[oldId].begin();
11321 for (; it != nodeDomains[oldId].end(); ++it)
11322 orderedDoms.push_back(it->first);
11324 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11325 //stringstream txt;
11326 //for (int i=0; i<orderedDoms.size(); i++)
11327 // txt << orderedDoms[i] << " ";
11328 //MESSAGE("orderedDoms " << txt.str());
11329 mutipleNodes[oldId] = orderedDoms;
11331 double *coords = grid->GetPoint(oldId);
11332 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11333 int newId = newNode->getVtkId();
11334 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11335 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11342 for (int idomain = 0; idomain < theElems.size(); idomain++)
11344 itface = faceDomains.begin();
11345 for (; itface != faceDomains.end(); ++itface)
11347 std::map<int, int> domvol = itface->second;
11348 if (!domvol.count(idomain))
11350 DownIdType face = itface->first;
11351 //MESSAGE(" --- face " << face.cellId);
11352 std::set<int> oldNodes;
11354 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11355 int nbMultipleNodes = 0;
11356 std::set<int>::iterator itn = oldNodes.begin();
11357 for (; itn != oldNodes.end(); ++itn)
11360 if (mutipleNodes.count(oldId))
11363 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11365 //MESSAGE("multiple Nodes detected on a shared face");
11366 int downId = itface->first.cellId;
11367 unsigned char cellType = itface->first.cellType;
11368 // --- shared edge or shared face ?
11369 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11372 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11373 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11374 if (mutipleNodes.count(nodes[i]))
11375 if (!mutipleNodesToFace.count(nodes[i]))
11376 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11378 else // shared face (between two volumes)
11380 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11381 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11382 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11383 for (int ie =0; ie < nbEdges; ie++)
11386 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11387 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11389 vector<int> vn0 = mutipleNodes[nodes[0]];
11390 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11392 for (int i0 = 0; i0 < vn0.size(); i0++)
11393 for (int i1 = 0; i1 < vn1.size(); i1++)
11394 if (vn0[i0] == vn1[i1])
11395 doms.push_back(vn0[i0]);
11396 if (doms.size() >2)
11398 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11399 double *coords = grid->GetPoint(nodes[0]);
11400 gp_Pnt p0(coords[0], coords[1], coords[2]);
11401 coords = grid->GetPoint(nodes[nbNodes - 1]);
11402 gp_Pnt p1(coords[0], coords[1], coords[2]);
11404 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11405 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11406 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11407 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11408 for (int id=0; id < doms.size(); id++)
11410 int idom = doms[id];
11411 for (int ivol=0; ivol<nbvol; ivol++)
11413 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11414 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11415 if (theElems[idom].count(elem))
11417 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11418 domvol[idom] = svol;
11419 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11421 vtkIdType npts = 0;
11422 vtkIdType* pts = 0;
11423 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11424 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11427 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11428 angleDom[idom] = 0;
11432 gp_Pnt g(values[0], values[1], values[2]);
11433 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11434 //MESSAGE(" angle=" << angleDom[idom]);
11440 map<double, int> sortedDom; // sort domains by angle
11441 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11442 sortedDom[ia->second] = ia->first;
11443 vector<int> vnodes;
11445 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11447 vdom.push_back(ib->second);
11448 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11450 for (int ino = 0; ino < nbNodes; ino++)
11451 vnodes.push_back(nodes[ino]);
11452 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11461 // --- iterate on shared faces (volumes to modify, face to extrude)
11462 // get node id's of the face (id SMDS = id VTK)
11463 // create flat element with old and new nodes if requested
11465 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11466 // (domain1 X domain2) = domain1 + MAXINT*domain2
11468 std::map<int, std::map<long,int> > nodeQuadDomains;
11469 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11471 if (createJointElems)
11474 string joints2DName = "joints2D";
11475 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11476 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11477 string joints3DName = "joints3D";
11478 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11479 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11481 itface = faceDomains.begin();
11482 for (; itface != faceDomains.end(); ++itface)
11484 DownIdType face = itface->first;
11485 std::set<int> oldNodes;
11486 std::set<int>::iterator itn;
11488 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11490 std::map<int, int> domvol = itface->second;
11491 std::map<int, int>::iterator itdom = domvol.begin();
11492 int dom1 = itdom->first;
11493 int vtkVolId = itdom->second;
11495 int dom2 = itdom->first;
11496 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11498 stringstream grpname;
11501 grpname << dom1 << "_" << dom2;
11503 grpname << dom2 << "_" << dom1;
11504 string namegrp = grpname.str();
11505 if (!mapOfJunctionGroups.count(namegrp))
11506 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11507 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11509 sgrp->Add(vol->GetID());
11510 if (vol->GetType() == SMDSAbs_Volume)
11511 joints3DGrp->Add(vol->GetID());
11512 else if (vol->GetType() == SMDSAbs_Face)
11513 joints2DGrp->Add(vol->GetID());
11517 // --- create volumes on multiple domain intersection if requested
11518 // iterate on mutipleNodesToFace
11519 // iterate on edgesMultiDomains
11521 if (createJointElems)
11523 // --- iterate on mutipleNodesToFace
11525 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11526 for (; itn != mutipleNodesToFace.end(); ++itn)
11528 int node = itn->first;
11529 vector<int> orderDom = itn->second;
11530 vector<vtkIdType> orderedNodes;
11531 for (int idom = 0; idom <orderDom.size(); idom++)
11532 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11533 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11535 stringstream grpname;
11537 grpname << 0 << "_" << 0;
11539 string namegrp = grpname.str();
11540 if (!mapOfJunctionGroups.count(namegrp))
11541 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11542 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11544 sgrp->Add(face->GetID());
11547 // --- iterate on edgesMultiDomains
11549 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11550 for (; ite != edgesMultiDomains.end(); ++ite)
11552 vector<int> nodes = ite->first;
11553 vector<int> orderDom = ite->second;
11554 vector<vtkIdType> orderedNodes;
11555 if (nodes.size() == 2)
11557 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11558 for (int ino=0; ino < nodes.size(); ino++)
11559 if (orderDom.size() == 3)
11560 for (int idom = 0; idom <orderDom.size(); idom++)
11561 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11563 for (int idom = orderDom.size()-1; idom >=0; idom--)
11564 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11565 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11568 string namegrp = "jointsMultiples";
11569 if (!mapOfJunctionGroups.count(namegrp))
11570 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11571 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11573 sgrp->Add(vol->GetID());
11577 INFOS("Quadratic multiple joints not implemented");
11578 // TODO quadratic nodes
11583 // --- list the explicit faces and edges of the mesh that need to be modified,
11584 // i.e. faces and edges built with one or more duplicated nodes.
11585 // associate these faces or edges to their corresponding domain.
11586 // only the first domain found is kept when a face or edge is shared
11588 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11589 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11590 faceOrEdgeDom.clear();
11593 for (int idomain = 0; idomain < theElems.size(); idomain++)
11595 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11596 for (; itnod != nodeDomains.end(); ++itnod)
11598 int oldId = itnod->first;
11599 //MESSAGE(" node " << oldId);
11600 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11601 for (int i = 0; i < l.ncells; i++)
11603 int vtkId = l.cells[i];
11604 int vtkType = grid->GetCellType(vtkId);
11605 int downId = grid->CellIdToDownId(vtkId);
11607 continue; // new cells: not to be modified
11608 DownIdType aCell(downId, vtkType);
11609 int volParents[1000];
11610 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11611 for (int j = 0; j < nbvol; j++)
11612 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11613 if (!feDom.count(vtkId))
11615 feDom[vtkId] = idomain;
11616 faceOrEdgeDom[aCell] = emptyMap;
11617 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11618 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11619 // << " type " << vtkType << " downId " << downId);
11625 // --- iterate on shared faces (volumes to modify, face to extrude)
11626 // get node id's of the face
11627 // replace old nodes by new nodes in volumes, and update inverse connectivity
11629 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11630 for (int m=0; m<3; m++)
11632 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11633 itface = (*amap).begin();
11634 for (; itface != (*amap).end(); ++itface)
11636 DownIdType face = itface->first;
11637 std::set<int> oldNodes;
11638 std::set<int>::iterator itn;
11640 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11641 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11642 std::map<int, int> localClonedNodeIds;
11644 std::map<int, int> domvol = itface->second;
11645 std::map<int, int>::iterator itdom = domvol.begin();
11646 for (; itdom != domvol.end(); ++itdom)
11648 int idom = itdom->first;
11649 int vtkVolId = itdom->second;
11650 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11651 localClonedNodeIds.clear();
11652 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11655 if (nodeDomains[oldId].count(idom))
11657 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11658 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11661 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11666 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11667 grid->BuildLinks();
11675 * \brief Double nodes on some external faces and create flat elements.
11676 * Flat elements are mainly used by some types of mechanic calculations.
11678 * Each group of the list must be constituted of faces.
11679 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11680 * @param theElems - list of groups of faces, where a group of faces is a set of
11681 * SMDS_MeshElements sorted by Id.
11682 * @return TRUE if operation has been completed successfully, FALSE otherwise
11684 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11686 MESSAGE("-------------------------------------------------");
11687 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11688 MESSAGE("-------------------------------------------------");
11690 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11692 // --- For each group of faces
11693 // duplicate the nodes, create a flat element based on the face
11694 // replace the nodes of the faces by their clones
11696 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11697 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11698 clonedNodes.clear();
11699 intermediateNodes.clear();
11700 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11701 mapOfJunctionGroups.clear();
11703 for (int idom = 0; idom < theElems.size(); idom++)
11705 const TIDSortedElemSet& domain = theElems[idom];
11706 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11707 for (; elemItr != domain.end(); ++elemItr)
11709 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11710 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11713 // MESSAGE("aFace=" << aFace->GetID());
11714 bool isQuad = aFace->IsQuadratic();
11715 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11717 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11719 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11720 while (nodeIt->more())
11722 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11723 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11725 ln2.push_back(node);
11727 ln0.push_back(node);
11729 const SMDS_MeshNode* clone = 0;
11730 if (!clonedNodes.count(node))
11732 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11733 clonedNodes[node] = clone;
11736 clone = clonedNodes[node];
11739 ln3.push_back(clone);
11741 ln1.push_back(clone);
11743 const SMDS_MeshNode* inter = 0;
11744 if (isQuad && (!isMedium))
11746 if (!intermediateNodes.count(node))
11748 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11749 intermediateNodes[node] = inter;
11752 inter = intermediateNodes[node];
11753 ln4.push_back(inter);
11757 // --- extrude the face
11759 vector<const SMDS_MeshNode*> ln;
11760 SMDS_MeshVolume* vol = 0;
11761 vtkIdType aType = aFace->GetVtkType();
11765 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11766 // MESSAGE("vol prism " << vol->GetID());
11767 ln.push_back(ln1[0]);
11768 ln.push_back(ln1[1]);
11769 ln.push_back(ln1[2]);
11772 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11773 // MESSAGE("vol hexa " << vol->GetID());
11774 ln.push_back(ln1[0]);
11775 ln.push_back(ln1[1]);
11776 ln.push_back(ln1[2]);
11777 ln.push_back(ln1[3]);
11779 case VTK_QUADRATIC_TRIANGLE:
11780 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11781 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11782 // MESSAGE("vol quad prism " << vol->GetID());
11783 ln.push_back(ln1[0]);
11784 ln.push_back(ln1[1]);
11785 ln.push_back(ln1[2]);
11786 ln.push_back(ln3[0]);
11787 ln.push_back(ln3[1]);
11788 ln.push_back(ln3[2]);
11790 case VTK_QUADRATIC_QUAD:
11791 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11792 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11793 // ln4[0], ln4[1], ln4[2], ln4[3]);
11794 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11795 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11796 ln4[0], ln4[1], ln4[2], ln4[3]);
11797 // MESSAGE("vol quad hexa " << vol->GetID());
11798 ln.push_back(ln1[0]);
11799 ln.push_back(ln1[1]);
11800 ln.push_back(ln1[2]);
11801 ln.push_back(ln1[3]);
11802 ln.push_back(ln3[0]);
11803 ln.push_back(ln3[1]);
11804 ln.push_back(ln3[2]);
11805 ln.push_back(ln3[3]);
11815 stringstream grpname;
11819 string namegrp = grpname.str();
11820 if (!mapOfJunctionGroups.count(namegrp))
11821 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11822 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11824 sgrp->Add(vol->GetID());
11827 // --- modify the face
11829 aFace->ChangeNodes(&ln[0], ln.size());
11836 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11837 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11838 * groups of faces to remove inside the object, (idem edges).
11839 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11841 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11842 const TopoDS_Shape& theShape,
11843 SMESH_NodeSearcher* theNodeSearcher,
11844 const char* groupName,
11845 std::vector<double>& nodesCoords,
11846 std::vector<std::vector<int> >& listOfListOfNodes)
11848 MESSAGE("--------------------------------");
11849 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11850 MESSAGE("--------------------------------");
11852 // --- zone of volumes to remove is given :
11853 // 1 either by a geom shape (one or more vertices) and a radius,
11854 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11855 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11856 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11857 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11858 // defined by it's name.
11860 SMESHDS_GroupBase* groupDS = 0;
11861 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11862 while ( groupIt->more() )
11865 SMESH_Group * group = groupIt->next();
11866 if ( !group ) continue;
11867 groupDS = group->GetGroupDS();
11868 if ( !groupDS || groupDS->IsEmpty() ) continue;
11869 std::string grpName = group->GetName();
11870 //MESSAGE("grpName=" << grpName);
11871 if (grpName == groupName)
11877 bool isNodeGroup = false;
11878 bool isNodeCoords = false;
11881 if (groupDS->GetType() != SMDSAbs_Node)
11883 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11886 if (nodesCoords.size() > 0)
11887 isNodeCoords = true; // a list o nodes given by their coordinates
11888 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11890 // --- define groups to build
11892 int idg; // --- group of SMDS volumes
11893 string grpvName = groupName;
11894 grpvName += "_vol";
11895 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11898 MESSAGE("group not created " << grpvName);
11901 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11903 int idgs; // --- group of SMDS faces on the skin
11904 string grpsName = groupName;
11905 grpsName += "_skin";
11906 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11909 MESSAGE("group not created " << grpsName);
11912 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11914 int idgi; // --- group of SMDS faces internal (several shapes)
11915 string grpiName = groupName;
11916 grpiName += "_internalFaces";
11917 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11920 MESSAGE("group not created " << grpiName);
11923 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11925 int idgei; // --- group of SMDS faces internal (several shapes)
11926 string grpeiName = groupName;
11927 grpeiName += "_internalEdges";
11928 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11931 MESSAGE("group not created " << grpeiName);
11934 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11936 // --- build downward connectivity
11938 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11939 meshDS->BuildDownWardConnectivity(true);
11940 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11942 // --- set of volumes detected inside
11944 std::set<int> setOfInsideVol;
11945 std::set<int> setOfVolToCheck;
11947 std::vector<gp_Pnt> gpnts;
11950 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11952 MESSAGE("group of nodes provided");
11953 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11954 while ( elemIt->more() )
11956 const SMDS_MeshElement* elem = elemIt->next();
11959 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11962 SMDS_MeshElement* vol = 0;
11963 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11964 while (volItr->more())
11966 vol = (SMDS_MeshElement*)volItr->next();
11967 setOfInsideVol.insert(vol->getVtkId());
11968 sgrp->Add(vol->GetID());
11972 else if (isNodeCoords)
11974 MESSAGE("list of nodes coordinates provided");
11977 while (i < nodesCoords.size()-2)
11979 double x = nodesCoords[i++];
11980 double y = nodesCoords[i++];
11981 double z = nodesCoords[i++];
11982 gp_Pnt p = gp_Pnt(x, y ,z);
11983 gpnts.push_back(p);
11984 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11987 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11989 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11990 TopTools_IndexedMapOfShape vertexMap;
11991 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11992 gp_Pnt p = gp_Pnt(0,0,0);
11993 if (vertexMap.Extent() < 1)
11996 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11998 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11999 p = BRep_Tool::Pnt(vertex);
12000 gpnts.push_back(p);
12001 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12005 if (gpnts.size() > 0)
12008 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12010 nodeId = startNode->GetID();
12011 MESSAGE("nodeId " << nodeId);
12013 double radius2 = radius*radius;
12014 MESSAGE("radius2 " << radius2);
12016 // --- volumes on start node
12018 setOfVolToCheck.clear();
12019 SMDS_MeshElement* startVol = 0;
12020 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12021 while (volItr->more())
12023 startVol = (SMDS_MeshElement*)volItr->next();
12024 setOfVolToCheck.insert(startVol->getVtkId());
12026 if (setOfVolToCheck.empty())
12028 MESSAGE("No volumes found");
12032 // --- starting with central volumes then their neighbors, check if they are inside
12033 // or outside the domain, until no more new neighbor volume is inside.
12034 // Fill the group of inside volumes
12036 std::map<int, double> mapOfNodeDistance2;
12037 mapOfNodeDistance2.clear();
12038 std::set<int> setOfOutsideVol;
12039 while (!setOfVolToCheck.empty())
12041 std::set<int>::iterator it = setOfVolToCheck.begin();
12043 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12044 bool volInside = false;
12045 vtkIdType npts = 0;
12046 vtkIdType* pts = 0;
12047 grid->GetCellPoints(vtkId, npts, pts);
12048 for (int i=0; i<npts; i++)
12050 double distance2 = 0;
12051 if (mapOfNodeDistance2.count(pts[i]))
12053 distance2 = mapOfNodeDistance2[pts[i]];
12054 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12058 double *coords = grid->GetPoint(pts[i]);
12059 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12061 for (int j=0; j<gpnts.size(); j++)
12063 double d2 = aPoint.SquareDistance(gpnts[j]);
12064 if (d2 < distance2)
12067 if (distance2 < radius2)
12071 mapOfNodeDistance2[pts[i]] = distance2;
12072 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12074 if (distance2 < radius2)
12076 volInside = true; // one or more nodes inside the domain
12077 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12083 setOfInsideVol.insert(vtkId);
12084 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12085 int neighborsVtkIds[NBMAXNEIGHBORS];
12086 int downIds[NBMAXNEIGHBORS];
12087 unsigned char downTypes[NBMAXNEIGHBORS];
12088 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12089 for (int n = 0; n < nbNeighbors; n++)
12090 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12091 setOfVolToCheck.insert(neighborsVtkIds[n]);
12095 setOfOutsideVol.insert(vtkId);
12096 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12098 setOfVolToCheck.erase(vtkId);
12102 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12103 // If yes, add the volume to the inside set
12105 bool addedInside = true;
12106 std::set<int> setOfVolToReCheck;
12107 while (addedInside)
12109 MESSAGE(" --------------------------- re check");
12110 addedInside = false;
12111 std::set<int>::iterator itv = setOfInsideVol.begin();
12112 for (; itv != setOfInsideVol.end(); ++itv)
12115 int neighborsVtkIds[NBMAXNEIGHBORS];
12116 int downIds[NBMAXNEIGHBORS];
12117 unsigned char downTypes[NBMAXNEIGHBORS];
12118 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12119 for (int n = 0; n < nbNeighbors; n++)
12120 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12121 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12123 setOfVolToCheck = setOfVolToReCheck;
12124 setOfVolToReCheck.clear();
12125 while (!setOfVolToCheck.empty())
12127 std::set<int>::iterator it = setOfVolToCheck.begin();
12129 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12131 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12132 int countInside = 0;
12133 int neighborsVtkIds[NBMAXNEIGHBORS];
12134 int downIds[NBMAXNEIGHBORS];
12135 unsigned char downTypes[NBMAXNEIGHBORS];
12136 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12137 for (int n = 0; n < nbNeighbors; n++)
12138 if (setOfInsideVol.count(neighborsVtkIds[n]))
12140 MESSAGE("countInside " << countInside);
12141 if (countInside > 1)
12143 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12144 setOfInsideVol.insert(vtkId);
12145 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12146 addedInside = true;
12149 setOfVolToReCheck.insert(vtkId);
12151 setOfVolToCheck.erase(vtkId);
12155 // --- map of Downward faces at the boundary, inside the global volume
12156 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12157 // fill group of SMDS faces inside the volume (when several volume shapes)
12158 // fill group of SMDS faces on the skin of the global volume (if skin)
12160 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12161 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12162 std::set<int>::iterator it = setOfInsideVol.begin();
12163 for (; it != setOfInsideVol.end(); ++it)
12166 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12167 int neighborsVtkIds[NBMAXNEIGHBORS];
12168 int downIds[NBMAXNEIGHBORS];
12169 unsigned char downTypes[NBMAXNEIGHBORS];
12170 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12171 for (int n = 0; n < nbNeighbors; n++)
12173 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12174 if (neighborDim == 3)
12176 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12178 DownIdType face(downIds[n], downTypes[n]);
12179 boundaryFaces[face] = vtkId;
12181 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12182 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12183 if (vtkFaceId >= 0)
12185 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12186 // find also the smds edges on this face
12187 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12188 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12189 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12190 for (int i = 0; i < nbEdges; i++)
12192 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12193 if (vtkEdgeId >= 0)
12194 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12198 else if (neighborDim == 2) // skin of the volume
12200 DownIdType face(downIds[n], downTypes[n]);
12201 skinFaces[face] = vtkId;
12202 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12203 if (vtkFaceId >= 0)
12204 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12209 // --- identify the edges constituting the wire of each subshape on the skin
12210 // define polylines with the nodes of edges, equivalent to wires
12211 // project polylines on subshapes, and partition, to get geom faces
12213 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12214 std::set<int> emptySet;
12216 std::set<int> shapeIds;
12218 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12219 while (itelem->more())
12221 const SMDS_MeshElement *elem = itelem->next();
12222 int shapeId = elem->getshapeId();
12223 int vtkId = elem->getVtkId();
12224 if (!shapeIdToVtkIdSet.count(shapeId))
12226 shapeIdToVtkIdSet[shapeId] = emptySet;
12227 shapeIds.insert(shapeId);
12229 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12232 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12233 std::set<DownIdType, DownIdCompare> emptyEdges;
12234 emptyEdges.clear();
12236 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12237 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12239 int shapeId = itShape->first;
12240 MESSAGE(" --- Shape ID --- "<< shapeId);
12241 shapeIdToEdges[shapeId] = emptyEdges;
12243 std::vector<int> nodesEdges;
12245 std::set<int>::iterator its = itShape->second.begin();
12246 for (; its != itShape->second.end(); ++its)
12249 MESSAGE(" " << vtkId);
12250 int neighborsVtkIds[NBMAXNEIGHBORS];
12251 int downIds[NBMAXNEIGHBORS];
12252 unsigned char downTypes[NBMAXNEIGHBORS];
12253 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12254 for (int n = 0; n < nbNeighbors; n++)
12256 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12258 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12259 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12260 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12262 DownIdType edge(downIds[n], downTypes[n]);
12263 if (!shapeIdToEdges[shapeId].count(edge))
12265 shapeIdToEdges[shapeId].insert(edge);
12267 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12268 nodesEdges.push_back(vtkNodeId[0]);
12269 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12270 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12276 std::list<int> order;
12278 if (nodesEdges.size() > 0)
12280 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12281 nodesEdges[0] = -1;
12282 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12283 nodesEdges[1] = -1; // do not reuse this edge
12287 int nodeTofind = order.back(); // try first to push back
12289 for (i = 0; i<nodesEdges.size(); i++)
12290 if (nodesEdges[i] == nodeTofind)
12292 if (i == nodesEdges.size())
12293 found = false; // no follower found on back
12296 if (i%2) // odd ==> use the previous one
12297 if (nodesEdges[i-1] < 0)
12301 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12302 nodesEdges[i-1] = -1;
12304 else // even ==> use the next one
12305 if (nodesEdges[i+1] < 0)
12309 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12310 nodesEdges[i+1] = -1;
12315 // try to push front
12317 nodeTofind = order.front(); // try to push front
12318 for (i = 0; i<nodesEdges.size(); i++)
12319 if (nodesEdges[i] == nodeTofind)
12321 if (i == nodesEdges.size())
12323 found = false; // no predecessor found on front
12326 if (i%2) // odd ==> use the previous one
12327 if (nodesEdges[i-1] < 0)
12331 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12332 nodesEdges[i-1] = -1;
12334 else // even ==> use the next one
12335 if (nodesEdges[i+1] < 0)
12339 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12340 nodesEdges[i+1] = -1;
12346 std::vector<int> nodes;
12347 nodes.push_back(shapeId);
12348 std::list<int>::iterator itl = order.begin();
12349 for (; itl != order.end(); itl++)
12351 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12352 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12354 listOfListOfNodes.push_back(nodes);
12357 // partition geom faces with blocFissure
12358 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12359 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12365 //================================================================================
12367 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12368 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12369 * \return TRUE if operation has been completed successfully, FALSE otherwise
12371 //================================================================================
12373 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12375 // iterates on volume elements and detect all free faces on them
12376 SMESHDS_Mesh* aMesh = GetMeshDS();
12379 //bool res = false;
12380 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12381 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12384 const SMDS_MeshVolume* volume = vIt->next();
12385 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12386 vTool.SetExternalNormal();
12387 //const bool isPoly = volume->IsPoly();
12388 const int iQuad = volume->IsQuadratic();
12389 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12391 if (!vTool.IsFreeFace(iface))
12394 vector<const SMDS_MeshNode *> nodes;
12395 int nbFaceNodes = vTool.NbFaceNodes(iface);
12396 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12398 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12399 nodes.push_back(faceNodes[inode]);
12400 if (iQuad) { // add medium nodes
12401 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12402 nodes.push_back(faceNodes[inode]);
12403 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12404 nodes.push_back(faceNodes[8]);
12406 // add new face based on volume nodes
12407 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12409 continue; // face already exsist
12411 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12415 return ( nbFree==(nbExisted+nbCreated) );
12420 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12422 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12424 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12427 //================================================================================
12429 * \brief Creates missing boundary elements
12430 * \param elements - elements whose boundary is to be checked
12431 * \param dimension - defines type of boundary elements to create
12432 * \param group - a group to store created boundary elements in
12433 * \param targetMesh - a mesh to store created boundary elements in
12434 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12435 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12436 * boundary elements will be copied into the targetMesh
12437 * \param toAddExistingBondary - if true, not only new but also pre-existing
12438 * boundary elements will be added into the new group
12439 * \param aroundElements - if true, elements will be created on boundary of given
12440 * elements else, on boundary of the whole mesh.
12441 * \return nb of added boundary elements
12443 //================================================================================
12445 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12446 Bnd_Dimension dimension,
12447 SMESH_Group* group/*=0*/,
12448 SMESH_Mesh* targetMesh/*=0*/,
12449 bool toCopyElements/*=false*/,
12450 bool toCopyExistingBoundary/*=false*/,
12451 bool toAddExistingBondary/*= false*/,
12452 bool aroundElements/*= false*/)
12454 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12455 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12456 // hope that all elements are of the same type, do not check them all
12457 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12458 throw SALOME_Exception(LOCALIZED("wrong element type"));
12461 toCopyElements = toCopyExistingBoundary = false;
12463 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12464 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12465 int nbAddedBnd = 0;
12467 // editor adding present bnd elements and optionally holding elements to add to the group
12468 SMESH_MeshEditor* presentEditor;
12469 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12470 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12472 SMESH_MesherHelper helper( *myMesh );
12473 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12474 SMDS_VolumeTool vTool;
12475 TIDSortedElemSet avoidSet;
12476 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12479 typedef vector<const SMDS_MeshNode*> TConnectivity;
12481 SMDS_ElemIteratorPtr eIt;
12482 if (elements.empty())
12483 eIt = aMesh->elementsIterator(elemType);
12485 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12487 while (eIt->more())
12489 const SMDS_MeshElement* elem = eIt->next();
12490 const int iQuad = elem->IsQuadratic();
12492 // ------------------------------------------------------------------------------------
12493 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12494 // ------------------------------------------------------------------------------------
12495 vector<const SMDS_MeshElement*> presentBndElems;
12496 vector<TConnectivity> missingBndElems;
12497 TConnectivity nodes;
12498 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12500 vTool.SetExternalNormal();
12501 const SMDS_MeshElement* otherVol = 0;
12502 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12504 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12505 ( !aroundElements || elements.count( otherVol )))
12507 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12508 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12509 if ( missType == SMDSAbs_Edge ) // boundary edges
12511 nodes.resize( 2+iQuad );
12512 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12514 for ( int j = 0; j < nodes.size(); ++j )
12516 if ( const SMDS_MeshElement* edge =
12517 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12518 presentBndElems.push_back( edge );
12520 missingBndElems.push_back( nodes );
12523 else // boundary face
12526 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12527 nodes.push_back( nn[inode] );
12528 if (iQuad) // add medium nodes
12529 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12530 nodes.push_back( nn[inode] );
12531 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12533 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12535 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12536 SMDSAbs_Face, /*noMedium=*/false ))
12537 presentBndElems.push_back( f );
12539 missingBndElems.push_back( nodes );
12541 if ( targetMesh != myMesh )
12543 // add 1D elements on face boundary to be added to a new mesh
12544 const SMDS_MeshElement* edge;
12545 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12548 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12550 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12551 if ( edge && avoidSet.insert( edge ).second )
12552 presentBndElems.push_back( edge );
12558 else // elem is a face ------------------------------------------
12560 avoidSet.clear(), avoidSet.insert( elem );
12561 int nbNodes = elem->NbCornerNodes();
12562 nodes.resize( 2 /*+ iQuad*/);
12563 for ( int i = 0; i < nbNodes; i++ )
12565 nodes[0] = elem->GetNode(i);
12566 nodes[1] = elem->GetNode((i+1)%nbNodes);
12567 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12568 continue; // not free link
12571 //nodes[2] = elem->GetNode( i + nbNodes );
12572 if ( const SMDS_MeshElement* edge =
12573 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12574 presentBndElems.push_back( edge );
12576 missingBndElems.push_back( nodes );
12580 // ---------------------------------
12581 // 2. Add missing boundary elements
12582 // ---------------------------------
12583 if ( targetMesh != myMesh )
12584 // instead of making a map of nodes in this mesh and targetMesh,
12585 // we create nodes with same IDs.
12586 for ( int i = 0; i < missingBndElems.size(); ++i )
12588 TConnectivity& srcNodes = missingBndElems[i];
12589 TConnectivity nodes( srcNodes.size() );
12590 for ( inode = 0; inode < nodes.size(); ++inode )
12591 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12592 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12594 /*noMedium=*/false))
12596 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12600 for ( int i = 0; i < missingBndElems.size(); ++i )
12602 TConnectivity& nodes = missingBndElems[i];
12603 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12605 /*noMedium=*/false))
12607 SMDS_MeshElement* elem =
12608 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12611 // try to set a new element to a shape
12612 if ( myMesh->HasShapeToMesh() )
12615 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12616 const int nbN = nodes.size() / (iQuad+1 );
12617 for ( inode = 0; inode < nbN && ok; ++inode )
12619 pair<int, TopAbs_ShapeEnum> i_stype =
12620 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12621 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12622 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12624 if ( ok && mediumShapes.size() > 1 )
12626 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12627 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12628 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12630 if (( ok = ( stype_i->first != stype_i_0.first )))
12631 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12632 aMesh->IndexToShape( stype_i_0.second ));
12635 if ( ok && mediumShapes.begin()->first == missShapeType )
12636 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12640 // ----------------------------------
12641 // 3. Copy present boundary elements
12642 // ----------------------------------
12643 if ( toCopyExistingBoundary )
12644 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12646 const SMDS_MeshElement* e = presentBndElems[i];
12647 TConnectivity nodes( e->NbNodes() );
12648 for ( inode = 0; inode < nodes.size(); ++inode )
12649 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12650 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12652 else // store present elements to add them to a group
12653 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12655 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12658 } // loop on given elements
12660 // ---------------------------------------------
12661 // 4. Fill group with boundary elements
12662 // ---------------------------------------------
12665 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12666 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12667 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12669 tgtEditor.myLastCreatedElems.Clear();
12670 tgtEditor2.myLastCreatedElems.Clear();
12672 // -----------------------
12673 // 5. Copy given elements
12674 // -----------------------
12675 if ( toCopyElements && targetMesh != myMesh )
12677 if (elements.empty())
12678 eIt = aMesh->elementsIterator(elemType);
12680 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12681 while (eIt->more())
12683 const SMDS_MeshElement* elem = eIt->next();
12684 TConnectivity nodes( elem->NbNodes() );
12685 for ( inode = 0; inode < nodes.size(); ++inode )
12686 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12687 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12689 tgtEditor.myLastCreatedElems.Clear();