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 orientated according to
1120 * \a theDirection and whose orientation defines orientation of other faces
1121 * \return number of reoriented faces.
1123 //================================================================================
1125 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1126 const gp_Dir& theDirection,
1127 const SMDS_MeshElement * theFace)
1130 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1132 if ( theFaces.empty() )
1134 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1135 while ( fIt->more() )
1136 theFaces.insert( theFaces.end(), fIt->next() );
1139 // orient theFace according to theDirection
1141 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1142 if ( normal * theDirection.XYZ() < 0 )
1143 nbReori += Reorient( theFace );
1145 // Orient other faces
1147 set< const SMDS_MeshElement* > startFaces;
1148 TIDSortedElemSet avoidSet;
1149 set< SMESH_TLink > checkedLinks;
1150 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1152 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1153 theFaces.erase( theFace );
1154 startFaces.insert( theFace );
1156 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1157 while ( startFace != startFaces.end() )
1159 theFace = *startFace;
1160 const int nbNodes = theFace->NbCornerNodes();
1163 avoidSet.insert(theFace);
1165 NLink link( theFace->GetNode( 0 ), 0 );
1166 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1168 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1169 linkIt_isNew = checkedLinks.insert( link );
1170 if ( !linkIt_isNew.second )
1172 // link has already been checked and won't be encountered more
1173 // if the group (theFaces) is manifold
1174 checkedLinks.erase( linkIt_isNew.first );
1178 int nodeInd1, nodeInd2;
1179 const SMDS_MeshElement* otherFace = FindFaceInSet( link.first, link.second,
1181 & nodeInd1, & nodeInd2);
1182 if ( otherFace && otherFace != theFace)
1184 // link must be reversed in otherFace if orientation ot otherFace
1185 // is same as that of theFace
1186 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1188 // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1189 // << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1190 nbReori += Reorient( otherFace );
1192 startFaces.insert( otherFace );
1193 if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1194 theFaces.erase( otherFace );
1197 std::swap( link.first, link.second );
1199 startFaces.erase( startFace );
1200 startFace = startFaces.begin();
1205 //=======================================================================
1206 //function : getBadRate
1208 //=======================================================================
1210 static double getBadRate (const SMDS_MeshElement* theElem,
1211 SMESH::Controls::NumericalFunctorPtr& theCrit)
1213 SMESH::Controls::TSequenceOfXYZ P;
1214 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1216 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1217 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1220 //=======================================================================
1221 //function : QuadToTri
1222 //purpose : Cut quadrangles into triangles.
1223 // theCrit is used to select a diagonal to cut
1224 //=======================================================================
1226 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1227 SMESH::Controls::NumericalFunctorPtr theCrit)
1229 myLastCreatedElems.Clear();
1230 myLastCreatedNodes.Clear();
1232 MESSAGE( "::QuadToTri()" );
1234 if ( !theCrit.get() )
1237 SMESHDS_Mesh * aMesh = GetMeshDS();
1239 Handle(Geom_Surface) surface;
1240 SMESH_MesherHelper helper( *GetMesh() );
1242 TIDSortedElemSet::iterator itElem;
1243 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1244 const SMDS_MeshElement* elem = *itElem;
1245 if ( !elem || elem->GetType() != SMDSAbs_Face )
1247 if ( elem->NbCornerNodes() != 4 )
1250 // retrieve element nodes
1251 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1253 // compare two sets of possible triangles
1254 double aBadRate1, aBadRate2; // to what extent a set is bad
1255 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1256 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1257 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1259 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1260 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1261 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1263 int aShapeId = FindShape( elem );
1264 const SMDS_MeshElement* newElem1 = 0;
1265 const SMDS_MeshElement* newElem2 = 0;
1267 if( !elem->IsQuadratic() ) {
1269 // split liner quadrangle
1270 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1271 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1272 if ( aBadRate1 <= aBadRate2 ) {
1273 // tr1 + tr2 is better
1274 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1275 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1278 // tr3 + tr4 is better
1279 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1280 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1285 // split quadratic quadrangle
1287 // get surface elem is on
1288 if ( aShapeId != helper.GetSubShapeID() ) {
1292 shape = aMesh->IndexToShape( aShapeId );
1293 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1294 TopoDS_Face face = TopoDS::Face( shape );
1295 surface = BRep_Tool::Surface( face );
1296 if ( !surface.IsNull() )
1297 helper.SetSubShape( shape );
1300 // find middle point for (0,1,2,3)
1301 // and create a node in this point;
1302 const SMDS_MeshNode* newN = 0;
1303 if ( aNodes.size() == 9 )
1305 // SMDSEntity_BiQuad_Quadrangle
1306 newN = aNodes.back();
1311 if ( surface.IsNull() )
1313 for ( int i = 0; i < 4; i++ )
1314 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1319 const SMDS_MeshNode* inFaceNode = 0;
1320 if ( helper.GetNodeUVneedInFaceNode() )
1321 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1322 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1323 inFaceNode = aNodes[ i ];
1325 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1327 for ( int i = 0; i < 4; i++ )
1328 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1330 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1332 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1333 myLastCreatedNodes.Append(newN);
1335 // create a new element
1336 if ( aBadRate1 <= aBadRate2 ) {
1337 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1338 aNodes[6], aNodes[7], newN );
1339 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1340 newN, aNodes[4], aNodes[5] );
1343 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1344 aNodes[7], aNodes[4], newN );
1345 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1346 newN, aNodes[5], aNodes[6] );
1350 // care of a new element
1352 myLastCreatedElems.Append(newElem1);
1353 myLastCreatedElems.Append(newElem2);
1354 AddToSameGroups( newElem1, elem, aMesh );
1355 AddToSameGroups( newElem2, elem, aMesh );
1357 // put a new triangle on the same shape
1360 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1361 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1363 aMesh->RemoveElement( elem );
1368 //=======================================================================
1369 //function : BestSplit
1370 //purpose : Find better diagonal for cutting.
1371 //=======================================================================
1373 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1374 SMESH::Controls::NumericalFunctorPtr theCrit)
1376 myLastCreatedElems.Clear();
1377 myLastCreatedNodes.Clear();
1382 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1385 if( theQuad->NbNodes()==4 ||
1386 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1388 // retrieve element nodes
1389 const SMDS_MeshNode* aNodes [4];
1390 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1392 //while (itN->more())
1394 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1396 // compare two sets of possible triangles
1397 double aBadRate1, aBadRate2; // to what extent a set is bad
1398 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1399 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1400 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1402 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1403 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1404 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1405 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1406 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1407 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1408 return 1; // diagonal 1-3
1410 return 2; // diagonal 2-4
1417 // Methods of splitting volumes into tetra
1419 const int theHexTo5_1[5*4+1] =
1421 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1423 const int theHexTo5_2[5*4+1] =
1425 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1427 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1429 const int theHexTo6_1[6*4+1] =
1431 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
1433 const int theHexTo6_2[6*4+1] =
1435 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
1437 const int theHexTo6_3[6*4+1] =
1439 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
1441 const int theHexTo6_4[6*4+1] =
1443 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
1445 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1447 const int thePyraTo2_1[2*4+1] =
1449 0, 1, 2, 4, 0, 2, 3, 4, -1
1451 const int thePyraTo2_2[2*4+1] =
1453 1, 2, 3, 4, 1, 3, 0, 4, -1
1455 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1457 const int thePentaTo3_1[3*4+1] =
1459 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1461 const int thePentaTo3_2[3*4+1] =
1463 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1465 const int thePentaTo3_3[3*4+1] =
1467 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1469 const int thePentaTo3_4[3*4+1] =
1471 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1473 const int thePentaTo3_5[3*4+1] =
1475 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1477 const int thePentaTo3_6[3*4+1] =
1479 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1481 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1482 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1484 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1487 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1488 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1489 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1494 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1495 bool _baryNode; //!< additional node is to be created at cell barycenter
1496 bool _ownConn; //!< to delete _connectivity in destructor
1497 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1499 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1500 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1501 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1502 bool hasFacet( const TTriangleFacet& facet ) const
1504 const int* tetConn = _connectivity;
1505 for ( ; tetConn[0] >= 0; tetConn += 4 )
1506 if (( facet.contains( tetConn[0] ) +
1507 facet.contains( tetConn[1] ) +
1508 facet.contains( tetConn[2] ) +
1509 facet.contains( tetConn[3] )) == 3 )
1515 //=======================================================================
1517 * \brief return TSplitMethod for the given element
1519 //=======================================================================
1521 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1523 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1525 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1526 // an edge and a face barycenter; tertaherdons are based on triangles and
1527 // a volume barycenter
1528 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1530 // Find out how adjacent volumes are split
1532 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1533 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1534 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1536 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1537 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1538 if ( nbNodes < 4 ) continue;
1540 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1541 const int* nInd = vol.GetFaceNodesIndices( iF );
1544 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1545 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1546 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1547 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1551 int iCom = 0; // common node of triangle faces to split into
1552 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1554 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1555 nInd[ iQ * ( (iCom+1)%nbNodes )],
1556 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1557 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1558 nInd[ iQ * ( (iCom+2)%nbNodes )],
1559 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1560 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1562 triaSplits.push_back( t012 );
1563 triaSplits.push_back( t023 );
1568 if ( !triaSplits.empty() )
1569 hasAdjacentSplits = true;
1572 // Among variants of split method select one compliant with adjacent volumes
1574 TSplitMethod method;
1575 if ( !vol.Element()->IsPoly() && !is24TetMode )
1577 int nbVariants = 2, nbTet = 0;
1578 const int** connVariants = 0;
1579 switch ( vol.Element()->GetEntityType() )
1581 case SMDSEntity_Hexa:
1582 case SMDSEntity_Quad_Hexa:
1583 case SMDSEntity_TriQuad_Hexa:
1584 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1585 connVariants = theHexTo5, nbTet = 5;
1587 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1589 case SMDSEntity_Pyramid:
1590 case SMDSEntity_Quad_Pyramid:
1591 connVariants = thePyraTo2; nbTet = 2;
1593 case SMDSEntity_Penta:
1594 case SMDSEntity_Quad_Penta:
1595 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1600 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1602 // check method compliancy with adjacent tetras,
1603 // all found splits must be among facets of tetras described by this method
1604 method = TSplitMethod( nbTet, connVariants[variant] );
1605 if ( hasAdjacentSplits && method._nbTetra > 0 )
1607 bool facetCreated = true;
1608 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1610 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1611 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1612 facetCreated = method.hasFacet( *facet );
1614 if ( !facetCreated )
1615 method = TSplitMethod(0); // incompatible method
1619 if ( method._nbTetra < 1 )
1621 // No standard method is applicable, use a generic solution:
1622 // each facet of a volume is split into triangles and
1623 // each of triangles and a volume barycenter form a tetrahedron.
1625 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1627 int* connectivity = new int[ maxTetConnSize + 1 ];
1628 method._connectivity = connectivity;
1629 method._ownConn = true;
1630 method._baryNode = !isHex27; // to create central node or not
1633 int baryCenInd = vol.NbNodes() - int( isHex27 );
1634 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1636 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1637 const int* nInd = vol.GetFaceNodesIndices( iF );
1638 // find common node of triangle facets of tetra to create
1639 int iCommon = 0; // index in linear numeration
1640 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1641 if ( !triaSplits.empty() )
1644 const TTriangleFacet* facet = &triaSplits.front();
1645 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1646 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1647 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1650 else if ( nbNodes > 3 && !is24TetMode )
1652 // find the best method of splitting into triangles by aspect ratio
1653 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1654 map< double, int > badness2iCommon;
1655 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1656 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1657 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1660 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1662 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1663 nodes[ iQ*((iLast-1)%nbNodes)],
1664 nodes[ iQ*((iLast )%nbNodes)]);
1665 badness += getBadRate( &tria, aspectRatio );
1667 badness2iCommon.insert( make_pair( badness, iCommon ));
1669 // use iCommon with lowest badness
1670 iCommon = badness2iCommon.begin()->second;
1672 if ( iCommon >= nbNodes )
1673 iCommon = 0; // something wrong
1675 // fill connectivity of tetrahedra based on a current face
1676 int nbTet = nbNodes - 2;
1677 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1682 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1683 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1687 method._faceBaryNode[ iF ] = 0;
1688 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1691 for ( int i = 0; i < nbTet; ++i )
1693 int i1 = i, i2 = (i+1) % nbNodes;
1694 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1695 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1696 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1697 connectivity[ connSize++ ] = faceBaryCenInd;
1698 connectivity[ connSize++ ] = baryCenInd;
1703 for ( int i = 0; i < nbTet; ++i )
1705 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1706 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1707 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1708 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1709 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1710 connectivity[ connSize++ ] = baryCenInd;
1713 method._nbTetra += nbTet;
1715 } // loop on volume faces
1717 connectivity[ connSize++ ] = -1;
1719 } // end of generic solution
1723 //================================================================================
1725 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1727 //================================================================================
1729 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1731 // find the tetrahedron including the three nodes of facet
1732 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1733 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1734 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1735 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1736 while ( volIt1->more() )
1738 const SMDS_MeshElement* v = volIt1->next();
1739 SMDSAbs_EntityType type = v->GetEntityType();
1740 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1742 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1743 continue; // medium node not allowed
1744 const int ind2 = v->GetNodeIndex( n2 );
1745 if ( ind2 < 0 || 3 < ind2 )
1747 const int ind3 = v->GetNodeIndex( n3 );
1748 if ( ind3 < 0 || 3 < ind3 )
1755 //=======================================================================
1757 * \brief A key of a face of volume
1759 //=======================================================================
1761 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1763 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1765 TIDSortedNodeSet sortedNodes;
1766 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1767 int nbNodes = vol.NbFaceNodes( iF );
1768 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1769 for ( int i = 0; i < nbNodes; i += iQ )
1770 sortedNodes.insert( fNodes[i] );
1771 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1772 first.first = (*(n++))->GetID();
1773 first.second = (*(n++))->GetID();
1774 second.first = (*(n++))->GetID();
1775 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1780 //=======================================================================
1781 //function : SplitVolumesIntoTetra
1782 //purpose : Split volume elements into tetrahedra.
1783 //=======================================================================
1785 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1786 const int theMethodFlags)
1788 // std-like iterator on coordinates of nodes of mesh element
1789 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1790 NXyzIterator xyzEnd;
1792 SMDS_VolumeTool volTool;
1793 SMESH_MesherHelper helper( *GetMesh());
1795 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1796 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1798 SMESH_SequenceOfElemPtr newNodes, newElems;
1800 // map face of volume to it's baricenrtic node
1801 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1804 TIDSortedElemSet::const_iterator elem = theElems.begin();
1805 for ( ; elem != theElems.end(); ++elem )
1807 if ( (*elem)->GetType() != SMDSAbs_Volume )
1809 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1810 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1813 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1815 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1816 if ( splitMethod._nbTetra < 1 ) continue;
1818 // find submesh to add new tetras to
1819 if ( !subMesh || !subMesh->Contains( *elem ))
1821 int shapeID = FindShape( *elem );
1822 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1823 subMesh = GetMeshDS()->MeshElements( shapeID );
1826 if ( (*elem)->IsQuadratic() )
1829 // add quadratic links to the helper
1830 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1832 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1833 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1834 for ( int iN = 0; iN < nbN; iN += iQ )
1835 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1837 helper.SetIsQuadratic( true );
1842 helper.SetIsQuadratic( false );
1844 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1845 helper.SetElementsOnShape( true );
1846 if ( splitMethod._baryNode )
1848 // make a node at barycenter
1849 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1850 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1851 nodes.push_back( gcNode );
1852 newNodes.Append( gcNode );
1854 if ( !splitMethod._faceBaryNode.empty() )
1856 // make or find baricentric nodes of faces
1857 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1858 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1860 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1861 volFace2BaryNode.insert
1862 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1865 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1866 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1868 nodes.push_back( iF_n->second = f_n->second );
1873 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1874 const int* tetConn = splitMethod._connectivity;
1875 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1876 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1877 nodes[ tetConn[1] ],
1878 nodes[ tetConn[2] ],
1879 nodes[ tetConn[3] ]));
1881 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1883 // Split faces on sides of the split volume
1885 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1886 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1888 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1889 if ( nbNodes < 4 ) continue;
1891 // find an existing face
1892 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1893 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1894 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1895 /*noMedium=*/false))
1898 helper.SetElementsOnShape( false );
1899 vector< const SMDS_MeshElement* > triangles;
1901 // find submesh to add new triangles in
1902 if ( !fSubMesh || !fSubMesh->Contains( face ))
1904 int shapeID = FindShape( face );
1905 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1907 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1908 if ( iF_n != splitMethod._faceBaryNode.end() )
1910 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1912 const SMDS_MeshNode* n1 = fNodes[iN];
1913 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1914 const SMDS_MeshNode *n3 = iF_n->second;
1915 if ( !volTool.IsFaceExternal( iF ))
1917 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1919 if ( fSubMesh && n3->getshapeId() < 1 )
1920 fSubMesh->AddNode( n3 );
1925 // among possible triangles create ones discribed by split method
1926 const int* nInd = volTool.GetFaceNodesIndices( iF );
1927 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1928 int iCom = 0; // common node of triangle faces to split into
1929 list< TTriangleFacet > facets;
1930 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1932 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1933 nInd[ iQ * ( (iCom+1)%nbNodes )],
1934 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1935 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1936 nInd[ iQ * ( (iCom+2)%nbNodes )],
1937 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1938 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1940 facets.push_back( t012 );
1941 facets.push_back( t023 );
1942 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1943 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1944 nInd[ iQ * ((iLast-1)%nbNodes )],
1945 nInd[ iQ * ((iLast )%nbNodes )]));
1949 list< TTriangleFacet >::iterator facet = facets.begin();
1950 for ( ; facet != facets.end(); ++facet )
1952 if ( !volTool.IsFaceExternal( iF ))
1953 swap( facet->_n2, facet->_n3 );
1954 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1955 volNodes[ facet->_n2 ],
1956 volNodes[ facet->_n3 ]));
1959 for ( int i = 0; i < triangles.size(); ++i )
1961 if ( !triangles[i] ) continue;
1963 fSubMesh->AddElement( triangles[i]);
1964 newElems.Append( triangles[i] );
1966 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1967 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1970 } // loop on volume faces to split them into triangles
1972 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1974 if ( geomType == SMDSEntity_TriQuad_Hexa )
1976 // remove medium nodes that could become free
1977 for ( int i = 20; i < volTool.NbNodes(); ++i )
1978 if ( volNodes[i]->NbInverseElements() == 0 )
1979 GetMeshDS()->RemoveNode( volNodes[i] );
1981 } // loop on volumes to split
1983 myLastCreatedNodes = newNodes;
1984 myLastCreatedElems = newElems;
1987 //=======================================================================
1988 //function : AddToSameGroups
1989 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1990 //=======================================================================
1992 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1993 const SMDS_MeshElement* elemInGroups,
1994 SMESHDS_Mesh * aMesh)
1996 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1997 if (!groups.empty()) {
1998 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1999 for ( ; grIt != groups.end(); grIt++ ) {
2000 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2001 if ( group && group->Contains( elemInGroups ))
2002 group->SMDSGroup().Add( elemToAdd );
2008 //=======================================================================
2009 //function : RemoveElemFromGroups
2010 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2011 //=======================================================================
2012 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2013 SMESHDS_Mesh * aMesh)
2015 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2016 if (!groups.empty())
2018 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2019 for (; GrIt != groups.end(); GrIt++)
2021 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2022 if (!grp || grp->IsEmpty()) continue;
2023 grp->SMDSGroup().Remove(removeelem);
2028 //================================================================================
2030 * \brief Replace elemToRm by elemToAdd in the all groups
2032 //================================================================================
2034 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2035 const SMDS_MeshElement* elemToAdd,
2036 SMESHDS_Mesh * aMesh)
2038 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2039 if (!groups.empty()) {
2040 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2041 for ( ; grIt != groups.end(); grIt++ ) {
2042 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2043 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2044 group->SMDSGroup().Add( elemToAdd );
2049 //================================================================================
2051 * \brief Replace elemToRm by elemToAdd in the all groups
2053 //================================================================================
2055 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2056 const vector<const SMDS_MeshElement*>& elemToAdd,
2057 SMESHDS_Mesh * aMesh)
2059 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2060 if (!groups.empty())
2062 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2063 for ( ; grIt != groups.end(); grIt++ ) {
2064 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2065 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2066 for ( int i = 0; i < elemToAdd.size(); ++i )
2067 group->SMDSGroup().Add( elemToAdd[ i ] );
2072 //=======================================================================
2073 //function : QuadToTri
2074 //purpose : Cut quadrangles into triangles.
2075 // theCrit is used to select a diagonal to cut
2076 //=======================================================================
2078 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2079 const bool the13Diag)
2081 myLastCreatedElems.Clear();
2082 myLastCreatedNodes.Clear();
2084 MESSAGE( "::QuadToTri()" );
2086 SMESHDS_Mesh * aMesh = GetMeshDS();
2088 Handle(Geom_Surface) surface;
2089 SMESH_MesherHelper helper( *GetMesh() );
2091 TIDSortedElemSet::iterator itElem;
2092 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2093 const SMDS_MeshElement* elem = *itElem;
2094 if ( !elem || elem->GetType() != SMDSAbs_Face )
2096 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2097 if(!isquad) continue;
2099 if(elem->NbNodes()==4) {
2100 // retrieve element nodes
2101 const SMDS_MeshNode* aNodes [4];
2102 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2104 while ( itN->more() )
2105 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2107 int aShapeId = FindShape( elem );
2108 const SMDS_MeshElement* newElem1 = 0;
2109 const SMDS_MeshElement* newElem2 = 0;
2111 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2112 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2115 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2116 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2118 myLastCreatedElems.Append(newElem1);
2119 myLastCreatedElems.Append(newElem2);
2120 // put a new triangle on the same shape and add to the same groups
2123 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2124 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2126 AddToSameGroups( newElem1, elem, aMesh );
2127 AddToSameGroups( newElem2, elem, aMesh );
2128 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2129 aMesh->RemoveElement( elem );
2132 // Quadratic quadrangle
2134 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2136 // get surface elem is on
2137 int aShapeId = FindShape( elem );
2138 if ( aShapeId != helper.GetSubShapeID() ) {
2142 shape = aMesh->IndexToShape( aShapeId );
2143 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2144 TopoDS_Face face = TopoDS::Face( shape );
2145 surface = BRep_Tool::Surface( face );
2146 if ( !surface.IsNull() )
2147 helper.SetSubShape( shape );
2151 const SMDS_MeshNode* aNodes [8];
2152 const SMDS_MeshNode* inFaceNode = 0;
2153 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2155 while ( itN->more() ) {
2156 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2157 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2158 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2160 inFaceNode = aNodes[ i-1 ];
2164 // find middle point for (0,1,2,3)
2165 // and create a node in this point;
2167 if ( surface.IsNull() ) {
2169 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2173 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2176 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2178 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2180 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2181 myLastCreatedNodes.Append(newN);
2183 // create a new element
2184 const SMDS_MeshElement* newElem1 = 0;
2185 const SMDS_MeshElement* newElem2 = 0;
2187 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2188 aNodes[6], aNodes[7], newN );
2189 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2190 newN, aNodes[4], aNodes[5] );
2193 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2194 aNodes[7], aNodes[4], newN );
2195 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2196 newN, aNodes[5], aNodes[6] );
2198 myLastCreatedElems.Append(newElem1);
2199 myLastCreatedElems.Append(newElem2);
2200 // put a new triangle on the same shape and add to the same groups
2203 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2204 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2206 AddToSameGroups( newElem1, elem, aMesh );
2207 AddToSameGroups( newElem2, elem, aMesh );
2208 aMesh->RemoveElement( elem );
2215 //=======================================================================
2216 //function : getAngle
2218 //=======================================================================
2220 double getAngle(const SMDS_MeshElement * tr1,
2221 const SMDS_MeshElement * tr2,
2222 const SMDS_MeshNode * n1,
2223 const SMDS_MeshNode * n2)
2225 double angle = 2. * M_PI; // bad angle
2228 SMESH::Controls::TSequenceOfXYZ P1, P2;
2229 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2230 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2233 if(!tr1->IsQuadratic())
2234 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2236 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2237 if ( N1.SquareMagnitude() <= gp::Resolution() )
2239 if(!tr2->IsQuadratic())
2240 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2242 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2243 if ( N2.SquareMagnitude() <= gp::Resolution() )
2246 // find the first diagonal node n1 in the triangles:
2247 // take in account a diagonal link orientation
2248 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2249 for ( int t = 0; t < 2; t++ ) {
2250 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2251 int i = 0, iDiag = -1;
2252 while ( it->more()) {
2253 const SMDS_MeshElement *n = it->next();
2254 if ( n == n1 || n == n2 ) {
2258 if ( i - iDiag == 1 )
2259 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2268 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2271 angle = N1.Angle( N2 );
2276 // =================================================
2277 // class generating a unique ID for a pair of nodes
2278 // and able to return nodes by that ID
2279 // =================================================
2283 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2284 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2287 long GetLinkID (const SMDS_MeshNode * n1,
2288 const SMDS_MeshNode * n2) const
2290 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2293 bool GetNodes (const long theLinkID,
2294 const SMDS_MeshNode* & theNode1,
2295 const SMDS_MeshNode* & theNode2) const
2297 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2298 if ( !theNode1 ) return false;
2299 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2300 if ( !theNode2 ) return false;
2306 const SMESHDS_Mesh* myMesh;
2311 //=======================================================================
2312 //function : TriToQuad
2313 //purpose : Fuse neighbour triangles into quadrangles.
2314 // theCrit is used to select a neighbour to fuse with.
2315 // theMaxAngle is a max angle between element normals at which
2316 // fusion is still performed.
2317 //=======================================================================
2319 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2320 SMESH::Controls::NumericalFunctorPtr theCrit,
2321 const double theMaxAngle)
2323 myLastCreatedElems.Clear();
2324 myLastCreatedNodes.Clear();
2326 MESSAGE( "::TriToQuad()" );
2328 if ( !theCrit.get() )
2331 SMESHDS_Mesh * aMesh = GetMeshDS();
2333 // Prepare data for algo: build
2334 // 1. map of elements with their linkIDs
2335 // 2. map of linkIDs with their elements
2337 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2338 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2339 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2340 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2342 TIDSortedElemSet::iterator itElem;
2343 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2344 const SMDS_MeshElement* elem = *itElem;
2345 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2346 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2347 if(!IsTria) continue;
2349 // retrieve element nodes
2350 const SMDS_MeshNode* aNodes [4];
2351 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2354 aNodes[ i++ ] = cast2Node( itN->next() );
2355 aNodes[ 3 ] = aNodes[ 0 ];
2358 for ( i = 0; i < 3; i++ ) {
2359 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2360 // check if elements sharing a link can be fused
2361 itLE = mapLi_listEl.find( link );
2362 if ( itLE != mapLi_listEl.end() ) {
2363 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2365 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2366 //if ( FindShape( elem ) != FindShape( elem2 ))
2367 // continue; // do not fuse triangles laying on different shapes
2368 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2369 continue; // avoid making badly shaped quads
2370 (*itLE).second.push_back( elem );
2373 mapLi_listEl[ link ].push_back( elem );
2375 mapEl_setLi [ elem ].insert( link );
2378 // Clean the maps from the links shared by a sole element, ie
2379 // links to which only one element is bound in mapLi_listEl
2381 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2382 int nbElems = (*itLE).second.size();
2383 if ( nbElems < 2 ) {
2384 const SMDS_MeshElement* elem = (*itLE).second.front();
2385 SMESH_TLink link = (*itLE).first;
2386 mapEl_setLi[ elem ].erase( link );
2387 if ( mapEl_setLi[ elem ].empty() )
2388 mapEl_setLi.erase( elem );
2392 // Algo: fuse triangles into quadrangles
2394 while ( ! mapEl_setLi.empty() ) {
2395 // Look for the start element:
2396 // the element having the least nb of shared links
2397 const SMDS_MeshElement* startElem = 0;
2399 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2400 int nbLinks = (*itEL).second.size();
2401 if ( nbLinks < minNbLinks ) {
2402 startElem = (*itEL).first;
2403 minNbLinks = nbLinks;
2404 if ( minNbLinks == 1 )
2409 // search elements to fuse starting from startElem or links of elements
2410 // fused earlyer - startLinks
2411 list< SMESH_TLink > startLinks;
2412 while ( startElem || !startLinks.empty() ) {
2413 while ( !startElem && !startLinks.empty() ) {
2414 // Get an element to start, by a link
2415 SMESH_TLink linkId = startLinks.front();
2416 startLinks.pop_front();
2417 itLE = mapLi_listEl.find( linkId );
2418 if ( itLE != mapLi_listEl.end() ) {
2419 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2420 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2421 for ( ; itE != listElem.end() ; itE++ )
2422 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2424 mapLi_listEl.erase( itLE );
2429 // Get candidates to be fused
2430 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2431 const SMESH_TLink *link12, *link13;
2433 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2434 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2435 ASSERT( !setLi.empty() );
2436 set< SMESH_TLink >::iterator itLi;
2437 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2439 const SMESH_TLink & link = (*itLi);
2440 itLE = mapLi_listEl.find( link );
2441 if ( itLE == mapLi_listEl.end() )
2444 const SMDS_MeshElement* elem = (*itLE).second.front();
2446 elem = (*itLE).second.back();
2447 mapLi_listEl.erase( itLE );
2448 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2459 // add other links of elem to list of links to re-start from
2460 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2461 set< SMESH_TLink >::iterator it;
2462 for ( it = links.begin(); it != links.end(); it++ ) {
2463 const SMESH_TLink& link2 = (*it);
2464 if ( link2 != link )
2465 startLinks.push_back( link2 );
2469 // Get nodes of possible quadrangles
2470 const SMDS_MeshNode *n12 [4], *n13 [4];
2471 bool Ok12 = false, Ok13 = false;
2472 const SMDS_MeshNode *linkNode1, *linkNode2;
2474 linkNode1 = link12->first;
2475 linkNode2 = link12->second;
2476 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2480 linkNode1 = link13->first;
2481 linkNode2 = link13->second;
2482 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2486 // Choose a pair to fuse
2487 if ( Ok12 && Ok13 ) {
2488 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2489 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2490 double aBadRate12 = getBadRate( &quad12, theCrit );
2491 double aBadRate13 = getBadRate( &quad13, theCrit );
2492 if ( aBadRate13 < aBadRate12 )
2499 // and remove fused elems and removed links from the maps
2500 mapEl_setLi.erase( tr1 );
2502 mapEl_setLi.erase( tr2 );
2503 mapLi_listEl.erase( *link12 );
2504 if(tr1->NbNodes()==3) {
2505 const SMDS_MeshElement* newElem = 0;
2506 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2507 myLastCreatedElems.Append(newElem);
2508 AddToSameGroups( newElem, tr1, aMesh );
2509 int aShapeId = tr1->getshapeId();
2512 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2514 aMesh->RemoveElement( tr1 );
2515 aMesh->RemoveElement( tr2 );
2518 const SMDS_MeshNode* N1 [6];
2519 const SMDS_MeshNode* N2 [6];
2520 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2521 // now we receive following N1 and N2 (using numeration as above image)
2522 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2523 // i.e. first nodes from both arrays determ new diagonal
2524 const SMDS_MeshNode* aNodes[8];
2533 const SMDS_MeshElement* newElem = 0;
2534 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2535 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2536 myLastCreatedElems.Append(newElem);
2537 AddToSameGroups( newElem, tr1, aMesh );
2538 int aShapeId = tr1->getshapeId();
2541 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2543 aMesh->RemoveElement( tr1 );
2544 aMesh->RemoveElement( tr2 );
2545 // remove middle node (9)
2546 GetMeshDS()->RemoveNode( N1[4] );
2550 mapEl_setLi.erase( tr3 );
2551 mapLi_listEl.erase( *link13 );
2552 if(tr1->NbNodes()==3) {
2553 const SMDS_MeshElement* newElem = 0;
2554 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2555 myLastCreatedElems.Append(newElem);
2556 AddToSameGroups( newElem, tr1, aMesh );
2557 int aShapeId = tr1->getshapeId();
2560 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2562 aMesh->RemoveElement( tr1 );
2563 aMesh->RemoveElement( tr3 );
2566 const SMDS_MeshNode* N1 [6];
2567 const SMDS_MeshNode* N2 [6];
2568 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2569 // now we receive following N1 and N2 (using numeration as above image)
2570 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2571 // i.e. first nodes from both arrays determ new diagonal
2572 const SMDS_MeshNode* aNodes[8];
2581 const SMDS_MeshElement* newElem = 0;
2582 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2583 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2584 myLastCreatedElems.Append(newElem);
2585 AddToSameGroups( newElem, tr1, aMesh );
2586 int aShapeId = tr1->getshapeId();
2589 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2591 aMesh->RemoveElement( tr1 );
2592 aMesh->RemoveElement( tr3 );
2593 // remove middle node (9)
2594 GetMeshDS()->RemoveNode( N1[4] );
2598 // Next element to fuse: the rejected one
2600 startElem = Ok12 ? tr3 : tr2;
2602 } // if ( startElem )
2603 } // while ( startElem || !startLinks.empty() )
2604 } // while ( ! mapEl_setLi.empty() )
2610 /*#define DUMPSO(txt) \
2611 // cout << txt << endl;
2612 //=============================================================================
2616 //=============================================================================
2617 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2621 int tmp = idNodes[ i1 ];
2622 idNodes[ i1 ] = idNodes[ i2 ];
2623 idNodes[ i2 ] = tmp;
2624 gp_Pnt Ptmp = P[ i1 ];
2627 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2630 //=======================================================================
2631 //function : SortQuadNodes
2632 //purpose : Set 4 nodes of a quadrangle face in a good order.
2633 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2635 //=======================================================================
2637 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2642 for ( i = 0; i < 4; i++ ) {
2643 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2645 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2648 gp_Vec V1(P[0], P[1]);
2649 gp_Vec V2(P[0], P[2]);
2650 gp_Vec V3(P[0], P[3]);
2652 gp_Vec Cross1 = V1 ^ V2;
2653 gp_Vec Cross2 = V2 ^ V3;
2656 if (Cross1.Dot(Cross2) < 0)
2661 if (Cross1.Dot(Cross2) < 0)
2665 swap ( i, i + 1, idNodes, P );
2667 // for ( int ii = 0; ii < 4; ii++ ) {
2668 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2669 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2675 //=======================================================================
2676 //function : SortHexaNodes
2677 //purpose : Set 8 nodes of a hexahedron in a good order.
2678 // Return success status
2679 //=======================================================================
2681 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2686 DUMPSO( "INPUT: ========================================");
2687 for ( i = 0; i < 8; i++ ) {
2688 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2689 if ( !n ) return false;
2690 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2691 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2693 DUMPSO( "========================================");
2696 set<int> faceNodes; // ids of bottom face nodes, to be found
2697 set<int> checkedId1; // ids of tried 2-nd nodes
2698 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2699 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2700 int iMin, iLoop1 = 0;
2702 // Loop to try the 2-nd nodes
2704 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2706 // Find not checked 2-nd node
2707 for ( i = 1; i < 8; i++ )
2708 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2709 int id1 = idNodes[i];
2710 swap ( 1, i, idNodes, P );
2711 checkedId1.insert ( id1 );
2715 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2716 // ie that all but meybe one (id3 which is on the same face) nodes
2717 // lay on the same side from the triangle plane.
2719 bool manyInPlane = false; // more than 4 nodes lay in plane
2721 while ( ++iLoop2 < 6 ) {
2723 // get 1-2-3 plane coeffs
2724 Standard_Real A, B, C, D;
2725 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2726 if ( N.SquareMagnitude() > gp::Resolution() )
2728 gp_Pln pln ( P[0], N );
2729 pln.Coefficients( A, B, C, D );
2731 // find the node (iMin) closest to pln
2732 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2734 for ( i = 3; i < 8; i++ ) {
2735 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2736 if ( fabs( dist[i] ) < minDist ) {
2737 minDist = fabs( dist[i] );
2740 if ( fabs( dist[i] ) <= tol )
2741 idInPln.insert( idNodes[i] );
2744 // there should not be more than 4 nodes in bottom plane
2745 if ( idInPln.size() > 1 )
2747 DUMPSO( "### idInPln.size() = " << idInPln.size());
2748 // idInPlane does not contain the first 3 nodes
2749 if ( manyInPlane || idInPln.size() == 5)
2750 return false; // all nodes in one plane
2753 // set the 1-st node to be not in plane
2754 for ( i = 3; i < 8; i++ ) {
2755 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2756 DUMPSO( "### Reset 0-th node");
2757 swap( 0, i, idNodes, P );
2762 // reset to re-check second nodes
2763 leastDist = DBL_MAX;
2767 break; // from iLoop2;
2770 // check that the other 4 nodes are on the same side
2771 bool sameSide = true;
2772 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2773 for ( i = 3; sameSide && i < 8; i++ ) {
2775 sameSide = ( isNeg == dist[i] <= 0.);
2778 // keep best solution
2779 if ( sameSide && minDist < leastDist ) {
2780 leastDist = minDist;
2782 faceNodes.insert( idNodes[ 1 ] );
2783 faceNodes.insert( idNodes[ 2 ] );
2784 faceNodes.insert( idNodes[ iMin ] );
2785 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2786 << " leastDist = " << leastDist);
2787 if ( leastDist <= DBL_MIN )
2792 // set next 3-d node to check
2793 int iNext = 2 + iLoop2;
2795 DUMPSO( "Try 2-nd");
2796 swap ( 2, iNext, idNodes, P );
2798 } // while ( iLoop2 < 6 )
2801 if ( faceNodes.empty() ) return false;
2803 // Put the faceNodes in proper places
2804 for ( i = 4; i < 8; i++ ) {
2805 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2806 // find a place to put
2808 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2810 DUMPSO( "Set faceNodes");
2811 swap ( iTo, i, idNodes, P );
2816 // Set nodes of the found bottom face in good order
2817 DUMPSO( " Found bottom face: ");
2818 i = SortQuadNodes( theMesh, idNodes );
2820 gp_Pnt Ptmp = P[ i ];
2825 // for ( int ii = 0; ii < 4; ii++ ) {
2826 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2827 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2830 // Gravity center of the top and bottom faces
2831 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2832 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2834 // Get direction from the bottom to the top face
2835 gp_Vec upDir ( aGCb, aGCt );
2836 Standard_Real upDirSize = upDir.Magnitude();
2837 if ( upDirSize <= gp::Resolution() ) return false;
2840 // Assure that the bottom face normal points up
2841 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2842 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2843 if ( Nb.Dot( upDir ) < 0 ) {
2844 DUMPSO( "Reverse bottom face");
2845 swap( 1, 3, idNodes, P );
2848 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2849 Standard_Real minDist = DBL_MAX;
2850 for ( i = 4; i < 8; i++ ) {
2851 // projection of P[i] to the plane defined by P[0] and upDir
2852 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2853 Standard_Real sqDist = P[0].SquareDistance( Pp );
2854 if ( sqDist < minDist ) {
2859 DUMPSO( "Set 4-th");
2860 swap ( 4, iMin, idNodes, P );
2862 // Set nodes of the top face in good order
2863 DUMPSO( "Sort top face");
2864 i = SortQuadNodes( theMesh, &idNodes[4] );
2867 gp_Pnt Ptmp = P[ i ];
2872 // Assure that direction of the top face normal is from the bottom face
2873 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2874 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2875 if ( Nt.Dot( upDir ) < 0 ) {
2876 DUMPSO( "Reverse top face");
2877 swap( 5, 7, idNodes, P );
2880 // DUMPSO( "OUTPUT: ========================================");
2881 // for ( i = 0; i < 8; i++ ) {
2882 // float *p = ugrid->GetPoint(idNodes[i]);
2883 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2889 //================================================================================
2891 * \brief Return nodes linked to the given one
2892 * \param theNode - the node
2893 * \param linkedNodes - the found nodes
2894 * \param type - the type of elements to check
2896 * Medium nodes are ignored
2898 //================================================================================
2900 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2901 TIDSortedElemSet & linkedNodes,
2902 SMDSAbs_ElementType type )
2904 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2905 while ( elemIt->more() )
2907 const SMDS_MeshElement* elem = elemIt->next();
2908 if(elem->GetType() == SMDSAbs_0DElement)
2911 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2912 if ( elem->GetType() == SMDSAbs_Volume )
2914 SMDS_VolumeTool vol( elem );
2915 while ( nodeIt->more() ) {
2916 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2917 if ( theNode != n && vol.IsLinked( theNode, n ))
2918 linkedNodes.insert( n );
2923 for ( int i = 0; nodeIt->more(); ++i ) {
2924 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2925 if ( n == theNode ) {
2926 int iBefore = i - 1;
2928 if ( elem->IsQuadratic() ) {
2929 int nb = elem->NbNodes() / 2;
2930 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2931 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2933 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2934 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2941 //=======================================================================
2942 //function : laplacianSmooth
2943 //purpose : pulls theNode toward the center of surrounding nodes directly
2944 // connected to that node along an element edge
2945 //=======================================================================
2947 void laplacianSmooth(const SMDS_MeshNode* theNode,
2948 const Handle(Geom_Surface)& theSurface,
2949 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2951 // find surrounding nodes
2953 TIDSortedElemSet nodeSet;
2954 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2956 // compute new coodrs
2958 double coord[] = { 0., 0., 0. };
2959 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2960 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2961 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2962 if ( theSurface.IsNull() ) { // smooth in 3D
2963 coord[0] += node->X();
2964 coord[1] += node->Y();
2965 coord[2] += node->Z();
2967 else { // smooth in 2D
2968 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2969 gp_XY* uv = theUVMap[ node ];
2970 coord[0] += uv->X();
2971 coord[1] += uv->Y();
2974 int nbNodes = nodeSet.size();
2977 coord[0] /= nbNodes;
2978 coord[1] /= nbNodes;
2980 if ( !theSurface.IsNull() ) {
2981 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2982 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2983 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2989 coord[2] /= nbNodes;
2993 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2996 //=======================================================================
2997 //function : centroidalSmooth
2998 //purpose : pulls theNode toward the element-area-weighted centroid of the
2999 // surrounding elements
3000 //=======================================================================
3002 void centroidalSmooth(const SMDS_MeshNode* theNode,
3003 const Handle(Geom_Surface)& theSurface,
3004 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3006 gp_XYZ aNewXYZ(0.,0.,0.);
3007 SMESH::Controls::Area anAreaFunc;
3008 double totalArea = 0.;
3013 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3014 while ( elemIt->more() )
3016 const SMDS_MeshElement* elem = elemIt->next();
3019 gp_XYZ elemCenter(0.,0.,0.);
3020 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3021 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3022 int nn = elem->NbNodes();
3023 if(elem->IsQuadratic()) nn = nn/2;
3025 //while ( itN->more() ) {
3027 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3029 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3030 aNodePoints.push_back( aP );
3031 if ( !theSurface.IsNull() ) { // smooth in 2D
3032 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3033 gp_XY* uv = theUVMap[ aNode ];
3034 aP.SetCoord( uv->X(), uv->Y(), 0. );
3038 double elemArea = anAreaFunc.GetValue( aNodePoints );
3039 totalArea += elemArea;
3041 aNewXYZ += elemCenter * elemArea;
3043 aNewXYZ /= totalArea;
3044 if ( !theSurface.IsNull() ) {
3045 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3046 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3051 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3054 //=======================================================================
3055 //function : getClosestUV
3056 //purpose : return UV of closest projection
3057 //=======================================================================
3059 static bool getClosestUV (Extrema_GenExtPS& projector,
3060 const gp_Pnt& point,
3063 projector.Perform( point );
3064 if ( projector.IsDone() ) {
3065 double u, v, minVal = DBL_MAX;
3066 for ( int i = projector.NbExt(); i > 0; i-- )
3067 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3068 if ( projector.SquareDistance( i ) < minVal ) {
3069 minVal = projector.SquareDistance( i );
3071 if ( projector.Value( i ) < minVal ) {
3072 minVal = projector.Value( i );
3074 projector.Point( i ).Parameter( u, v );
3076 result.SetCoord( u, v );
3082 //=======================================================================
3084 //purpose : Smooth theElements during theNbIterations or until a worst
3085 // element has aspect ratio <= theTgtAspectRatio.
3086 // Aspect Ratio varies in range [1.0, inf].
3087 // If theElements is empty, the whole mesh is smoothed.
3088 // theFixedNodes contains additionally fixed nodes. Nodes built
3089 // on edges and boundary nodes are always fixed.
3090 //=======================================================================
3092 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3093 set<const SMDS_MeshNode*> & theFixedNodes,
3094 const SmoothMethod theSmoothMethod,
3095 const int theNbIterations,
3096 double theTgtAspectRatio,
3099 myLastCreatedElems.Clear();
3100 myLastCreatedNodes.Clear();
3102 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3104 if ( theTgtAspectRatio < 1.0 )
3105 theTgtAspectRatio = 1.0;
3107 const double disttol = 1.e-16;
3109 SMESH::Controls::AspectRatio aQualityFunc;
3111 SMESHDS_Mesh* aMesh = GetMeshDS();
3113 if ( theElems.empty() ) {
3114 // add all faces to theElems
3115 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3116 while ( fIt->more() ) {
3117 const SMDS_MeshElement* face = fIt->next();
3118 theElems.insert( theElems.end(), face );
3121 // get all face ids theElems are on
3122 set< int > faceIdSet;
3123 TIDSortedElemSet::iterator itElem;
3125 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3126 int fId = FindShape( *itElem );
3127 // check that corresponding submesh exists and a shape is face
3129 faceIdSet.find( fId ) == faceIdSet.end() &&
3130 aMesh->MeshElements( fId )) {
3131 TopoDS_Shape F = aMesh->IndexToShape( fId );
3132 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3133 faceIdSet.insert( fId );
3136 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3138 // ===============================================
3139 // smooth elements on each TopoDS_Face separately
3140 // ===============================================
3142 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3143 for ( ; fId != faceIdSet.rend(); ++fId ) {
3144 // get face surface and submesh
3145 Handle(Geom_Surface) surface;
3146 SMESHDS_SubMesh* faceSubMesh = 0;
3148 double fToler2 = 0, f,l;
3149 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3150 bool isUPeriodic = false, isVPeriodic = false;
3152 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3153 surface = BRep_Tool::Surface( face );
3154 faceSubMesh = aMesh->MeshElements( *fId );
3155 fToler2 = BRep_Tool::Tolerance( face );
3156 fToler2 *= fToler2 * 10.;
3157 isUPeriodic = surface->IsUPeriodic();
3160 isVPeriodic = surface->IsVPeriodic();
3163 surface->Bounds( u1, u2, v1, v2 );
3165 // ---------------------------------------------------------
3166 // for elements on a face, find movable and fixed nodes and
3167 // compute UV for them
3168 // ---------------------------------------------------------
3169 bool checkBoundaryNodes = false;
3170 bool isQuadratic = false;
3171 set<const SMDS_MeshNode*> setMovableNodes;
3172 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3173 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3174 list< const SMDS_MeshElement* > elemsOnFace;
3176 Extrema_GenExtPS projector;
3177 GeomAdaptor_Surface surfAdaptor;
3178 if ( !surface.IsNull() ) {
3179 surfAdaptor.Load( surface );
3180 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3182 int nbElemOnFace = 0;
3183 itElem = theElems.begin();
3184 // loop on not yet smoothed elements: look for elems on a face
3185 while ( itElem != theElems.end() ) {
3186 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3187 break; // all elements found
3189 const SMDS_MeshElement* elem = *itElem;
3190 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3191 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3195 elemsOnFace.push_back( elem );
3196 theElems.erase( itElem++ );
3200 isQuadratic = elem->IsQuadratic();
3202 // get movable nodes of elem
3203 const SMDS_MeshNode* node;
3204 SMDS_TypeOfPosition posType;
3205 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3206 int nn = 0, nbn = elem->NbNodes();
3207 if(elem->IsQuadratic())
3209 while ( nn++ < nbn ) {
3210 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3211 const SMDS_PositionPtr& pos = node->GetPosition();
3212 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3213 if (posType != SMDS_TOP_EDGE &&
3214 posType != SMDS_TOP_VERTEX &&
3215 theFixedNodes.find( node ) == theFixedNodes.end())
3217 // check if all faces around the node are on faceSubMesh
3218 // because a node on edge may be bound to face
3219 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3221 if ( faceSubMesh ) {
3222 while ( eIt->more() && all ) {
3223 const SMDS_MeshElement* e = eIt->next();
3224 all = faceSubMesh->Contains( e );
3228 setMovableNodes.insert( node );
3230 checkBoundaryNodes = true;
3232 if ( posType == SMDS_TOP_3DSPACE )
3233 checkBoundaryNodes = true;
3236 if ( surface.IsNull() )
3239 // get nodes to check UV
3240 list< const SMDS_MeshNode* > uvCheckNodes;
3241 itN = elem->nodesIterator();
3242 nn = 0; nbn = elem->NbNodes();
3243 if(elem->IsQuadratic())
3245 while ( nn++ < nbn ) {
3246 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3247 if ( uvMap.find( node ) == uvMap.end() )
3248 uvCheckNodes.push_back( node );
3249 // add nodes of elems sharing node
3250 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3251 // while ( eIt->more() ) {
3252 // const SMDS_MeshElement* e = eIt->next();
3253 // if ( e != elem ) {
3254 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3255 // while ( nIt->more() ) {
3256 // const SMDS_MeshNode* n =
3257 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3258 // if ( uvMap.find( n ) == uvMap.end() )
3259 // uvCheckNodes.push_back( n );
3265 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3266 for ( ; n != uvCheckNodes.end(); ++n ) {
3269 const SMDS_PositionPtr& pos = node->GetPosition();
3270 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3272 switch ( posType ) {
3273 case SMDS_TOP_FACE: {
3274 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3275 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3278 case SMDS_TOP_EDGE: {
3279 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3280 Handle(Geom2d_Curve) pcurve;
3281 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3282 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3283 if ( !pcurve.IsNull() ) {
3284 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3285 uv = pcurve->Value( u ).XY();
3289 case SMDS_TOP_VERTEX: {
3290 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3291 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3292 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3297 // check existing UV
3298 bool project = true;
3299 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3300 double dist1 = DBL_MAX, dist2 = 0;
3301 if ( posType != SMDS_TOP_3DSPACE ) {
3302 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3303 project = dist1 > fToler2;
3305 if ( project ) { // compute new UV
3307 if ( !getClosestUV( projector, pNode, newUV )) {
3308 MESSAGE("Node Projection Failed " << node);
3312 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3314 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3316 if ( posType != SMDS_TOP_3DSPACE )
3317 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3318 if ( dist2 < dist1 )
3322 // store UV in the map
3323 listUV.push_back( uv );
3324 uvMap.insert( make_pair( node, &listUV.back() ));
3326 } // loop on not yet smoothed elements
3328 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3329 checkBoundaryNodes = true;
3331 // fix nodes on mesh boundary
3333 if ( checkBoundaryNodes ) {
3334 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3335 map< SMESH_TLink, int >::iterator link_nb;
3336 // put all elements links to linkNbMap
3337 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3338 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3339 const SMDS_MeshElement* elem = (*elemIt);
3340 int nbn = elem->NbCornerNodes();
3341 // loop on elem links: insert them in linkNbMap
3342 for ( int iN = 0; iN < nbn; ++iN ) {
3343 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3344 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3345 SMESH_TLink link( n1, n2 );
3346 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3350 // remove nodes that are in links encountered only once from setMovableNodes
3351 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3352 if ( link_nb->second == 1 ) {
3353 setMovableNodes.erase( link_nb->first.node1() );
3354 setMovableNodes.erase( link_nb->first.node2() );
3359 // -----------------------------------------------------
3360 // for nodes on seam edge, compute one more UV ( uvMap2 );
3361 // find movable nodes linked to nodes on seam and which
3362 // are to be smoothed using the second UV ( uvMap2 )
3363 // -----------------------------------------------------
3365 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3366 if ( !surface.IsNull() ) {
3367 TopExp_Explorer eExp( face, TopAbs_EDGE );
3368 for ( ; eExp.More(); eExp.Next() ) {
3369 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3370 if ( !BRep_Tool::IsClosed( edge, face ))
3372 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3373 if ( !sm ) continue;
3374 // find out which parameter varies for a node on seam
3377 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3378 if ( pcurve.IsNull() ) continue;
3379 uv1 = pcurve->Value( f );
3381 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3382 if ( pcurve.IsNull() ) continue;
3383 uv2 = pcurve->Value( f );
3384 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3386 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3387 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3389 // get nodes on seam and its vertices
3390 list< const SMDS_MeshNode* > seamNodes;
3391 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3392 while ( nSeamIt->more() ) {
3393 const SMDS_MeshNode* node = nSeamIt->next();
3394 if ( !isQuadratic || !IsMedium( node ))
3395 seamNodes.push_back( node );
3397 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3398 for ( ; vExp.More(); vExp.Next() ) {
3399 sm = aMesh->MeshElements( vExp.Current() );
3401 nSeamIt = sm->GetNodes();
3402 while ( nSeamIt->more() )
3403 seamNodes.push_back( nSeamIt->next() );
3406 // loop on nodes on seam
3407 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3408 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3409 const SMDS_MeshNode* nSeam = *noSeIt;
3410 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3411 if ( n_uv == uvMap.end() )
3414 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3415 // set the second UV
3416 listUV.push_back( *n_uv->second );
3417 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3418 if ( uvMap2.empty() )
3419 uvMap2 = uvMap; // copy the uvMap contents
3420 uvMap2[ nSeam ] = &listUV.back();
3422 // collect movable nodes linked to ones on seam in nodesNearSeam
3423 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3424 while ( eIt->more() ) {
3425 const SMDS_MeshElement* e = eIt->next();
3426 int nbUseMap1 = 0, nbUseMap2 = 0;
3427 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3428 int nn = 0, nbn = e->NbNodes();
3429 if(e->IsQuadratic()) nbn = nbn/2;
3430 while ( nn++ < nbn )
3432 const SMDS_MeshNode* n =
3433 static_cast<const SMDS_MeshNode*>( nIt->next() );
3435 setMovableNodes.find( n ) == setMovableNodes.end() )
3437 // add only nodes being closer to uv2 than to uv1
3438 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3439 0.5 * ( n->Y() + nSeam->Y() ),
3440 0.5 * ( n->Z() + nSeam->Z() ));
3442 getClosestUV( projector, pMid, uv );
3443 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3444 nodesNearSeam.insert( n );
3450 // for centroidalSmooth all element nodes must
3451 // be on one side of a seam
3452 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3453 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3455 while ( nn++ < nbn ) {
3456 const SMDS_MeshNode* n =
3457 static_cast<const SMDS_MeshNode*>( nIt->next() );
3458 setMovableNodes.erase( n );
3462 } // loop on nodes on seam
3463 } // loop on edge of a face
3464 } // if ( !face.IsNull() )
3466 if ( setMovableNodes.empty() ) {
3467 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3468 continue; // goto next face
3476 double maxRatio = -1., maxDisplacement = -1.;
3477 set<const SMDS_MeshNode*>::iterator nodeToMove;
3478 for ( it = 0; it < theNbIterations; it++ ) {
3479 maxDisplacement = 0.;
3480 nodeToMove = setMovableNodes.begin();
3481 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3482 const SMDS_MeshNode* node = (*nodeToMove);
3483 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3486 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3487 if ( theSmoothMethod == LAPLACIAN )
3488 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3490 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3492 // node displacement
3493 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3494 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3495 if ( aDispl > maxDisplacement )
3496 maxDisplacement = aDispl;
3498 // no node movement => exit
3499 //if ( maxDisplacement < 1.e-16 ) {
3500 if ( maxDisplacement < disttol ) {
3501 MESSAGE("-- no node movement --");
3505 // check elements quality
3507 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3508 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3509 const SMDS_MeshElement* elem = (*elemIt);
3510 if ( !elem || elem->GetType() != SMDSAbs_Face )
3512 SMESH::Controls::TSequenceOfXYZ aPoints;
3513 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3514 double aValue = aQualityFunc.GetValue( aPoints );
3515 if ( aValue > maxRatio )
3519 if ( maxRatio <= theTgtAspectRatio ) {
3520 MESSAGE("-- quality achived --");
3523 if (it+1 == theNbIterations) {
3524 MESSAGE("-- Iteration limit exceeded --");
3526 } // smoothing iterations
3528 MESSAGE(" Face id: " << *fId <<
3529 " Nb iterstions: " << it <<
3530 " Displacement: " << maxDisplacement <<
3531 " Aspect Ratio " << maxRatio);
3533 // ---------------------------------------
3534 // new nodes positions are computed,
3535 // record movement in DS and set new UV
3536 // ---------------------------------------
3537 nodeToMove = setMovableNodes.begin();
3538 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3539 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3540 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3541 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3542 if ( node_uv != uvMap.end() ) {
3543 gp_XY* uv = node_uv->second;
3545 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3549 // move medium nodes of quadratic elements
3552 SMESH_MesherHelper helper( *GetMesh() );
3553 if ( !face.IsNull() )
3554 helper.SetSubShape( face );
3555 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3556 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3557 const SMDS_VtkFace* QF =
3558 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3559 if(QF && QF->IsQuadratic()) {
3560 vector<const SMDS_MeshNode*> Ns;
3561 Ns.reserve(QF->NbNodes()+1);
3562 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3563 while ( anIter->more() )
3564 Ns.push_back( cast2Node(anIter->next()) );
3565 Ns.push_back( Ns[0] );
3567 for(int i=0; i<QF->NbNodes(); i=i+2) {
3568 if ( !surface.IsNull() ) {
3569 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3570 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3571 gp_XY uv = ( uv1 + uv2 ) / 2.;
3572 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3573 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3576 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3577 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3578 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3580 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3581 fabs( Ns[i+1]->Y() - y ) > disttol ||
3582 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3583 // we have to move i+1 node
3584 aMesh->MoveNode( Ns[i+1], x, y, z );
3591 } // loop on face ids
3595 //=======================================================================
3596 //function : isReverse
3597 //purpose : Return true if normal of prevNodes is not co-directied with
3598 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3599 // iNotSame is where prevNodes and nextNodes are different.
3600 // If result is true then future volume orientation is OK
3601 //=======================================================================
3603 static bool isReverse(const SMDS_MeshElement* face,
3604 const vector<const SMDS_MeshNode*>& prevNodes,
3605 const vector<const SMDS_MeshNode*>& nextNodes,
3609 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3610 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3611 gp_XYZ extrDir( pN - pP ), faceNorm;
3612 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3614 return faceNorm * extrDir < 0.0;
3617 //=======================================================================
3619 * \brief Create elements by sweeping an element
3620 * \param elem - element to sweep
3621 * \param newNodesItVec - nodes generated from each node of the element
3622 * \param newElems - generated elements
3623 * \param nbSteps - number of sweeping steps
3624 * \param srcElements - to append elem for each generated element
3626 //=======================================================================
3628 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3629 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3630 list<const SMDS_MeshElement*>& newElems,
3632 SMESH_SequenceOfElemPtr& srcElements)
3634 //MESSAGE("sweepElement " << nbSteps);
3635 SMESHDS_Mesh* aMesh = GetMeshDS();
3637 const int nbNodes = elem->NbNodes();
3638 const int nbCorners = elem->NbCornerNodes();
3639 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3640 polyhedron creation !!! */
3641 // Loop on elem nodes:
3642 // find new nodes and detect same nodes indices
3643 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3644 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3645 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3646 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3648 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3649 vector<int> sames(nbNodes);
3650 vector<bool> isSingleNode(nbNodes);
3652 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3653 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3654 const SMDS_MeshNode* node = nnIt->first;
3655 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3656 if ( listNewNodes.empty() )
3659 itNN [ iNode ] = listNewNodes.begin();
3660 prevNod[ iNode ] = node;
3661 nextNod[ iNode ] = listNewNodes.front();
3663 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3664 corner node of linear */
3665 if ( prevNod[ iNode ] != nextNod [ iNode ])
3666 nbDouble += !isSingleNode[iNode];
3668 if( iNode < nbCorners ) { // check corners only
3669 if ( prevNod[ iNode ] == nextNod [ iNode ])
3670 sames[nbSame++] = iNode;
3672 iNotSameNode = iNode;
3676 if ( nbSame == nbNodes || nbSame > 2) {
3677 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3681 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3683 // fix nodes order to have bottom normal external
3684 if ( baseType == SMDSEntity_Polygon )
3686 std::reverse( itNN.begin(), itNN.end() );
3687 std::reverse( prevNod.begin(), prevNod.end() );
3688 std::reverse( midlNod.begin(), midlNod.end() );
3689 std::reverse( nextNod.begin(), nextNod.end() );
3690 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3694 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3695 SMDS_MeshCell::applyInterlace( ind, itNN );
3696 SMDS_MeshCell::applyInterlace( ind, prevNod );
3697 SMDS_MeshCell::applyInterlace( ind, nextNod );
3698 SMDS_MeshCell::applyInterlace( ind, midlNod );
3699 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3702 sames[nbSame] = iNotSameNode;
3703 for ( int j = 0; j <= nbSame; ++j )
3704 for ( size_t i = 0; i < ind.size(); ++i )
3705 if ( ind[i] == sames[j] )
3710 iNotSameNode = sames[nbSame];
3715 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3717 iSameNode = sames[ nbSame-1 ];
3718 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3719 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3720 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3723 // make new elements
3724 for (int iStep = 0; iStep < nbSteps; iStep++ )
3727 for ( iNode = 0; iNode < nbNodes; iNode++ )
3729 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3730 nextNod[ iNode ] = *itNN[ iNode ]++;
3733 SMDS_MeshElement* aNewElem = 0;
3734 /*if(!elem->IsPoly())*/ {
3735 switch ( baseType ) {
3737 case SMDSEntity_Node: { // sweep NODE
3738 if ( nbSame == 0 ) {
3739 if ( isSingleNode[0] )
3740 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3742 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3748 case SMDSEntity_Edge: { // sweep EDGE
3749 if ( nbDouble == 0 )
3751 if ( nbSame == 0 ) // ---> quadrangle
3752 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3753 nextNod[ 1 ], nextNod[ 0 ] );
3754 else // ---> triangle
3755 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3756 nextNod[ iNotSameNode ] );
3758 else // ---> polygon
3760 vector<const SMDS_MeshNode*> poly_nodes;
3761 poly_nodes.push_back( prevNod[0] );
3762 poly_nodes.push_back( prevNod[1] );
3763 if ( prevNod[1] != nextNod[1] )
3765 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3766 poly_nodes.push_back( nextNod[1] );
3768 if ( prevNod[0] != nextNod[0] )
3770 poly_nodes.push_back( nextNod[0] );
3771 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3773 switch ( poly_nodes.size() ) {
3775 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3778 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3779 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3782 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3787 case SMDSEntity_Triangle: // TRIANGLE --->
3789 if ( nbDouble > 0 ) break;
3790 if ( nbSame == 0 ) // ---> pentahedron
3791 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3792 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3794 else if ( nbSame == 1 ) // ---> pyramid
3795 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3796 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3797 nextNod[ iSameNode ]);
3799 else // 2 same nodes: ---> tetrahedron
3800 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3801 nextNod[ iNotSameNode ]);
3804 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3808 if ( nbDouble+nbSame == 2 )
3810 if(nbSame==0) { // ---> quadratic quadrangle
3811 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3812 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3814 else { //(nbSame==1) // ---> quadratic triangle
3816 return; // medium node on axis
3818 else if(sames[0]==0)
3819 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3820 nextNod[2], midlNod[1], prevNod[2]);
3822 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3823 midlNod[0], nextNod[2], prevNod[2]);
3826 else if ( nbDouble == 3 )
3828 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3829 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3830 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3837 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3838 if ( nbDouble > 0 ) break;
3840 if ( nbSame == 0 ) // ---> hexahedron
3841 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3842 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3844 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3845 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3846 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3847 nextNod[ iSameNode ]);
3848 newElems.push_back( aNewElem );
3849 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3850 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3851 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3853 else if ( nbSame == 2 ) { // ---> pentahedron
3854 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3855 // iBeforeSame is same too
3856 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3857 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3858 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3860 // iAfterSame is same too
3861 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3862 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3863 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3867 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3868 if ( nbDouble+nbSame != 3 ) break;
3870 // ---> pentahedron with 15 nodes
3871 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3872 nextNod[0], nextNod[1], nextNod[2],
3873 prevNod[3], prevNod[4], prevNod[5],
3874 nextNod[3], nextNod[4], nextNod[5],
3875 midlNod[0], midlNod[1], midlNod[2]);
3877 else if(nbSame==1) {
3878 // ---> 2d order pyramid of 13 nodes
3879 int apex = iSameNode;
3880 int i0 = ( apex + 1 ) % nbCorners;
3881 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3885 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3886 nextNod[i0], nextNod[i1], prevNod[apex],
3887 prevNod[i01], midlNod[i0],
3888 nextNod[i01], midlNod[i1],
3889 prevNod[i1a], prevNod[i0a],
3890 nextNod[i0a], nextNod[i1a]);
3892 else if(nbSame==2) {
3893 // ---> 2d order tetrahedron of 10 nodes
3894 int n1 = iNotSameNode;
3895 int n2 = ( n1 + 1 ) % nbCorners;
3896 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3900 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3901 prevNod[n12], prevNod[n23], prevNod[n31],
3902 midlNod[n1], nextNod[n12], nextNod[n31]);
3906 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3908 if ( nbDouble != 4 ) break;
3909 // ---> hexahedron with 20 nodes
3910 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3911 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3912 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3913 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3914 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3916 else if(nbSame==1) {
3917 // ---> pyramid + pentahedron - can not be created since it is needed
3918 // additional middle node at the center of face
3919 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3922 else if( nbSame == 2 ) {
3923 if ( nbDouble != 2 ) break;
3924 // ---> 2d order Pentahedron with 15 nodes
3926 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3927 // iBeforeSame is same too
3934 // iAfterSame is same too
3944 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3945 prevNod[n4], prevNod[n5], nextNod[n5],
3946 prevNod[n12], midlNod[n2], nextNod[n12],
3947 prevNod[n45], midlNod[n5], nextNod[n45],
3948 prevNod[n14], prevNod[n25], nextNod[n25]);
3952 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3954 if( nbSame == 0 && nbDouble == 9 ) {
3955 // ---> tri-quadratic hexahedron with 27 nodes
3956 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3957 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3958 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3959 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3960 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3961 prevNod[8], // bottom center
3962 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3963 nextNod[8], // top center
3964 midlNod[8]);// elem center
3972 case SMDSEntity_Polygon: { // sweep POLYGON
3974 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3975 // ---> hexagonal prism
3976 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3977 prevNod[3], prevNod[4], prevNod[5],
3978 nextNod[0], nextNod[1], nextNod[2],
3979 nextNod[3], nextNod[4], nextNod[5]);
3983 case SMDSEntity_Ball:
3991 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3993 if ( baseType != SMDSEntity_Polygon )
3995 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3996 SMDS_MeshCell::applyInterlace( ind, prevNod );
3997 SMDS_MeshCell::applyInterlace( ind, nextNod );
3998 SMDS_MeshCell::applyInterlace( ind, midlNod );
3999 SMDS_MeshCell::applyInterlace( ind, itNN );
4000 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4001 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4003 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4004 vector<int> quantities (nbNodes + 2);
4005 polyedre_nodes.clear();
4009 for (int inode = 0; inode < nbNodes; inode++)
4010 polyedre_nodes.push_back( prevNod[inode] );
4011 quantities.push_back( nbNodes );
4014 polyedre_nodes.push_back( nextNod[0] );
4015 for (int inode = nbNodes; inode-1; --inode )
4016 polyedre_nodes.push_back( nextNod[inode-1] );
4017 quantities.push_back( nbNodes );
4020 for (int iface = 0; iface < nbNodes; iface++)
4022 const int prevNbNodes = polyedre_nodes.size();
4023 int inextface = (iface+1) % nbNodes;
4024 polyedre_nodes.push_back( prevNod[inextface] );
4025 polyedre_nodes.push_back( prevNod[iface] );
4026 if ( prevNod[iface] != nextNod[iface] )
4028 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4029 polyedre_nodes.push_back( nextNod[iface] );
4031 if ( prevNod[inextface] != nextNod[inextface] )
4033 polyedre_nodes.push_back( nextNod[inextface] );
4034 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4036 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4037 if ( nbFaceNodes > 2 )
4038 quantities.push_back( nbFaceNodes );
4039 else // degenerated face
4040 polyedre_nodes.resize( prevNbNodes );
4042 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4046 newElems.push_back( aNewElem );
4047 myLastCreatedElems.Append(aNewElem);
4048 srcElements.Append( elem );
4051 // set new prev nodes
4052 for ( iNode = 0; iNode < nbNodes; iNode++ )
4053 prevNod[ iNode ] = nextNod[ iNode ];
4058 //=======================================================================
4060 * \brief Create 1D and 2D elements around swept elements
4061 * \param mapNewNodes - source nodes and ones generated from them
4062 * \param newElemsMap - source elements and ones generated from them
4063 * \param elemNewNodesMap - nodes generated from each node of each element
4064 * \param elemSet - all swept elements
4065 * \param nbSteps - number of sweeping steps
4066 * \param srcElements - to append elem for each generated element
4068 //=======================================================================
4070 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4071 TElemOfElemListMap & newElemsMap,
4072 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4073 TIDSortedElemSet& elemSet,
4075 SMESH_SequenceOfElemPtr& srcElements)
4077 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4078 SMESHDS_Mesh* aMesh = GetMeshDS();
4080 // Find nodes belonging to only one initial element - sweep them to get edges.
4082 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4083 for ( ; nList != mapNewNodes.end(); nList++ )
4085 const SMDS_MeshNode* node =
4086 static_cast<const SMDS_MeshNode*>( nList->first );
4087 if ( newElemsMap.count( node ))
4088 continue; // node was extruded into edge
4089 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4090 int nbInitElems = 0;
4091 const SMDS_MeshElement* el = 0;
4092 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4093 while ( eIt->more() && nbInitElems < 2 ) {
4095 SMDSAbs_ElementType type = el->GetType();
4096 if ( type == SMDSAbs_Volume || type < highType ) continue;
4097 if ( type > highType ) {
4101 nbInitElems += elemSet.count(el);
4103 if ( nbInitElems < 2 ) {
4104 bool NotCreateEdge = el && el->IsMediumNode(node);
4105 if(!NotCreateEdge) {
4106 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4107 list<const SMDS_MeshElement*> newEdges;
4108 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4113 // Make a ceiling for each element ie an equal element of last new nodes.
4114 // Find free links of faces - make edges and sweep them into faces.
4116 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4117 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4118 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4120 const SMDS_MeshElement* elem = itElem->first;
4121 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4123 if(itElem->second.size()==0) continue;
4125 const bool isQuadratic = elem->IsQuadratic();
4127 if ( elem->GetType() == SMDSAbs_Edge ) {
4128 // create a ceiling edge
4129 if ( !isQuadratic ) {
4130 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4131 vecNewNodes[ 1 ]->second.back())) {
4132 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4133 vecNewNodes[ 1 ]->second.back()));
4134 srcElements.Append( myLastCreatedElems.Last() );
4138 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4139 vecNewNodes[ 1 ]->second.back(),
4140 vecNewNodes[ 2 ]->second.back())) {
4141 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4142 vecNewNodes[ 1 ]->second.back(),
4143 vecNewNodes[ 2 ]->second.back()));
4144 srcElements.Append( myLastCreatedElems.Last() );
4148 if ( elem->GetType() != SMDSAbs_Face )
4151 bool hasFreeLinks = false;
4153 TIDSortedElemSet avoidSet;
4154 avoidSet.insert( elem );
4156 set<const SMDS_MeshNode*> aFaceLastNodes;
4157 int iNode, nbNodes = vecNewNodes.size();
4158 if ( !isQuadratic ) {
4159 // loop on the face nodes
4160 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4161 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4162 // look for free links of the face
4163 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4164 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4165 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4166 // check if a link is free
4167 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4168 hasFreeLinks = true;
4169 // make an edge and a ceiling for a new edge
4170 if ( !aMesh->FindEdge( n1, n2 )) {
4171 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4172 srcElements.Append( myLastCreatedElems.Last() );
4174 n1 = vecNewNodes[ iNode ]->second.back();
4175 n2 = vecNewNodes[ iNext ]->second.back();
4176 if ( !aMesh->FindEdge( n1, n2 )) {
4177 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4178 srcElements.Append( myLastCreatedElems.Last() );
4183 else { // elem is quadratic face
4184 int nbn = nbNodes/2;
4185 for ( iNode = 0; iNode < nbn; iNode++ ) {
4186 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4187 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4188 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4189 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4190 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4191 // check if a link is free
4192 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4193 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4194 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4195 hasFreeLinks = true;
4196 // make an edge and a ceiling for a new edge
4198 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4199 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4200 srcElements.Append( myLastCreatedElems.Last() );
4202 n1 = vecNewNodes[ iNode ]->second.back();
4203 n2 = vecNewNodes[ iNext ]->second.back();
4204 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4205 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4206 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4207 srcElements.Append( myLastCreatedElems.Last() );
4211 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4212 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4216 // sweep free links into faces
4218 if ( hasFreeLinks ) {
4219 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4220 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4222 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4223 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4224 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4225 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4227 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4228 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4229 std::advance( v, volNb );
4230 // find indices of free faces of a volume and their source edges
4231 list< int > freeInd;
4232 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4233 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4234 int iF, nbF = vTool.NbFaces();
4235 for ( iF = 0; iF < nbF; iF ++ ) {
4236 if (vTool.IsFreeFace( iF ) &&
4237 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4238 initNodeSet != faceNodeSet) // except an initial face
4240 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4242 freeInd.push_back( iF );
4243 // find source edge of a free face iF
4244 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4245 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4246 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4247 initNodeSet.begin(), initNodeSet.end(),
4248 commonNodes.begin());
4249 if ( (*v)->IsQuadratic() )
4250 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4252 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4254 if ( !srcEdges.back() )
4256 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4257 << iF << " of volume #" << vTool.ID() << endl;
4262 if ( freeInd.empty() )
4265 // create faces for all steps;
4266 // if such a face has been already created by sweep of edge,
4267 // assure that its orientation is OK
4268 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4269 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4270 vTool.SetExternalNormal();
4271 const int nextShift = vTool.IsForward() ? +1 : -1;
4272 list< int >::iterator ind = freeInd.begin();
4273 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4274 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4276 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4277 int nbn = vTool.NbFaceNodes( *ind );
4278 const SMDS_MeshElement * f = 0;
4279 if ( nbn == 3 ) ///// triangle
4281 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4283 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4285 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4287 nodes[ 1 + nextShift ] };
4289 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4291 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4295 else if ( nbn == 4 ) ///// quadrangle
4297 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4299 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4301 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4302 nodes[ 2 ], nodes[ 2+nextShift ] };
4304 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4306 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4307 newOrder[ 2 ], newOrder[ 3 ]));
4310 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4312 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4314 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4316 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4318 nodes[2 + 2*nextShift],
4319 nodes[3 - 2*nextShift],
4321 nodes[3 + 2*nextShift]};
4323 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4325 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4333 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4335 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4336 nodes[1], nodes[3], nodes[5], nodes[7] );
4338 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4340 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4341 nodes[4 - 2*nextShift],
4343 nodes[4 + 2*nextShift],
4345 nodes[5 - 2*nextShift],
4347 nodes[5 + 2*nextShift] };
4349 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4351 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4352 newOrder[ 2 ], newOrder[ 3 ],
4353 newOrder[ 4 ], newOrder[ 5 ],
4354 newOrder[ 6 ], newOrder[ 7 ]));
4357 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4359 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4360 SMDSAbs_Face, /*noMedium=*/false);
4362 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4364 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4365 nodes[4 - 2*nextShift],
4367 nodes[4 + 2*nextShift],
4369 nodes[5 - 2*nextShift],
4371 nodes[5 + 2*nextShift],
4374 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4376 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4377 newOrder[ 2 ], newOrder[ 3 ],
4378 newOrder[ 4 ], newOrder[ 5 ],
4379 newOrder[ 6 ], newOrder[ 7 ],
4383 else //////// polygon
4385 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4386 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4388 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4390 if ( !vTool.IsForward() )
4391 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4393 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4395 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4399 while ( srcElements.Length() < myLastCreatedElems.Length() )
4400 srcElements.Append( *srcEdge );
4402 } // loop on free faces
4404 // go to the next volume
4406 while ( iVol++ < nbVolumesByStep ) v++;
4409 } // loop on volumes of one step
4410 } // sweep free links into faces
4412 // Make a ceiling face with a normal external to a volume
4414 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4416 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4418 lastVol.SetExternalNormal();
4419 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4420 int nbn = lastVol.NbFaceNodes( iF );
4422 if (!hasFreeLinks ||
4423 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4424 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4426 else if ( nbn == 4 )
4428 if (!hasFreeLinks ||
4429 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4430 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4432 else if ( nbn == 6 && isQuadratic )
4434 if (!hasFreeLinks ||
4435 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4436 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4437 nodes[1], nodes[3], nodes[5]));
4439 else if ( nbn == 8 && isQuadratic )
4441 if (!hasFreeLinks ||
4442 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4443 nodes[1], nodes[3], nodes[5], nodes[7]) )
4444 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4445 nodes[1], nodes[3], nodes[5], nodes[7]));
4447 else if ( nbn == 9 && isQuadratic )
4449 if (!hasFreeLinks ||
4450 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4451 SMDSAbs_Face, /*noMedium=*/false) )
4452 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4453 nodes[1], nodes[3], nodes[5], nodes[7],
4457 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4458 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4459 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4462 while ( srcElements.Length() < myLastCreatedElems.Length() )
4463 srcElements.Append( myLastCreatedElems.Last() );
4465 } // loop on swept elements
4468 //=======================================================================
4469 //function : RotationSweep
4471 //=======================================================================
4473 SMESH_MeshEditor::PGroupIDs
4474 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4475 const gp_Ax1& theAxis,
4476 const double theAngle,
4477 const int theNbSteps,
4478 const double theTol,
4479 const bool theMakeGroups,
4480 const bool theMakeWalls)
4482 myLastCreatedElems.Clear();
4483 myLastCreatedNodes.Clear();
4485 // source elements for each generated one
4486 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4488 MESSAGE( "RotationSweep()");
4490 aTrsf.SetRotation( theAxis, theAngle );
4492 aTrsf2.SetRotation( theAxis, theAngle/2. );
4494 gp_Lin aLine( theAxis );
4495 double aSqTol = theTol * theTol;
4497 SMESHDS_Mesh* aMesh = GetMeshDS();
4499 TNodeOfNodeListMap mapNewNodes;
4500 TElemOfVecOfNnlmiMap mapElemNewNodes;
4501 TElemOfElemListMap newElemsMap;
4503 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4504 myMesh->NbFaces(ORDER_QUADRATIC) +
4505 myMesh->NbVolumes(ORDER_QUADRATIC) );
4507 TIDSortedElemSet::iterator itElem;
4508 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4509 const SMDS_MeshElement* elem = *itElem;
4510 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4512 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4513 newNodesItVec.reserve( elem->NbNodes() );
4515 // loop on elem nodes
4516 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4517 while ( itN->more() )
4519 // check if a node has been already sweeped
4520 const SMDS_MeshNode* node = cast2Node( itN->next() );
4522 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4524 aXYZ.Coord( coord[0], coord[1], coord[2] );
4525 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4527 TNodeOfNodeListMapItr nIt =
4528 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4529 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4530 if ( listNewNodes.empty() )
4532 // check if we are to create medium nodes between corner ones
4533 bool needMediumNodes = false;
4534 if ( isQuadraticMesh )
4536 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4537 while (it->more() && !needMediumNodes )
4539 const SMDS_MeshElement* invElem = it->next();
4540 if ( invElem != elem && !theElems.count( invElem )) continue;
4541 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4542 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4543 needMediumNodes = true;
4548 const SMDS_MeshNode * newNode = node;
4549 for ( int i = 0; i < theNbSteps; i++ ) {
4551 if ( needMediumNodes ) // create a medium node
4553 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4554 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4555 myLastCreatedNodes.Append(newNode);
4556 srcNodes.Append( node );
4557 listNewNodes.push_back( newNode );
4558 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4561 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4563 // create a corner node
4564 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4565 myLastCreatedNodes.Append(newNode);
4566 srcNodes.Append( node );
4567 listNewNodes.push_back( newNode );
4570 listNewNodes.push_back( newNode );
4571 // if ( needMediumNodes )
4572 // listNewNodes.push_back( newNode );
4576 newNodesItVec.push_back( nIt );
4578 // make new elements
4579 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4583 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4585 PGroupIDs newGroupIDs;
4586 if ( theMakeGroups )
4587 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4593 //=======================================================================
4594 //function : CreateNode
4596 //=======================================================================
4597 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4600 const double tolnode,
4601 SMESH_SequenceOfNode& aNodes)
4603 // myLastCreatedElems.Clear();
4604 // myLastCreatedNodes.Clear();
4607 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4609 // try to search in sequence of existing nodes
4610 // if aNodes.Length()>0 we 'nave to use given sequence
4611 // else - use all nodes of mesh
4612 if(aNodes.Length()>0) {
4614 for(i=1; i<=aNodes.Length(); i++) {
4615 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4616 if(P1.Distance(P2)<tolnode)
4617 return aNodes.Value(i);
4621 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4622 while(itn->more()) {
4623 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4624 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4625 if(P1.Distance(P2)<tolnode)
4630 // create new node and return it
4631 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4632 //myLastCreatedNodes.Append(NewNode);
4637 //=======================================================================
4638 //function : ExtrusionSweep
4640 //=======================================================================
4642 SMESH_MeshEditor::PGroupIDs
4643 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4644 const gp_Vec& theStep,
4645 const int theNbSteps,
4646 TElemOfElemListMap& newElemsMap,
4647 const bool theMakeGroups,
4649 const double theTolerance)
4651 ExtrusParam aParams;
4652 aParams.myDir = gp_Dir(theStep);
4653 aParams.myNodes.Clear();
4654 aParams.mySteps = new TColStd_HSequenceOfReal;
4656 for(i=1; i<=theNbSteps; i++)
4657 aParams.mySteps->Append(theStep.Magnitude());
4660 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4664 //=======================================================================
4665 //function : ExtrusionSweep
4667 //=======================================================================
4669 SMESH_MeshEditor::PGroupIDs
4670 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4671 ExtrusParam& theParams,
4672 TElemOfElemListMap& newElemsMap,
4673 const bool theMakeGroups,
4675 const double theTolerance)
4677 myLastCreatedElems.Clear();
4678 myLastCreatedNodes.Clear();
4680 // source elements for each generated one
4681 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4683 SMESHDS_Mesh* aMesh = GetMeshDS();
4685 int nbsteps = theParams.mySteps->Length();
4687 TNodeOfNodeListMap mapNewNodes;
4688 //TNodeOfNodeVecMap mapNewNodes;
4689 TElemOfVecOfNnlmiMap mapElemNewNodes;
4690 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4692 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4693 myMesh->NbFaces(ORDER_QUADRATIC) +
4694 myMesh->NbVolumes(ORDER_QUADRATIC) );
4696 TIDSortedElemSet::iterator itElem;
4697 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4698 // check element type
4699 const SMDS_MeshElement* elem = *itElem;
4700 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4703 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4704 newNodesItVec.reserve( elem->NbNodes() );
4706 // loop on elem nodes
4707 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4708 while ( itN->more() )
4710 // check if a node has been already sweeped
4711 const SMDS_MeshNode* node = cast2Node( itN->next() );
4712 TNodeOfNodeListMap::iterator nIt =
4713 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4714 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4715 if ( listNewNodes.empty() )
4719 // check if we are to create medium nodes between corner ones
4720 bool needMediumNodes = false;
4721 if ( isQuadraticMesh )
4723 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4724 while (it->more() && !needMediumNodes )
4726 const SMDS_MeshElement* invElem = it->next();
4727 if ( invElem != elem && !theElems.count( invElem )) continue;
4728 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4729 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4730 needMediumNodes = true;
4734 double coord[] = { node->X(), node->Y(), node->Z() };
4735 for ( int i = 0; i < nbsteps; i++ )
4737 if ( needMediumNodes ) // create a medium node
4739 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4740 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4741 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4742 if( theFlags & EXTRUSION_FLAG_SEW ) {
4743 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4744 theTolerance, theParams.myNodes);
4745 listNewNodes.push_back( newNode );
4748 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4749 myLastCreatedNodes.Append(newNode);
4750 srcNodes.Append( node );
4751 listNewNodes.push_back( newNode );
4754 // create a corner node
4755 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4756 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4757 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4758 if( theFlags & EXTRUSION_FLAG_SEW ) {
4759 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4760 theTolerance, theParams.myNodes);
4761 listNewNodes.push_back( newNode );
4764 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4765 myLastCreatedNodes.Append(newNode);
4766 srcNodes.Append( node );
4767 listNewNodes.push_back( newNode );
4771 newNodesItVec.push_back( nIt );
4773 // make new elements
4774 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4777 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4778 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4780 PGroupIDs newGroupIDs;
4781 if ( theMakeGroups )
4782 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4787 //=======================================================================
4788 //function : ExtrusionAlongTrack
4790 //=======================================================================
4791 SMESH_MeshEditor::Extrusion_Error
4792 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4793 SMESH_subMesh* theTrack,
4794 const SMDS_MeshNode* theN1,
4795 const bool theHasAngles,
4796 list<double>& theAngles,
4797 const bool theLinearVariation,
4798 const bool theHasRefPoint,
4799 const gp_Pnt& theRefPoint,
4800 const bool theMakeGroups)
4802 MESSAGE("ExtrusionAlongTrack");
4803 myLastCreatedElems.Clear();
4804 myLastCreatedNodes.Clear();
4807 std::list<double> aPrms;
4808 TIDSortedElemSet::iterator itElem;
4811 TopoDS_Edge aTrackEdge;
4812 TopoDS_Vertex aV1, aV2;
4814 SMDS_ElemIteratorPtr aItE;
4815 SMDS_NodeIteratorPtr aItN;
4816 SMDSAbs_ElementType aTypeE;
4818 TNodeOfNodeListMap mapNewNodes;
4821 aNbE = theElements.size();
4824 return EXTR_NO_ELEMENTS;
4826 // 1.1 Track Pattern
4829 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4831 aItE = pSubMeshDS->GetElements();
4832 while ( aItE->more() ) {
4833 const SMDS_MeshElement* pE = aItE->next();
4834 aTypeE = pE->GetType();
4835 // Pattern must contain links only
4836 if ( aTypeE != SMDSAbs_Edge )
4837 return EXTR_PATH_NOT_EDGE;
4840 list<SMESH_MeshEditor_PathPoint> fullList;
4842 const TopoDS_Shape& aS = theTrack->GetSubShape();
4843 // Sub-shape for the Pattern must be an Edge or Wire
4844 if( aS.ShapeType() == TopAbs_EDGE ) {
4845 aTrackEdge = TopoDS::Edge( aS );
4846 // the Edge must not be degenerated
4847 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4848 return EXTR_BAD_PATH_SHAPE;
4849 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4850 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4851 const SMDS_MeshNode* aN1 = aItN->next();
4852 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4853 const SMDS_MeshNode* aN2 = aItN->next();
4854 // starting node must be aN1 or aN2
4855 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4856 return EXTR_BAD_STARTING_NODE;
4857 aItN = pSubMeshDS->GetNodes();
4858 while ( aItN->more() ) {
4859 const SMDS_MeshNode* pNode = aItN->next();
4860 const SMDS_EdgePosition* pEPos =
4861 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4862 double aT = pEPos->GetUParameter();
4863 aPrms.push_back( aT );
4865 //Extrusion_Error err =
4866 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4867 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4868 list< SMESH_subMesh* > LSM;
4869 TopTools_SequenceOfShape Edges;
4870 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4871 while(itSM->more()) {
4872 SMESH_subMesh* SM = itSM->next();
4874 const TopoDS_Shape& aS = SM->GetSubShape();
4877 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4878 int startNid = theN1->GetID();
4879 TColStd_MapOfInteger UsedNums;
4881 int NbEdges = Edges.Length();
4883 for(; i<=NbEdges; i++) {
4885 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4886 for(; itLSM!=LSM.end(); itLSM++) {
4888 if(UsedNums.Contains(k)) continue;
4889 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4890 SMESH_subMesh* locTrack = *itLSM;
4891 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4892 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4893 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4894 const SMDS_MeshNode* aN1 = aItN->next();
4895 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4896 const SMDS_MeshNode* aN2 = aItN->next();
4897 // starting node must be aN1 or aN2
4898 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4899 // 2. Collect parameters on the track edge
4901 aItN = locMeshDS->GetNodes();
4902 while ( aItN->more() ) {
4903 const SMDS_MeshNode* pNode = aItN->next();
4904 const SMDS_EdgePosition* pEPos =
4905 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4906 double aT = pEPos->GetUParameter();
4907 aPrms.push_back( aT );
4909 list<SMESH_MeshEditor_PathPoint> LPP;
4910 //Extrusion_Error err =
4911 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4912 LLPPs.push_back(LPP);
4914 // update startN for search following egde
4915 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4916 else startNid = aN1->GetID();
4920 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4921 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4922 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4923 for(; itPP!=firstList.end(); itPP++) {
4924 fullList.push_back( *itPP );
4926 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4927 fullList.pop_back();
4929 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4930 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4931 itPP = currList.begin();
4932 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4933 gp_Dir D1 = PP1.Tangent();
4934 gp_Dir D2 = PP2.Tangent();
4935 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4936 (D1.Z()+D2.Z())/2 ) );
4937 PP1.SetTangent(Dnew);
4938 fullList.push_back(PP1);
4940 for(; itPP!=firstList.end(); itPP++) {
4941 fullList.push_back( *itPP );
4943 PP1 = fullList.back();
4944 fullList.pop_back();
4946 // if wire not closed
4947 fullList.push_back(PP1);
4951 return EXTR_BAD_PATH_SHAPE;
4954 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4955 theHasRefPoint, theRefPoint, theMakeGroups);
4959 //=======================================================================
4960 //function : ExtrusionAlongTrack
4962 //=======================================================================
4963 SMESH_MeshEditor::Extrusion_Error
4964 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4965 SMESH_Mesh* theTrack,
4966 const SMDS_MeshNode* theN1,
4967 const bool theHasAngles,
4968 list<double>& theAngles,
4969 const bool theLinearVariation,
4970 const bool theHasRefPoint,
4971 const gp_Pnt& theRefPoint,
4972 const bool theMakeGroups)
4974 myLastCreatedElems.Clear();
4975 myLastCreatedNodes.Clear();
4978 std::list<double> aPrms;
4979 TIDSortedElemSet::iterator itElem;
4982 TopoDS_Edge aTrackEdge;
4983 TopoDS_Vertex aV1, aV2;
4985 SMDS_ElemIteratorPtr aItE;
4986 SMDS_NodeIteratorPtr aItN;
4987 SMDSAbs_ElementType aTypeE;
4989 TNodeOfNodeListMap mapNewNodes;
4992 aNbE = theElements.size();
4995 return EXTR_NO_ELEMENTS;
4997 // 1.1 Track Pattern
5000 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5002 aItE = pMeshDS->elementsIterator();
5003 while ( aItE->more() ) {
5004 const SMDS_MeshElement* pE = aItE->next();
5005 aTypeE = pE->GetType();
5006 // Pattern must contain links only
5007 if ( aTypeE != SMDSAbs_Edge )
5008 return EXTR_PATH_NOT_EDGE;
5011 list<SMESH_MeshEditor_PathPoint> fullList;
5013 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5015 if( aS == SMESH_Mesh::PseudoShape() ) {
5016 //Mesh without shape
5017 const SMDS_MeshNode* currentNode = NULL;
5018 const SMDS_MeshNode* prevNode = theN1;
5019 std::vector<const SMDS_MeshNode*> aNodesList;
5020 aNodesList.push_back(theN1);
5021 int nbEdges = 0, conn=0;
5022 const SMDS_MeshElement* prevElem = NULL;
5023 const SMDS_MeshElement* currentElem = NULL;
5024 int totalNbEdges = theTrack->NbEdges();
5025 SMDS_ElemIteratorPtr nIt;
5028 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5029 return EXTR_BAD_STARTING_NODE;
5032 conn = nbEdgeConnectivity(theN1);
5034 return EXTR_PATH_NOT_EDGE;
5036 aItE = theN1->GetInverseElementIterator();
5037 prevElem = aItE->next();
5038 currentElem = prevElem;
5040 if(totalNbEdges == 1 ) {
5041 nIt = currentElem->nodesIterator();
5042 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5043 if(currentNode == prevNode)
5044 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5045 aNodesList.push_back(currentNode);
5047 nIt = currentElem->nodesIterator();
5048 while( nIt->more() ) {
5049 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5050 if(currentNode == prevNode)
5051 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5052 aNodesList.push_back(currentNode);
5054 //case of the closed mesh
5055 if(currentNode == theN1) {
5060 conn = nbEdgeConnectivity(currentNode);
5062 return EXTR_PATH_NOT_EDGE;
5063 }else if( conn == 1 && nbEdges > 0 ) {
5068 prevNode = currentNode;
5069 aItE = currentNode->GetInverseElementIterator();
5070 currentElem = aItE->next();
5071 if( currentElem == prevElem)
5072 currentElem = aItE->next();
5073 nIt = currentElem->nodesIterator();
5074 prevElem = currentElem;
5080 if(nbEdges != totalNbEdges)
5081 return EXTR_PATH_NOT_EDGE;
5083 TopTools_SequenceOfShape Edges;
5084 double x1,x2,y1,y2,z1,z2;
5085 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5086 int startNid = theN1->GetID();
5087 for(int i = 1; i < aNodesList.size(); i++) {
5088 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5089 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5090 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5091 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5092 list<SMESH_MeshEditor_PathPoint> LPP;
5094 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5095 LLPPs.push_back(LPP);
5096 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5097 else startNid = aNodesList[i-1]->GetID();
5101 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5102 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5103 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5104 for(; itPP!=firstList.end(); itPP++) {
5105 fullList.push_back( *itPP );
5108 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5109 SMESH_MeshEditor_PathPoint PP2;
5110 fullList.pop_back();
5112 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5113 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5114 itPP = currList.begin();
5115 PP2 = currList.front();
5116 gp_Dir D1 = PP1.Tangent();
5117 gp_Dir D2 = PP2.Tangent();
5118 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5119 (D1.Z()+D2.Z())/2 ) );
5120 PP1.SetTangent(Dnew);
5121 fullList.push_back(PP1);
5123 for(; itPP!=currList.end(); itPP++) {
5124 fullList.push_back( *itPP );
5126 PP1 = fullList.back();
5127 fullList.pop_back();
5129 fullList.push_back(PP1);
5131 } // Sub-shape for the Pattern must be an Edge or Wire
5132 else if( aS.ShapeType() == TopAbs_EDGE ) {
5133 aTrackEdge = TopoDS::Edge( aS );
5134 // the Edge must not be degenerated
5135 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5136 return EXTR_BAD_PATH_SHAPE;
5137 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5138 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5139 const SMDS_MeshNode* aN1 = aItN->next();
5140 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5141 const SMDS_MeshNode* aN2 = aItN->next();
5142 // starting node must be aN1 or aN2
5143 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5144 return EXTR_BAD_STARTING_NODE;
5145 aItN = pMeshDS->nodesIterator();
5146 while ( aItN->more() ) {
5147 const SMDS_MeshNode* pNode = aItN->next();
5148 if( pNode==aN1 || pNode==aN2 ) continue;
5149 const SMDS_EdgePosition* pEPos =
5150 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5151 double aT = pEPos->GetUParameter();
5152 aPrms.push_back( aT );
5154 //Extrusion_Error err =
5155 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5157 else if( aS.ShapeType() == TopAbs_WIRE ) {
5158 list< SMESH_subMesh* > LSM;
5159 TopTools_SequenceOfShape Edges;
5160 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5161 for(; eExp.More(); eExp.Next()) {
5162 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5163 if( BRep_Tool::Degenerated(E) ) continue;
5164 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5170 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5171 int startNid = theN1->GetID();
5172 TColStd_MapOfInteger UsedNums;
5173 int NbEdges = Edges.Length();
5175 for(; i<=NbEdges; i++) {
5177 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5178 for(; itLSM!=LSM.end(); itLSM++) {
5180 if(UsedNums.Contains(k)) continue;
5181 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5182 SMESH_subMesh* locTrack = *itLSM;
5183 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5184 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5185 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5186 const SMDS_MeshNode* aN1 = aItN->next();
5187 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5188 const SMDS_MeshNode* aN2 = aItN->next();
5189 // starting node must be aN1 or aN2
5190 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5191 // 2. Collect parameters on the track edge
5193 aItN = locMeshDS->GetNodes();
5194 while ( aItN->more() ) {
5195 const SMDS_MeshNode* pNode = aItN->next();
5196 const SMDS_EdgePosition* pEPos =
5197 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5198 double aT = pEPos->GetUParameter();
5199 aPrms.push_back( aT );
5201 list<SMESH_MeshEditor_PathPoint> LPP;
5202 //Extrusion_Error err =
5203 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5204 LLPPs.push_back(LPP);
5206 // update startN for search following egde
5207 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5208 else startNid = aN1->GetID();
5212 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5213 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5214 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5215 for(; itPP!=firstList.end(); itPP++) {
5216 fullList.push_back( *itPP );
5218 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5219 fullList.pop_back();
5221 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5222 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5223 itPP = currList.begin();
5224 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5225 gp_Dir D1 = PP1.Tangent();
5226 gp_Dir D2 = PP2.Tangent();
5227 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5228 (D1.Z()+D2.Z())/2 ) );
5229 PP1.SetTangent(Dnew);
5230 fullList.push_back(PP1);
5232 for(; itPP!=currList.end(); itPP++) {
5233 fullList.push_back( *itPP );
5235 PP1 = fullList.back();
5236 fullList.pop_back();
5238 // if wire not closed
5239 fullList.push_back(PP1);
5243 return EXTR_BAD_PATH_SHAPE;
5246 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5247 theHasRefPoint, theRefPoint, theMakeGroups);
5251 //=======================================================================
5252 //function : MakeEdgePathPoints
5253 //purpose : auxilary for ExtrusionAlongTrack
5254 //=======================================================================
5255 SMESH_MeshEditor::Extrusion_Error
5256 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5257 const TopoDS_Edge& aTrackEdge,
5259 list<SMESH_MeshEditor_PathPoint>& LPP)
5261 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5263 aTolVec2=aTolVec*aTolVec;
5265 TopoDS_Vertex aV1, aV2;
5266 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5267 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5268 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5269 // 2. Collect parameters on the track edge
5270 aPrms.push_front( aT1 );
5271 aPrms.push_back( aT2 );
5274 if( FirstIsStart ) {
5285 SMESH_MeshEditor_PathPoint aPP;
5286 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5287 std::list<double>::iterator aItD = aPrms.begin();
5288 for(; aItD != aPrms.end(); ++aItD) {
5292 aC3D->D1( aT, aP3D, aVec );
5293 aL2 = aVec.SquareMagnitude();
5294 if ( aL2 < aTolVec2 )
5295 return EXTR_CANT_GET_TANGENT;
5296 gp_Dir aTgt( aVec );
5298 aPP.SetTangent( aTgt );
5299 aPP.SetParameter( aT );
5306 //=======================================================================
5307 //function : MakeExtrElements
5308 //purpose : auxilary for ExtrusionAlongTrack
5309 //=======================================================================
5310 SMESH_MeshEditor::Extrusion_Error
5311 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5312 list<SMESH_MeshEditor_PathPoint>& fullList,
5313 const bool theHasAngles,
5314 list<double>& theAngles,
5315 const bool theLinearVariation,
5316 const bool theHasRefPoint,
5317 const gp_Pnt& theRefPoint,
5318 const bool theMakeGroups)
5320 MESSAGE("MakeExtrElements");
5321 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5322 int aNbTP = fullList.size();
5323 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5325 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5326 LinearAngleVariation(aNbTP-1, theAngles);
5328 vector<double> aAngles( aNbTP );
5330 for(; j<aNbTP; ++j) {
5333 if ( theHasAngles ) {
5335 std::list<double>::iterator aItD = theAngles.begin();
5336 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5338 aAngles[j] = anAngle;
5341 // fill vector of path points with angles
5342 //aPPs.resize(fullList.size());
5344 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5345 for(; itPP!=fullList.end(); itPP++) {
5347 SMESH_MeshEditor_PathPoint PP = *itPP;
5348 PP.SetAngle(aAngles[j]);
5352 TNodeOfNodeListMap mapNewNodes;
5353 TElemOfVecOfNnlmiMap mapElemNewNodes;
5354 TElemOfElemListMap newElemsMap;
5355 TIDSortedElemSet::iterator itElem;
5358 SMDSAbs_ElementType aTypeE;
5359 // source elements for each generated one
5360 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5362 // 3. Center of rotation aV0
5363 gp_Pnt aV0 = theRefPoint;
5365 if ( !theHasRefPoint ) {
5367 aGC.SetCoord( 0.,0.,0. );
5369 itElem = theElements.begin();
5370 for ( ; itElem != theElements.end(); itElem++ ) {
5371 const SMDS_MeshElement* elem = *itElem;
5373 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5374 while ( itN->more() ) {
5375 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5380 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5381 list<const SMDS_MeshNode*> aLNx;
5382 mapNewNodes[node] = aLNx;
5384 gp_XYZ aXYZ( aX, aY, aZ );
5392 } // if (!theHasRefPoint) {
5393 mapNewNodes.clear();
5395 // 4. Processing the elements
5396 SMESHDS_Mesh* aMesh = GetMeshDS();
5398 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5399 // check element type
5400 const SMDS_MeshElement* elem = *itElem;
5401 aTypeE = elem->GetType();
5402 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5405 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5406 newNodesItVec.reserve( elem->NbNodes() );
5408 // loop on elem nodes
5410 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5411 while ( itN->more() )
5414 // check if a node has been already processed
5415 const SMDS_MeshNode* node =
5416 static_cast<const SMDS_MeshNode*>( itN->next() );
5417 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5418 if ( nIt == mapNewNodes.end() ) {
5419 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5420 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5423 aX = node->X(); aY = node->Y(); aZ = node->Z();
5425 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5426 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5427 gp_Ax1 anAx1, anAxT1T0;
5428 gp_Dir aDT1x, aDT0x, aDT1T0;
5433 aPN0.SetCoord(aX, aY, aZ);
5435 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5437 aDT0x= aPP0.Tangent();
5438 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5440 for ( j = 1; j < aNbTP; ++j ) {
5441 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5443 aDT1x = aPP1.Tangent();
5444 aAngle1x = aPP1.Angle();
5446 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5448 gp_Vec aV01x( aP0x, aP1x );
5449 aTrsf.SetTranslation( aV01x );
5452 aV1x = aV0x.Transformed( aTrsf );
5453 aPN1 = aPN0.Transformed( aTrsf );
5455 // rotation 1 [ T1,T0 ]
5456 aAngleT1T0=-aDT1x.Angle( aDT0x );
5457 if (fabs(aAngleT1T0) > aTolAng) {
5459 anAxT1T0.SetLocation( aV1x );
5460 anAxT1T0.SetDirection( aDT1T0 );
5461 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5463 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5467 if ( theHasAngles ) {
5468 anAx1.SetLocation( aV1x );
5469 anAx1.SetDirection( aDT1x );
5470 aTrsfRot.SetRotation( anAx1, aAngle1x );
5472 aPN1 = aPN1.Transformed( aTrsfRot );
5476 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5477 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5478 // create additional node
5479 double x = ( aPN1.X() + aPN0.X() )/2.;
5480 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5481 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5482 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5483 myLastCreatedNodes.Append(newNode);
5484 srcNodes.Append( node );
5485 listNewNodes.push_back( newNode );
5490 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5491 myLastCreatedNodes.Append(newNode);
5492 srcNodes.Append( node );
5493 listNewNodes.push_back( newNode );
5503 // if current elem is quadratic and current node is not medium
5504 // we have to check - may be it is needed to insert additional nodes
5505 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5506 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5507 if(listNewNodes.size()==aNbTP-1) {
5508 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5509 gp_XYZ P(node->X(), node->Y(), node->Z());
5510 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5512 for(i=0; i<aNbTP-1; i++) {
5513 const SMDS_MeshNode* N = *it;
5514 double x = ( N->X() + P.X() )/2.;
5515 double y = ( N->Y() + P.Y() )/2.;
5516 double z = ( N->Z() + P.Z() )/2.;
5517 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5518 srcNodes.Append( node );
5519 myLastCreatedNodes.Append(newN);
5522 P = gp_XYZ(N->X(),N->Y(),N->Z());
5524 listNewNodes.clear();
5525 for(i=0; i<2*(aNbTP-1); i++) {
5526 listNewNodes.push_back(aNodes[i]);
5532 newNodesItVec.push_back( nIt );
5534 // make new elements
5535 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5536 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5537 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5540 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5542 if ( theMakeGroups )
5543 generateGroups( srcNodes, srcElems, "extruded");
5549 //=======================================================================
5550 //function : LinearAngleVariation
5551 //purpose : auxilary for ExtrusionAlongTrack
5552 //=======================================================================
5553 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5554 list<double>& Angles)
5556 int nbAngles = Angles.size();
5557 if( nbSteps > nbAngles ) {
5558 vector<double> theAngles(nbAngles);
5559 list<double>::iterator it = Angles.begin();
5561 for(; it!=Angles.end(); it++) {
5563 theAngles[i] = (*it);
5566 double rAn2St = double( nbAngles ) / double( nbSteps );
5567 double angPrev = 0, angle;
5568 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5569 double angCur = rAn2St * ( iSt+1 );
5570 double angCurFloor = floor( angCur );
5571 double angPrevFloor = floor( angPrev );
5572 if ( angPrevFloor == angCurFloor )
5573 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5575 int iP = int( angPrevFloor );
5576 double angPrevCeil = ceil(angPrev);
5577 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5579 int iC = int( angCurFloor );
5580 if ( iC < nbAngles )
5581 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5583 iP = int( angPrevCeil );
5585 angle += theAngles[ iC ];
5587 res.push_back(angle);
5592 for(; it!=res.end(); it++)
5593 Angles.push_back( *it );
5598 //================================================================================
5600 * \brief Move or copy theElements applying theTrsf to their nodes
5601 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5602 * \param theTrsf - transformation to apply
5603 * \param theCopy - if true, create translated copies of theElems
5604 * \param theMakeGroups - if true and theCopy, create translated groups
5605 * \param theTargetMesh - mesh to copy translated elements into
5606 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5608 //================================================================================
5610 SMESH_MeshEditor::PGroupIDs
5611 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5612 const gp_Trsf& theTrsf,
5614 const bool theMakeGroups,
5615 SMESH_Mesh* theTargetMesh)
5617 myLastCreatedElems.Clear();
5618 myLastCreatedNodes.Clear();
5620 bool needReverse = false;
5621 string groupPostfix;
5622 switch ( theTrsf.Form() ) {
5624 MESSAGE("gp_PntMirror");
5626 groupPostfix = "mirrored";
5629 MESSAGE("gp_Ax1Mirror");
5630 groupPostfix = "mirrored";
5633 MESSAGE("gp_Ax2Mirror");
5635 groupPostfix = "mirrored";
5638 MESSAGE("gp_Rotation");
5639 groupPostfix = "rotated";
5641 case gp_Translation:
5642 MESSAGE("gp_Translation");
5643 groupPostfix = "translated";
5646 MESSAGE("gp_Scale");
5647 groupPostfix = "scaled";
5649 case gp_CompoundTrsf: // different scale by axis
5650 MESSAGE("gp_CompoundTrsf");
5651 groupPostfix = "scaled";
5655 needReverse = false;
5656 groupPostfix = "transformed";
5659 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5660 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5661 SMESHDS_Mesh* aMesh = GetMeshDS();
5664 // map old node to new one
5665 TNodeNodeMap nodeMap;
5667 // elements sharing moved nodes; those of them which have all
5668 // nodes mirrored but are not in theElems are to be reversed
5669 TIDSortedElemSet inverseElemSet;
5671 // source elements for each generated one
5672 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5674 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5675 TIDSortedElemSet orphanNode;
5677 if ( theElems.empty() ) // transform the whole mesh
5680 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5681 while ( eIt->more() ) theElems.insert( eIt->next() );
5683 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5684 while ( nIt->more() )
5686 const SMDS_MeshNode* node = nIt->next();
5687 if ( node->NbInverseElements() == 0)
5688 orphanNode.insert( node );
5692 // loop on elements to transform nodes : first orphan nodes then elems
5693 TIDSortedElemSet::iterator itElem;
5694 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5695 for (int i=0; i<2; i++)
5696 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5697 const SMDS_MeshElement* elem = *itElem;
5701 // loop on elem nodes
5702 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5703 while ( itN->more() ) {
5705 const SMDS_MeshNode* node = cast2Node( itN->next() );
5706 // check if a node has been already transformed
5707 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5708 nodeMap.insert( make_pair ( node, node ));
5709 if ( !n2n_isnew.second )
5713 coord[0] = node->X();
5714 coord[1] = node->Y();
5715 coord[2] = node->Z();
5716 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5717 if ( theTargetMesh ) {
5718 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5719 n2n_isnew.first->second = newNode;
5720 myLastCreatedNodes.Append(newNode);
5721 srcNodes.Append( node );
5723 else if ( theCopy ) {
5724 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5725 n2n_isnew.first->second = newNode;
5726 myLastCreatedNodes.Append(newNode);
5727 srcNodes.Append( node );
5730 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5731 // node position on shape becomes invalid
5732 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5733 ( SMDS_SpacePosition::originSpacePosition() );
5736 // keep inverse elements
5737 if ( !theCopy && !theTargetMesh && needReverse ) {
5738 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5739 while ( invElemIt->more() ) {
5740 const SMDS_MeshElement* iel = invElemIt->next();
5741 inverseElemSet.insert( iel );
5747 // either create new elements or reverse mirrored ones
5748 if ( !theCopy && !needReverse && !theTargetMesh )
5751 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5752 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5753 theElems.insert( *invElemIt );
5755 // Replicate or reverse elements
5757 std::vector<int> iForw;
5758 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5760 const SMDS_MeshElement* elem = *itElem;
5761 if ( !elem ) continue;
5763 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5764 int nbNodes = elem->NbNodes();
5765 if ( geomType == SMDSGeom_NONE ) continue; // node
5767 switch ( geomType ) {
5769 case SMDSGeom_POLYGON: // ---------------------- polygon
5771 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5773 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5774 while (itN->more()) {
5775 const SMDS_MeshNode* node =
5776 static_cast<const SMDS_MeshNode*>(itN->next());
5777 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5778 if (nodeMapIt == nodeMap.end())
5779 break; // not all nodes transformed
5781 // reverse mirrored faces and volumes
5782 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5784 poly_nodes[iNode] = (*nodeMapIt).second;
5788 if ( iNode != nbNodes )
5789 continue; // not all nodes transformed
5791 if ( theTargetMesh ) {
5792 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5793 srcElems.Append( elem );
5795 else if ( theCopy ) {
5796 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5797 srcElems.Append( elem );
5800 aMesh->ChangePolygonNodes(elem, poly_nodes);
5805 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5807 const SMDS_VtkVolume* aPolyedre =
5808 dynamic_cast<const SMDS_VtkVolume*>( elem );
5810 MESSAGE("Warning: bad volumic element");
5814 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5815 vector<int> quantities; quantities.reserve( nbNodes );
5817 bool allTransformed = true;
5818 int nbFaces = aPolyedre->NbFaces();
5819 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5820 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5821 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5822 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5823 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5824 if (nodeMapIt == nodeMap.end()) {
5825 allTransformed = false; // not all nodes transformed
5827 poly_nodes.push_back((*nodeMapIt).second);
5829 if ( needReverse && allTransformed )
5830 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5832 quantities.push_back(nbFaceNodes);
5834 if ( !allTransformed )
5835 continue; // not all nodes transformed
5837 if ( theTargetMesh ) {
5838 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5839 srcElems.Append( elem );
5841 else if ( theCopy ) {
5842 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5843 srcElems.Append( elem );
5846 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5851 case SMDSGeom_BALL: // -------------------- Ball
5853 if ( !theCopy && !theTargetMesh ) continue;
5855 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5856 if (nodeMapIt == nodeMap.end())
5857 continue; // not all nodes transformed
5859 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5860 if ( theTargetMesh ) {
5861 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5862 srcElems.Append( elem );
5865 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5866 srcElems.Append( elem );
5871 default: // ----------------------- Regular elements
5873 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5874 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5875 const std::vector<int>& i = needReverse ? iRev : iForw;
5877 // find transformed nodes
5878 vector<const SMDS_MeshNode*> nodes(nbNodes);
5880 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5881 while ( itN->more() ) {
5882 const SMDS_MeshNode* node =
5883 static_cast<const SMDS_MeshNode*>( itN->next() );
5884 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5885 if ( nodeMapIt == nodeMap.end() )
5886 break; // not all nodes transformed
5887 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5889 if ( iNode != nbNodes )
5890 continue; // not all nodes transformed
5892 if ( theTargetMesh ) {
5893 if ( SMDS_MeshElement* copy =
5894 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5895 myLastCreatedElems.Append( copy );
5896 srcElems.Append( elem );
5899 else if ( theCopy ) {
5900 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5901 srcElems.Append( elem );
5904 // reverse element as it was reversed by transformation
5906 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5908 } // switch ( geomType )
5910 } // loop on elements
5912 PGroupIDs newGroupIDs;
5914 if ( ( theMakeGroups && theCopy ) ||
5915 ( theMakeGroups && theTargetMesh ) )
5916 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5921 //=======================================================================
5923 * \brief Create groups of elements made during transformation
5924 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5925 * \param elemGens - elements making corresponding myLastCreatedElems
5926 * \param postfix - to append to names of new groups
5928 //=======================================================================
5930 SMESH_MeshEditor::PGroupIDs
5931 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5932 const SMESH_SequenceOfElemPtr& elemGens,
5933 const std::string& postfix,
5934 SMESH_Mesh* targetMesh)
5936 PGroupIDs newGroupIDs( new list<int> );
5937 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5939 // Sort existing groups by types and collect their names
5941 // to store an old group and a generated new one
5942 typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5943 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5944 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5946 set< string > groupNames;
5948 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5949 if ( !groupIt->more() ) return newGroupIDs;
5951 int newGroupID = mesh->GetGroupIds().back()+1;
5952 while ( groupIt->more() )
5954 SMESH_Group * group = groupIt->next();
5955 if ( !group ) continue;
5956 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5957 if ( !groupDS || groupDS->IsEmpty() ) continue;
5958 groupNames.insert( group->GetName() );
5959 groupDS->SetStoreName( group->GetName() );
5960 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5961 groupDS->GetType() );
5962 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5963 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5966 // Loop on nodes and elements to add them in new groups
5968 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5970 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5971 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5972 if ( gens.Length() != elems.Length() )
5973 throw SALOME_Exception(LOCALIZED("invalid args"));
5975 // loop on created elements
5976 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5978 const SMDS_MeshElement* sourceElem = gens( iElem );
5979 if ( !sourceElem ) {
5980 MESSAGE("generateGroups(): NULL source element");
5983 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5984 if ( groupsOldNew.empty() ) { // no groups of this type at all
5985 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5986 ++iElem; // skip all elements made by sourceElem
5989 // collect all elements made by sourceElem
5990 list< const SMDS_MeshElement* > resultElems;
5991 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5992 if ( resElem != sourceElem )
5993 resultElems.push_back( resElem );
5994 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5995 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5996 if ( resElem != sourceElem )
5997 resultElems.push_back( resElem );
5999 // add resultElems to groups made by ones the sourceElem belongs to
6000 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6001 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6003 SMESHDS_GroupBase* oldGroup = gOldNew->first;
6004 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6006 // fill in a new group
6007 SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6008 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6009 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6010 newGroup.Add( *resElemIt );
6013 } // loop on created elements
6014 }// loop on nodes and elements
6016 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6018 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6020 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6021 SMESHDS_Group* newGroupDS = orderedOldNewGroups[i]->second;
6022 if ( newGroupDS->IsEmpty() )
6024 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6029 string name = oldGroupDS->GetStoreName();
6030 if ( !targetMesh ) {
6034 while ( !groupNames.insert( name ).second ) // name exists
6035 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6037 newGroupDS->SetStoreName( name.c_str() );
6039 // make a SMESH_Groups
6040 mesh->AddGroup( newGroupDS );
6041 newGroupIDs->push_back( newGroupDS->GetID() );
6044 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6051 //================================================================================
6053 * \brief Return list of group of nodes close to each other within theTolerance
6054 * Search among theNodes or in the whole mesh if theNodes is empty using
6055 * an Octree algorithm
6057 //================================================================================
6059 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6060 const double theTolerance,
6061 TListOfListOfNodes & theGroupsOfNodes)
6063 myLastCreatedElems.Clear();
6064 myLastCreatedNodes.Clear();
6066 if ( theNodes.empty() )
6067 { // get all nodes in the mesh
6068 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6069 while ( nIt->more() )
6070 theNodes.insert( theNodes.end(),nIt->next());
6073 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6077 //=======================================================================
6079 * \brief Implementation of search for the node closest to point
6081 //=======================================================================
6083 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6085 //---------------------------------------------------------------------
6087 * \brief Constructor
6089 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6091 myMesh = ( SMESHDS_Mesh* ) theMesh;
6093 TIDSortedNodeSet nodes;
6095 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6096 while ( nIt->more() )
6097 nodes.insert( nodes.end(), nIt->next() );
6099 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6101 // get max size of a leaf box
6102 SMESH_OctreeNode* tree = myOctreeNode;
6103 while ( !tree->isLeaf() )
6105 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6109 myHalfLeafSize = tree->maxSize() / 2.;
6112 //---------------------------------------------------------------------
6114 * \brief Move node and update myOctreeNode accordingly
6116 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6118 myOctreeNode->UpdateByMoveNode( node, toPnt );
6119 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6122 //---------------------------------------------------------------------
6124 * \brief Do it's job
6126 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6128 map<double, const SMDS_MeshNode*> dist2Nodes;
6129 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6130 if ( !dist2Nodes.empty() )
6131 return dist2Nodes.begin()->second;
6132 list<const SMDS_MeshNode*> nodes;
6133 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6135 double minSqDist = DBL_MAX;
6136 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6138 // sort leafs by their distance from thePnt
6139 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6140 TDistTreeMap treeMap;
6141 list< SMESH_OctreeNode* > treeList;
6142 list< SMESH_OctreeNode* >::iterator trIt;
6143 treeList.push_back( myOctreeNode );
6145 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6146 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6147 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6149 SMESH_OctreeNode* tree = *trIt;
6150 if ( !tree->isLeaf() ) // put children to the queue
6152 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6153 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6154 while ( cIt->more() )
6155 treeList.push_back( cIt->next() );
6157 else if ( tree->NbNodes() ) // put a tree to the treeMap
6159 const Bnd_B3d& box = *tree->getBox();
6160 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6161 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6162 if ( !it_in.second ) // not unique distance to box center
6163 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6166 // find distance after which there is no sense to check tree's
6167 double sqLimit = DBL_MAX;
6168 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6169 if ( treeMap.size() > 5 ) {
6170 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6171 const Bnd_B3d& box = *closestTree->getBox();
6172 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6173 sqLimit = limit * limit;
6175 // get all nodes from trees
6176 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6177 if ( sqDist_tree->first > sqLimit )
6179 SMESH_OctreeNode* tree = sqDist_tree->second;
6180 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6183 // find closest among nodes
6184 minSqDist = DBL_MAX;
6185 const SMDS_MeshNode* closestNode = 0;
6186 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6187 for ( ; nIt != nodes.end(); ++nIt ) {
6188 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6189 if ( minSqDist > sqDist ) {
6197 //---------------------------------------------------------------------
6201 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6203 //---------------------------------------------------------------------
6205 * \brief Return the node tree
6207 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6210 SMESH_OctreeNode* myOctreeNode;
6211 SMESHDS_Mesh* myMesh;
6212 double myHalfLeafSize; // max size of a leaf box
6215 //=======================================================================
6217 * \brief Return SMESH_NodeSearcher
6219 //=======================================================================
6221 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6223 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6226 // ========================================================================
6227 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6229 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6230 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6231 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6233 //=======================================================================
6235 * \brief Octal tree of bounding boxes of elements
6237 //=======================================================================
6239 class ElementBndBoxTree : public SMESH_Octree
6243 ElementBndBoxTree(const SMDS_Mesh& mesh,
6244 SMDSAbs_ElementType elemType,
6245 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6246 double tolerance = NodeRadius );
6247 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6248 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6249 void getElementsInSphere ( const gp_XYZ& center,
6250 const double radius, TIDSortedElemSet& foundElems);
6251 size_t getSize() { return std::max( _size, _elements.size() ); }
6252 ~ElementBndBoxTree();
6255 ElementBndBoxTree():_size(0) {}
6256 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6257 void buildChildrenData();
6258 Bnd_B3d* buildRootBox();
6260 //!< Bounding box of element
6261 struct ElementBox : public Bnd_B3d
6263 const SMDS_MeshElement* _element;
6264 int _refCount; // an ElementBox can be included in several tree branches
6265 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6267 vector< ElementBox* > _elements;
6271 //================================================================================
6273 * \brief ElementBndBoxTree creation
6275 //================================================================================
6277 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6278 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6280 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6281 _elements.reserve( nbElems );
6283 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6284 while ( elemIt->more() )
6285 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6290 //================================================================================
6294 //================================================================================
6296 ElementBndBoxTree::~ElementBndBoxTree()
6298 for ( int i = 0; i < _elements.size(); ++i )
6299 if ( --_elements[i]->_refCount <= 0 )
6300 delete _elements[i];
6303 //================================================================================
6305 * \brief Return the maximal box
6307 //================================================================================
6309 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6311 Bnd_B3d* box = new Bnd_B3d;
6312 for ( int i = 0; i < _elements.size(); ++i )
6313 box->Add( *_elements[i] );
6317 //================================================================================
6319 * \brief Redistrubute element boxes among children
6321 //================================================================================
6323 void ElementBndBoxTree::buildChildrenData()
6325 for ( int i = 0; i < _elements.size(); ++i )
6327 for (int j = 0; j < 8; j++)
6329 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6331 _elements[i]->_refCount++;
6332 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6335 _elements[i]->_refCount--;
6337 _size = _elements.size();
6338 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6340 for (int j = 0; j < 8; j++)
6342 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6343 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6344 child->myIsLeaf = true;
6346 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6347 SMESHUtils::CompactVector( child->_elements );
6351 //================================================================================
6353 * \brief Return elements which can include the point
6355 //================================================================================
6357 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6358 TIDSortedElemSet& foundElems)
6360 if ( getBox()->IsOut( point.XYZ() ))
6365 for ( int i = 0; i < _elements.size(); ++i )
6366 if ( !_elements[i]->IsOut( point.XYZ() ))
6367 foundElems.insert( _elements[i]->_element );
6371 for (int i = 0; i < 8; i++)
6372 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6376 //================================================================================
6378 * \brief Return elements which can be intersected by the line
6380 //================================================================================
6382 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6383 TIDSortedElemSet& foundElems)
6385 if ( getBox()->IsOut( line ))
6390 for ( int i = 0; i < _elements.size(); ++i )
6391 if ( !_elements[i]->IsOut( line ))
6392 foundElems.insert( _elements[i]->_element );
6396 for (int i = 0; i < 8; i++)
6397 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6401 //================================================================================
6403 * \brief Return elements from leaves intersecting the sphere
6405 //================================================================================
6407 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6408 const double radius,
6409 TIDSortedElemSet& foundElems)
6411 if ( getBox()->IsOut( center, radius ))
6416 for ( int i = 0; i < _elements.size(); ++i )
6417 if ( !_elements[i]->IsOut( center, radius ))
6418 foundElems.insert( _elements[i]->_element );
6422 for (int i = 0; i < 8; i++)
6423 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6427 //================================================================================
6429 * \brief Construct the element box
6431 //================================================================================
6433 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6437 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6438 while ( nIt->more() )
6439 Add( SMESH_TNodeXYZ( nIt->next() ));
6440 Enlarge( tolerance );
6445 //=======================================================================
6447 * \brief Implementation of search for the elements by point and
6448 * of classification of point in 2D mesh
6450 //=======================================================================
6452 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6454 SMESHDS_Mesh* _mesh;
6455 SMDS_ElemIteratorPtr _meshPartIt;
6456 ElementBndBoxTree* _ebbTree;
6457 SMESH_NodeSearcherImpl* _nodeSearcher;
6458 SMDSAbs_ElementType _elementType;
6460 bool _outerFacesFound;
6461 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6463 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6464 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6465 ~SMESH_ElementSearcherImpl()
6467 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6468 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6470 virtual int FindElementsByPoint(const gp_Pnt& point,
6471 SMDSAbs_ElementType type,
6472 vector< const SMDS_MeshElement* >& foundElements);
6473 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6474 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6475 SMDSAbs_ElementType type );
6477 void GetElementsNearLine( const gp_Ax1& line,
6478 SMDSAbs_ElementType type,
6479 vector< const SMDS_MeshElement* >& foundElems);
6480 double getTolerance();
6481 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6482 const double tolerance, double & param);
6483 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6484 bool isOuterBoundary(const SMDS_MeshElement* face) const
6486 return _outerFaces.empty() || _outerFaces.count(face);
6488 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6490 const SMDS_MeshElement* _face;
6492 bool _coincides; //!< the line lays in face plane
6493 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6494 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6496 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6499 TIDSortedElemSet _faces;
6500 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6501 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6505 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6507 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6508 << ", _coincides="<<i._coincides << ")";
6511 //=======================================================================
6513 * \brief define tolerance for search
6515 //=======================================================================
6517 double SMESH_ElementSearcherImpl::getTolerance()
6519 if ( _tolerance < 0 )
6521 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6524 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6526 double boxSize = _nodeSearcher->getTree()->maxSize();
6527 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6529 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6531 double boxSize = _ebbTree->maxSize();
6532 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6534 if ( _tolerance == 0 )
6536 // define tolerance by size of a most complex element
6537 int complexType = SMDSAbs_Volume;
6538 while ( complexType > SMDSAbs_All &&
6539 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6541 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6543 if ( complexType == int( SMDSAbs_Node ))
6545 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6547 if ( meshInfo.NbNodes() > 2 )
6548 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6552 SMDS_ElemIteratorPtr elemIt =
6553 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6554 const SMDS_MeshElement* elem = elemIt->next();
6555 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6556 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6558 while ( nodeIt->more() )
6560 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6561 elemSize = max( dist, elemSize );
6564 _tolerance = 1e-4 * elemSize;
6570 //================================================================================
6572 * \brief Find intersection of the line and an edge of face and return parameter on line
6574 //================================================================================
6576 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6577 const SMDS_MeshElement* face,
6584 GeomAPI_ExtremaCurveCurve anExtCC;
6585 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6587 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6588 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6590 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6591 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6592 anExtCC.Init( lineCurve, edge);
6593 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6595 Quantity_Parameter pl, pe;
6596 anExtCC.LowerDistanceParameters( pl, pe );
6598 if ( ++nbInts == 2 )
6602 if ( nbInts > 0 ) param /= nbInts;
6605 //================================================================================
6607 * \brief Find all faces belonging to the outer boundary of mesh
6609 //================================================================================
6611 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6613 if ( _outerFacesFound ) return;
6615 // Collect all outer faces by passing from one outer face to another via their links
6616 // and BTW find out if there are internal faces at all.
6618 // checked links and links where outer boundary meets internal one
6619 set< SMESH_TLink > visitedLinks, seamLinks;
6621 // links to treat with already visited faces sharing them
6622 list < TFaceLink > startLinks;
6624 // load startLinks with the first outerFace
6625 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6626 _outerFaces.insert( outerFace );
6628 TIDSortedElemSet emptySet;
6629 while ( !startLinks.empty() )
6631 const SMESH_TLink& link = startLinks.front()._link;
6632 TIDSortedElemSet& faces = startLinks.front()._faces;
6634 outerFace = *faces.begin();
6635 // find other faces sharing the link
6636 const SMDS_MeshElement* f;
6637 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6640 // select another outer face among the found
6641 const SMDS_MeshElement* outerFace2 = 0;
6642 if ( faces.size() == 2 )
6644 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6646 else if ( faces.size() > 2 )
6648 seamLinks.insert( link );
6650 // link direction within the outerFace
6651 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6652 SMESH_TNodeXYZ( link.node2()));
6653 int i1 = outerFace->GetNodeIndex( link.node1() );
6654 int i2 = outerFace->GetNodeIndex( link.node2() );
6655 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6656 if ( rev ) n1n2.Reverse();
6658 gp_XYZ ofNorm, fNorm;
6659 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6661 // direction from the link inside outerFace
6662 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6663 // sort all other faces by angle with the dirInOF
6664 map< double, const SMDS_MeshElement* > angle2Face;
6665 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6666 for ( ; face != faces.end(); ++face )
6668 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6670 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6671 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6672 if ( angle < 0 ) angle += 2. * M_PI;
6673 angle2Face.insert( make_pair( angle, *face ));
6675 if ( !angle2Face.empty() )
6676 outerFace2 = angle2Face.begin()->second;
6679 // store the found outer face and add its links to continue seaching from
6682 _outerFaces.insert( outerFace );
6683 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6684 for ( int i = 0; i < nbNodes; ++i )
6686 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6687 if ( visitedLinks.insert( link2 ).second )
6688 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6691 startLinks.pop_front();
6693 _outerFacesFound = true;
6695 if ( !seamLinks.empty() )
6697 // There are internal boundaries touching the outher one,
6698 // find all faces of internal boundaries in order to find
6699 // faces of boundaries of holes, if any.
6704 _outerFaces.clear();
6708 //=======================================================================
6710 * \brief Find elements of given type where the given point is IN or ON.
6711 * Returns nb of found elements and elements them-selves.
6713 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6715 //=======================================================================
6717 int SMESH_ElementSearcherImpl::
6718 FindElementsByPoint(const gp_Pnt& point,
6719 SMDSAbs_ElementType type,
6720 vector< const SMDS_MeshElement* >& foundElements)
6722 foundElements.clear();
6724 double tolerance = getTolerance();
6726 // =================================================================================
6727 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6729 if ( !_nodeSearcher )
6730 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6732 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6733 if ( !closeNode ) return foundElements.size();
6735 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6736 return foundElements.size(); // to far from any node
6738 if ( type == SMDSAbs_Node )
6740 foundElements.push_back( closeNode );
6744 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6745 while ( elemIt->more() )
6746 foundElements.push_back( elemIt->next() );
6749 // =================================================================================
6750 else // elements more complex than 0D
6752 if ( !_ebbTree || _elementType != type )
6754 if ( _ebbTree ) delete _ebbTree;
6755 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6757 TIDSortedElemSet suspectElems;
6758 _ebbTree->getElementsNearPoint( point, suspectElems );
6759 TIDSortedElemSet::iterator elem = suspectElems.begin();
6760 for ( ; elem != suspectElems.end(); ++elem )
6761 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6762 foundElements.push_back( *elem );
6764 return foundElements.size();
6767 //=======================================================================
6769 * \brief Find an element of given type most close to the given point
6771 * WARNING: Only face search is implemeneted so far
6773 //=======================================================================
6775 const SMDS_MeshElement*
6776 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6777 SMDSAbs_ElementType type )
6779 const SMDS_MeshElement* closestElem = 0;
6781 if ( type == SMDSAbs_Face )
6783 if ( !_ebbTree || _elementType != type )
6785 if ( _ebbTree ) delete _ebbTree;
6786 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6788 TIDSortedElemSet suspectElems;
6789 _ebbTree->getElementsNearPoint( point, suspectElems );
6791 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6793 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6794 _ebbTree->getBox()->CornerMax() );
6796 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6797 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6799 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6800 while ( suspectElems.empty() )
6802 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6806 double minDist = std::numeric_limits<double>::max();
6807 multimap< double, const SMDS_MeshElement* > dist2face;
6808 TIDSortedElemSet::iterator elem = suspectElems.begin();
6809 for ( ; elem != suspectElems.end(); ++elem )
6811 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6813 if ( dist < minDist + 1e-10)
6816 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6819 if ( !dist2face.empty() )
6821 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6822 closestElem = d2f->second;
6823 // if there are several elements at the same distance, select one
6824 // with GC closest to the point
6825 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6826 double minDistToGC = 0;
6827 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6829 if ( minDistToGC == 0 )
6832 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6833 TXyzIterator(), gc ) / closestElem->NbNodes();
6834 minDistToGC = point.SquareDistance( gc );
6837 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6838 TXyzIterator(), gc ) / d2f->second->NbNodes();
6839 double d = point.SquareDistance( gc );
6840 if ( d < minDistToGC )
6843 closestElem = d2f->second;
6846 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6847 // <<closestElem->GetID() << " DIST " << minDist << endl;
6852 // NOT IMPLEMENTED SO FAR
6858 //================================================================================
6860 * \brief Classify the given point in the closed 2D mesh
6862 //================================================================================
6864 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6866 double tolerance = getTolerance();
6867 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6869 if ( _ebbTree ) delete _ebbTree;
6870 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6872 // Algo: analyse transition of a line starting at the point through mesh boundary;
6873 // try three lines parallel to axis of the coordinate system and perform rough
6874 // analysis. If solution is not clear perform thorough analysis.
6876 const int nbAxes = 3;
6877 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6878 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6879 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6880 multimap< int, int > nbInt2Axis; // to find the simplest case
6881 for ( int axis = 0; axis < nbAxes; ++axis )
6883 gp_Ax1 lineAxis( point, axisDir[axis]);
6884 gp_Lin line ( lineAxis );
6886 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6887 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6889 // Intersect faces with the line
6891 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6892 TIDSortedElemSet::iterator face = suspectFaces.begin();
6893 for ( ; face != suspectFaces.end(); ++face )
6897 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6898 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6900 // perform intersection
6901 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6902 if ( !intersection.IsDone() )
6904 if ( intersection.IsInQuadric() )
6906 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6908 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6910 gp_Pnt intersectionPoint = intersection.Point(1);
6911 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6912 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6915 // Analyse intersections roughly
6917 int nbInter = u2inters.size();
6921 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6922 if ( nbInter == 1 ) // not closed mesh
6923 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6925 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6928 if ( (f<0) == (l<0) )
6931 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6932 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6933 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6936 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6938 if ( _outerFacesFound ) break; // pass to thorough analysis
6940 } // three attempts - loop on CS axes
6942 // Analyse intersections thoroughly.
6943 // We make two loops maximum, on the first one we only exclude touching intersections,
6944 // on the second, if situation is still unclear, we gather and use information on
6945 // position of faces (internal or outer). If faces position is already gathered,
6946 // we make the second loop right away.
6948 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6950 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6951 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6953 int axis = nb_axis->second;
6954 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6956 gp_Ax1 lineAxis( point, axisDir[axis]);
6957 gp_Lin line ( lineAxis );
6959 // add tangent intersections to u2inters
6961 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6962 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6963 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6964 u2inters.insert(make_pair( param, *tgtInt ));
6965 tangentInters[ axis ].clear();
6967 // Count intersections before and after the point excluding touching ones.
6968 // If hasPositionInfo we count intersections of outer boundary only
6970 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6971 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6972 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6973 bool ok = ! u_int1->second._coincides;
6974 while ( ok && u_int1 != u2inters.end() )
6976 double u = u_int1->first;
6977 bool touchingInt = false;
6978 if ( ++u_int2 != u2inters.end() )
6980 // skip intersections at the same point (if the line passes through edge or node)
6982 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6988 // skip tangent intersections
6990 const SMDS_MeshElement* prevFace = u_int1->second._face;
6991 while ( ok && u_int2->second._coincides )
6993 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6999 ok = ( u_int2 != u2inters.end() );
7004 // skip intersections at the same point after tangent intersections
7007 double u2 = u_int2->first;
7009 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7015 // decide if we skipped a touching intersection
7016 if ( nbSamePnt + nbTgt > 0 )
7018 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7019 map< double, TInters >::iterator u_int = u_int1;
7020 for ( ; u_int != u_int2; ++u_int )
7022 if ( u_int->second._coincides ) continue;
7023 double dot = u_int->second._faceNorm * line.Direction();
7024 if ( dot > maxDot ) maxDot = dot;
7025 if ( dot < minDot ) minDot = dot;
7027 touchingInt = ( minDot*maxDot < 0 );
7032 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7043 u_int1 = u_int2; // to next intersection
7045 } // loop on intersections with one line
7049 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7052 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7055 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7056 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7058 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7061 if ( (f<0) == (l<0) )
7064 if ( hasPositionInfo )
7065 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7067 } // loop on intersections of the tree lines - thorough analysis
7069 if ( !hasPositionInfo )
7071 // gather info on faces position - is face in the outer boundary or not
7072 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7073 findOuterBoundary( u2inters.begin()->second._face );
7076 } // two attempts - with and w/o faces position info in the mesh
7078 return TopAbs_UNKNOWN;
7081 //=======================================================================
7083 * \brief Return elements possibly intersecting the line
7085 //=======================================================================
7087 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7088 SMDSAbs_ElementType type,
7089 vector< const SMDS_MeshElement* >& foundElems)
7091 if ( !_ebbTree || _elementType != type )
7093 if ( _ebbTree ) delete _ebbTree;
7094 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7096 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7097 _ebbTree->getElementsNearLine( line, suspectFaces );
7098 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7101 //=======================================================================
7103 * \brief Return SMESH_ElementSearcher
7105 //=======================================================================
7107 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7109 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7112 //=======================================================================
7114 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7116 //=======================================================================
7118 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7120 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7123 //=======================================================================
7125 * \brief Return true if the point is IN or ON of the element
7127 //=======================================================================
7129 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7131 if ( element->GetType() == SMDSAbs_Volume)
7133 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7136 // get ordered nodes
7138 vector< gp_XYZ > xyz;
7139 vector<const SMDS_MeshNode*> nodeList;
7141 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7142 if ( element->IsQuadratic() ) {
7143 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7144 nodeIt = f->interlacedNodesElemIterator();
7145 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7146 nodeIt = e->interlacedNodesElemIterator();
7148 while ( nodeIt->more() )
7150 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7151 xyz.push_back( SMESH_TNodeXYZ(node) );
7152 nodeList.push_back(node);
7155 int i, nbNodes = element->NbNodes();
7157 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7159 // compute face normal
7160 gp_Vec faceNorm(0,0,0);
7161 xyz.push_back( xyz.front() );
7162 nodeList.push_back( nodeList.front() );
7163 for ( i = 0; i < nbNodes; ++i )
7165 gp_Vec edge1( xyz[i+1], xyz[i]);
7166 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7167 faceNorm += edge1 ^ edge2;
7169 double normSize = faceNorm.Magnitude();
7170 if ( normSize <= tol )
7172 // degenerated face: point is out if it is out of all face edges
7173 for ( i = 0; i < nbNodes; ++i )
7175 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7176 if ( !IsOut( &edge, point, tol ))
7181 faceNorm /= normSize;
7183 // check if the point lays on face plane
7184 gp_Vec n2p( xyz[0], point );
7185 if ( fabs( n2p * faceNorm ) > tol )
7186 return true; // not on face plane
7188 // check if point is out of face boundary:
7189 // define it by closest transition of a ray point->infinity through face boundary
7190 // on the face plane.
7191 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7192 // to find intersections of the ray with the boundary.
7194 gp_Vec plnNorm = ray ^ faceNorm;
7195 normSize = plnNorm.Magnitude();
7196 if ( normSize <= tol ) return false; // point coincides with the first node
7197 plnNorm /= normSize;
7198 // for each node of the face, compute its signed distance to the plane
7199 vector<double> dist( nbNodes + 1);
7200 for ( i = 0; i < nbNodes; ++i )
7202 gp_Vec n2p( xyz[i], point );
7203 dist[i] = n2p * plnNorm;
7205 dist.back() = dist.front();
7206 // find the closest intersection
7208 double rClosest, distClosest = 1e100;;
7210 for ( i = 0; i < nbNodes; ++i )
7213 if ( fabs( dist[i]) < tol )
7215 else if ( fabs( dist[i+1]) < tol )
7217 else if ( dist[i] * dist[i+1] < 0 )
7218 r = dist[i] / ( dist[i] - dist[i+1] );
7220 continue; // no intersection
7221 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7222 gp_Vec p2int ( point, pInt);
7223 if ( p2int * ray > -tol ) // right half-space
7225 double intDist = p2int.SquareMagnitude();
7226 if ( intDist < distClosest )
7231 distClosest = intDist;
7236 return true; // no intesections - out
7238 // analyse transition
7239 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7240 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7241 gp_Vec p2int ( point, pClosest );
7242 bool out = (edgeNorm * p2int) < -tol;
7243 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7246 // ray pass through a face node; analyze transition through an adjacent edge
7247 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7248 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7249 gp_Vec edgeAdjacent( p1, p2 );
7250 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7251 bool out2 = (edgeNorm2 * p2int) < -tol;
7253 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7254 return covexCorner ? (out || out2) : (out && out2);
7256 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7258 // point is out of edge if it is NOT ON any straight part of edge
7259 // (we consider quadratic edge as being composed of two straight parts)
7260 for ( i = 1; i < nbNodes; ++i )
7262 gp_Vec edge( xyz[i-1], xyz[i]);
7263 gp_Vec n1p ( xyz[i-1], point);
7264 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7267 gp_Vec n2p( xyz[i], point );
7268 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7270 return false; // point is ON this part
7274 // Node or 0D element -------------------------------------------------------------------------
7276 gp_Vec n2p ( xyz[0], point );
7277 return n2p.Magnitude() <= tol;
7282 //=======================================================================
7286 // Position of a point relative to a segment
7290 // VERTEX 1 o----ON-----> VERTEX 2
7294 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7295 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7299 int _index; // index of vertex or segment
7301 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7302 bool operator < (const PointPos& other ) const
7304 if ( _name == other._name )
7305 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7306 return _name < other._name;
7310 //================================================================================
7312 * \brief Return of a point relative to a segment
7313 * \param point2D - the point to analyze position of
7314 * \param xyVec - end points of segments
7315 * \param index0 - 0-based index of the first point of segment
7316 * \param posToFindOut - flags of positions to detect
7317 * \retval PointPos - point position
7319 //================================================================================
7321 PointPos getPointPosition( const gp_XY& point2D,
7322 const gp_XY* segEnds,
7323 const int index0 = 0,
7324 const int posToFindOut = POS_ALL)
7326 const gp_XY& p1 = segEnds[ index0 ];
7327 const gp_XY& p2 = segEnds[ index0+1 ];
7328 const gp_XY grad = p2 - p1;
7330 if ( posToFindOut & POS_VERTEX )
7332 // check if the point2D is at "vertex 1" zone
7333 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7334 p1.Y() + grad.X() ) };
7335 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7336 return PointPos( POS_VERTEX, index0 );
7338 // check if the point2D is at "vertex 2" zone
7339 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7340 p2.Y() + grad.X() ) };
7341 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7342 return PointPos( POS_VERTEX, index0 + 1);
7344 double edgeEquation =
7345 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7346 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7350 //=======================================================================
7352 * \brief Return minimal distance from a point to a face
7354 * Currently we ignore non-planarity and 2nd order of face
7356 //=======================================================================
7358 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7359 const gp_Pnt& point )
7361 double badDistance = -1;
7362 if ( !face ) return badDistance;
7364 // coordinates of nodes (medium nodes, if any, ignored)
7365 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7366 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7367 xyz.resize( face->NbCornerNodes()+1 );
7369 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7370 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7372 gp_Vec OZ ( xyz[0], xyz[1] );
7373 gp_Vec OX ( xyz[0], xyz[2] );
7374 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7376 if ( xyz.size() < 4 ) return badDistance;
7377 OZ = gp_Vec ( xyz[0], xyz[2] );
7378 OX = gp_Vec ( xyz[0], xyz[3] );
7382 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7384 catch ( Standard_Failure ) {
7387 trsf.SetTransformation( tgtCS );
7389 // move all the nodes to 2D
7390 vector<gp_XY> xy( xyz.size() );
7391 for ( size_t i = 0;i < xyz.size()-1; ++i )
7393 gp_XYZ p3d = xyz[i];
7394 trsf.Transforms( p3d );
7395 xy[i].SetCoord( p3d.X(), p3d.Z() );
7397 xyz.back() = xyz.front();
7398 xy.back() = xy.front();
7400 // // move the point in 2D
7401 gp_XYZ tmpPnt = point.XYZ();
7402 trsf.Transforms( tmpPnt );
7403 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7405 // loop on segments of the face to analyze point position ralative to the face
7406 set< PointPos > pntPosSet;
7407 for ( size_t i = 1; i < xy.size(); ++i )
7409 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7410 pntPosSet.insert( pos );
7414 PointPos pos = *pntPosSet.begin();
7415 // cout << "Face " << face->GetID() << " DIST: ";
7416 switch ( pos._name )
7419 // point is most close to a segment
7420 gp_Vec p0p1( point, xyz[ pos._index ] );
7421 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7423 double projDist = p0p1 * p1p2; // distance projected to the segment
7424 gp_Vec projVec = p1p2 * projDist;
7425 gp_Vec distVec = p0p1 - projVec;
7426 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7427 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7428 return distVec.Magnitude();
7431 // point is inside the face
7432 double distToFacePlane = tmpPnt.Y();
7433 // cout << distToFacePlane << ", INSIDE " << endl;
7434 return Abs( distToFacePlane );
7437 // point is most close to a node
7438 gp_Vec distVec( point, xyz[ pos._index ]);
7439 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7440 return distVec.Magnitude();
7446 //=======================================================================
7447 //function : SimplifyFace
7449 //=======================================================================
7450 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7451 vector<const SMDS_MeshNode *>& poly_nodes,
7452 vector<int>& quantities) const
7454 int nbNodes = faceNodes.size();
7459 set<const SMDS_MeshNode*> nodeSet;
7461 // get simple seq of nodes
7462 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7463 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7464 int iSimple = 0, nbUnique = 0;
7466 simpleNodes[iSimple++] = faceNodes[0];
7468 for (int iCur = 1; iCur < nbNodes; iCur++) {
7469 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7470 simpleNodes[iSimple++] = faceNodes[iCur];
7471 if (nodeSet.insert( faceNodes[iCur] ).second)
7475 int nbSimple = iSimple;
7476 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7486 bool foundLoop = (nbSimple > nbUnique);
7489 set<const SMDS_MeshNode*> loopSet;
7490 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7491 const SMDS_MeshNode* n = simpleNodes[iSimple];
7492 if (!loopSet.insert( n ).second) {
7496 int iC = 0, curLast = iSimple;
7497 for (; iC < curLast; iC++) {
7498 if (simpleNodes[iC] == n) break;
7500 int loopLen = curLast - iC;
7502 // create sub-element
7504 quantities.push_back(loopLen);
7505 for (; iC < curLast; iC++) {
7506 poly_nodes.push_back(simpleNodes[iC]);
7509 // shift the rest nodes (place from the first loop position)
7510 for (iC = curLast + 1; iC < nbSimple; iC++) {
7511 simpleNodes[iC - loopLen] = simpleNodes[iC];
7513 nbSimple -= loopLen;
7516 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7517 } // while (foundLoop)
7521 quantities.push_back(iSimple);
7522 for (int i = 0; i < iSimple; i++)
7523 poly_nodes.push_back(simpleNodes[i]);
7529 //=======================================================================
7530 //function : MergeNodes
7531 //purpose : In each group, the cdr of nodes are substituted by the first one
7533 //=======================================================================
7535 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7537 MESSAGE("MergeNodes");
7538 myLastCreatedElems.Clear();
7539 myLastCreatedNodes.Clear();
7541 SMESHDS_Mesh* aMesh = GetMeshDS();
7543 TNodeNodeMap nodeNodeMap; // node to replace - new node
7544 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7545 list< int > rmElemIds, rmNodeIds;
7547 // Fill nodeNodeMap and elems
7549 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7550 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7551 list<const SMDS_MeshNode*>& nodes = *grIt;
7552 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7553 const SMDS_MeshNode* nToKeep = *nIt;
7554 //MESSAGE("node to keep " << nToKeep->GetID());
7555 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7556 const SMDS_MeshNode* nToRemove = *nIt;
7557 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7558 if ( nToRemove != nToKeep ) {
7559 //MESSAGE(" node to remove " << nToRemove->GetID());
7560 rmNodeIds.push_back( nToRemove->GetID() );
7561 AddToSameGroups( nToKeep, nToRemove, aMesh );
7564 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7565 while ( invElemIt->more() ) {
7566 const SMDS_MeshElement* elem = invElemIt->next();
7571 // Change element nodes or remove an element
7573 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7574 for ( ; eIt != elems.end(); eIt++ ) {
7575 const SMDS_MeshElement* elem = *eIt;
7576 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7577 int nbNodes = elem->NbNodes();
7578 int aShapeId = FindShape( elem );
7580 set<const SMDS_MeshNode*> nodeSet;
7581 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7582 int iUnique = 0, iCur = 0, nbRepl = 0;
7583 vector<int> iRepl( nbNodes );
7585 // get new seq of nodes
7586 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7587 while ( itN->more() ) {
7588 const SMDS_MeshNode* n =
7589 static_cast<const SMDS_MeshNode*>( itN->next() );
7591 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7592 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7594 // BUG 0020185: begin
7596 bool stopRecur = false;
7597 set<const SMDS_MeshNode*> nodesRecur;
7598 nodesRecur.insert(n);
7599 while (!stopRecur) {
7600 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7601 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7602 n = (*nnIt_i).second;
7603 if (!nodesRecur.insert(n).second) {
7604 // error: recursive dependancy
7614 curNodes[ iCur ] = n;
7615 bool isUnique = nodeSet.insert( n ).second;
7617 uniqueNodes[ iUnique++ ] = n;
7619 iRepl[ nbRepl++ ] = iCur;
7623 // Analyse element topology after replacement
7626 int nbUniqueNodes = nodeSet.size();
7627 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7628 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7629 // Polygons and Polyhedral volumes
7630 if (elem->IsPoly()) {
7632 if (elem->GetType() == SMDSAbs_Face) {
7634 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7636 for (; inode < nbNodes; inode++) {
7637 face_nodes[inode] = curNodes[inode];
7640 vector<const SMDS_MeshNode *> polygons_nodes;
7641 vector<int> quantities;
7642 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7645 for (int iface = 0; iface < nbNew; iface++) {
7646 int nbNodes = quantities[iface];
7647 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7648 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7649 poly_nodes[ii] = polygons_nodes[inode];
7651 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7652 myLastCreatedElems.Append(newElem);
7654 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7657 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7658 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7659 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7661 if (nbNew > 0) quid = nbNew - 1;
7662 vector<int> newquant(quantities.begin()+quid, quantities.end());
7663 const SMDS_MeshElement* newElem = 0;
7664 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7665 myLastCreatedElems.Append(newElem);
7666 if ( aShapeId && newElem )
7667 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7668 rmElemIds.push_back(elem->GetID());
7671 rmElemIds.push_back(elem->GetID());
7675 else if (elem->GetType() == SMDSAbs_Volume) {
7676 // Polyhedral volume
7677 if (nbUniqueNodes < 4) {
7678 rmElemIds.push_back(elem->GetID());
7681 // each face has to be analyzed in order to check volume validity
7682 const SMDS_VtkVolume* aPolyedre =
7683 dynamic_cast<const SMDS_VtkVolume*>( elem );
7685 int nbFaces = aPolyedre->NbFaces();
7687 vector<const SMDS_MeshNode *> poly_nodes;
7688 vector<int> quantities;
7690 for (int iface = 1; iface <= nbFaces; iface++) {
7691 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7692 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7694 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7695 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7696 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7697 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7698 faceNode = (*nnIt).second;
7700 faceNodes[inode - 1] = faceNode;
7703 SimplifyFace(faceNodes, poly_nodes, quantities);
7706 if (quantities.size() > 3) {
7707 // to be done: remove coincident faces
7710 if (quantities.size() > 3)
7712 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7713 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7714 const SMDS_MeshElement* newElem = 0;
7715 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7716 myLastCreatedElems.Append(newElem);
7717 if ( aShapeId && newElem )
7718 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7719 rmElemIds.push_back(elem->GetID());
7723 rmElemIds.push_back(elem->GetID());
7734 // TODO not all the possible cases are solved. Find something more generic?
7735 switch ( nbNodes ) {
7736 case 2: ///////////////////////////////////// EDGE
7737 isOk = false; break;
7738 case 3: ///////////////////////////////////// TRIANGLE
7739 isOk = false; break;
7741 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7743 else { //////////////////////////////////// QUADRANGLE
7744 if ( nbUniqueNodes < 3 )
7746 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7747 isOk = false; // opposite nodes stick
7748 //MESSAGE("isOk " << isOk);
7751 case 6: ///////////////////////////////////// PENTAHEDRON
7752 if ( nbUniqueNodes == 4 ) {
7753 // ---------------------------------> tetrahedron
7755 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7756 // all top nodes stick: reverse a bottom
7757 uniqueNodes[ 0 ] = curNodes [ 1 ];
7758 uniqueNodes[ 1 ] = curNodes [ 0 ];
7760 else if (nbRepl == 3 &&
7761 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7762 // all bottom nodes stick: set a top before
7763 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7764 uniqueNodes[ 0 ] = curNodes [ 3 ];
7765 uniqueNodes[ 1 ] = curNodes [ 4 ];
7766 uniqueNodes[ 2 ] = curNodes [ 5 ];
7768 else if (nbRepl == 4 &&
7769 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7770 // a lateral face turns into a line: reverse a bottom
7771 uniqueNodes[ 0 ] = curNodes [ 1 ];
7772 uniqueNodes[ 1 ] = curNodes [ 0 ];
7777 else if ( nbUniqueNodes == 5 ) {
7778 // PENTAHEDRON --------------------> 2 tetrahedrons
7779 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7780 // a bottom node sticks with a linked top one
7782 SMDS_MeshElement* newElem =
7783 aMesh->AddVolume(curNodes[ 3 ],
7786 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7787 myLastCreatedElems.Append(newElem);
7789 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7790 // 2. : reverse a bottom
7791 uniqueNodes[ 0 ] = curNodes [ 1 ];
7792 uniqueNodes[ 1 ] = curNodes [ 0 ];
7802 if(elem->IsQuadratic()) { // Quadratic quadrangle
7814 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7817 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7819 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7820 uniqueNodes[0] = curNodes[0];
7821 uniqueNodes[1] = curNodes[2];
7822 uniqueNodes[2] = curNodes[3];
7823 uniqueNodes[3] = curNodes[5];
7824 uniqueNodes[4] = curNodes[6];
7825 uniqueNodes[5] = curNodes[7];
7828 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7829 uniqueNodes[0] = curNodes[0];
7830 uniqueNodes[1] = curNodes[1];
7831 uniqueNodes[2] = curNodes[2];
7832 uniqueNodes[3] = curNodes[4];
7833 uniqueNodes[4] = curNodes[5];
7834 uniqueNodes[5] = curNodes[6];
7837 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7838 uniqueNodes[0] = curNodes[1];
7839 uniqueNodes[1] = curNodes[2];
7840 uniqueNodes[2] = curNodes[3];
7841 uniqueNodes[3] = curNodes[5];
7842 uniqueNodes[4] = curNodes[6];
7843 uniqueNodes[5] = curNodes[0];
7846 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7847 uniqueNodes[0] = curNodes[0];
7848 uniqueNodes[1] = curNodes[1];
7849 uniqueNodes[2] = curNodes[3];
7850 uniqueNodes[3] = curNodes[4];
7851 uniqueNodes[4] = curNodes[6];
7852 uniqueNodes[5] = curNodes[7];
7855 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7856 uniqueNodes[0] = curNodes[0];
7857 uniqueNodes[1] = curNodes[2];
7858 uniqueNodes[2] = curNodes[3];
7859 uniqueNodes[3] = curNodes[1];
7860 uniqueNodes[4] = curNodes[6];
7861 uniqueNodes[5] = curNodes[7];
7864 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7865 uniqueNodes[0] = curNodes[0];
7866 uniqueNodes[1] = curNodes[1];
7867 uniqueNodes[2] = curNodes[2];
7868 uniqueNodes[3] = curNodes[4];
7869 uniqueNodes[4] = curNodes[5];
7870 uniqueNodes[5] = curNodes[7];
7873 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7874 uniqueNodes[0] = curNodes[0];
7875 uniqueNodes[1] = curNodes[1];
7876 uniqueNodes[2] = curNodes[3];
7877 uniqueNodes[3] = curNodes[4];
7878 uniqueNodes[4] = curNodes[2];
7879 uniqueNodes[5] = curNodes[7];
7882 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7883 uniqueNodes[0] = curNodes[0];
7884 uniqueNodes[1] = curNodes[1];
7885 uniqueNodes[2] = curNodes[2];
7886 uniqueNodes[3] = curNodes[4];
7887 uniqueNodes[4] = curNodes[5];
7888 uniqueNodes[5] = curNodes[3];
7893 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7896 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7900 //////////////////////////////////// HEXAHEDRON
7902 SMDS_VolumeTool hexa (elem);
7903 hexa.SetExternalNormal();
7904 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7905 //////////////////////// HEX ---> 1 tetrahedron
7906 for ( int iFace = 0; iFace < 6; iFace++ ) {
7907 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7908 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7909 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7910 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7911 // one face turns into a point ...
7912 int iOppFace = hexa.GetOppFaceIndex( iFace );
7913 ind = hexa.GetFaceNodesIndices( iOppFace );
7915 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7916 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7919 if ( nbStick == 1 ) {
7920 // ... and the opposite one - into a triangle.
7922 ind = hexa.GetFaceNodesIndices( iFace );
7923 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7930 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7931 //////////////////////// HEX ---> 1 prism
7932 int nbTria = 0, iTria[3];
7933 const int *ind; // indices of face nodes
7934 // look for triangular faces
7935 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7936 ind = hexa.GetFaceNodesIndices( iFace );
7937 TIDSortedNodeSet faceNodes;
7938 for ( iCur = 0; iCur < 4; iCur++ )
7939 faceNodes.insert( curNodes[ind[iCur]] );
7940 if ( faceNodes.size() == 3 )
7941 iTria[ nbTria++ ] = iFace;
7943 // check if triangles are opposite
7944 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7947 // set nodes of the bottom triangle
7948 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7950 for ( iCur = 0; iCur < 4; iCur++ )
7951 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7952 indB.push_back( ind[iCur] );
7953 if ( !hexa.IsForward() )
7954 std::swap( indB[0], indB[2] );
7955 for ( iCur = 0; iCur < 3; iCur++ )
7956 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7957 // set nodes of the top triangle
7958 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7959 for ( iCur = 0; iCur < 3; ++iCur )
7960 for ( int j = 0; j < 4; ++j )
7961 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7963 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7969 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7970 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7971 for ( int iFace = 0; iFace < 6; iFace++ ) {
7972 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7973 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7974 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7975 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7976 // one face turns into a point ...
7977 int iOppFace = hexa.GetOppFaceIndex( iFace );
7978 ind = hexa.GetFaceNodesIndices( iOppFace );
7980 iUnique = 2; // reverse a tetrahedron 1 bottom
7981 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7982 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7984 else if ( iUnique >= 0 )
7985 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7987 if ( nbStick == 0 ) {
7988 // ... and the opposite one is a quadrangle
7990 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7991 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7994 SMDS_MeshElement* newElem =
7995 aMesh->AddVolume(curNodes[ind[ 0 ]],
7998 curNodes[indTop[ 0 ]]);
7999 myLastCreatedElems.Append(newElem);
8001 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8008 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8009 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8010 // find indices of quad and tri faces
8011 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8012 for ( iFace = 0; iFace < 6; iFace++ ) {
8013 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8015 for ( iCur = 0; iCur < 4; iCur++ )
8016 nodeSet.insert( curNodes[ind[ iCur ]] );
8017 nbUniqueNodes = nodeSet.size();
8018 if ( nbUniqueNodes == 3 )
8019 iTriFace[ nbTri++ ] = iFace;
8020 else if ( nbUniqueNodes == 4 )
8021 iQuadFace[ nbQuad++ ] = iFace;
8023 if (nbQuad == 2 && nbTri == 4 &&
8024 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8025 // 2 opposite quadrangles stuck with a diagonal;
8026 // sample groups of merged indices: (0-4)(2-6)
8027 // --------------------------------------------> 2 tetrahedrons
8028 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8029 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8030 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8031 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8032 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8033 // stuck with 0-2 diagonal
8041 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8042 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8043 // stuck with 1-3 diagonal
8055 uniqueNodes[ 0 ] = curNodes [ i0 ];
8056 uniqueNodes[ 1 ] = curNodes [ i1d ];
8057 uniqueNodes[ 2 ] = curNodes [ i3d ];
8058 uniqueNodes[ 3 ] = curNodes [ i0t ];
8061 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8065 myLastCreatedElems.Append(newElem);
8067 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8070 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8071 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8072 // --------------------------------------------> prism
8073 // find 2 opposite triangles
8075 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8076 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8077 // find indices of kept and replaced nodes
8078 // and fill unique nodes of 2 opposite triangles
8079 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8080 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8081 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8082 // fill unique nodes
8085 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8086 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8087 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8089 // iCur of a linked node of the opposite face (make normals co-directed):
8090 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8091 // check that correspondent corners of triangles are linked
8092 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8095 uniqueNodes[ iUnique ] = n;
8096 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8105 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8108 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8115 } // switch ( nbNodes )
8117 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8119 if ( isOk ) { // the elem remains valid after sticking nodes
8120 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8122 // Change nodes of polyedre
8123 const SMDS_VtkVolume* aPolyedre =
8124 dynamic_cast<const SMDS_VtkVolume*>( elem );
8126 int nbFaces = aPolyedre->NbFaces();
8128 vector<const SMDS_MeshNode *> poly_nodes;
8129 vector<int> quantities (nbFaces);
8131 for (int iface = 1; iface <= nbFaces; iface++) {
8132 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8133 quantities[iface - 1] = nbFaceNodes;
8135 for (inode = 1; inode <= nbFaceNodes; inode++) {
8136 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8138 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8139 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8140 curNode = (*nnIt).second;
8142 poly_nodes.push_back(curNode);
8145 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8148 else // replace non-polyhedron elements
8150 const SMDSAbs_ElementType etyp = elem->GetType();
8151 const int elemId = elem->GetID();
8152 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8153 uniqueNodes.resize(nbUniqueNodes);
8155 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8157 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8158 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8159 if ( sm && newElem )
8160 sm->AddElement( newElem );
8161 if ( elem != newElem )
8162 ReplaceElemInGroups( elem, newElem, aMesh );
8166 // Remove invalid regular element or invalid polygon
8167 rmElemIds.push_back( elem->GetID() );
8170 } // loop on elements
8172 // Remove bad elements, then equal nodes (order important)
8174 Remove( rmElemIds, false );
8175 Remove( rmNodeIds, true );
8180 // ========================================================
8181 // class : SortableElement
8182 // purpose : allow sorting elements basing on their nodes
8183 // ========================================================
8184 class SortableElement : public set <const SMDS_MeshElement*>
8188 SortableElement( const SMDS_MeshElement* theElem )
8191 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8192 while ( nodeIt->more() )
8193 this->insert( nodeIt->next() );
8196 const SMDS_MeshElement* Get() const
8199 void Set(const SMDS_MeshElement* e) const
8204 mutable const SMDS_MeshElement* myElem;
8207 //=======================================================================
8208 //function : FindEqualElements
8209 //purpose : Return list of group of elements built on the same nodes.
8210 // Search among theElements or in the whole mesh if theElements is empty
8211 //=======================================================================
8213 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8214 TListOfListOfElementsID & theGroupsOfElementsID)
8216 myLastCreatedElems.Clear();
8217 myLastCreatedNodes.Clear();
8219 typedef map< SortableElement, int > TMapOfNodeSet;
8220 typedef list<int> TGroupOfElems;
8222 if ( theElements.empty() )
8223 { // get all elements in the mesh
8224 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8225 while ( eIt->more() )
8226 theElements.insert( theElements.end(), eIt->next());
8229 vector< TGroupOfElems > arrayOfGroups;
8230 TGroupOfElems groupOfElems;
8231 TMapOfNodeSet mapOfNodeSet;
8233 TIDSortedElemSet::iterator elemIt = theElements.begin();
8234 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8235 const SMDS_MeshElement* curElem = *elemIt;
8236 SortableElement SE(curElem);
8239 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8240 if( !(pp.second) ) {
8241 TMapOfNodeSet::iterator& itSE = pp.first;
8242 ind = (*itSE).second;
8243 arrayOfGroups[ind].push_back(curElem->GetID());
8246 groupOfElems.clear();
8247 groupOfElems.push_back(curElem->GetID());
8248 arrayOfGroups.push_back(groupOfElems);
8253 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8254 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8255 groupOfElems = *groupIt;
8256 if ( groupOfElems.size() > 1 ) {
8257 groupOfElems.sort();
8258 theGroupsOfElementsID.push_back(groupOfElems);
8263 //=======================================================================
8264 //function : MergeElements
8265 //purpose : In each given group, substitute all elements by the first one.
8266 //=======================================================================
8268 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8270 myLastCreatedElems.Clear();
8271 myLastCreatedNodes.Clear();
8273 typedef list<int> TListOfIDs;
8274 TListOfIDs rmElemIds; // IDs of elems to remove
8276 SMESHDS_Mesh* aMesh = GetMeshDS();
8278 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8279 while ( groupsIt != theGroupsOfElementsID.end() ) {
8280 TListOfIDs& aGroupOfElemID = *groupsIt;
8281 aGroupOfElemID.sort();
8282 int elemIDToKeep = aGroupOfElemID.front();
8283 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8284 aGroupOfElemID.pop_front();
8285 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8286 while ( idIt != aGroupOfElemID.end() ) {
8287 int elemIDToRemove = *idIt;
8288 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8289 // add the kept element in groups of removed one (PAL15188)
8290 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8291 rmElemIds.push_back( elemIDToRemove );
8297 Remove( rmElemIds, false );
8300 //=======================================================================
8301 //function : MergeEqualElements
8302 //purpose : Remove all but one of elements built on the same nodes.
8303 //=======================================================================
8305 void SMESH_MeshEditor::MergeEqualElements()
8307 TIDSortedElemSet aMeshElements; /* empty input ==
8308 to merge equal elements in the whole mesh */
8309 TListOfListOfElementsID aGroupsOfElementsID;
8310 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8311 MergeElements(aGroupsOfElementsID);
8314 //=======================================================================
8315 //function : FindFaceInSet
8316 //purpose : Return a face having linked nodes n1 and n2 and which is
8317 // - not in avoidSet,
8318 // - in elemSet provided that !elemSet.empty()
8319 // i1 and i2 optionally returns indices of n1 and n2
8320 //=======================================================================
8322 const SMDS_MeshElement*
8323 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8324 const SMDS_MeshNode* n2,
8325 const TIDSortedElemSet& elemSet,
8326 const TIDSortedElemSet& avoidSet,
8332 const SMDS_MeshElement* face = 0;
8334 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8335 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8336 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8338 //MESSAGE("in while ( invElemIt->more() && !face )");
8339 const SMDS_MeshElement* elem = invElemIt->next();
8340 if (avoidSet.count( elem ))
8342 if ( !elemSet.empty() && !elemSet.count( elem ))
8345 i1 = elem->GetNodeIndex( n1 );
8346 // find a n2 linked to n1
8347 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8348 for ( int di = -1; di < 2 && !face; di += 2 )
8350 i2 = (i1+di+nbN) % nbN;
8351 if ( elem->GetNode( i2 ) == n2 )
8354 if ( !face && elem->IsQuadratic())
8356 // analysis for quadratic elements using all nodes
8357 const SMDS_VtkFace* F =
8358 dynamic_cast<const SMDS_VtkFace*>(elem);
8359 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8360 // use special nodes iterator
8361 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8362 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8363 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8365 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8366 if ( n1 == prevN && n2 == n )
8370 else if ( n2 == prevN && n1 == n )
8372 face = elem; swap( i1, i2 );
8378 if ( n1ind ) *n1ind = i1;
8379 if ( n2ind ) *n2ind = i2;
8383 //=======================================================================
8384 //function : findAdjacentFace
8386 //=======================================================================
8388 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8389 const SMDS_MeshNode* n2,
8390 const SMDS_MeshElement* elem)
8392 TIDSortedElemSet elemSet, avoidSet;
8394 avoidSet.insert ( elem );
8395 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8398 //=======================================================================
8399 //function : FindFreeBorder
8401 //=======================================================================
8403 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8405 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8406 const SMDS_MeshNode* theSecondNode,
8407 const SMDS_MeshNode* theLastNode,
8408 list< const SMDS_MeshNode* > & theNodes,
8409 list< const SMDS_MeshElement* >& theFaces)
8411 if ( !theFirstNode || !theSecondNode )
8413 // find border face between theFirstNode and theSecondNode
8414 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8418 theFaces.push_back( curElem );
8419 theNodes.push_back( theFirstNode );
8420 theNodes.push_back( theSecondNode );
8422 //vector<const SMDS_MeshNode*> nodes;
8423 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8424 TIDSortedElemSet foundElems;
8425 bool needTheLast = ( theLastNode != 0 );
8427 while ( nStart != theLastNode ) {
8428 if ( nStart == theFirstNode )
8429 return !needTheLast;
8431 // find all free border faces sharing form nStart
8433 list< const SMDS_MeshElement* > curElemList;
8434 list< const SMDS_MeshNode* > nStartList;
8435 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8436 while ( invElemIt->more() ) {
8437 const SMDS_MeshElement* e = invElemIt->next();
8438 if ( e == curElem || foundElems.insert( e ).second ) {
8440 int iNode = 0, nbNodes = e->NbNodes();
8441 //const SMDS_MeshNode* nodes[nbNodes+1];
8442 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8444 if(e->IsQuadratic()) {
8445 const SMDS_VtkFace* F =
8446 dynamic_cast<const SMDS_VtkFace*>(e);
8447 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8448 // use special nodes iterator
8449 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8450 while( anIter->more() ) {
8451 nodes[ iNode++ ] = cast2Node(anIter->next());
8455 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8456 while ( nIt->more() )
8457 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8459 nodes[ iNode ] = nodes[ 0 ];
8461 for ( iNode = 0; iNode < nbNodes; iNode++ )
8462 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8463 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8464 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8466 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8467 curElemList.push_back( e );
8471 // analyse the found
8473 int nbNewBorders = curElemList.size();
8474 if ( nbNewBorders == 0 ) {
8475 // no free border furthermore
8476 return !needTheLast;
8478 else if ( nbNewBorders == 1 ) {
8479 // one more element found
8481 nStart = nStartList.front();
8482 curElem = curElemList.front();
8483 theFaces.push_back( curElem );
8484 theNodes.push_back( nStart );
8487 // several continuations found
8488 list< const SMDS_MeshElement* >::iterator curElemIt;
8489 list< const SMDS_MeshNode* >::iterator nStartIt;
8490 // check if one of them reached the last node
8491 if ( needTheLast ) {
8492 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8493 curElemIt!= curElemList.end();
8494 curElemIt++, nStartIt++ )
8495 if ( *nStartIt == theLastNode ) {
8496 theFaces.push_back( *curElemIt );
8497 theNodes.push_back( *nStartIt );
8501 // find the best free border by the continuations
8502 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8503 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8504 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8505 curElemIt!= curElemList.end();
8506 curElemIt++, nStartIt++ )
8508 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8509 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8510 // find one more free border
8511 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8515 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8516 // choice: clear a worse one
8517 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8518 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8519 contNodes[ iWorse ].clear();
8520 contFaces[ iWorse ].clear();
8523 if ( contNodes[0].empty() && contNodes[1].empty() )
8526 // append the best free border
8527 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8528 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8529 theNodes.pop_back(); // remove nIgnore
8530 theNodes.pop_back(); // remove nStart
8531 theFaces.pop_back(); // remove curElem
8532 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8533 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8534 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8535 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8538 } // several continuations found
8539 } // while ( nStart != theLastNode )
8544 //=======================================================================
8545 //function : CheckFreeBorderNodes
8546 //purpose : Return true if the tree nodes are on a free border
8547 //=======================================================================
8549 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8550 const SMDS_MeshNode* theNode2,
8551 const SMDS_MeshNode* theNode3)
8553 list< const SMDS_MeshNode* > nodes;
8554 list< const SMDS_MeshElement* > faces;
8555 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8558 //=======================================================================
8559 //function : SewFreeBorder
8561 //=======================================================================
8563 SMESH_MeshEditor::Sew_Error
8564 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8565 const SMDS_MeshNode* theBordSecondNode,
8566 const SMDS_MeshNode* theBordLastNode,
8567 const SMDS_MeshNode* theSideFirstNode,
8568 const SMDS_MeshNode* theSideSecondNode,
8569 const SMDS_MeshNode* theSideThirdNode,
8570 const bool theSideIsFreeBorder,
8571 const bool toCreatePolygons,
8572 const bool toCreatePolyedrs)
8574 myLastCreatedElems.Clear();
8575 myLastCreatedNodes.Clear();
8577 MESSAGE("::SewFreeBorder()");
8578 Sew_Error aResult = SEW_OK;
8580 // ====================================
8581 // find side nodes and elements
8582 // ====================================
8584 list< const SMDS_MeshNode* > nSide[ 2 ];
8585 list< const SMDS_MeshElement* > eSide[ 2 ];
8586 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8587 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8591 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8592 nSide[0], eSide[0])) {
8593 MESSAGE(" Free Border 1 not found " );
8594 aResult = SEW_BORDER1_NOT_FOUND;
8596 if (theSideIsFreeBorder) {
8599 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8600 nSide[1], eSide[1])) {
8601 MESSAGE(" Free Border 2 not found " );
8602 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8605 if ( aResult != SEW_OK )
8608 if (!theSideIsFreeBorder) {
8612 // -------------------------------------------------------------------------
8614 // 1. If nodes to merge are not coincident, move nodes of the free border
8615 // from the coord sys defined by the direction from the first to last
8616 // nodes of the border to the correspondent sys of the side 2
8617 // 2. On the side 2, find the links most co-directed with the correspondent
8618 // links of the free border
8619 // -------------------------------------------------------------------------
8621 // 1. Since sewing may break if there are volumes to split on the side 2,
8622 // we wont move nodes but just compute new coordinates for them
8623 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8624 TNodeXYZMap nBordXYZ;
8625 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8626 list< const SMDS_MeshNode* >::iterator nBordIt;
8628 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8629 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8630 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8631 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8632 double tol2 = 1.e-8;
8633 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8634 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8635 // Need node movement.
8637 // find X and Z axes to create trsf
8638 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8640 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8642 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8645 gp_Ax3 toBordAx( Pb1, Zb, X );
8646 gp_Ax3 fromSideAx( Ps1, Zs, X );
8647 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8649 gp_Trsf toBordSys, fromSide2Sys;
8650 toBordSys.SetTransformation( toBordAx );
8651 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8652 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8655 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8656 const SMDS_MeshNode* n = *nBordIt;
8657 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8658 toBordSys.Transforms( xyz );
8659 fromSide2Sys.Transforms( xyz );
8660 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8664 // just insert nodes XYZ in the nBordXYZ map
8665 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8666 const SMDS_MeshNode* n = *nBordIt;
8667 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8671 // 2. On the side 2, find the links most co-directed with the correspondent
8672 // links of the free border
8674 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8675 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8676 sideNodes.push_back( theSideFirstNode );
8678 bool hasVolumes = false;
8679 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8680 set<long> foundSideLinkIDs, checkedLinkIDs;
8681 SMDS_VolumeTool volume;
8682 //const SMDS_MeshNode* faceNodes[ 4 ];
8684 const SMDS_MeshNode* sideNode;
8685 const SMDS_MeshElement* sideElem;
8686 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8687 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8688 nBordIt = bordNodes.begin();
8690 // border node position and border link direction to compare with
8691 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8692 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8693 // choose next side node by link direction or by closeness to
8694 // the current border node:
8695 bool searchByDir = ( *nBordIt != theBordLastNode );
8697 // find the next node on the Side 2
8699 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8701 checkedLinkIDs.clear();
8702 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8704 // loop on inverse elements of current node (prevSideNode) on the Side 2
8705 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8706 while ( invElemIt->more() )
8708 const SMDS_MeshElement* elem = invElemIt->next();
8709 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8710 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8711 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8712 bool isVolume = volume.Set( elem );
8713 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8714 if ( isVolume ) // --volume
8716 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8717 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8718 if(elem->IsQuadratic()) {
8719 const SMDS_VtkFace* F =
8720 dynamic_cast<const SMDS_VtkFace*>(elem);
8721 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8722 // use special nodes iterator
8723 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8724 while( anIter->more() ) {
8725 nodes[ iNode ] = cast2Node(anIter->next());
8726 if ( nodes[ iNode++ ] == prevSideNode )
8727 iPrevNode = iNode - 1;
8731 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8732 while ( nIt->more() ) {
8733 nodes[ iNode ] = cast2Node( nIt->next() );
8734 if ( nodes[ iNode++ ] == prevSideNode )
8735 iPrevNode = iNode - 1;
8738 // there are 2 links to check
8743 // loop on links, to be precise, on the second node of links
8744 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8745 const SMDS_MeshNode* n = nodes[ iNode ];
8747 if ( !volume.IsLinked( n, prevSideNode ))
8751 if ( iNode ) // a node before prevSideNode
8752 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8753 else // a node after prevSideNode
8754 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8756 // check if this link was already used
8757 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8758 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8759 if (!isJustChecked &&
8760 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8762 // test a link geometrically
8763 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8764 bool linkIsBetter = false;
8765 double dot = 0.0, dist = 0.0;
8766 if ( searchByDir ) { // choose most co-directed link
8767 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8768 linkIsBetter = ( dot > maxDot );
8770 else { // choose link with the node closest to bordPos
8771 dist = ( nextXYZ - bordPos ).SquareModulus();
8772 linkIsBetter = ( dist < minDist );
8774 if ( linkIsBetter ) {
8783 } // loop on inverse elements of prevSideNode
8786 MESSAGE(" Cant find path by links of the Side 2 ");
8787 return SEW_BAD_SIDE_NODES;
8789 sideNodes.push_back( sideNode );
8790 sideElems.push_back( sideElem );
8791 foundSideLinkIDs.insert ( linkID );
8792 prevSideNode = sideNode;
8794 if ( *nBordIt == theBordLastNode )
8795 searchByDir = false;
8797 // find the next border link to compare with
8798 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8799 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8800 // move to next border node if sideNode is before forward border node (bordPos)
8801 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8802 prevBordNode = *nBordIt;
8804 bordPos = nBordXYZ[ *nBordIt ];
8805 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8806 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8810 while ( sideNode != theSideSecondNode );
8812 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8813 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8814 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8816 } // end nodes search on the side 2
8818 // ============================
8819 // sew the border to the side 2
8820 // ============================
8822 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8823 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8825 TListOfListOfNodes nodeGroupsToMerge;
8826 if ( nbNodes[0] == nbNodes[1] ||
8827 ( theSideIsFreeBorder && !theSideThirdNode)) {
8829 // all nodes are to be merged
8831 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8832 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8833 nIt[0]++, nIt[1]++ )
8835 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8836 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8837 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8842 // insert new nodes into the border and the side to get equal nb of segments
8844 // get normalized parameters of nodes on the borders
8845 //double param[ 2 ][ maxNbNodes ];
8847 param[0] = new double [ maxNbNodes ];
8848 param[1] = new double [ maxNbNodes ];
8850 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8851 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8852 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8853 const SMDS_MeshNode* nPrev = *nIt;
8854 double bordLength = 0;
8855 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8856 const SMDS_MeshNode* nCur = *nIt;
8857 gp_XYZ segment (nCur->X() - nPrev->X(),
8858 nCur->Y() - nPrev->Y(),
8859 nCur->Z() - nPrev->Z());
8860 double segmentLen = segment.Modulus();
8861 bordLength += segmentLen;
8862 param[ iBord ][ iNode ] = bordLength;
8865 // normalize within [0,1]
8866 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8867 param[ iBord ][ iNode ] /= bordLength;
8871 // loop on border segments
8872 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8873 int i[ 2 ] = { 0, 0 };
8874 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8875 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8877 TElemOfNodeListMap insertMap;
8878 TElemOfNodeListMap::iterator insertMapIt;
8880 // key: elem to insert nodes into
8881 // value: 2 nodes to insert between + nodes to be inserted
8883 bool next[ 2 ] = { false, false };
8885 // find min adjacent segment length after sewing
8886 double nextParam = 10., prevParam = 0;
8887 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8888 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8889 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8890 if ( i[ iBord ] > 0 )
8891 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8893 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8894 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8895 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8897 // choose to insert or to merge nodes
8898 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8899 if ( Abs( du ) <= minSegLen * 0.2 ) {
8902 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8903 const SMDS_MeshNode* n0 = *nIt[0];
8904 const SMDS_MeshNode* n1 = *nIt[1];
8905 nodeGroupsToMerge.back().push_back( n1 );
8906 nodeGroupsToMerge.back().push_back( n0 );
8907 // position of node of the border changes due to merge
8908 param[ 0 ][ i[0] ] += du;
8909 // move n1 for the sake of elem shape evaluation during insertion.
8910 // n1 will be removed by MergeNodes() anyway
8911 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8912 next[0] = next[1] = true;
8917 int intoBord = ( du < 0 ) ? 0 : 1;
8918 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8919 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8920 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8921 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8922 if ( intoBord == 1 ) {
8923 // move node of the border to be on a link of elem of the side
8924 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8925 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8926 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8927 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8928 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8930 insertMapIt = insertMap.find( elem );
8931 bool notFound = ( insertMapIt == insertMap.end() );
8932 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8934 // insert into another link of the same element:
8935 // 1. perform insertion into the other link of the elem
8936 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8937 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8938 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8939 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8940 // 2. perform insertion into the link of adjacent faces
8942 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8944 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8948 if (toCreatePolyedrs) {
8949 // perform insertion into the links of adjacent volumes
8950 UpdateVolumes(n12, n22, nodeList);
8952 // 3. find an element appeared on n1 and n2 after the insertion
8953 insertMap.erase( elem );
8954 elem = findAdjacentFace( n1, n2, 0 );
8956 if ( notFound || otherLink ) {
8957 // add element and nodes of the side into the insertMap
8958 insertMapIt = insertMap.insert
8959 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8960 (*insertMapIt).second.push_back( n1 );
8961 (*insertMapIt).second.push_back( n2 );
8963 // add node to be inserted into elem
8964 (*insertMapIt).second.push_back( nIns );
8965 next[ 1 - intoBord ] = true;
8968 // go to the next segment
8969 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8970 if ( next[ iBord ] ) {
8971 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8973 nPrev[ iBord ] = *nIt[ iBord ];
8974 nIt[ iBord ]++; i[ iBord ]++;
8978 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8980 // perform insertion of nodes into elements
8982 for (insertMapIt = insertMap.begin();
8983 insertMapIt != insertMap.end();
8986 const SMDS_MeshElement* elem = (*insertMapIt).first;
8987 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8988 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8989 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8991 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8993 if ( !theSideIsFreeBorder ) {
8994 // look for and insert nodes into the faces adjacent to elem
8996 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8998 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9003 if (toCreatePolyedrs) {
9004 // perform insertion into the links of adjacent volumes
9005 UpdateVolumes(n1, n2, nodeList);
9011 } // end: insert new nodes
9013 MergeNodes ( nodeGroupsToMerge );
9018 //=======================================================================
9019 //function : InsertNodesIntoLink
9020 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9021 // and theBetweenNode2 and split theElement
9022 //=======================================================================
9024 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9025 const SMDS_MeshNode* theBetweenNode1,
9026 const SMDS_MeshNode* theBetweenNode2,
9027 list<const SMDS_MeshNode*>& theNodesToInsert,
9028 const bool toCreatePoly)
9030 if ( theFace->GetType() != SMDSAbs_Face ) return;
9032 // find indices of 2 link nodes and of the rest nodes
9033 int iNode = 0, il1, il2, i3, i4;
9034 il1 = il2 = i3 = i4 = -1;
9035 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9036 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9038 if(theFace->IsQuadratic()) {
9039 const SMDS_VtkFace* F =
9040 dynamic_cast<const SMDS_VtkFace*>(theFace);
9041 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9042 // use special nodes iterator
9043 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9044 while( anIter->more() ) {
9045 const SMDS_MeshNode* n = cast2Node(anIter->next());
9046 if ( n == theBetweenNode1 )
9048 else if ( n == theBetweenNode2 )
9054 nodes[ iNode++ ] = n;
9058 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9059 while ( nodeIt->more() ) {
9060 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9061 if ( n == theBetweenNode1 )
9063 else if ( n == theBetweenNode2 )
9069 nodes[ iNode++ ] = n;
9072 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9075 // arrange link nodes to go one after another regarding the face orientation
9076 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9077 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9082 aNodesToInsert.reverse();
9084 // check that not link nodes of a quadrangles are in good order
9085 int nbFaceNodes = theFace->NbNodes();
9086 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9092 if (toCreatePoly || theFace->IsPoly()) {
9095 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9097 // add nodes of face up to first node of link
9100 if(theFace->IsQuadratic()) {
9101 const SMDS_VtkFace* F =
9102 dynamic_cast<const SMDS_VtkFace*>(theFace);
9103 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9104 // use special nodes iterator
9105 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9106 while( anIter->more() && !isFLN ) {
9107 const SMDS_MeshNode* n = cast2Node(anIter->next());
9108 poly_nodes[iNode++] = n;
9109 if (n == nodes[il1]) {
9113 // add nodes to insert
9114 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9115 for (; nIt != aNodesToInsert.end(); nIt++) {
9116 poly_nodes[iNode++] = *nIt;
9118 // add nodes of face starting from last node of link
9119 while ( anIter->more() ) {
9120 poly_nodes[iNode++] = cast2Node(anIter->next());
9124 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9125 while ( nodeIt->more() && !isFLN ) {
9126 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9127 poly_nodes[iNode++] = n;
9128 if (n == nodes[il1]) {
9132 // add nodes to insert
9133 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9134 for (; nIt != aNodesToInsert.end(); nIt++) {
9135 poly_nodes[iNode++] = *nIt;
9137 // add nodes of face starting from last node of link
9138 while ( nodeIt->more() ) {
9139 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9140 poly_nodes[iNode++] = n;
9144 // edit or replace the face
9145 SMESHDS_Mesh *aMesh = GetMeshDS();
9147 if (theFace->IsPoly()) {
9148 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9151 int aShapeId = FindShape( theFace );
9153 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9154 myLastCreatedElems.Append(newElem);
9155 if ( aShapeId && newElem )
9156 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9158 aMesh->RemoveElement(theFace);
9163 SMESHDS_Mesh *aMesh = GetMeshDS();
9164 if( !theFace->IsQuadratic() ) {
9166 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9167 int nbLinkNodes = 2 + aNodesToInsert.size();
9168 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9169 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9170 linkNodes[ 0 ] = nodes[ il1 ];
9171 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9172 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9173 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9174 linkNodes[ iNode++ ] = *nIt;
9176 // decide how to split a quadrangle: compare possible variants
9177 // and choose which of splits to be a quadrangle
9178 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9179 if ( nbFaceNodes == 3 ) {
9180 iBestQuad = nbSplits;
9183 else if ( nbFaceNodes == 4 ) {
9184 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9185 double aBestRate = DBL_MAX;
9186 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9188 double aBadRate = 0;
9189 // evaluate elements quality
9190 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9191 if ( iSplit == iQuad ) {
9192 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9196 aBadRate += getBadRate( &quad, aCrit );
9199 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9201 nodes[ iSplit < iQuad ? i4 : i3 ]);
9202 aBadRate += getBadRate( &tria, aCrit );
9206 if ( aBadRate < aBestRate ) {
9208 aBestRate = aBadRate;
9213 // create new elements
9214 int aShapeId = FindShape( theFace );
9217 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9218 SMDS_MeshElement* newElem = 0;
9219 if ( iSplit == iBestQuad )
9220 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9225 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9227 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9228 myLastCreatedElems.Append(newElem);
9229 if ( aShapeId && newElem )
9230 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9233 // change nodes of theFace
9234 const SMDS_MeshNode* newNodes[ 4 ];
9235 newNodes[ 0 ] = linkNodes[ i1 ];
9236 newNodes[ 1 ] = linkNodes[ i2 ];
9237 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9238 newNodes[ 3 ] = nodes[ i4 ];
9239 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9240 const SMDS_MeshElement* newElem = 0;
9241 if (iSplit == iBestQuad)
9242 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9244 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9245 myLastCreatedElems.Append(newElem);
9246 if ( aShapeId && newElem )
9247 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9248 } // end if(!theFace->IsQuadratic())
9249 else { // theFace is quadratic
9250 // we have to split theFace on simple triangles and one simple quadrangle
9252 int nbshift = tmp*2;
9253 // shift nodes in nodes[] by nbshift
9255 for(i=0; i<nbshift; i++) {
9256 const SMDS_MeshNode* n = nodes[0];
9257 for(j=0; j<nbFaceNodes-1; j++) {
9258 nodes[j] = nodes[j+1];
9260 nodes[nbFaceNodes-1] = n;
9262 il1 = il1 - nbshift;
9263 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9264 // n0 n1 n2 n0 n1 n2
9265 // +-----+-----+ +-----+-----+
9274 // create new elements
9275 int aShapeId = FindShape( theFace );
9278 if(nbFaceNodes==6) { // quadratic triangle
9279 SMDS_MeshElement* newElem =
9280 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9281 myLastCreatedElems.Append(newElem);
9282 if ( aShapeId && newElem )
9283 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9284 if(theFace->IsMediumNode(nodes[il1])) {
9285 // create quadrangle
9286 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9287 myLastCreatedElems.Append(newElem);
9288 if ( aShapeId && newElem )
9289 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9295 // create quadrangle
9296 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9297 myLastCreatedElems.Append(newElem);
9298 if ( aShapeId && newElem )
9299 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9305 else { // nbFaceNodes==8 - quadratic quadrangle
9306 SMDS_MeshElement* newElem =
9307 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9308 myLastCreatedElems.Append(newElem);
9309 if ( aShapeId && newElem )
9310 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9311 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9312 myLastCreatedElems.Append(newElem);
9313 if ( aShapeId && newElem )
9314 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9315 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9316 myLastCreatedElems.Append(newElem);
9317 if ( aShapeId && newElem )
9318 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9319 if(theFace->IsMediumNode(nodes[il1])) {
9320 // create quadrangle
9321 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9322 myLastCreatedElems.Append(newElem);
9323 if ( aShapeId && newElem )
9324 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9330 // create quadrangle
9331 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9332 myLastCreatedElems.Append(newElem);
9333 if ( aShapeId && newElem )
9334 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9340 // create needed triangles using n1,n2,n3 and inserted nodes
9341 int nbn = 2 + aNodesToInsert.size();
9342 //const SMDS_MeshNode* aNodes[nbn];
9343 vector<const SMDS_MeshNode*> aNodes(nbn);
9344 aNodes[0] = nodes[n1];
9345 aNodes[nbn-1] = nodes[n2];
9346 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9347 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9348 aNodes[iNode++] = *nIt;
9350 for(i=1; i<nbn; i++) {
9351 SMDS_MeshElement* newElem =
9352 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9353 myLastCreatedElems.Append(newElem);
9354 if ( aShapeId && newElem )
9355 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9359 aMesh->RemoveElement(theFace);
9362 //=======================================================================
9363 //function : UpdateVolumes
9365 //=======================================================================
9366 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9367 const SMDS_MeshNode* theBetweenNode2,
9368 list<const SMDS_MeshNode*>& theNodesToInsert)
9370 myLastCreatedElems.Clear();
9371 myLastCreatedNodes.Clear();
9373 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9374 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9375 const SMDS_MeshElement* elem = invElemIt->next();
9377 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9378 SMDS_VolumeTool aVolume (elem);
9379 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9382 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9383 int iface, nbFaces = aVolume.NbFaces();
9384 vector<const SMDS_MeshNode *> poly_nodes;
9385 vector<int> quantities (nbFaces);
9387 for (iface = 0; iface < nbFaces; iface++) {
9388 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9389 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9390 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9392 for (int inode = 0; inode < nbFaceNodes; inode++) {
9393 poly_nodes.push_back(faceNodes[inode]);
9395 if (nbInserted == 0) {
9396 if (faceNodes[inode] == theBetweenNode1) {
9397 if (faceNodes[inode + 1] == theBetweenNode2) {
9398 nbInserted = theNodesToInsert.size();
9400 // add nodes to insert
9401 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9402 for (; nIt != theNodesToInsert.end(); nIt++) {
9403 poly_nodes.push_back(*nIt);
9407 else if (faceNodes[inode] == theBetweenNode2) {
9408 if (faceNodes[inode + 1] == theBetweenNode1) {
9409 nbInserted = theNodesToInsert.size();
9411 // add nodes to insert in reversed order
9412 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9414 for (; nIt != theNodesToInsert.begin(); nIt--) {
9415 poly_nodes.push_back(*nIt);
9417 poly_nodes.push_back(*nIt);
9424 quantities[iface] = nbFaceNodes + nbInserted;
9427 // Replace or update the volume
9428 SMESHDS_Mesh *aMesh = GetMeshDS();
9430 if (elem->IsPoly()) {
9431 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9435 int aShapeId = FindShape( elem );
9437 SMDS_MeshElement* newElem =
9438 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9439 myLastCreatedElems.Append(newElem);
9440 if (aShapeId && newElem)
9441 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9443 aMesh->RemoveElement(elem);
9450 //================================================================================
9452 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9454 //================================================================================
9456 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9457 vector<const SMDS_MeshNode *> & nodes,
9458 vector<int> & nbNodeInFaces )
9461 nbNodeInFaces.clear();
9462 SMDS_VolumeTool vTool ( elem );
9463 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9465 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9466 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9467 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9472 //=======================================================================
9474 * \brief Convert elements contained in a submesh to quadratic
9475 * \return int - nb of checked elements
9477 //=======================================================================
9479 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9480 SMESH_MesherHelper& theHelper,
9481 const bool theForce3d)
9484 if( !theSm ) return nbElem;
9486 vector<int> nbNodeInFaces;
9487 vector<const SMDS_MeshNode *> nodes;
9488 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9489 while(ElemItr->more())
9492 const SMDS_MeshElement* elem = ElemItr->next();
9493 if( !elem || elem->IsQuadratic() ) continue;
9495 // get elem data needed to re-create it
9497 const int id = elem->GetID();
9498 const int nbNodes = elem->NbNodes();
9499 const SMDSAbs_ElementType aType = elem->GetType();
9500 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9501 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9502 if ( aGeomType == SMDSEntity_Polyhedra )
9503 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9504 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9505 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9507 // remove a linear element
9508 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9510 const SMDS_MeshElement* NewElem = 0;
9516 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9524 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9527 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9530 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9535 case SMDSAbs_Volume :
9539 case SMDSEntity_Tetra:
9540 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9542 case SMDSEntity_Pyramid:
9543 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9545 case SMDSEntity_Penta:
9546 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9548 case SMDSEntity_Hexa:
9549 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9550 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9552 case SMDSEntity_Hexagonal_Prism:
9554 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9561 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9563 theSm->AddElement( NewElem );
9568 //=======================================================================
9569 //function : ConvertToQuadratic
9571 //=======================================================================
9573 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9575 SMESHDS_Mesh* meshDS = GetMeshDS();
9577 SMESH_MesherHelper aHelper(*myMesh);
9578 aHelper.SetIsQuadratic( true );
9580 int nbCheckedElems = 0;
9581 if ( myMesh->HasShapeToMesh() )
9583 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9585 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9586 while ( smIt->more() ) {
9587 SMESH_subMesh* sm = smIt->next();
9588 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9589 aHelper.SetSubShape( sm->GetSubShape() );
9590 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9595 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9596 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9598 SMESHDS_SubMesh *smDS = 0;
9599 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9600 while(aEdgeItr->more())
9602 const SMDS_MeshEdge* edge = aEdgeItr->next();
9603 if(edge && !edge->IsQuadratic())
9605 int id = edge->GetID();
9606 //MESSAGE("edge->GetID() " << id);
9607 const SMDS_MeshNode* n1 = edge->GetNode(0);
9608 const SMDS_MeshNode* n2 = edge->GetNode(1);
9610 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9612 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9613 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9616 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9617 while(aFaceItr->more())
9619 const SMDS_MeshFace* face = aFaceItr->next();
9620 if(!face || face->IsQuadratic() ) continue;
9622 const int id = face->GetID();
9623 const SMDSAbs_EntityType type = face->GetEntityType();
9624 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9626 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9628 SMDS_MeshFace * NewFace = 0;
9631 case SMDSEntity_Triangle:
9632 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9634 case SMDSEntity_Quadrangle:
9635 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9638 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9640 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9642 vector<int> nbNodeInFaces;
9643 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9644 while(aVolumeItr->more())
9646 const SMDS_MeshVolume* volume = aVolumeItr->next();
9647 if(!volume || volume->IsQuadratic() ) continue;
9649 const int id = volume->GetID();
9650 const SMDSAbs_EntityType type = volume->GetEntityType();
9651 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9652 if ( type == SMDSEntity_Polyhedra )
9653 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9654 else if ( type == SMDSEntity_Hexagonal_Prism )
9655 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9657 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9659 SMDS_MeshVolume * NewVolume = 0;
9662 case SMDSEntity_Tetra:
9663 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9665 case SMDSEntity_Hexa:
9666 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9667 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9669 case SMDSEntity_Pyramid:
9670 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9671 nodes[3], nodes[4], id, theForce3d);
9673 case SMDSEntity_Penta:
9674 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9675 nodes[3], nodes[4], nodes[5], id, theForce3d);
9677 case SMDSEntity_Hexagonal_Prism:
9679 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9681 ReplaceElemInGroups(volume, NewVolume, meshDS);
9686 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9687 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9688 aHelper.FixQuadraticElements(myError);
9692 //================================================================================
9694 * \brief Makes given elements quadratic
9695 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9696 * \param theElements - elements to make quadratic
9698 //================================================================================
9700 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9701 TIDSortedElemSet& theElements)
9703 if ( theElements.empty() ) return;
9705 // we believe that all theElements are of the same type
9706 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9708 // get all nodes shared by theElements
9709 TIDSortedNodeSet allNodes;
9710 TIDSortedElemSet::iterator eIt = theElements.begin();
9711 for ( ; eIt != theElements.end(); ++eIt )
9712 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9714 // complete theElements with elements of lower dim whose all nodes are in allNodes
9716 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9717 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9718 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9719 for ( ; nIt != allNodes.end(); ++nIt )
9721 const SMDS_MeshNode* n = *nIt;
9722 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9723 while ( invIt->more() )
9725 const SMDS_MeshElement* e = invIt->next();
9726 if ( e->IsQuadratic() )
9728 quadAdjacentElems[ e->GetType() ].insert( e );
9731 if ( e->GetType() >= elemType )
9733 continue; // same type of more complex linear element
9736 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9737 continue; // e is already checked
9741 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9742 while ( nodeIt->more() && allIn )
9743 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9745 theElements.insert(e );
9749 SMESH_MesherHelper helper(*myMesh);
9750 helper.SetIsQuadratic( true );
9752 // add links of quadratic adjacent elements to the helper
9754 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9755 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9756 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9758 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9760 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9761 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9762 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9764 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9766 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9767 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9768 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9770 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9773 // make quadratic elements instead of linear ones
9775 SMESHDS_Mesh* meshDS = GetMeshDS();
9776 SMESHDS_SubMesh* smDS = 0;
9777 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9779 const SMDS_MeshElement* elem = *eIt;
9780 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9783 const int id = elem->GetID();
9784 const SMDSAbs_ElementType type = elem->GetType();
9785 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9787 if ( !smDS || !smDS->Contains( elem ))
9788 smDS = meshDS->MeshElements( elem->getshapeId() );
9789 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9791 SMDS_MeshElement * newElem = 0;
9792 switch( nodes.size() )
9794 case 4: // cases for most frequently used element types go first (for optimization)
9795 if ( type == SMDSAbs_Volume )
9796 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9798 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9801 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9802 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9805 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9808 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9811 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9812 nodes[4], id, theForce3d);
9815 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9816 nodes[4], nodes[5], id, theForce3d);
9820 ReplaceElemInGroups( elem, newElem, meshDS);
9821 if( newElem && smDS )
9822 smDS->AddElement( newElem );
9825 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9826 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9827 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9828 helper.FixQuadraticElements( myError );
9832 //=======================================================================
9834 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9835 * \return int - nb of checked elements
9837 //=======================================================================
9839 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9840 SMDS_ElemIteratorPtr theItr,
9841 const int theShapeID)
9844 SMESHDS_Mesh* meshDS = GetMeshDS();
9846 while( theItr->more() )
9848 const SMDS_MeshElement* elem = theItr->next();
9850 if( elem && elem->IsQuadratic())
9852 int id = elem->GetID();
9853 int nbCornerNodes = elem->NbCornerNodes();
9854 SMDSAbs_ElementType aType = elem->GetType();
9856 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9858 //remove a quadratic element
9859 if ( !theSm || !theSm->Contains( elem ))
9860 theSm = meshDS->MeshElements( elem->getshapeId() );
9861 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9863 // remove medium nodes
9864 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9865 if ( nodes[i]->NbInverseElements() == 0 )
9866 meshDS->RemoveFreeNode( nodes[i], theSm );
9868 // add a linear element
9869 nodes.resize( nbCornerNodes );
9870 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9871 ReplaceElemInGroups(elem, newElem, meshDS);
9872 if( theSm && newElem )
9873 theSm->AddElement( newElem );
9879 //=======================================================================
9880 //function : ConvertFromQuadratic
9882 //=======================================================================
9884 bool SMESH_MeshEditor::ConvertFromQuadratic()
9886 int nbCheckedElems = 0;
9887 if ( myMesh->HasShapeToMesh() )
9889 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9891 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9892 while ( smIt->more() ) {
9893 SMESH_subMesh* sm = smIt->next();
9894 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9895 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9901 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9902 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9904 SMESHDS_SubMesh *aSM = 0;
9905 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9913 //================================================================================
9915 * \brief Return true if all medium nodes of the element are in the node set
9917 //================================================================================
9919 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9921 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9922 if ( !nodeSet.count( elem->GetNode(i) ))
9928 //================================================================================
9930 * \brief Makes given elements linear
9932 //================================================================================
9934 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9936 if ( theElements.empty() ) return;
9938 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9939 set<int> mediumNodeIDs;
9940 TIDSortedElemSet::iterator eIt = theElements.begin();
9941 for ( ; eIt != theElements.end(); ++eIt )
9943 const SMDS_MeshElement* e = *eIt;
9944 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9945 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9948 // replace given elements by linear ones
9949 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9950 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9951 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9953 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9954 // except those elements sharing medium nodes of quadratic element whose medium nodes
9955 // are not all in mediumNodeIDs
9957 // get remaining medium nodes
9958 TIDSortedNodeSet mediumNodes;
9959 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9960 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9961 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9962 mediumNodes.insert( mediumNodes.end(), n );
9964 // find more quadratic elements to convert
9965 TIDSortedElemSet moreElemsToConvert;
9966 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9967 for ( ; nIt != mediumNodes.end(); ++nIt )
9969 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9970 while ( invIt->more() )
9972 const SMDS_MeshElement* e = invIt->next();
9973 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9975 // find a more complex element including e and
9976 // whose medium nodes are not in mediumNodes
9977 bool complexFound = false;
9978 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9980 SMDS_ElemIteratorPtr invIt2 =
9981 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9982 while ( invIt2->more() )
9984 const SMDS_MeshElement* eComplex = invIt2->next();
9985 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9987 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9988 if ( nbCommonNodes == e->NbNodes())
9990 complexFound = true;
9991 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9997 if ( !complexFound )
9998 moreElemsToConvert.insert( e );
10002 elemIt = SMDS_ElemIteratorPtr
10003 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10004 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10007 //=======================================================================
10008 //function : SewSideElements
10010 //=======================================================================
10012 SMESH_MeshEditor::Sew_Error
10013 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10014 TIDSortedElemSet& theSide2,
10015 const SMDS_MeshNode* theFirstNode1,
10016 const SMDS_MeshNode* theFirstNode2,
10017 const SMDS_MeshNode* theSecondNode1,
10018 const SMDS_MeshNode* theSecondNode2)
10020 myLastCreatedElems.Clear();
10021 myLastCreatedNodes.Clear();
10023 MESSAGE ("::::SewSideElements()");
10024 if ( theSide1.size() != theSide2.size() )
10025 return SEW_DIFF_NB_OF_ELEMENTS;
10027 Sew_Error aResult = SEW_OK;
10029 // 1. Build set of faces representing each side
10030 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10031 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10033 // =======================================================================
10034 // 1. Build set of faces representing each side:
10035 // =======================================================================
10036 // a. build set of nodes belonging to faces
10037 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10038 // c. create temporary faces representing side of volumes if correspondent
10039 // face does not exist
10041 SMESHDS_Mesh* aMesh = GetMeshDS();
10042 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10043 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10044 TIDSortedElemSet faceSet1, faceSet2;
10045 set<const SMDS_MeshElement*> volSet1, volSet2;
10046 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10047 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10048 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10049 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10050 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10051 int iSide, iFace, iNode;
10053 list<const SMDS_MeshElement* > tempFaceList;
10054 for ( iSide = 0; iSide < 2; iSide++ ) {
10055 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10056 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10057 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10058 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10059 set<const SMDS_MeshElement*>::iterator vIt;
10060 TIDSortedElemSet::iterator eIt;
10061 set<const SMDS_MeshNode*>::iterator nIt;
10063 // check that given nodes belong to given elements
10064 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10065 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10066 int firstIndex = -1, secondIndex = -1;
10067 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10068 const SMDS_MeshElement* elem = *eIt;
10069 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10070 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10071 if ( firstIndex > -1 && secondIndex > -1 ) break;
10073 if ( firstIndex < 0 || secondIndex < 0 ) {
10074 // we can simply return until temporary faces created
10075 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10078 // -----------------------------------------------------------
10079 // 1a. Collect nodes of existing faces
10080 // and build set of face nodes in order to detect missing
10081 // faces corresponding to sides of volumes
10082 // -----------------------------------------------------------
10084 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10086 // loop on the given element of a side
10087 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10088 //const SMDS_MeshElement* elem = *eIt;
10089 const SMDS_MeshElement* elem = *eIt;
10090 if ( elem->GetType() == SMDSAbs_Face ) {
10091 faceSet->insert( elem );
10092 set <const SMDS_MeshNode*> faceNodeSet;
10093 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10094 while ( nodeIt->more() ) {
10095 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10096 nodeSet->insert( n );
10097 faceNodeSet.insert( n );
10099 setOfFaceNodeSet.insert( faceNodeSet );
10101 else if ( elem->GetType() == SMDSAbs_Volume )
10102 volSet->insert( elem );
10104 // ------------------------------------------------------------------------------
10105 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10106 // ------------------------------------------------------------------------------
10108 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10109 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10110 while ( fIt->more() ) { // loop on faces sharing a node
10111 const SMDS_MeshElement* f = fIt->next();
10112 if ( faceSet->find( f ) == faceSet->end() ) {
10113 // check if all nodes are in nodeSet and
10114 // complete setOfFaceNodeSet if they are
10115 set <const SMDS_MeshNode*> faceNodeSet;
10116 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10117 bool allInSet = true;
10118 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10119 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10120 if ( nodeSet->find( n ) == nodeSet->end() )
10123 faceNodeSet.insert( n );
10126 faceSet->insert( f );
10127 setOfFaceNodeSet.insert( faceNodeSet );
10133 // -------------------------------------------------------------------------
10134 // 1c. Create temporary faces representing sides of volumes if correspondent
10135 // face does not exist
10136 // -------------------------------------------------------------------------
10138 if ( !volSet->empty() ) {
10139 //int nodeSetSize = nodeSet->size();
10141 // loop on given volumes
10142 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10143 SMDS_VolumeTool vol (*vIt);
10144 // loop on volume faces: find free faces
10145 // --------------------------------------
10146 list<const SMDS_MeshElement* > freeFaceList;
10147 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10148 if ( !vol.IsFreeFace( iFace ))
10150 // check if there is already a face with same nodes in a face set
10151 const SMDS_MeshElement* aFreeFace = 0;
10152 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10153 int nbNodes = vol.NbFaceNodes( iFace );
10154 set <const SMDS_MeshNode*> faceNodeSet;
10155 vol.GetFaceNodes( iFace, faceNodeSet );
10156 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10158 // no such a face is given but it still can exist, check it
10159 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10160 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10162 if ( !aFreeFace ) {
10163 // create a temporary face
10164 if ( nbNodes == 3 ) {
10165 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10166 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10168 else if ( nbNodes == 4 ) {
10169 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10170 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10173 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10174 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10175 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10178 tempFaceList.push_back( aFreeFace );
10182 freeFaceList.push_back( aFreeFace );
10184 } // loop on faces of a volume
10186 // choose one of several free faces of a volume
10187 // --------------------------------------------
10188 if ( freeFaceList.size() > 1 ) {
10189 // choose a face having max nb of nodes shared by other elems of a side
10190 int maxNbNodes = -1;
10191 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10192 while ( fIt != freeFaceList.end() ) { // loop on free faces
10193 int nbSharedNodes = 0;
10194 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10195 while ( nodeIt->more() ) { // loop on free face nodes
10196 const SMDS_MeshNode* n =
10197 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10198 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10199 while ( invElemIt->more() ) {
10200 const SMDS_MeshElement* e = invElemIt->next();
10201 nbSharedNodes += faceSet->count( e );
10202 nbSharedNodes += elemSet->count( e );
10205 if ( nbSharedNodes > maxNbNodes ) {
10206 maxNbNodes = nbSharedNodes;
10207 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10209 else if ( nbSharedNodes == maxNbNodes ) {
10213 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10216 if ( freeFaceList.size() > 1 )
10218 // could not choose one face, use another way
10219 // choose a face most close to the bary center of the opposite side
10220 gp_XYZ aBC( 0., 0., 0. );
10221 set <const SMDS_MeshNode*> addedNodes;
10222 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10223 eIt = elemSet2->begin();
10224 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10225 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10226 while ( nodeIt->more() ) { // loop on free face nodes
10227 const SMDS_MeshNode* n =
10228 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10229 if ( addedNodes.insert( n ).second )
10230 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10233 aBC /= addedNodes.size();
10234 double minDist = DBL_MAX;
10235 fIt = freeFaceList.begin();
10236 while ( fIt != freeFaceList.end() ) { // loop on free faces
10238 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10239 while ( nodeIt->more() ) { // loop on free face nodes
10240 const SMDS_MeshNode* n =
10241 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10242 gp_XYZ p( n->X(),n->Y(),n->Z() );
10243 dist += ( aBC - p ).SquareModulus();
10245 if ( dist < minDist ) {
10247 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10250 fIt = freeFaceList.erase( fIt++ );
10253 } // choose one of several free faces of a volume
10255 if ( freeFaceList.size() == 1 ) {
10256 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10257 faceSet->insert( aFreeFace );
10258 // complete a node set with nodes of a found free face
10259 // for ( iNode = 0; iNode < ; iNode++ )
10260 // nodeSet->insert( fNodes[ iNode ] );
10263 } // loop on volumes of a side
10265 // // complete a set of faces if new nodes in a nodeSet appeared
10266 // // ----------------------------------------------------------
10267 // if ( nodeSetSize != nodeSet->size() ) {
10268 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10269 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10270 // while ( fIt->more() ) { // loop on faces sharing a node
10271 // const SMDS_MeshElement* f = fIt->next();
10272 // if ( faceSet->find( f ) == faceSet->end() ) {
10273 // // check if all nodes are in nodeSet and
10274 // // complete setOfFaceNodeSet if they are
10275 // set <const SMDS_MeshNode*> faceNodeSet;
10276 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10277 // bool allInSet = true;
10278 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10279 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10280 // if ( nodeSet->find( n ) == nodeSet->end() )
10281 // allInSet = false;
10283 // faceNodeSet.insert( n );
10285 // if ( allInSet ) {
10286 // faceSet->insert( f );
10287 // setOfFaceNodeSet.insert( faceNodeSet );
10293 } // Create temporary faces, if there are volumes given
10296 if ( faceSet1.size() != faceSet2.size() ) {
10297 // delete temporary faces: they are in reverseElements of actual nodes
10298 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10299 // while ( tmpFaceIt->more() )
10300 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10301 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10302 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10303 // aMesh->RemoveElement(*tmpFaceIt);
10304 MESSAGE("Diff nb of faces");
10305 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10308 // ============================================================
10309 // 2. Find nodes to merge:
10310 // bind a node to remove to a node to put instead
10311 // ============================================================
10313 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10314 if ( theFirstNode1 != theFirstNode2 )
10315 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10316 if ( theSecondNode1 != theSecondNode2 )
10317 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10319 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10320 set< long > linkIdSet; // links to process
10321 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10323 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10324 list< NLink > linkList[2];
10325 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10326 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10327 // loop on links in linkList; find faces by links and append links
10328 // of the found faces to linkList
10329 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10330 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10332 NLink link[] = { *linkIt[0], *linkIt[1] };
10333 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10334 if ( !linkIdSet.count( linkID ) )
10337 // by links, find faces in the face sets,
10338 // and find indices of link nodes in the found faces;
10339 // in a face set, there is only one or no face sharing a link
10340 // ---------------------------------------------------------------
10342 const SMDS_MeshElement* face[] = { 0, 0 };
10343 vector<const SMDS_MeshNode*> fnodes[2];
10344 int iLinkNode[2][2];
10345 TIDSortedElemSet avoidSet;
10346 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10347 const SMDS_MeshNode* n1 = link[iSide].first;
10348 const SMDS_MeshNode* n2 = link[iSide].second;
10349 //cout << "Side " << iSide << " ";
10350 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10351 // find a face by two link nodes
10352 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10353 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10354 if ( face[ iSide ])
10356 //cout << " F " << face[ iSide]->GetID() <<endl;
10357 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10358 // put face nodes to fnodes
10359 if ( face[ iSide ]->IsQuadratic() )
10361 // use interlaced nodes iterator
10362 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10363 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10364 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10365 while ( nIter->more() )
10366 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10370 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10371 face[ iSide ]->end_nodes() );
10373 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10377 // check similarity of elements of the sides
10378 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10379 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10380 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10381 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10384 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10386 break; // do not return because it's necessary to remove tmp faces
10389 // set nodes to merge
10390 // -------------------
10392 if ( face[0] && face[1] ) {
10393 const int nbNodes = face[0]->NbNodes();
10394 if ( nbNodes != face[1]->NbNodes() ) {
10395 MESSAGE("Diff nb of face nodes");
10396 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10397 break; // do not return because it s necessary to remove tmp faces
10399 bool reverse[] = { false, false }; // order of nodes in the link
10400 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10401 // analyse link orientation in faces
10402 int i1 = iLinkNode[ iSide ][ 0 ];
10403 int i2 = iLinkNode[ iSide ][ 1 ];
10404 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10406 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10407 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10408 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10410 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10411 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10414 // add other links of the faces to linkList
10415 // -----------------------------------------
10417 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10418 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10419 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10420 if ( !iter_isnew.second ) { // already in a set: no need to process
10421 linkIdSet.erase( iter_isnew.first );
10423 else // new in set == encountered for the first time: add
10425 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10426 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10427 linkList[0].push_back ( NLink( n1, n2 ));
10428 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10433 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10436 } // loop on link lists
10438 if ( aResult == SEW_OK &&
10439 ( //linkIt[0] != linkList[0].end() ||
10440 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10441 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10442 " " << (faceSetPtr[1]->empty()));
10443 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10446 // ====================================================================
10447 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10448 // ====================================================================
10450 // delete temporary faces
10451 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10452 // while ( tmpFaceIt->more() )
10453 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10454 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10455 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10456 aMesh->RemoveElement(*tmpFaceIt);
10458 if ( aResult != SEW_OK)
10461 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10462 // loop on nodes replacement map
10463 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10464 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10465 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10466 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10467 nodeIDsToRemove.push_back( nToRemove->GetID() );
10468 // loop on elements sharing nToRemove
10469 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10470 while ( invElemIt->more() ) {
10471 const SMDS_MeshElement* e = invElemIt->next();
10472 // get a new suite of nodes: make replacement
10473 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10474 vector< const SMDS_MeshNode*> nodes( nbNodes );
10475 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10476 while ( nIt->more() ) {
10477 const SMDS_MeshNode* n =
10478 static_cast<const SMDS_MeshNode*>( nIt->next() );
10479 nnIt = nReplaceMap.find( n );
10480 if ( nnIt != nReplaceMap.end() ) {
10482 n = (*nnIt).second;
10486 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10487 // elemIDsToRemove.push_back( e->GetID() );
10491 SMDSAbs_ElementType etyp = e->GetType();
10492 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10495 myLastCreatedElems.Append(newElem);
10496 AddToSameGroups(newElem, e, aMesh);
10497 int aShapeId = e->getshapeId();
10500 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10503 aMesh->RemoveElement(e);
10508 Remove( nodeIDsToRemove, true );
10513 //================================================================================
10515 * \brief Find corresponding nodes in two sets of faces
10516 * \param theSide1 - first face set
10517 * \param theSide2 - second first face
10518 * \param theFirstNode1 - a boundary node of set 1
10519 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10520 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10521 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10522 * \param nReplaceMap - output map of corresponding nodes
10523 * \return bool - is a success or not
10525 //================================================================================
10528 //#define DEBUG_MATCHING_NODES
10531 SMESH_MeshEditor::Sew_Error
10532 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10533 set<const SMDS_MeshElement*>& theSide2,
10534 const SMDS_MeshNode* theFirstNode1,
10535 const SMDS_MeshNode* theFirstNode2,
10536 const SMDS_MeshNode* theSecondNode1,
10537 const SMDS_MeshNode* theSecondNode2,
10538 TNodeNodeMap & nReplaceMap)
10540 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10542 nReplaceMap.clear();
10543 if ( theFirstNode1 != theFirstNode2 )
10544 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10545 if ( theSecondNode1 != theSecondNode2 )
10546 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10548 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10549 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10551 list< NLink > linkList[2];
10552 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10553 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10555 // loop on links in linkList; find faces by links and append links
10556 // of the found faces to linkList
10557 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10558 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10559 NLink link[] = { *linkIt[0], *linkIt[1] };
10560 if ( linkSet.find( link[0] ) == linkSet.end() )
10563 // by links, find faces in the face sets,
10564 // and find indices of link nodes in the found faces;
10565 // in a face set, there is only one or no face sharing a link
10566 // ---------------------------------------------------------------
10568 const SMDS_MeshElement* face[] = { 0, 0 };
10569 list<const SMDS_MeshNode*> notLinkNodes[2];
10570 //bool reverse[] = { false, false }; // order of notLinkNodes
10572 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10574 const SMDS_MeshNode* n1 = link[iSide].first;
10575 const SMDS_MeshNode* n2 = link[iSide].second;
10576 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10577 set< const SMDS_MeshElement* > facesOfNode1;
10578 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10580 // during a loop of the first node, we find all faces around n1,
10581 // during a loop of the second node, we find one face sharing both n1 and n2
10582 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10583 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10584 while ( fIt->more() ) { // loop on faces sharing a node
10585 const SMDS_MeshElement* f = fIt->next();
10586 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10587 ! facesOfNode1.insert( f ).second ) // f encounters twice
10589 if ( face[ iSide ] ) {
10590 MESSAGE( "2 faces per link " );
10591 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10594 faceSet->erase( f );
10596 // get not link nodes
10597 int nbN = f->NbNodes();
10598 if ( f->IsQuadratic() )
10600 nbNodes[ iSide ] = nbN;
10601 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10602 int i1 = f->GetNodeIndex( n1 );
10603 int i2 = f->GetNodeIndex( n2 );
10604 int iEnd = nbN, iBeg = -1, iDelta = 1;
10605 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10607 std::swap( iEnd, iBeg ); iDelta = -1;
10612 if ( i == iEnd ) i = iBeg + iDelta;
10613 if ( i == i1 ) break;
10614 nodes.push_back ( f->GetNode( i ) );
10620 // check similarity of elements of the sides
10621 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10622 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10623 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10624 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10627 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10631 // set nodes to merge
10632 // -------------------
10634 if ( face[0] && face[1] ) {
10635 if ( nbNodes[0] != nbNodes[1] ) {
10636 MESSAGE("Diff nb of face nodes");
10637 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10639 #ifdef DEBUG_MATCHING_NODES
10640 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10641 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10642 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10644 int nbN = nbNodes[0];
10646 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10647 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10648 for ( int i = 0 ; i < nbN - 2; ++i ) {
10649 #ifdef DEBUG_MATCHING_NODES
10650 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10652 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10656 // add other links of the face 1 to linkList
10657 // -----------------------------------------
10659 const SMDS_MeshElement* f0 = face[0];
10660 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10661 for ( int i = 0; i < nbN; i++ )
10663 const SMDS_MeshNode* n2 = f0->GetNode( i );
10664 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10665 linkSet.insert( SMESH_TLink( n1, n2 ));
10666 if ( !iter_isnew.second ) { // already in a set: no need to process
10667 linkSet.erase( iter_isnew.first );
10669 else // new in set == encountered for the first time: add
10671 #ifdef DEBUG_MATCHING_NODES
10672 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10673 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10675 linkList[0].push_back ( NLink( n1, n2 ));
10676 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10681 } // loop on link lists
10686 //================================================================================
10688 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10689 \param theElems - the list of elements (edges or faces) to be replicated
10690 The nodes for duplication could be found from these elements
10691 \param theNodesNot - list of nodes to NOT replicate
10692 \param theAffectedElems - the list of elements (cells and edges) to which the
10693 replicated nodes should be associated to.
10694 \return TRUE if operation has been completed successfully, FALSE otherwise
10696 //================================================================================
10698 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10699 const TIDSortedElemSet& theNodesNot,
10700 const TIDSortedElemSet& theAffectedElems )
10702 myLastCreatedElems.Clear();
10703 myLastCreatedNodes.Clear();
10705 if ( theElems.size() == 0 )
10708 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10713 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10714 // duplicate elements and nodes
10715 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10716 // replce nodes by duplications
10717 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10721 //================================================================================
10723 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10724 \param theMeshDS - mesh instance
10725 \param theElems - the elements replicated or modified (nodes should be changed)
10726 \param theNodesNot - nodes to NOT replicate
10727 \param theNodeNodeMap - relation of old node to new created node
10728 \param theIsDoubleElem - flag os to replicate element or modify
10729 \return TRUE if operation has been completed successfully, FALSE otherwise
10731 //================================================================================
10733 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10734 const TIDSortedElemSet& theElems,
10735 const TIDSortedElemSet& theNodesNot,
10736 std::map< const SMDS_MeshNode*,
10737 const SMDS_MeshNode* >& theNodeNodeMap,
10738 const bool theIsDoubleElem )
10740 MESSAGE("doubleNodes");
10741 // iterate on through element and duplicate them (by nodes duplication)
10743 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10744 for ( ; elemItr != theElems.end(); ++elemItr )
10746 const SMDS_MeshElement* anElem = *elemItr;
10750 bool isDuplicate = false;
10751 // duplicate nodes to duplicate element
10752 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10753 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10755 while ( anIter->more() )
10758 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10759 SMDS_MeshNode* aNewNode = aCurrNode;
10760 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10761 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10762 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10765 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10766 theNodeNodeMap[ aCurrNode ] = aNewNode;
10767 myLastCreatedNodes.Append( aNewNode );
10769 isDuplicate |= (aCurrNode != aNewNode);
10770 newNodes[ ind++ ] = aNewNode;
10772 if ( !isDuplicate )
10775 if ( theIsDoubleElem )
10776 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10779 MESSAGE("ChangeElementNodes");
10780 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10787 //================================================================================
10789 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10790 \param theNodes - identifiers of nodes to be doubled
10791 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10792 nodes. If list of element identifiers is empty then nodes are doubled but
10793 they not assigned to elements
10794 \return TRUE if operation has been completed successfully, FALSE otherwise
10796 //================================================================================
10798 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10799 const std::list< int >& theListOfModifiedElems )
10801 MESSAGE("DoubleNodes");
10802 myLastCreatedElems.Clear();
10803 myLastCreatedNodes.Clear();
10805 if ( theListOfNodes.size() == 0 )
10808 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10812 // iterate through nodes and duplicate them
10814 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10816 std::list< int >::const_iterator aNodeIter;
10817 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10819 int aCurr = *aNodeIter;
10820 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10826 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10829 anOldNodeToNewNode[ aNode ] = aNewNode;
10830 myLastCreatedNodes.Append( aNewNode );
10834 // Create map of new nodes for modified elements
10836 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10838 std::list< int >::const_iterator anElemIter;
10839 for ( anElemIter = theListOfModifiedElems.begin();
10840 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10842 int aCurr = *anElemIter;
10843 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10847 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10849 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10851 while ( anIter->more() )
10853 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10854 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10856 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10857 aNodeArr[ ind++ ] = aNewNode;
10860 aNodeArr[ ind++ ] = aCurrNode;
10862 anElemToNodes[ anElem ] = aNodeArr;
10865 // Change nodes of elements
10867 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10868 anElemToNodesIter = anElemToNodes.begin();
10869 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10871 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10872 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10875 MESSAGE("ChangeElementNodes");
10876 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10885 //================================================================================
10887 \brief Check if element located inside shape
10888 \return TRUE if IN or ON shape, FALSE otherwise
10890 //================================================================================
10892 template<class Classifier>
10893 bool isInside(const SMDS_MeshElement* theElem,
10894 Classifier& theClassifier,
10895 const double theTol)
10897 gp_XYZ centerXYZ (0, 0, 0);
10898 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10899 while (aNodeItr->more())
10900 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10902 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10903 theClassifier.Perform(aPnt, theTol);
10904 TopAbs_State aState = theClassifier.State();
10905 return (aState == TopAbs_IN || aState == TopAbs_ON );
10908 //================================================================================
10910 * \brief Classifier of the 3D point on the TopoDS_Face
10911 * with interaface suitable for isInside()
10913 //================================================================================
10915 struct _FaceClassifier
10917 Extrema_ExtPS _extremum;
10918 BRepAdaptor_Surface _surface;
10919 TopAbs_State _state;
10921 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10923 _extremum.Initialize( _surface,
10924 _surface.FirstUParameter(), _surface.LastUParameter(),
10925 _surface.FirstVParameter(), _surface.LastVParameter(),
10926 _surface.Tolerance(), _surface.Tolerance() );
10928 void Perform(const gp_Pnt& aPnt, double theTol)
10930 _state = TopAbs_OUT;
10931 _extremum.Perform(aPnt);
10932 if ( _extremum.IsDone() )
10933 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10934 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10935 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10937 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10940 TopAbs_State State() const
10947 //================================================================================
10949 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10950 This method is the first step of DoubleNodeElemGroupsInRegion.
10951 \param theElems - list of groups of elements (edges or faces) to be replicated
10952 \param theNodesNot - list of groups of nodes not to replicated
10953 \param theShape - shape to detect affected elements (element which geometric center
10954 located on or inside shape).
10955 The replicated nodes should be associated to affected elements.
10956 \return groups of affected elements
10957 \sa DoubleNodeElemGroupsInRegion()
10959 //================================================================================
10961 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10962 const TIDSortedElemSet& theNodesNot,
10963 const TopoDS_Shape& theShape,
10964 TIDSortedElemSet& theAffectedElems)
10966 if ( theShape.IsNull() )
10969 const double aTol = Precision::Confusion();
10970 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10971 auto_ptr<_FaceClassifier> aFaceClassifier;
10972 if ( theShape.ShapeType() == TopAbs_SOLID )
10974 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10975 bsc3d->PerformInfinitePoint(aTol);
10977 else if (theShape.ShapeType() == TopAbs_FACE )
10979 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10982 // iterates on indicated elements and get elements by back references from their nodes
10983 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10984 for ( ; elemItr != theElems.end(); ++elemItr )
10986 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10990 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10991 while ( nodeItr->more() )
10993 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10994 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10996 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10997 while ( backElemItr->more() )
10999 const SMDS_MeshElement* curElem = backElemItr->next();
11000 if ( curElem && theElems.find(curElem) == theElems.end() &&
11002 isInside( curElem, *bsc3d, aTol ) :
11003 isInside( curElem, *aFaceClassifier, aTol )))
11004 theAffectedElems.insert( curElem );
11011 //================================================================================
11013 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11014 \param theElems - group of of elements (edges or faces) to be replicated
11015 \param theNodesNot - group of nodes not to replicate
11016 \param theShape - shape to detect affected elements (element which geometric center
11017 located on or inside shape).
11018 The replicated nodes should be associated to affected elements.
11019 \return TRUE if operation has been completed successfully, FALSE otherwise
11021 //================================================================================
11023 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11024 const TIDSortedElemSet& theNodesNot,
11025 const TopoDS_Shape& theShape )
11027 if ( theShape.IsNull() )
11030 const double aTol = Precision::Confusion();
11031 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11032 auto_ptr<_FaceClassifier> aFaceClassifier;
11033 if ( theShape.ShapeType() == TopAbs_SOLID )
11035 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11036 bsc3d->PerformInfinitePoint(aTol);
11038 else if (theShape.ShapeType() == TopAbs_FACE )
11040 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11043 // iterates on indicated elements and get elements by back references from their nodes
11044 TIDSortedElemSet anAffected;
11045 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11046 for ( ; elemItr != theElems.end(); ++elemItr )
11048 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11052 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11053 while ( nodeItr->more() )
11055 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11056 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11058 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11059 while ( backElemItr->more() )
11061 const SMDS_MeshElement* curElem = backElemItr->next();
11062 if ( curElem && theElems.find(curElem) == theElems.end() &&
11064 isInside( curElem, *bsc3d, aTol ) :
11065 isInside( curElem, *aFaceClassifier, aTol )))
11066 anAffected.insert( curElem );
11070 return DoubleNodes( theElems, theNodesNot, anAffected );
11074 * \brief compute an oriented angle between two planes defined by four points.
11075 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11076 * @param p0 base of the rotation axe
11077 * @param p1 extremity of the rotation axe
11078 * @param g1 belongs to the first plane
11079 * @param g2 belongs to the second plane
11081 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11083 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11084 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11085 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11086 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11087 gp_Vec vref(p0, p1);
11090 gp_Vec n1 = vref.Crossed(v1);
11091 gp_Vec n2 = vref.Crossed(v2);
11092 return n2.AngleWithRef(n1, vref);
11096 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11097 * The list of groups must describe a partition of the mesh volumes.
11098 * The nodes of the internal faces at the boundaries of the groups are doubled.
11099 * In option, the internal faces are replaced by flat elements.
11100 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11101 * The flat elements are stored in groups of volumes.
11102 * @param theElems - list of groups of volumes, where a group of volume is a set of
11103 * SMDS_MeshElements sorted by Id.
11104 * @param createJointElems - if TRUE, create the elements
11105 * @return TRUE if operation has been completed successfully, FALSE otherwise
11107 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11108 bool createJointElems)
11110 MESSAGE("----------------------------------------------");
11111 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11112 MESSAGE("----------------------------------------------");
11114 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11115 meshDS->BuildDownWardConnectivity(true);
11117 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11119 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11120 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11121 // build the list of nodes shared by 2 or more domains, with their domain indexes
11123 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11124 std::map<int,int>celldom; // cell vtkId --> domain
11125 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11126 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11127 faceDomains.clear();
11129 cellDomains.clear();
11130 nodeDomains.clear();
11131 std::map<int,int> emptyMap;
11132 std::set<int> emptySet;
11135 for (int idom = 0; idom < theElems.size(); idom++)
11138 // --- build a map (face to duplicate --> volume to modify)
11139 // with all the faces shared by 2 domains (group of elements)
11140 // and corresponding volume of this domain, for each shared face.
11141 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11143 //MESSAGE("Domain " << idom);
11144 const TIDSortedElemSet& domain = theElems[idom];
11145 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11146 for (; elemItr != domain.end(); ++elemItr)
11148 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11151 int vtkId = anElem->getVtkId();
11152 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11153 int neighborsVtkIds[NBMAXNEIGHBORS];
11154 int downIds[NBMAXNEIGHBORS];
11155 unsigned char downTypes[NBMAXNEIGHBORS];
11156 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11157 for (int n = 0; n < nbNeighbors; n++)
11159 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11160 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11161 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11163 DownIdType face(downIds[n], downTypes[n]);
11164 if (!faceDomains.count(face))
11165 faceDomains[face] = emptyMap; // create an empty entry for face
11166 if (!faceDomains[face].count(idom))
11168 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11169 celldom[vtkId] = idom;
11170 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11177 //MESSAGE("Number of shared faces " << faceDomains.size());
11178 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11180 // --- explore the shared faces domain by domain,
11181 // explore the nodes of the face and see if they belong to a cell in the domain,
11182 // which has only a node or an edge on the border (not a shared face)
11184 for (int idomain = 0; idomain < theElems.size(); idomain++)
11186 //MESSAGE("Domain " << idomain);
11187 const TIDSortedElemSet& domain = theElems[idomain];
11188 itface = faceDomains.begin();
11189 for (; itface != faceDomains.end(); ++itface)
11191 std::map<int, int> domvol = itface->second;
11192 if (!domvol.count(idomain))
11194 DownIdType face = itface->first;
11195 //MESSAGE(" --- face " << face.cellId);
11196 std::set<int> oldNodes;
11198 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11199 std::set<int>::iterator itn = oldNodes.begin();
11200 for (; itn != oldNodes.end(); ++itn)
11203 //MESSAGE(" node " << oldId);
11204 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11205 for (int i=0; i<l.ncells; i++)
11207 int vtkId = l.cells[i];
11208 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11209 if (!domain.count(anElem))
11211 int vtkType = grid->GetCellType(vtkId);
11212 int downId = grid->CellIdToDownId(vtkId);
11215 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11216 continue; // not OK at this stage of the algorithm:
11217 //no cells created after BuildDownWardConnectivity
11219 DownIdType aCell(downId, vtkType);
11220 if (!cellDomains.count(aCell))
11221 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11222 cellDomains[aCell][idomain] = vtkId;
11223 celldom[vtkId] = idomain;
11224 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11230 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11231 // for each shared face, get the nodes
11232 // for each node, for each domain of the face, create a clone of the node
11234 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11235 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11236 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11238 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11239 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11240 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11242 for (int idomain = 0; idomain < theElems.size(); idomain++)
11244 itface = faceDomains.begin();
11245 for (; itface != faceDomains.end(); ++itface)
11247 std::map<int, int> domvol = itface->second;
11248 if (!domvol.count(idomain))
11250 DownIdType face = itface->first;
11251 //MESSAGE(" --- face " << face.cellId);
11252 std::set<int> oldNodes;
11254 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11255 std::set<int>::iterator itn = oldNodes.begin();
11256 for (; itn != oldNodes.end(); ++itn)
11259 //MESSAGE("-+-+-a node " << oldId);
11260 if (!nodeDomains.count(oldId))
11261 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11262 if (nodeDomains[oldId].empty())
11264 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11265 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11267 std::map<int, int>::iterator itdom = domvol.begin();
11268 for (; itdom != domvol.end(); ++itdom)
11270 int idom = itdom->first;
11271 //MESSAGE(" domain " << idom);
11272 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11274 if (nodeDomains[oldId].size() >= 2) // a multiple node
11276 vector<int> orderedDoms;
11277 //MESSAGE("multiple node " << oldId);
11278 if (mutipleNodes.count(oldId))
11279 orderedDoms = mutipleNodes[oldId];
11282 map<int,int>::iterator it = nodeDomains[oldId].begin();
11283 for (; it != nodeDomains[oldId].end(); ++it)
11284 orderedDoms.push_back(it->first);
11286 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11287 //stringstream txt;
11288 //for (int i=0; i<orderedDoms.size(); i++)
11289 // txt << orderedDoms[i] << " ";
11290 //MESSAGE("orderedDoms " << txt.str());
11291 mutipleNodes[oldId] = orderedDoms;
11293 double *coords = grid->GetPoint(oldId);
11294 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11295 int newId = newNode->getVtkId();
11296 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11297 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11304 for (int idomain = 0; idomain < theElems.size(); idomain++)
11306 itface = faceDomains.begin();
11307 for (; itface != faceDomains.end(); ++itface)
11309 std::map<int, int> domvol = itface->second;
11310 if (!domvol.count(idomain))
11312 DownIdType face = itface->first;
11313 //MESSAGE(" --- face " << face.cellId);
11314 std::set<int> oldNodes;
11316 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11317 int nbMultipleNodes = 0;
11318 std::set<int>::iterator itn = oldNodes.begin();
11319 for (; itn != oldNodes.end(); ++itn)
11322 if (mutipleNodes.count(oldId))
11325 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11327 //MESSAGE("multiple Nodes detected on a shared face");
11328 int downId = itface->first.cellId;
11329 unsigned char cellType = itface->first.cellType;
11330 // --- shared edge or shared face ?
11331 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11334 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11335 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11336 if (mutipleNodes.count(nodes[i]))
11337 if (!mutipleNodesToFace.count(nodes[i]))
11338 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11340 else // shared face (between two volumes)
11342 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11343 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11344 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11345 for (int ie =0; ie < nbEdges; ie++)
11348 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11349 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11351 vector<int> vn0 = mutipleNodes[nodes[0]];
11352 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11354 for (int i0 = 0; i0 < vn0.size(); i0++)
11355 for (int i1 = 0; i1 < vn1.size(); i1++)
11356 if (vn0[i0] == vn1[i1])
11357 doms.push_back(vn0[i0]);
11358 if (doms.size() >2)
11360 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11361 double *coords = grid->GetPoint(nodes[0]);
11362 gp_Pnt p0(coords[0], coords[1], coords[2]);
11363 coords = grid->GetPoint(nodes[nbNodes - 1]);
11364 gp_Pnt p1(coords[0], coords[1], coords[2]);
11366 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11367 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11368 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11369 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11370 for (int id=0; id < doms.size(); id++)
11372 int idom = doms[id];
11373 for (int ivol=0; ivol<nbvol; ivol++)
11375 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11376 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11377 if (theElems[idom].count(elem))
11379 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11380 domvol[idom] = svol;
11381 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11383 vtkIdType npts = 0;
11384 vtkIdType* pts = 0;
11385 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11386 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11389 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11390 angleDom[idom] = 0;
11394 gp_Pnt g(values[0], values[1], values[2]);
11395 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11396 //MESSAGE(" angle=" << angleDom[idom]);
11402 map<double, int> sortedDom; // sort domains by angle
11403 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11404 sortedDom[ia->second] = ia->first;
11405 vector<int> vnodes;
11407 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11409 vdom.push_back(ib->second);
11410 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11412 for (int ino = 0; ino < nbNodes; ino++)
11413 vnodes.push_back(nodes[ino]);
11414 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11423 // --- iterate on shared faces (volumes to modify, face to extrude)
11424 // get node id's of the face (id SMDS = id VTK)
11425 // create flat element with old and new nodes if requested
11427 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11428 // (domain1 X domain2) = domain1 + MAXINT*domain2
11430 std::map<int, std::map<long,int> > nodeQuadDomains;
11431 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11433 if (createJointElems)
11436 string joints2DName = "joints2D";
11437 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11438 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11439 string joints3DName = "joints3D";
11440 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11441 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11443 itface = faceDomains.begin();
11444 for (; itface != faceDomains.end(); ++itface)
11446 DownIdType face = itface->first;
11447 std::set<int> oldNodes;
11448 std::set<int>::iterator itn;
11450 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11452 std::map<int, int> domvol = itface->second;
11453 std::map<int, int>::iterator itdom = domvol.begin();
11454 int dom1 = itdom->first;
11455 int vtkVolId = itdom->second;
11457 int dom2 = itdom->first;
11458 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11460 stringstream grpname;
11463 grpname << dom1 << "_" << dom2;
11465 grpname << dom2 << "_" << dom1;
11466 string namegrp = grpname.str();
11467 if (!mapOfJunctionGroups.count(namegrp))
11468 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11469 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11471 sgrp->Add(vol->GetID());
11472 if (vol->GetType() == SMDSAbs_Volume)
11473 joints3DGrp->Add(vol->GetID());
11474 else if (vol->GetType() == SMDSAbs_Face)
11475 joints2DGrp->Add(vol->GetID());
11479 // --- create volumes on multiple domain intersection if requested
11480 // iterate on mutipleNodesToFace
11481 // iterate on edgesMultiDomains
11483 if (createJointElems)
11485 // --- iterate on mutipleNodesToFace
11487 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11488 for (; itn != mutipleNodesToFace.end(); ++itn)
11490 int node = itn->first;
11491 vector<int> orderDom = itn->second;
11492 vector<vtkIdType> orderedNodes;
11493 for (int idom = 0; idom <orderDom.size(); idom++)
11494 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11495 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11497 stringstream grpname;
11499 grpname << 0 << "_" << 0;
11501 string namegrp = grpname.str();
11502 if (!mapOfJunctionGroups.count(namegrp))
11503 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11504 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11506 sgrp->Add(face->GetID());
11509 // --- iterate on edgesMultiDomains
11511 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11512 for (; ite != edgesMultiDomains.end(); ++ite)
11514 vector<int> nodes = ite->first;
11515 vector<int> orderDom = ite->second;
11516 vector<vtkIdType> orderedNodes;
11517 if (nodes.size() == 2)
11519 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11520 for (int ino=0; ino < nodes.size(); ino++)
11521 if (orderDom.size() == 3)
11522 for (int idom = 0; idom <orderDom.size(); idom++)
11523 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11525 for (int idom = orderDom.size()-1; idom >=0; idom--)
11526 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11527 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11530 string namegrp = "jointsMultiples";
11531 if (!mapOfJunctionGroups.count(namegrp))
11532 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11533 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11535 sgrp->Add(vol->GetID());
11539 INFOS("Quadratic multiple joints not implemented");
11540 // TODO quadratic nodes
11545 // --- list the explicit faces and edges of the mesh that need to be modified,
11546 // i.e. faces and edges built with one or more duplicated nodes.
11547 // associate these faces or edges to their corresponding domain.
11548 // only the first domain found is kept when a face or edge is shared
11550 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11551 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11552 faceOrEdgeDom.clear();
11555 for (int idomain = 0; idomain < theElems.size(); idomain++)
11557 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11558 for (; itnod != nodeDomains.end(); ++itnod)
11560 int oldId = itnod->first;
11561 //MESSAGE(" node " << oldId);
11562 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11563 for (int i = 0; i < l.ncells; i++)
11565 int vtkId = l.cells[i];
11566 int vtkType = grid->GetCellType(vtkId);
11567 int downId = grid->CellIdToDownId(vtkId);
11569 continue; // new cells: not to be modified
11570 DownIdType aCell(downId, vtkType);
11571 int volParents[1000];
11572 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11573 for (int j = 0; j < nbvol; j++)
11574 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11575 if (!feDom.count(vtkId))
11577 feDom[vtkId] = idomain;
11578 faceOrEdgeDom[aCell] = emptyMap;
11579 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11580 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11581 // << " type " << vtkType << " downId " << downId);
11587 // --- iterate on shared faces (volumes to modify, face to extrude)
11588 // get node id's of the face
11589 // replace old nodes by new nodes in volumes, and update inverse connectivity
11591 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11592 for (int m=0; m<3; m++)
11594 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11595 itface = (*amap).begin();
11596 for (; itface != (*amap).end(); ++itface)
11598 DownIdType face = itface->first;
11599 std::set<int> oldNodes;
11600 std::set<int>::iterator itn;
11602 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11603 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11604 std::map<int, int> localClonedNodeIds;
11606 std::map<int, int> domvol = itface->second;
11607 std::map<int, int>::iterator itdom = domvol.begin();
11608 for (; itdom != domvol.end(); ++itdom)
11610 int idom = itdom->first;
11611 int vtkVolId = itdom->second;
11612 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11613 localClonedNodeIds.clear();
11614 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11617 if (nodeDomains[oldId].count(idom))
11619 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11620 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11623 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11628 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11629 grid->BuildLinks();
11637 * \brief Double nodes on some external faces and create flat elements.
11638 * Flat elements are mainly used by some types of mechanic calculations.
11640 * Each group of the list must be constituted of faces.
11641 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11642 * @param theElems - list of groups of faces, where a group of faces is a set of
11643 * SMDS_MeshElements sorted by Id.
11644 * @return TRUE if operation has been completed successfully, FALSE otherwise
11646 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11648 MESSAGE("-------------------------------------------------");
11649 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11650 MESSAGE("-------------------------------------------------");
11652 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11654 // --- For each group of faces
11655 // duplicate the nodes, create a flat element based on the face
11656 // replace the nodes of the faces by their clones
11658 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11659 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11660 clonedNodes.clear();
11661 intermediateNodes.clear();
11662 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11663 mapOfJunctionGroups.clear();
11665 for (int idom = 0; idom < theElems.size(); idom++)
11667 const TIDSortedElemSet& domain = theElems[idom];
11668 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11669 for (; elemItr != domain.end(); ++elemItr)
11671 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11672 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11675 // MESSAGE("aFace=" << aFace->GetID());
11676 bool isQuad = aFace->IsQuadratic();
11677 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11679 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11681 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11682 while (nodeIt->more())
11684 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11685 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11687 ln2.push_back(node);
11689 ln0.push_back(node);
11691 const SMDS_MeshNode* clone = 0;
11692 if (!clonedNodes.count(node))
11694 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11695 clonedNodes[node] = clone;
11698 clone = clonedNodes[node];
11701 ln3.push_back(clone);
11703 ln1.push_back(clone);
11705 const SMDS_MeshNode* inter = 0;
11706 if (isQuad && (!isMedium))
11708 if (!intermediateNodes.count(node))
11710 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11711 intermediateNodes[node] = inter;
11714 inter = intermediateNodes[node];
11715 ln4.push_back(inter);
11719 // --- extrude the face
11721 vector<const SMDS_MeshNode*> ln;
11722 SMDS_MeshVolume* vol = 0;
11723 vtkIdType aType = aFace->GetVtkType();
11727 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11728 // MESSAGE("vol prism " << vol->GetID());
11729 ln.push_back(ln1[0]);
11730 ln.push_back(ln1[1]);
11731 ln.push_back(ln1[2]);
11734 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11735 // MESSAGE("vol hexa " << vol->GetID());
11736 ln.push_back(ln1[0]);
11737 ln.push_back(ln1[1]);
11738 ln.push_back(ln1[2]);
11739 ln.push_back(ln1[3]);
11741 case VTK_QUADRATIC_TRIANGLE:
11742 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11743 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11744 // MESSAGE("vol quad prism " << vol->GetID());
11745 ln.push_back(ln1[0]);
11746 ln.push_back(ln1[1]);
11747 ln.push_back(ln1[2]);
11748 ln.push_back(ln3[0]);
11749 ln.push_back(ln3[1]);
11750 ln.push_back(ln3[2]);
11752 case VTK_QUADRATIC_QUAD:
11753 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11754 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11755 // ln4[0], ln4[1], ln4[2], ln4[3]);
11756 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11757 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11758 ln4[0], ln4[1], ln4[2], ln4[3]);
11759 // MESSAGE("vol quad hexa " << vol->GetID());
11760 ln.push_back(ln1[0]);
11761 ln.push_back(ln1[1]);
11762 ln.push_back(ln1[2]);
11763 ln.push_back(ln1[3]);
11764 ln.push_back(ln3[0]);
11765 ln.push_back(ln3[1]);
11766 ln.push_back(ln3[2]);
11767 ln.push_back(ln3[3]);
11777 stringstream grpname;
11781 string namegrp = grpname.str();
11782 if (!mapOfJunctionGroups.count(namegrp))
11783 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11784 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11786 sgrp->Add(vol->GetID());
11789 // --- modify the face
11791 aFace->ChangeNodes(&ln[0], ln.size());
11798 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11799 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11800 * groups of faces to remove inside the object, (idem edges).
11801 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11803 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11804 const TopoDS_Shape& theShape,
11805 SMESH_NodeSearcher* theNodeSearcher,
11806 const char* groupName,
11807 std::vector<double>& nodesCoords,
11808 std::vector<std::vector<int> >& listOfListOfNodes)
11810 MESSAGE("--------------------------------");
11811 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11812 MESSAGE("--------------------------------");
11814 // --- zone of volumes to remove is given :
11815 // 1 either by a geom shape (one or more vertices) and a radius,
11816 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11817 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11818 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11819 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11820 // defined by it's name.
11822 SMESHDS_GroupBase* groupDS = 0;
11823 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11824 while ( groupIt->more() )
11827 SMESH_Group * group = groupIt->next();
11828 if ( !group ) continue;
11829 groupDS = group->GetGroupDS();
11830 if ( !groupDS || groupDS->IsEmpty() ) continue;
11831 std::string grpName = group->GetName();
11832 //MESSAGE("grpName=" << grpName);
11833 if (grpName == groupName)
11839 bool isNodeGroup = false;
11840 bool isNodeCoords = false;
11843 if (groupDS->GetType() != SMDSAbs_Node)
11845 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11848 if (nodesCoords.size() > 0)
11849 isNodeCoords = true; // a list o nodes given by their coordinates
11850 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11852 // --- define groups to build
11854 int idg; // --- group of SMDS volumes
11855 string grpvName = groupName;
11856 grpvName += "_vol";
11857 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11860 MESSAGE("group not created " << grpvName);
11863 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11865 int idgs; // --- group of SMDS faces on the skin
11866 string grpsName = groupName;
11867 grpsName += "_skin";
11868 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11871 MESSAGE("group not created " << grpsName);
11874 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11876 int idgi; // --- group of SMDS faces internal (several shapes)
11877 string grpiName = groupName;
11878 grpiName += "_internalFaces";
11879 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11882 MESSAGE("group not created " << grpiName);
11885 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11887 int idgei; // --- group of SMDS faces internal (several shapes)
11888 string grpeiName = groupName;
11889 grpeiName += "_internalEdges";
11890 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11893 MESSAGE("group not created " << grpeiName);
11896 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11898 // --- build downward connectivity
11900 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11901 meshDS->BuildDownWardConnectivity(true);
11902 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11904 // --- set of volumes detected inside
11906 std::set<int> setOfInsideVol;
11907 std::set<int> setOfVolToCheck;
11909 std::vector<gp_Pnt> gpnts;
11912 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11914 MESSAGE("group of nodes provided");
11915 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11916 while ( elemIt->more() )
11918 const SMDS_MeshElement* elem = elemIt->next();
11921 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11924 SMDS_MeshElement* vol = 0;
11925 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11926 while (volItr->more())
11928 vol = (SMDS_MeshElement*)volItr->next();
11929 setOfInsideVol.insert(vol->getVtkId());
11930 sgrp->Add(vol->GetID());
11934 else if (isNodeCoords)
11936 MESSAGE("list of nodes coordinates provided");
11939 while (i < nodesCoords.size()-2)
11941 double x = nodesCoords[i++];
11942 double y = nodesCoords[i++];
11943 double z = nodesCoords[i++];
11944 gp_Pnt p = gp_Pnt(x, y ,z);
11945 gpnts.push_back(p);
11946 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11949 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11951 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11952 TopTools_IndexedMapOfShape vertexMap;
11953 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11954 gp_Pnt p = gp_Pnt(0,0,0);
11955 if (vertexMap.Extent() < 1)
11958 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11960 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11961 p = BRep_Tool::Pnt(vertex);
11962 gpnts.push_back(p);
11963 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11967 if (gpnts.size() > 0)
11970 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11972 nodeId = startNode->GetID();
11973 MESSAGE("nodeId " << nodeId);
11975 double radius2 = radius*radius;
11976 MESSAGE("radius2 " << radius2);
11978 // --- volumes on start node
11980 setOfVolToCheck.clear();
11981 SMDS_MeshElement* startVol = 0;
11982 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11983 while (volItr->more())
11985 startVol = (SMDS_MeshElement*)volItr->next();
11986 setOfVolToCheck.insert(startVol->getVtkId());
11988 if (setOfVolToCheck.empty())
11990 MESSAGE("No volumes found");
11994 // --- starting with central volumes then their neighbors, check if they are inside
11995 // or outside the domain, until no more new neighbor volume is inside.
11996 // Fill the group of inside volumes
11998 std::map<int, double> mapOfNodeDistance2;
11999 mapOfNodeDistance2.clear();
12000 std::set<int> setOfOutsideVol;
12001 while (!setOfVolToCheck.empty())
12003 std::set<int>::iterator it = setOfVolToCheck.begin();
12005 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12006 bool volInside = false;
12007 vtkIdType npts = 0;
12008 vtkIdType* pts = 0;
12009 grid->GetCellPoints(vtkId, npts, pts);
12010 for (int i=0; i<npts; i++)
12012 double distance2 = 0;
12013 if (mapOfNodeDistance2.count(pts[i]))
12015 distance2 = mapOfNodeDistance2[pts[i]];
12016 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12020 double *coords = grid->GetPoint(pts[i]);
12021 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12023 for (int j=0; j<gpnts.size(); j++)
12025 double d2 = aPoint.SquareDistance(gpnts[j]);
12026 if (d2 < distance2)
12029 if (distance2 < radius2)
12033 mapOfNodeDistance2[pts[i]] = distance2;
12034 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12036 if (distance2 < radius2)
12038 volInside = true; // one or more nodes inside the domain
12039 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12045 setOfInsideVol.insert(vtkId);
12046 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12047 int neighborsVtkIds[NBMAXNEIGHBORS];
12048 int downIds[NBMAXNEIGHBORS];
12049 unsigned char downTypes[NBMAXNEIGHBORS];
12050 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12051 for (int n = 0; n < nbNeighbors; n++)
12052 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12053 setOfVolToCheck.insert(neighborsVtkIds[n]);
12057 setOfOutsideVol.insert(vtkId);
12058 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12060 setOfVolToCheck.erase(vtkId);
12064 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12065 // If yes, add the volume to the inside set
12067 bool addedInside = true;
12068 std::set<int> setOfVolToReCheck;
12069 while (addedInside)
12071 MESSAGE(" --------------------------- re check");
12072 addedInside = false;
12073 std::set<int>::iterator itv = setOfInsideVol.begin();
12074 for (; itv != setOfInsideVol.end(); ++itv)
12077 int neighborsVtkIds[NBMAXNEIGHBORS];
12078 int downIds[NBMAXNEIGHBORS];
12079 unsigned char downTypes[NBMAXNEIGHBORS];
12080 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12081 for (int n = 0; n < nbNeighbors; n++)
12082 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12083 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12085 setOfVolToCheck = setOfVolToReCheck;
12086 setOfVolToReCheck.clear();
12087 while (!setOfVolToCheck.empty())
12089 std::set<int>::iterator it = setOfVolToCheck.begin();
12091 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12093 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12094 int countInside = 0;
12095 int neighborsVtkIds[NBMAXNEIGHBORS];
12096 int downIds[NBMAXNEIGHBORS];
12097 unsigned char downTypes[NBMAXNEIGHBORS];
12098 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12099 for (int n = 0; n < nbNeighbors; n++)
12100 if (setOfInsideVol.count(neighborsVtkIds[n]))
12102 MESSAGE("countInside " << countInside);
12103 if (countInside > 1)
12105 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12106 setOfInsideVol.insert(vtkId);
12107 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12108 addedInside = true;
12111 setOfVolToReCheck.insert(vtkId);
12113 setOfVolToCheck.erase(vtkId);
12117 // --- map of Downward faces at the boundary, inside the global volume
12118 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12119 // fill group of SMDS faces inside the volume (when several volume shapes)
12120 // fill group of SMDS faces on the skin of the global volume (if skin)
12122 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12123 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12124 std::set<int>::iterator it = setOfInsideVol.begin();
12125 for (; it != setOfInsideVol.end(); ++it)
12128 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12129 int neighborsVtkIds[NBMAXNEIGHBORS];
12130 int downIds[NBMAXNEIGHBORS];
12131 unsigned char downTypes[NBMAXNEIGHBORS];
12132 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12133 for (int n = 0; n < nbNeighbors; n++)
12135 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12136 if (neighborDim == 3)
12138 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12140 DownIdType face(downIds[n], downTypes[n]);
12141 boundaryFaces[face] = vtkId;
12143 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12144 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12145 if (vtkFaceId >= 0)
12147 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12148 // find also the smds edges on this face
12149 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12150 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12151 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12152 for (int i = 0; i < nbEdges; i++)
12154 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12155 if (vtkEdgeId >= 0)
12156 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12160 else if (neighborDim == 2) // skin of the volume
12162 DownIdType face(downIds[n], downTypes[n]);
12163 skinFaces[face] = vtkId;
12164 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12165 if (vtkFaceId >= 0)
12166 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12171 // --- identify the edges constituting the wire of each subshape on the skin
12172 // define polylines with the nodes of edges, equivalent to wires
12173 // project polylines on subshapes, and partition, to get geom faces
12175 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12176 std::set<int> emptySet;
12178 std::set<int> shapeIds;
12180 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12181 while (itelem->more())
12183 const SMDS_MeshElement *elem = itelem->next();
12184 int shapeId = elem->getshapeId();
12185 int vtkId = elem->getVtkId();
12186 if (!shapeIdToVtkIdSet.count(shapeId))
12188 shapeIdToVtkIdSet[shapeId] = emptySet;
12189 shapeIds.insert(shapeId);
12191 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12194 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12195 std::set<DownIdType, DownIdCompare> emptyEdges;
12196 emptyEdges.clear();
12198 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12199 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12201 int shapeId = itShape->first;
12202 MESSAGE(" --- Shape ID --- "<< shapeId);
12203 shapeIdToEdges[shapeId] = emptyEdges;
12205 std::vector<int> nodesEdges;
12207 std::set<int>::iterator its = itShape->second.begin();
12208 for (; its != itShape->second.end(); ++its)
12211 MESSAGE(" " << vtkId);
12212 int neighborsVtkIds[NBMAXNEIGHBORS];
12213 int downIds[NBMAXNEIGHBORS];
12214 unsigned char downTypes[NBMAXNEIGHBORS];
12215 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12216 for (int n = 0; n < nbNeighbors; n++)
12218 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12220 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12221 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12222 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12224 DownIdType edge(downIds[n], downTypes[n]);
12225 if (!shapeIdToEdges[shapeId].count(edge))
12227 shapeIdToEdges[shapeId].insert(edge);
12229 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12230 nodesEdges.push_back(vtkNodeId[0]);
12231 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12232 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12238 std::list<int> order;
12240 if (nodesEdges.size() > 0)
12242 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12243 nodesEdges[0] = -1;
12244 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12245 nodesEdges[1] = -1; // do not reuse this edge
12249 int nodeTofind = order.back(); // try first to push back
12251 for (i = 0; i<nodesEdges.size(); i++)
12252 if (nodesEdges[i] == nodeTofind)
12254 if (i == nodesEdges.size())
12255 found = false; // no follower found on back
12258 if (i%2) // odd ==> use the previous one
12259 if (nodesEdges[i-1] < 0)
12263 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12264 nodesEdges[i-1] = -1;
12266 else // even ==> use the next one
12267 if (nodesEdges[i+1] < 0)
12271 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12272 nodesEdges[i+1] = -1;
12277 // try to push front
12279 nodeTofind = order.front(); // try to push front
12280 for (i = 0; i<nodesEdges.size(); i++)
12281 if (nodesEdges[i] == nodeTofind)
12283 if (i == nodesEdges.size())
12285 found = false; // no predecessor found on front
12288 if (i%2) // odd ==> use the previous one
12289 if (nodesEdges[i-1] < 0)
12293 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12294 nodesEdges[i-1] = -1;
12296 else // even ==> use the next one
12297 if (nodesEdges[i+1] < 0)
12301 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12302 nodesEdges[i+1] = -1;
12308 std::vector<int> nodes;
12309 nodes.push_back(shapeId);
12310 std::list<int>::iterator itl = order.begin();
12311 for (; itl != order.end(); itl++)
12313 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12314 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12316 listOfListOfNodes.push_back(nodes);
12319 // partition geom faces with blocFissure
12320 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12321 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12327 //================================================================================
12329 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12330 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12331 * \return TRUE if operation has been completed successfully, FALSE otherwise
12333 //================================================================================
12335 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12337 // iterates on volume elements and detect all free faces on them
12338 SMESHDS_Mesh* aMesh = GetMeshDS();
12341 //bool res = false;
12342 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12343 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12346 const SMDS_MeshVolume* volume = vIt->next();
12347 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12348 vTool.SetExternalNormal();
12349 //const bool isPoly = volume->IsPoly();
12350 const int iQuad = volume->IsQuadratic();
12351 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12353 if (!vTool.IsFreeFace(iface))
12356 vector<const SMDS_MeshNode *> nodes;
12357 int nbFaceNodes = vTool.NbFaceNodes(iface);
12358 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12360 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12361 nodes.push_back(faceNodes[inode]);
12362 if (iQuad) { // add medium nodes
12363 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12364 nodes.push_back(faceNodes[inode]);
12365 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12366 nodes.push_back(faceNodes[8]);
12368 // add new face based on volume nodes
12369 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12371 continue; // face already exsist
12373 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12377 return ( nbFree==(nbExisted+nbCreated) );
12382 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12384 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12386 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12389 //================================================================================
12391 * \brief Creates missing boundary elements
12392 * \param elements - elements whose boundary is to be checked
12393 * \param dimension - defines type of boundary elements to create
12394 * \param group - a group to store created boundary elements in
12395 * \param targetMesh - a mesh to store created boundary elements in
12396 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12397 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12398 * boundary elements will be copied into the targetMesh
12399 * \param toAddExistingBondary - if true, not only new but also pre-existing
12400 * boundary elements will be added into the new group
12401 * \param aroundElements - if true, elements will be created on boundary of given
12402 * elements else, on boundary of the whole mesh.
12403 * \return nb of added boundary elements
12405 //================================================================================
12407 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12408 Bnd_Dimension dimension,
12409 SMESH_Group* group/*=0*/,
12410 SMESH_Mesh* targetMesh/*=0*/,
12411 bool toCopyElements/*=false*/,
12412 bool toCopyExistingBoundary/*=false*/,
12413 bool toAddExistingBondary/*= false*/,
12414 bool aroundElements/*= false*/)
12416 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12417 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12418 // hope that all elements are of the same type, do not check them all
12419 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12420 throw SALOME_Exception(LOCALIZED("wrong element type"));
12423 toCopyElements = toCopyExistingBoundary = false;
12425 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12426 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12427 int nbAddedBnd = 0;
12429 // editor adding present bnd elements and optionally holding elements to add to the group
12430 SMESH_MeshEditor* presentEditor;
12431 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12432 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12434 SMESH_MesherHelper helper( *myMesh );
12435 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12436 SMDS_VolumeTool vTool;
12437 TIDSortedElemSet avoidSet;
12438 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12441 typedef vector<const SMDS_MeshNode*> TConnectivity;
12443 SMDS_ElemIteratorPtr eIt;
12444 if (elements.empty())
12445 eIt = aMesh->elementsIterator(elemType);
12447 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12449 while (eIt->more())
12451 const SMDS_MeshElement* elem = eIt->next();
12452 const int iQuad = elem->IsQuadratic();
12454 // ------------------------------------------------------------------------------------
12455 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12456 // ------------------------------------------------------------------------------------
12457 vector<const SMDS_MeshElement*> presentBndElems;
12458 vector<TConnectivity> missingBndElems;
12459 TConnectivity nodes;
12460 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12462 vTool.SetExternalNormal();
12463 const SMDS_MeshElement* otherVol = 0;
12464 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12466 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12467 ( !aroundElements || elements.count( otherVol )))
12469 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12470 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12471 if ( missType == SMDSAbs_Edge ) // boundary edges
12473 nodes.resize( 2+iQuad );
12474 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12476 for ( int j = 0; j < nodes.size(); ++j )
12478 if ( const SMDS_MeshElement* edge =
12479 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12480 presentBndElems.push_back( edge );
12482 missingBndElems.push_back( nodes );
12485 else // boundary face
12488 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12489 nodes.push_back( nn[inode] );
12490 if (iQuad) // add medium nodes
12491 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12492 nodes.push_back( nn[inode] );
12493 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12495 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12497 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12498 SMDSAbs_Face, /*noMedium=*/false ))
12499 presentBndElems.push_back( f );
12501 missingBndElems.push_back( nodes );
12503 if ( targetMesh != myMesh )
12505 // add 1D elements on face boundary to be added to a new mesh
12506 const SMDS_MeshElement* edge;
12507 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12510 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12512 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12513 if ( edge && avoidSet.insert( edge ).second )
12514 presentBndElems.push_back( edge );
12520 else // elem is a face ------------------------------------------
12522 avoidSet.clear(), avoidSet.insert( elem );
12523 int nbNodes = elem->NbCornerNodes();
12524 nodes.resize( 2 /*+ iQuad*/);
12525 for ( int i = 0; i < nbNodes; i++ )
12527 nodes[0] = elem->GetNode(i);
12528 nodes[1] = elem->GetNode((i+1)%nbNodes);
12529 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12530 continue; // not free link
12533 //nodes[2] = elem->GetNode( i + nbNodes );
12534 if ( const SMDS_MeshElement* edge =
12535 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12536 presentBndElems.push_back( edge );
12538 missingBndElems.push_back( nodes );
12542 // ---------------------------------
12543 // 2. Add missing boundary elements
12544 // ---------------------------------
12545 if ( targetMesh != myMesh )
12546 // instead of making a map of nodes in this mesh and targetMesh,
12547 // we create nodes with same IDs.
12548 for ( int i = 0; i < missingBndElems.size(); ++i )
12550 TConnectivity& srcNodes = missingBndElems[i];
12551 TConnectivity nodes( srcNodes.size() );
12552 for ( inode = 0; inode < nodes.size(); ++inode )
12553 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12554 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12556 /*noMedium=*/false))
12558 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12562 for ( int i = 0; i < missingBndElems.size(); ++i )
12564 TConnectivity& nodes = missingBndElems[i];
12565 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12567 /*noMedium=*/false))
12569 SMDS_MeshElement* elem =
12570 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12573 // try to set a new element to a shape
12574 if ( myMesh->HasShapeToMesh() )
12577 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12578 const int nbN = nodes.size() / (iQuad+1 );
12579 for ( inode = 0; inode < nbN && ok; ++inode )
12581 pair<int, TopAbs_ShapeEnum> i_stype =
12582 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12583 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12584 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12586 if ( ok && mediumShapes.size() > 1 )
12588 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12589 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12590 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12592 if (( ok = ( stype_i->first != stype_i_0.first )))
12593 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12594 aMesh->IndexToShape( stype_i_0.second ));
12597 if ( ok && mediumShapes.begin()->first == missShapeType )
12598 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12602 // ----------------------------------
12603 // 3. Copy present boundary elements
12604 // ----------------------------------
12605 if ( toCopyExistingBoundary )
12606 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12608 const SMDS_MeshElement* e = presentBndElems[i];
12609 TConnectivity nodes( e->NbNodes() );
12610 for ( inode = 0; inode < nodes.size(); ++inode )
12611 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12612 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12614 else // store present elements to add them to a group
12615 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12617 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12620 } // loop on given elements
12622 // ---------------------------------------------
12623 // 4. Fill group with boundary elements
12624 // ---------------------------------------------
12627 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12628 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12629 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12631 tgtEditor.myLastCreatedElems.Clear();
12632 tgtEditor2.myLastCreatedElems.Clear();
12634 // -----------------------
12635 // 5. Copy given elements
12636 // -----------------------
12637 if ( toCopyElements && targetMesh != myMesh )
12639 if (elements.empty())
12640 eIt = aMesh->elementsIterator(elemType);
12642 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12643 while (eIt->more())
12645 const SMDS_MeshElement* elem = eIt->next();
12646 TConnectivity nodes( elem->NbNodes() );
12647 for ( inode = 0; inode < nodes.size(); ++inode )
12648 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12649 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12651 tgtEditor.myLastCreatedElems.Clear();