1 // Copyright (C) 2007-2016 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, or (at your option) any later version.
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_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
97 #include <Standard_Failure.hxx>
98 #include <Standard_ErrorHandler.hxx>
99 #include <OSD_Parallel.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
110 template < class ELEM_SET >
111 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
113 typedef SMDS_SetIterator
114 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
115 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
119 //=======================================================================
120 //function : SMESH_MeshEditor
122 //=======================================================================
124 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
125 :myMesh( theMesh ) // theMesh may be NULL
129 //================================================================================
131 * \brief Return mesh DS
133 //================================================================================
135 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
137 return myMesh->GetMeshDS();
141 //================================================================================
143 * \brief Clears myLastCreatedNodes and myLastCreatedElems
145 //================================================================================
147 void SMESH_MeshEditor::ClearLastCreated()
149 myLastCreatedNodes.Clear();
150 myLastCreatedElems.Clear();
153 //================================================================================
155 * \brief Initializes members by an existing element
156 * \param [in] elem - the source element
157 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
159 //================================================================================
161 SMESH_MeshEditor::ElemFeatures&
162 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
166 myType = elem->GetType();
167 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
169 myIsPoly = elem->IsPoly();
172 myIsQuad = elem->IsQuadratic();
173 if ( myType == SMDSAbs_Volume && !basicOnly )
175 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
176 myPolyhedQuantities.swap( quant );
180 else if ( myType == SMDSAbs_Ball && !basicOnly )
182 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
188 //=======================================================================
192 //=======================================================================
195 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
196 const ElemFeatures& features)
198 SMDS_MeshElement* e = 0;
199 int nbnode = node.size();
200 SMESHDS_Mesh* mesh = GetMeshDS();
201 const int ID = features.myID;
203 switch ( features.myType ) {
205 if ( !features.myIsPoly ) {
207 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
208 else e = mesh->AddFace (node[0], node[1], node[2] );
210 else if (nbnode == 4) {
211 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
212 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
214 else if (nbnode == 6) {
215 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], ID);
217 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
220 else if (nbnode == 7) {
221 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], ID);
223 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6] );
226 else if (nbnode == 8) {
227 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
228 node[4], node[5], node[6], node[7], ID);
229 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
230 node[4], node[5], node[6], node[7] );
232 else if (nbnode == 9) {
233 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
234 node[4], node[5], node[6], node[7], node[8], ID);
235 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
236 node[4], node[5], node[6], node[7], node[8] );
239 else if ( !features.myIsQuad )
241 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
242 else e = mesh->AddPolygonalFace (node );
244 else if ( nbnode % 2 == 0 ) // just a protection
246 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
247 else e = mesh->AddQuadPolygonalFace (node );
252 if ( !features.myIsPoly ) {
254 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
255 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
257 else if (nbnode == 5) {
258 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 else if (nbnode == 6) {
264 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265 node[4], node[5], ID);
266 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
269 else if (nbnode == 8) {
270 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
271 node[4], node[5], node[6], node[7], ID);
272 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7] );
275 else if (nbnode == 10) {
276 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
277 node[4], node[5], node[6], node[7],
278 node[8], node[9], ID);
279 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
283 else if (nbnode == 12) {
284 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
285 node[4], node[5], node[6], node[7],
286 node[8], node[9], node[10], node[11], ID);
287 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
288 node[4], node[5], node[6], node[7],
289 node[8], node[9], node[10], node[11] );
291 else if (nbnode == 13) {
292 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
293 node[4], node[5], node[6], node[7],
294 node[8], node[9], node[10],node[11],
296 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
297 node[4], node[5], node[6], node[7],
298 node[8], node[9], node[10],node[11],
301 else if (nbnode == 15) {
302 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
303 node[4], node[5], node[6], node[7],
304 node[8], node[9], node[10],node[11],
305 node[12],node[13],node[14],ID);
306 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
307 node[4], node[5], node[6], node[7],
308 node[8], node[9], node[10],node[11],
309 node[12],node[13],node[14] );
311 else if (nbnode == 20) {
312 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313 node[4], node[5], node[6], node[7],
314 node[8], node[9], node[10],node[11],
315 node[12],node[13],node[14],node[15],
316 node[16],node[17],node[18],node[19],ID);
317 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
318 node[4], node[5], node[6], node[7],
319 node[8], node[9], node[10],node[11],
320 node[12],node[13],node[14],node[15],
321 node[16],node[17],node[18],node[19] );
323 else if (nbnode == 27) {
324 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
325 node[4], node[5], node[6], node[7],
326 node[8], node[9], node[10],node[11],
327 node[12],node[13],node[14],node[15],
328 node[16],node[17],node[18],node[19],
329 node[20],node[21],node[22],node[23],
330 node[24],node[25],node[26], ID);
331 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
332 node[4], node[5], node[6], node[7],
333 node[8], node[9], node[10],node[11],
334 node[12],node[13],node[14],node[15],
335 node[16],node[17],node[18],node[19],
336 node[20],node[21],node[22],node[23],
337 node[24],node[25],node[26] );
340 else if ( !features.myIsQuad )
342 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
343 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
347 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
348 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
354 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
355 else e = mesh->AddEdge (node[0], node[1] );
357 else if ( nbnode == 3 ) {
358 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
359 else e = mesh->AddEdge (node[0], node[1], node[2] );
363 case SMDSAbs_0DElement:
365 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
366 else e = mesh->Add0DElement (node[0] );
371 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
372 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
376 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
377 else e = mesh->AddBall (node[0], features.myBallDiameter );
382 if ( e ) myLastCreatedElems.Append( e );
386 //=======================================================================
390 //=======================================================================
392 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
393 const ElemFeatures& features)
395 vector<const SMDS_MeshNode*> nodes;
396 nodes.reserve( nodeIDs.size() );
397 vector<int>::const_iterator id = nodeIDs.begin();
398 while ( id != nodeIDs.end() ) {
399 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
400 nodes.push_back( node );
404 return AddElement( nodes, features );
407 //=======================================================================
409 //purpose : Remove a node or an element.
410 // Modify a compute state of sub-meshes which become empty
411 //=======================================================================
413 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
416 myLastCreatedElems.Clear();
417 myLastCreatedNodes.Clear();
419 SMESHDS_Mesh* aMesh = GetMeshDS();
420 set< SMESH_subMesh *> smmap;
423 list<int>::const_iterator it = theIDs.begin();
424 for ( ; it != theIDs.end(); it++ ) {
425 const SMDS_MeshElement * elem;
427 elem = aMesh->FindNode( *it );
429 elem = aMesh->FindElement( *it );
433 // Notify VERTEX sub-meshes about modification
435 const SMDS_MeshNode* node = cast2Node( elem );
436 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
437 if ( int aShapeID = node->getshapeId() )
438 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
441 // Find sub-meshes to notify about modification
442 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
443 // while ( nodeIt->more() ) {
444 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
445 // const SMDS_PositionPtr& aPosition = node->GetPosition();
446 // if ( aPosition.get() ) {
447 // if ( int aShapeID = aPosition->GetShapeId() ) {
448 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
449 // smmap.insert( sm );
456 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
458 aMesh->RemoveElement( elem );
462 // Notify sub-meshes about modification
463 if ( !smmap.empty() ) {
464 set< SMESH_subMesh *>::iterator smIt;
465 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
466 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
469 // // Check if the whole mesh becomes empty
470 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
471 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
476 //================================================================================
478 * \brief Create 0D elements on all nodes of the given object.
479 * \param elements - Elements on whose nodes to create 0D elements; if empty,
480 * the all mesh is treated
481 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
482 * \param duplicateElements - to add one more 0D element to a node or not
484 //================================================================================
486 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
487 TIDSortedElemSet& all0DElems,
488 const bool duplicateElements )
490 SMDS_ElemIteratorPtr elemIt;
491 if ( elements.empty() )
493 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
497 elemIt = elemSetIterator( elements );
500 while ( elemIt->more() )
502 const SMDS_MeshElement* e = elemIt->next();
503 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
504 while ( nodeIt->more() )
506 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
507 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
508 if ( duplicateElements || !it0D->more() )
510 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
511 all0DElems.insert( myLastCreatedElems.Last() );
513 while ( it0D->more() )
514 all0DElems.insert( it0D->next() );
519 //=======================================================================
520 //function : FindShape
521 //purpose : Return an index of the shape theElem is on
522 // or zero if a shape not found
523 //=======================================================================
525 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
527 myLastCreatedElems.Clear();
528 myLastCreatedNodes.Clear();
530 SMESHDS_Mesh * aMesh = GetMeshDS();
531 if ( aMesh->ShapeToMesh().IsNull() )
534 int aShapeID = theElem->getshapeId();
538 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
539 if ( sm->Contains( theElem ))
542 if ( theElem->GetType() == SMDSAbs_Node ) {
543 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
546 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
549 TopoDS_Shape aShape; // the shape a node of theElem is on
550 if ( theElem->GetType() != SMDSAbs_Node )
552 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
553 while ( nodeIt->more() ) {
554 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
555 if ((aShapeID = node->getshapeId()) > 0) {
556 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
557 if ( sm->Contains( theElem ))
559 if ( aShape.IsNull() )
560 aShape = aMesh->IndexToShape( aShapeID );
566 // None of nodes is on a proper shape,
567 // find the shape among ancestors of aShape on which a node is
568 if ( !aShape.IsNull() ) {
569 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
570 for ( ; ancIt.More(); ancIt.Next() ) {
571 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
572 if ( sm && sm->Contains( theElem ))
573 return aMesh->ShapeToIndex( ancIt.Value() );
578 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
579 while ( const SMESHDS_SubMesh* sm = smIt->next() )
580 if ( sm->Contains( theElem ))
587 //=======================================================================
588 //function : IsMedium
590 //=======================================================================
592 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
593 const SMDSAbs_ElementType typeToCheck)
595 bool isMedium = false;
596 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
597 while (it->more() && !isMedium ) {
598 const SMDS_MeshElement* elem = it->next();
599 isMedium = elem->IsMediumNode(node);
604 //=======================================================================
605 //function : shiftNodesQuadTria
606 //purpose : Shift nodes in the array corresponded to quadratic triangle
607 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
608 //=======================================================================
610 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
612 const SMDS_MeshNode* nd1 = aNodes[0];
613 aNodes[0] = aNodes[1];
614 aNodes[1] = aNodes[2];
616 const SMDS_MeshNode* nd2 = aNodes[3];
617 aNodes[3] = aNodes[4];
618 aNodes[4] = aNodes[5];
622 //=======================================================================
623 //function : nbEdgeConnectivity
624 //purpose : return number of the edges connected with the theNode.
625 // if theEdges has connections with the other type of the
626 // elements, return -1
627 //=======================================================================
629 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
631 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
633 // while(elemIt->more()) {
638 return theNode->NbInverseElements();
641 //=======================================================================
642 //function : getNodesFromTwoTria
644 //=======================================================================
646 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
647 const SMDS_MeshElement * theTria2,
648 vector< const SMDS_MeshNode*>& N1,
649 vector< const SMDS_MeshNode*>& N2)
651 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
652 if ( N1.size() < 6 ) return false;
653 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
654 if ( N2.size() < 6 ) return false;
656 int sames[3] = {-1,-1,-1};
668 if(nbsames!=2) return false;
670 shiftNodesQuadTria(N1);
672 shiftNodesQuadTria(N1);
675 i = sames[0] + sames[1] + sames[2];
677 shiftNodesQuadTria(N2);
679 // now we receive following N1 and N2 (using numeration as in the image below)
680 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
681 // i.e. first nodes from both arrays form a new diagonal
685 //=======================================================================
686 //function : InverseDiag
687 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
688 // but having other common link.
689 // Return False if args are improper
690 //=======================================================================
692 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
693 const SMDS_MeshElement * theTria2 )
695 myLastCreatedElems.Clear();
696 myLastCreatedNodes.Clear();
698 if (!theTria1 || !theTria2)
701 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
702 if (!F1) return false;
703 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
704 if (!F2) return false;
705 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
706 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
708 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
709 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
713 // put nodes in array and find out indices of the same ones
714 const SMDS_MeshNode* aNodes [6];
715 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
717 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
718 while ( it->more() ) {
719 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
721 if ( i > 2 ) // theTria2
722 // find same node of theTria1
723 for ( int j = 0; j < 3; j++ )
724 if ( aNodes[ i ] == aNodes[ j ]) {
733 return false; // theTria1 is not a triangle
734 it = theTria2->nodesIterator();
736 if ( i == 6 && it->more() )
737 return false; // theTria2 is not a triangle
740 // find indices of 1,2 and of A,B in theTria1
741 int iA = -1, iB = 0, i1 = 0, i2 = 0;
742 for ( i = 0; i < 6; i++ ) {
743 if ( sameInd [ i ] == -1 ) {
748 if ( iA >= 0) iB = i;
752 // nodes 1 and 2 should not be the same
753 if ( aNodes[ i1 ] == aNodes[ i2 ] )
757 aNodes[ iA ] = aNodes[ i2 ];
759 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
761 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
762 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
766 } // end if(F1 && F2)
768 // check case of quadratic faces
769 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
770 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
772 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
773 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
777 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
778 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
786 vector< const SMDS_MeshNode* > N1;
787 vector< const SMDS_MeshNode* > N2;
788 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
790 // now we receive following N1 and N2 (using numeration as above image)
791 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
792 // i.e. first nodes from both arrays determ new diagonal
794 vector< const SMDS_MeshNode*> N1new( N1.size() );
795 vector< const SMDS_MeshNode*> N2new( N2.size() );
796 N1new.back() = N1.back(); // central node of biquadratic
797 N2new.back() = N2.back();
798 N1new[0] = N1[0]; N2new[0] = N1[0];
799 N1new[1] = N2[0]; N2new[1] = N1[1];
800 N1new[2] = N2[1]; N2new[2] = N2[0];
801 N1new[3] = N1[4]; N2new[3] = N1[3];
802 N1new[4] = N2[3]; N2new[4] = N2[5];
803 N1new[5] = N1[5]; N2new[5] = N1[4];
804 // change nodes in faces
805 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
806 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
808 // move the central node of biquadratic triangle
809 SMESH_MesherHelper helper( *GetMesh() );
810 for ( int is2nd = 0; is2nd < 2; ++is2nd )
812 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
813 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
814 if ( nodes.size() < 7 )
816 helper.SetSubShape( tria->getshapeId() );
817 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
821 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
822 SMESH_TNodeXYZ( nodes[4] ) +
823 SMESH_TNodeXYZ( nodes[5] )) / 3.;
828 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
829 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
830 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
832 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
833 xyz = S->Value( uv.X(), uv.Y() );
834 xyz.Transform( loc );
835 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
836 nodes[6]->getshapeId() > 0 )
837 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
839 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
844 //=======================================================================
845 //function : findTriangles
846 //purpose : find triangles sharing theNode1-theNode2 link
847 //=======================================================================
849 static bool findTriangles(const SMDS_MeshNode * theNode1,
850 const SMDS_MeshNode * theNode2,
851 const SMDS_MeshElement*& theTria1,
852 const SMDS_MeshElement*& theTria2)
854 if ( !theNode1 || !theNode2 ) return false;
856 theTria1 = theTria2 = 0;
858 set< const SMDS_MeshElement* > emap;
859 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
861 const SMDS_MeshElement* elem = it->next();
862 if ( elem->NbCornerNodes() == 3 )
865 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
867 const SMDS_MeshElement* elem = it->next();
868 if ( emap.count( elem )) {
876 // theTria1 must be element with minimum ID
877 if ( theTria2->GetID() < theTria1->GetID() )
878 std::swap( theTria2, theTria1 );
886 //=======================================================================
887 //function : InverseDiag
888 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
889 // with ones built on the same 4 nodes but having other common link.
890 // Return false if proper faces not found
891 //=======================================================================
893 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
894 const SMDS_MeshNode * theNode2)
896 myLastCreatedElems.Clear();
897 myLastCreatedNodes.Clear();
899 const SMDS_MeshElement *tr1, *tr2;
900 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
903 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
904 if (!F1) return false;
905 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
906 if (!F2) return false;
907 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
908 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
910 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
911 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
915 // put nodes in array
916 // and find indices of 1,2 and of A in tr1 and of B in tr2
917 int i, iA1 = 0, i1 = 0;
918 const SMDS_MeshNode* aNodes1 [3];
919 SMDS_ElemIteratorPtr it;
920 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
921 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
922 if ( aNodes1[ i ] == theNode1 )
923 iA1 = i; // node A in tr1
924 else if ( aNodes1[ i ] != theNode2 )
928 const SMDS_MeshNode* aNodes2 [3];
929 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
930 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
931 if ( aNodes2[ i ] == theNode2 )
932 iB2 = i; // node B in tr2
933 else if ( aNodes2[ i ] != theNode1 )
937 // nodes 1 and 2 should not be the same
938 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
942 aNodes1[ iA1 ] = aNodes2[ i2 ];
944 aNodes2[ iB2 ] = aNodes1[ i1 ];
946 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
947 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
952 // check case of quadratic faces
953 return InverseDiag(tr1,tr2);
956 //=======================================================================
957 //function : getQuadrangleNodes
958 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
959 // fusion of triangles tr1 and tr2 having shared link on
960 // theNode1 and theNode2
961 //=======================================================================
963 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
964 const SMDS_MeshNode * theNode1,
965 const SMDS_MeshNode * theNode2,
966 const SMDS_MeshElement * tr1,
967 const SMDS_MeshElement * tr2 )
969 if( tr1->NbNodes() != tr2->NbNodes() )
971 // find the 4-th node to insert into tr1
972 const SMDS_MeshNode* n4 = 0;
973 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
975 while ( !n4 && i<3 ) {
976 const SMDS_MeshNode * n = cast2Node( it->next() );
978 bool isDiag = ( n == theNode1 || n == theNode2 );
982 // Make an array of nodes to be in a quadrangle
983 int iNode = 0, iFirstDiag = -1;
984 it = tr1->nodesIterator();
987 const SMDS_MeshNode * n = cast2Node( it->next() );
989 bool isDiag = ( n == theNode1 || n == theNode2 );
991 if ( iFirstDiag < 0 )
993 else if ( iNode - iFirstDiag == 1 )
994 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
996 else if ( n == n4 ) {
997 return false; // tr1 and tr2 should not have all the same nodes
999 theQuadNodes[ iNode++ ] = n;
1001 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1002 theQuadNodes[ iNode ] = n4;
1007 //=======================================================================
1008 //function : DeleteDiag
1009 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1010 // with a quadrangle built on the same 4 nodes.
1011 // Return false if proper faces not found
1012 //=======================================================================
1014 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1015 const SMDS_MeshNode * theNode2)
1017 myLastCreatedElems.Clear();
1018 myLastCreatedNodes.Clear();
1020 const SMDS_MeshElement *tr1, *tr2;
1021 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1024 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1025 if (!F1) return false;
1026 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1027 if (!F2) return false;
1028 SMESHDS_Mesh * aMesh = GetMeshDS();
1030 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1031 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1033 const SMDS_MeshNode* aNodes [ 4 ];
1034 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1037 const SMDS_MeshElement* newElem = 0;
1038 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1039 myLastCreatedElems.Append(newElem);
1040 AddToSameGroups( newElem, tr1, aMesh );
1041 int aShapeId = tr1->getshapeId();
1044 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1046 aMesh->RemoveElement( tr1 );
1047 aMesh->RemoveElement( tr2 );
1052 // check case of quadratic faces
1053 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1055 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1059 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1060 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1068 vector< const SMDS_MeshNode* > N1;
1069 vector< const SMDS_MeshNode* > N2;
1070 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1072 // now we receive following N1 and N2 (using numeration as above image)
1073 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1074 // i.e. first nodes from both arrays determ new diagonal
1076 const SMDS_MeshNode* aNodes[8];
1086 const SMDS_MeshElement* newElem = 0;
1087 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1088 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1089 myLastCreatedElems.Append(newElem);
1090 AddToSameGroups( newElem, tr1, aMesh );
1091 int aShapeId = tr1->getshapeId();
1094 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1096 aMesh->RemoveElement( tr1 );
1097 aMesh->RemoveElement( tr2 );
1099 // remove middle node (9)
1100 GetMeshDS()->RemoveNode( N1[4] );
1105 //=======================================================================
1106 //function : Reorient
1107 //purpose : Reverse theElement orientation
1108 //=======================================================================
1110 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1112 myLastCreatedElems.Clear();
1113 myLastCreatedNodes.Clear();
1117 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1118 if ( !it || !it->more() )
1121 const SMDSAbs_ElementType type = theElem->GetType();
1122 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1125 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1126 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1128 const SMDS_VtkVolume* aPolyedre =
1129 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1131 MESSAGE("Warning: bad volumic element");
1134 const int nbFaces = aPolyedre->NbFaces();
1135 vector<const SMDS_MeshNode *> poly_nodes;
1136 vector<int> quantities (nbFaces);
1138 // reverse each face of the polyedre
1139 for (int iface = 1; iface <= nbFaces; iface++) {
1140 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1141 quantities[iface - 1] = nbFaceNodes;
1143 for (inode = nbFaceNodes; inode >= 1; inode--) {
1144 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1145 poly_nodes.push_back(curNode);
1148 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1150 else // other elements
1152 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1153 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1154 if ( interlace.empty() )
1156 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1160 SMDS_MeshCell::applyInterlace( interlace, nodes );
1162 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1167 //================================================================================
1169 * \brief Reorient faces.
1170 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1171 * \param theDirection - desired direction of normal of \a theFace
1172 * \param theFace - one of \a theFaces that should be oriented according to
1173 * \a theDirection and whose orientation defines orientation of other faces
1174 * \return number of reoriented faces.
1176 //================================================================================
1178 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1179 const gp_Dir& theDirection,
1180 const SMDS_MeshElement * theFace)
1183 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1185 if ( theFaces.empty() )
1187 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1188 while ( fIt->more() )
1189 theFaces.insert( theFaces.end(), fIt->next() );
1192 // orient theFace according to theDirection
1194 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1195 if ( normal * theDirection.XYZ() < 0 )
1196 nbReori += Reorient( theFace );
1198 // Orient other faces
1200 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1201 TIDSortedElemSet avoidSet;
1202 set< SMESH_TLink > checkedLinks;
1203 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1205 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1206 theFaces.erase( theFace );
1207 startFaces.insert( theFace );
1209 int nodeInd1, nodeInd2;
1210 const SMDS_MeshElement* otherFace;
1211 vector< const SMDS_MeshElement* > facesNearLink;
1212 vector< std::pair< int, int > > nodeIndsOfFace;
1214 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1215 while ( !startFaces.empty() )
1217 startFace = startFaces.begin();
1218 theFace = *startFace;
1219 startFaces.erase( startFace );
1220 if ( !visitedFaces.insert( theFace ).second )
1224 avoidSet.insert(theFace);
1226 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1228 const int nbNodes = theFace->NbCornerNodes();
1229 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1231 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1232 linkIt_isNew = checkedLinks.insert( link );
1233 if ( !linkIt_isNew.second )
1235 // link has already been checked and won't be encountered more
1236 // if the group (theFaces) is manifold
1237 //checkedLinks.erase( linkIt_isNew.first );
1241 facesNearLink.clear();
1242 nodeIndsOfFace.clear();
1243 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1245 &nodeInd1, &nodeInd2 )))
1246 if ( otherFace != theFace)
1248 facesNearLink.push_back( otherFace );
1249 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1250 avoidSet.insert( otherFace );
1252 if ( facesNearLink.size() > 1 )
1254 // NON-MANIFOLD mesh shell !
1255 // select a face most co-directed with theFace,
1256 // other faces won't be visited this time
1258 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1259 double proj, maxProj = -1;
1260 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1261 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1262 if (( proj = Abs( NF * NOF )) > maxProj ) {
1264 otherFace = facesNearLink[i];
1265 nodeInd1 = nodeIndsOfFace[i].first;
1266 nodeInd2 = nodeIndsOfFace[i].second;
1269 // not to visit rejected faces
1270 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1271 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1272 visitedFaces.insert( facesNearLink[i] );
1274 else if ( facesNearLink.size() == 1 )
1276 otherFace = facesNearLink[0];
1277 nodeInd1 = nodeIndsOfFace.back().first;
1278 nodeInd2 = nodeIndsOfFace.back().second;
1280 if ( otherFace && otherFace != theFace)
1282 // link must be reverse in otherFace if orientation ot otherFace
1283 // is same as that of theFace
1284 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1286 nbReori += Reorient( otherFace );
1288 startFaces.insert( otherFace );
1291 std::swap( link.first, link.second ); // reverse the link
1297 //================================================================================
1299 * \brief Reorient faces basing on orientation of adjacent volumes.
1300 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1301 * \param theVolumes - reference volumes.
1302 * \param theOutsideNormal - to orient faces to have their normal
1303 * pointing either \a outside or \a inside the adjacent volumes.
1304 * \return number of reoriented faces.
1306 //================================================================================
1308 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1309 TIDSortedElemSet & theVolumes,
1310 const bool theOutsideNormal)
1314 SMDS_ElemIteratorPtr faceIt;
1315 if ( theFaces.empty() )
1316 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1318 faceIt = elemSetIterator( theFaces );
1320 vector< const SMDS_MeshNode* > faceNodes;
1321 TIDSortedElemSet checkedVolumes;
1322 set< const SMDS_MeshNode* > faceNodesSet;
1323 SMDS_VolumeTool volumeTool;
1325 while ( faceIt->more() ) // loop on given faces
1327 const SMDS_MeshElement* face = faceIt->next();
1328 if ( face->GetType() != SMDSAbs_Face )
1331 const size_t nbCornersNodes = face->NbCornerNodes();
1332 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1334 checkedVolumes.clear();
1335 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1336 while ( vIt->more() )
1338 const SMDS_MeshElement* volume = vIt->next();
1340 if ( !checkedVolumes.insert( volume ).second )
1342 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1345 // is volume adjacent?
1346 bool allNodesCommon = true;
1347 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1348 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1349 if ( !allNodesCommon )
1352 // get nodes of a corresponding volume facet
1353 faceNodesSet.clear();
1354 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1355 volumeTool.Set( volume );
1356 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1357 if ( facetID < 0 ) continue;
1358 volumeTool.SetExternalNormal();
1359 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1361 // compare order of faceNodes and facetNodes
1362 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1364 for ( int i = 0; i < 2; ++i )
1366 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1367 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1368 if ( faceNodes[ iN ] == n )
1374 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1375 if ( isOutside != theOutsideNormal )
1376 nbReori += Reorient( face );
1378 } // loop on given faces
1383 //=======================================================================
1384 //function : getBadRate
1386 //=======================================================================
1388 static double getBadRate (const SMDS_MeshElement* theElem,
1389 SMESH::Controls::NumericalFunctorPtr& theCrit)
1391 SMESH::Controls::TSequenceOfXYZ P;
1392 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1394 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1395 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1398 //=======================================================================
1399 //function : QuadToTri
1400 //purpose : Cut quadrangles into triangles.
1401 // theCrit is used to select a diagonal to cut
1402 //=======================================================================
1404 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1405 SMESH::Controls::NumericalFunctorPtr theCrit)
1407 myLastCreatedElems.Clear();
1408 myLastCreatedNodes.Clear();
1410 if ( !theCrit.get() )
1413 SMESHDS_Mesh * aMesh = GetMeshDS();
1415 Handle(Geom_Surface) surface;
1416 SMESH_MesherHelper helper( *GetMesh() );
1418 TIDSortedElemSet::iterator itElem;
1419 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1421 const SMDS_MeshElement* elem = *itElem;
1422 if ( !elem || elem->GetType() != SMDSAbs_Face )
1424 if ( elem->NbCornerNodes() != 4 )
1427 // retrieve element nodes
1428 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1430 // compare two sets of possible triangles
1431 double aBadRate1, aBadRate2; // to what extent a set is bad
1432 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1433 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1434 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1436 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1437 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1438 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1440 const int aShapeId = FindShape( elem );
1441 const SMDS_MeshElement* newElem1 = 0;
1442 const SMDS_MeshElement* newElem2 = 0;
1444 if ( !elem->IsQuadratic() ) // split liner quadrangle
1446 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1447 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1448 if ( aBadRate1 <= aBadRate2 ) {
1449 // tr1 + tr2 is better
1450 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1451 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1454 // tr3 + tr4 is better
1455 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1456 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1459 else // split quadratic quadrangle
1461 helper.SetIsQuadratic( true );
1462 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1464 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1465 if ( aNodes.size() == 9 )
1467 helper.SetIsBiQuadratic( true );
1468 if ( aBadRate1 <= aBadRate2 )
1469 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1471 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1473 // create a new element
1474 if ( aBadRate1 <= aBadRate2 ) {
1475 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1476 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1479 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1480 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1484 // care of a new element
1486 myLastCreatedElems.Append(newElem1);
1487 myLastCreatedElems.Append(newElem2);
1488 AddToSameGroups( newElem1, elem, aMesh );
1489 AddToSameGroups( newElem2, elem, aMesh );
1491 // put a new triangle on the same shape
1493 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1494 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1496 aMesh->RemoveElement( elem );
1501 //=======================================================================
1503 * \brief Split each of given quadrangles into 4 triangles.
1504 * \param theElems - The faces to be splitted. If empty all faces are split.
1506 //=======================================================================
1508 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1510 myLastCreatedElems.Clear();
1511 myLastCreatedNodes.Clear();
1513 SMESH_MesherHelper helper( *GetMesh() );
1514 helper.SetElementsOnShape( true );
1516 SMDS_ElemIteratorPtr faceIt;
1517 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1518 else faceIt = elemSetIterator( theElems );
1521 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1523 vector< const SMDS_MeshNode* > nodes;
1524 SMESHDS_SubMesh* subMeshDS = 0;
1526 Handle(Geom_Surface) surface;
1527 TopLoc_Location loc;
1529 while ( faceIt->more() )
1531 const SMDS_MeshElement* quad = faceIt->next();
1532 if ( !quad || quad->NbCornerNodes() != 4 )
1535 // get a surface the quad is on
1537 if ( quad->getshapeId() < 1 )
1540 helper.SetSubShape( 0 );
1543 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1545 helper.SetSubShape( quad->getshapeId() );
1546 if ( !helper.GetSubShape().IsNull() &&
1547 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1549 F = TopoDS::Face( helper.GetSubShape() );
1550 surface = BRep_Tool::Surface( F, loc );
1551 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1555 helper.SetSubShape( 0 );
1560 // create a central node
1562 const SMDS_MeshNode* nCentral;
1563 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1565 if ( nodes.size() == 9 )
1567 nCentral = nodes.back();
1574 for ( ; iN < nodes.size(); ++iN )
1575 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1577 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1578 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1580 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1581 xyz[0], xyz[1], xyz[2], xyz[3],
1582 xyz[4], xyz[5], xyz[6], xyz[7] );
1586 for ( ; iN < nodes.size(); ++iN )
1587 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1589 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1590 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1592 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1593 uv[0], uv[1], uv[2], uv[3],
1594 uv[4], uv[5], uv[6], uv[7] );
1596 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1600 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1601 uv[8].X(), uv[8].Y() );
1602 myLastCreatedNodes.Append( nCentral );
1605 // create 4 triangles
1607 helper.SetIsQuadratic ( nodes.size() > 4 );
1608 helper.SetIsBiQuadratic( nodes.size() == 9 );
1609 if ( helper.GetIsQuadratic() )
1610 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1612 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1614 for ( int i = 0; i < 4; ++i )
1616 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1619 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1620 myLastCreatedElems.Append( tria );
1625 //=======================================================================
1626 //function : BestSplit
1627 //purpose : Find better diagonal for cutting.
1628 //=======================================================================
1630 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1631 SMESH::Controls::NumericalFunctorPtr theCrit)
1633 myLastCreatedElems.Clear();
1634 myLastCreatedNodes.Clear();
1639 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1642 if( theQuad->NbNodes()==4 ||
1643 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1645 // retrieve element nodes
1646 const SMDS_MeshNode* aNodes [4];
1647 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1649 //while (itN->more())
1651 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1653 // compare two sets of possible triangles
1654 double aBadRate1, aBadRate2; // to what extent a set is bad
1655 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1656 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1657 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1659 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1660 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1661 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1662 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1663 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1664 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1665 return 1; // diagonal 1-3
1667 return 2; // diagonal 2-4
1674 // Methods of splitting volumes into tetra
1676 const int theHexTo5_1[5*4+1] =
1678 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1680 const int theHexTo5_2[5*4+1] =
1682 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1684 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1686 const int theHexTo6_1[6*4+1] =
1688 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
1690 const int theHexTo6_2[6*4+1] =
1692 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
1694 const int theHexTo6_3[6*4+1] =
1696 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
1698 const int theHexTo6_4[6*4+1] =
1700 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
1702 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1704 const int thePyraTo2_1[2*4+1] =
1706 0, 1, 2, 4, 0, 2, 3, 4, -1
1708 const int thePyraTo2_2[2*4+1] =
1710 1, 2, 3, 4, 1, 3, 0, 4, -1
1712 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1714 const int thePentaTo3_1[3*4+1] =
1716 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1718 const int thePentaTo3_2[3*4+1] =
1720 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1722 const int thePentaTo3_3[3*4+1] =
1724 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1726 const int thePentaTo3_4[3*4+1] =
1728 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1730 const int thePentaTo3_5[3*4+1] =
1732 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1734 const int thePentaTo3_6[3*4+1] =
1736 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1738 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1739 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1741 // Methods of splitting hexahedron into prisms
1743 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1745 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1747 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1749 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1751 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1753 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1756 const int theHexTo2Prisms_BT_1[6*2+1] =
1758 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1760 const int theHexTo2Prisms_BT_2[6*2+1] =
1762 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1764 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1766 const int theHexTo2Prisms_LR_1[6*2+1] =
1768 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1770 const int theHexTo2Prisms_LR_2[6*2+1] =
1772 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1774 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1776 const int theHexTo2Prisms_FB_1[6*2+1] =
1778 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1780 const int theHexTo2Prisms_FB_2[6*2+1] =
1782 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1784 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1787 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1790 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1791 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1792 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1793 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1799 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1800 bool _baryNode; //!< additional node is to be created at cell barycenter
1801 bool _ownConn; //!< to delete _connectivity in destructor
1802 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1804 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1805 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1806 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1807 bool hasFacet( const TTriangleFacet& facet ) const
1809 if ( _nbCorners == 4 )
1811 const int* tetConn = _connectivity;
1812 for ( ; tetConn[0] >= 0; tetConn += 4 )
1813 if (( facet.contains( tetConn[0] ) +
1814 facet.contains( tetConn[1] ) +
1815 facet.contains( tetConn[2] ) +
1816 facet.contains( tetConn[3] )) == 3 )
1819 else // prism, _nbCorners == 6
1821 const int* prismConn = _connectivity;
1822 for ( ; prismConn[0] >= 0; prismConn += 6 )
1824 if (( facet.contains( prismConn[0] ) &&
1825 facet.contains( prismConn[1] ) &&
1826 facet.contains( prismConn[2] ))
1828 ( facet.contains( prismConn[3] ) &&
1829 facet.contains( prismConn[4] ) &&
1830 facet.contains( prismConn[5] )))
1838 //=======================================================================
1840 * \brief return TSplitMethod for the given element to split into tetrahedra
1842 //=======================================================================
1844 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1846 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1848 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1849 // an edge and a face barycenter; tertaherdons are based on triangles and
1850 // a volume barycenter
1851 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1853 // Find out how adjacent volumes are split
1855 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1856 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1857 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1859 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1860 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1861 if ( nbNodes < 4 ) continue;
1863 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1864 const int* nInd = vol.GetFaceNodesIndices( iF );
1867 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1868 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1869 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1870 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1874 int iCom = 0; // common node of triangle faces to split into
1875 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1877 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1878 nInd[ iQ * ( (iCom+1)%nbNodes )],
1879 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1880 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1881 nInd[ iQ * ( (iCom+2)%nbNodes )],
1882 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1883 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1885 triaSplits.push_back( t012 );
1886 triaSplits.push_back( t023 );
1891 if ( !triaSplits.empty() )
1892 hasAdjacentSplits = true;
1895 // Among variants of split method select one compliant with adjacent volumes
1897 TSplitMethod method;
1898 if ( !vol.Element()->IsPoly() && !is24TetMode )
1900 int nbVariants = 2, nbTet = 0;
1901 const int** connVariants = 0;
1902 switch ( vol.Element()->GetEntityType() )
1904 case SMDSEntity_Hexa:
1905 case SMDSEntity_Quad_Hexa:
1906 case SMDSEntity_TriQuad_Hexa:
1907 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1908 connVariants = theHexTo5, nbTet = 5;
1910 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1912 case SMDSEntity_Pyramid:
1913 case SMDSEntity_Quad_Pyramid:
1914 connVariants = thePyraTo2; nbTet = 2;
1916 case SMDSEntity_Penta:
1917 case SMDSEntity_Quad_Penta:
1918 case SMDSEntity_BiQuad_Penta:
1919 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1924 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1926 // check method compliancy with adjacent tetras,
1927 // all found splits must be among facets of tetras described by this method
1928 method = TSplitMethod( nbTet, connVariants[variant] );
1929 if ( hasAdjacentSplits && method._nbSplits > 0 )
1931 bool facetCreated = true;
1932 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1934 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1935 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1936 facetCreated = method.hasFacet( *facet );
1938 if ( !facetCreated )
1939 method = TSplitMethod(0); // incompatible method
1943 if ( method._nbSplits < 1 )
1945 // No standard method is applicable, use a generic solution:
1946 // each facet of a volume is split into triangles and
1947 // each of triangles and a volume barycenter form a tetrahedron.
1949 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1951 int* connectivity = new int[ maxTetConnSize + 1 ];
1952 method._connectivity = connectivity;
1953 method._ownConn = true;
1954 method._baryNode = !isHex27; // to create central node or not
1957 int baryCenInd = vol.NbNodes() - int( isHex27 );
1958 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1960 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1961 const int* nInd = vol.GetFaceNodesIndices( iF );
1962 // find common node of triangle facets of tetra to create
1963 int iCommon = 0; // index in linear numeration
1964 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1965 if ( !triaSplits.empty() )
1968 const TTriangleFacet* facet = &triaSplits.front();
1969 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1970 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1971 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1974 else if ( nbNodes > 3 && !is24TetMode )
1976 // find the best method of splitting into triangles by aspect ratio
1977 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1978 map< double, int > badness2iCommon;
1979 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1980 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1981 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1984 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1986 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1987 nodes[ iQ*((iLast-1)%nbNodes)],
1988 nodes[ iQ*((iLast )%nbNodes)]);
1989 badness += getBadRate( &tria, aspectRatio );
1991 badness2iCommon.insert( make_pair( badness, iCommon ));
1993 // use iCommon with lowest badness
1994 iCommon = badness2iCommon.begin()->second;
1996 if ( iCommon >= nbNodes )
1997 iCommon = 0; // something wrong
1999 // fill connectivity of tetrahedra based on a current face
2000 int nbTet = nbNodes - 2;
2001 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2006 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2007 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2011 method._faceBaryNode[ iF ] = 0;
2012 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2015 for ( int i = 0; i < nbTet; ++i )
2017 int i1 = i, i2 = (i+1) % nbNodes;
2018 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2019 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2020 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2021 connectivity[ connSize++ ] = faceBaryCenInd;
2022 connectivity[ connSize++ ] = baryCenInd;
2027 for ( int i = 0; i < nbTet; ++i )
2029 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2030 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2031 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2032 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2033 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2034 connectivity[ connSize++ ] = baryCenInd;
2037 method._nbSplits += nbTet;
2039 } // loop on volume faces
2041 connectivity[ connSize++ ] = -1;
2043 } // end of generic solution
2047 //=======================================================================
2049 * \brief return TSplitMethod to split haxhedron into prisms
2051 //=======================================================================
2053 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2054 const int methodFlags,
2055 const int facetToSplit)
2057 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2059 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2061 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2063 static TSplitMethod to4methods[4]; // order BT, LR, FB
2064 if ( to4methods[iF]._nbSplits == 0 )
2068 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2069 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2070 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2073 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2074 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2075 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2078 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2079 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2080 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2082 default: return to4methods[3];
2084 to4methods[iF]._nbSplits = 4;
2085 to4methods[iF]._nbCorners = 6;
2087 return to4methods[iF];
2089 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2091 TSplitMethod method;
2093 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2095 const int nbVariants = 2, nbSplits = 2;
2096 const int** connVariants = 0;
2098 case 0: connVariants = theHexTo2Prisms_BT; break;
2099 case 1: connVariants = theHexTo2Prisms_LR; break;
2100 case 2: connVariants = theHexTo2Prisms_FB; break;
2101 default: return method;
2104 // look for prisms adjacent via facetToSplit and an opposite one
2105 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2107 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2108 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2109 if ( nbNodes != 4 ) return method;
2111 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2112 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2113 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2115 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2117 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2122 // there are adjacent prism
2123 for ( int variant = 0; variant < nbVariants; ++variant )
2125 // check method compliancy with adjacent prisms,
2126 // the found prism facets must be among facets of prisms described by current method
2127 method._nbSplits = nbSplits;
2128 method._nbCorners = 6;
2129 method._connectivity = connVariants[ variant ];
2130 if ( method.hasFacet( *t ))
2135 // No adjacent prisms. Select a variant with a best aspect ratio.
2137 double badness[2] = { 0., 0. };
2138 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2139 const SMDS_MeshNode** nodes = vol.GetNodes();
2140 for ( int variant = 0; variant < nbVariants; ++variant )
2141 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2143 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2144 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2146 method._connectivity = connVariants[ variant ];
2147 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2148 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2149 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2151 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2154 badness[ variant ] += getBadRate( &tria, aspectRatio );
2156 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2158 method._nbSplits = nbSplits;
2159 method._nbCorners = 6;
2160 method._connectivity = connVariants[ iBetter ];
2165 //================================================================================
2167 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2169 //================================================================================
2171 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2172 const SMDSAbs_GeometryType geom ) const
2174 // find the tetrahedron including the three nodes of facet
2175 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2176 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2177 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2178 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2179 while ( volIt1->more() )
2181 const SMDS_MeshElement* v = volIt1->next();
2182 if ( v->GetGeomType() != geom )
2184 const int lastCornerInd = v->NbCornerNodes() - 1;
2185 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2186 continue; // medium node not allowed
2187 const int ind2 = v->GetNodeIndex( n2 );
2188 if ( ind2 < 0 || lastCornerInd < ind2 )
2190 const int ind3 = v->GetNodeIndex( n3 );
2191 if ( ind3 < 0 || lastCornerInd < ind3 )
2198 //=======================================================================
2200 * \brief A key of a face of volume
2202 //=======================================================================
2204 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2206 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2208 TIDSortedNodeSet sortedNodes;
2209 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2210 int nbNodes = vol.NbFaceNodes( iF );
2211 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2212 for ( int i = 0; i < nbNodes; i += iQ )
2213 sortedNodes.insert( fNodes[i] );
2214 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2215 first.first = (*(n++))->GetID();
2216 first.second = (*(n++))->GetID();
2217 second.first = (*(n++))->GetID();
2218 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2223 //=======================================================================
2224 //function : SplitVolumes
2225 //purpose : Split volume elements into tetrahedra or prisms.
2226 // If facet ID < 0, element is split into tetrahedra,
2227 // else a hexahedron is split into prisms so that the given facet is
2228 // split into triangles
2229 //=======================================================================
2231 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2232 const int theMethodFlags)
2234 SMDS_VolumeTool volTool;
2235 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2236 fHelper.ToFixNodeParameters( true );
2238 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2239 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2241 SMESH_SequenceOfElemPtr newNodes, newElems;
2243 // map face of volume to it's baricenrtic node
2244 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2246 vector<const SMDS_MeshElement* > splitVols;
2248 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2249 for ( ; elem2facet != theElems.end(); ++elem2facet )
2251 const SMDS_MeshElement* elem = elem2facet->first;
2252 const int facetToSplit = elem2facet->second;
2253 if ( elem->GetType() != SMDSAbs_Volume )
2255 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2256 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2259 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2261 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2262 getTetraSplitMethod( volTool, theMethodFlags ) :
2263 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2264 if ( splitMethod._nbSplits < 1 ) continue;
2266 // find submesh to add new tetras to
2267 if ( !subMesh || !subMesh->Contains( elem ))
2269 int shapeID = FindShape( elem );
2270 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2271 subMesh = GetMeshDS()->MeshElements( shapeID );
2274 if ( elem->IsQuadratic() )
2277 // add quadratic links to the helper
2278 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2280 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2281 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2282 for ( int iN = 0; iN < nbN; iN += iQ )
2283 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2285 helper.SetIsQuadratic( true );
2290 helper.SetIsQuadratic( false );
2292 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2293 volTool.GetNodes() + elem->NbNodes() );
2294 helper.SetElementsOnShape( true );
2295 if ( splitMethod._baryNode )
2297 // make a node at barycenter
2298 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2299 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2300 nodes.push_back( gcNode );
2301 newNodes.Append( gcNode );
2303 if ( !splitMethod._faceBaryNode.empty() )
2305 // make or find baricentric nodes of faces
2306 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2307 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2309 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2310 volFace2BaryNode.insert
2311 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2314 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2315 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2317 nodes.push_back( iF_n->second = f_n->second );
2322 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2323 const int* volConn = splitMethod._connectivity;
2324 if ( splitMethod._nbCorners == 4 ) // tetra
2325 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2326 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2327 nodes[ volConn[1] ],
2328 nodes[ volConn[2] ],
2329 nodes[ volConn[3] ]));
2331 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2332 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2333 nodes[ volConn[1] ],
2334 nodes[ volConn[2] ],
2335 nodes[ volConn[3] ],
2336 nodes[ volConn[4] ],
2337 nodes[ volConn[5] ]));
2339 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2341 // Split faces on sides of the split volume
2343 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2344 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2346 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2347 if ( nbNodes < 4 ) continue;
2349 // find an existing face
2350 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2351 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2352 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2353 /*noMedium=*/false))
2356 helper.SetElementsOnShape( false );
2357 vector< const SMDS_MeshElement* > triangles;
2359 // find submesh to add new triangles in
2360 if ( !fSubMesh || !fSubMesh->Contains( face ))
2362 int shapeID = FindShape( face );
2363 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2365 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2366 if ( iF_n != splitMethod._faceBaryNode.end() )
2368 const SMDS_MeshNode *baryNode = iF_n->second;
2369 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2371 const SMDS_MeshNode* n1 = fNodes[iN];
2372 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2373 const SMDS_MeshNode *n3 = baryNode;
2374 if ( !volTool.IsFaceExternal( iF ))
2376 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2378 if ( fSubMesh ) // update position of the bary node on geometry
2381 subMesh->RemoveNode( baryNode, false );
2382 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2383 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2384 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2386 fHelper.SetSubShape( s );
2387 gp_XY uv( 1e100, 1e100 );
2389 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2390 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2393 // node is too far from the surface
2394 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2395 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2396 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2403 // among possible triangles create ones described by split method
2404 const int* nInd = volTool.GetFaceNodesIndices( iF );
2405 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2406 int iCom = 0; // common node of triangle faces to split into
2407 list< TTriangleFacet > facets;
2408 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2410 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2411 nInd[ iQ * ( (iCom+1)%nbNodes )],
2412 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2413 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2414 nInd[ iQ * ( (iCom+2)%nbNodes )],
2415 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2416 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2418 facets.push_back( t012 );
2419 facets.push_back( t023 );
2420 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2421 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2422 nInd[ iQ * ((iLast-1)%nbNodes )],
2423 nInd[ iQ * ((iLast )%nbNodes )]));
2427 list< TTriangleFacet >::iterator facet = facets.begin();
2428 if ( facet == facets.end() )
2430 for ( ; facet != facets.end(); ++facet )
2432 if ( !volTool.IsFaceExternal( iF ))
2433 swap( facet->_n2, facet->_n3 );
2434 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2435 volNodes[ facet->_n2 ],
2436 volNodes[ facet->_n3 ]));
2439 for ( size_t i = 0; i < triangles.size(); ++i )
2441 if ( !triangles[ i ]) continue;
2443 fSubMesh->AddElement( triangles[ i ]);
2444 newElems.Append( triangles[ i ]);
2446 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2447 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2449 } // while a face based on facet nodes exists
2450 } // loop on volume faces to split them into triangles
2452 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2454 if ( geomType == SMDSEntity_TriQuad_Hexa )
2456 // remove medium nodes that could become free
2457 for ( int i = 20; i < volTool.NbNodes(); ++i )
2458 if ( volNodes[i]->NbInverseElements() == 0 )
2459 GetMeshDS()->RemoveNode( volNodes[i] );
2461 } // loop on volumes to split
2463 myLastCreatedNodes = newNodes;
2464 myLastCreatedElems = newElems;
2467 //=======================================================================
2468 //function : GetHexaFacetsToSplit
2469 //purpose : For hexahedra that will be split into prisms, finds facets to
2470 // split into triangles. Only hexahedra adjacent to the one closest
2471 // to theFacetNormal.Location() are returned.
2472 //param [in,out] theHexas - the hexahedra
2473 //param [in] theFacetNormal - facet normal
2474 //param [out] theFacets - the hexahedra and found facet IDs
2475 //=======================================================================
2477 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2478 const gp_Ax1& theFacetNormal,
2479 TFacetOfElem & theFacets)
2481 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2483 // Find a hexa closest to the location of theFacetNormal
2485 const SMDS_MeshElement* startHex;
2487 // get SMDS_ElemIteratorPtr on theHexas
2488 typedef const SMDS_MeshElement* TValue;
2489 typedef TIDSortedElemSet::iterator TSetIterator;
2490 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2491 typedef SMDS_MeshElement::GeomFilter TFilter;
2492 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2493 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2494 ( new TElemSetIter( theHexas.begin(),
2496 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2498 SMESH_ElementSearcher* searcher =
2499 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2501 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2506 throw SALOME_Exception( THIS_METHOD "startHex not found");
2509 // Select a facet of startHex by theFacetNormal
2511 SMDS_VolumeTool vTool( startHex );
2512 double norm[3], dot, maxDot = 0;
2514 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2515 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2517 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2525 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2527 // Fill theFacets starting from facetID of startHex
2529 // facets used for searching of volumes adjacent to already treated ones
2530 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2531 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2532 TFacetMap facetsToCheck;
2534 set<const SMDS_MeshNode*> facetNodes;
2535 const SMDS_MeshElement* curHex;
2537 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2541 // move in two directions from startHex via facetID
2542 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2545 int curFacet = facetID;
2546 if ( is2nd ) // do not treat startHex twice
2548 vTool.Set( curHex );
2549 if ( vTool.IsFreeFace( curFacet, &curHex ))
2555 vTool.GetFaceNodes( curFacet, facetNodes );
2556 vTool.Set( curHex );
2557 curFacet = vTool.GetFaceIndex( facetNodes );
2562 // store a facet to split
2563 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2565 theFacets.insert( make_pair( curHex, -1 ));
2568 if ( !allHex && !theHexas.count( curHex ))
2571 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2572 theFacets.insert( make_pair( curHex, curFacet ));
2573 if ( !facetIt2isNew.second )
2576 // remember not-to-split facets in facetsToCheck
2577 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2578 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2580 if ( iF == curFacet && iF == oppFacet )
2582 TVolumeFaceKey facetKey ( vTool, iF );
2583 TElemFacets elemFacet( facetIt2isNew.first, iF );
2584 pair< TFacetMap::iterator, bool > it2isnew =
2585 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2586 if ( !it2isnew.second )
2587 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2589 // pass to a volume adjacent via oppFacet
2590 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2596 // get a new curFacet
2597 vTool.GetFaceNodes( oppFacet, facetNodes );
2598 vTool.Set( curHex );
2599 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2602 } // move in two directions from startHex via facetID
2604 // Find a new startHex by facetsToCheck
2608 TFacetMap::iterator fIt = facetsToCheck.begin();
2609 while ( !startHex && fIt != facetsToCheck.end() )
2611 const TElemFacets& elemFacets = fIt->second;
2612 const SMDS_MeshElement* hex = elemFacets.first->first;
2613 int splitFacet = elemFacets.first->second;
2614 int lateralFacet = elemFacets.second;
2615 facetsToCheck.erase( fIt );
2616 fIt = facetsToCheck.begin();
2619 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2620 curHex->GetGeomType() != SMDSGeom_HEXA )
2622 if ( !allHex && !theHexas.count( curHex ))
2627 // find a facet of startHex to split
2629 set<const SMDS_MeshNode*> lateralNodes;
2630 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2631 vTool.GetFaceNodes( splitFacet, facetNodes );
2632 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2633 vTool.Set( startHex );
2634 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2636 // look for a facet of startHex having common nodes with facetNodes
2637 // but not lateralFacet
2638 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2640 if ( iF == lateralFacet )
2642 int nbCommonNodes = 0;
2643 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2644 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2645 nbCommonNodes += facetNodes.count( nn[ iN ]);
2647 if ( nbCommonNodes >= 2 )
2654 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2656 } // while ( startHex )
2663 //================================================================================
2665 * \brief Selects nodes of several elements according to a given interlace
2666 * \param [in] srcNodes - nodes to select from
2667 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2668 * \param [in] interlace - indices of nodes for all elements
2669 * \param [in] nbElems - nb of elements
2670 * \param [in] nbNodes - nb of nodes in each element
2671 * \param [in] mesh - the mesh
2672 * \param [out] elemQueue - a list to push elements found by the selected nodes
2673 * \param [in] type - type of elements to look for
2675 //================================================================================
2677 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2678 vector< const SMDS_MeshNode* >* tgtNodesVec,
2679 const int* interlace,
2682 SMESHDS_Mesh* mesh = 0,
2683 list< const SMDS_MeshElement* >* elemQueue=0,
2684 SMDSAbs_ElementType type=SMDSAbs_All)
2686 for ( int iE = 0; iE < nbElems; ++iE )
2688 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2689 const int* select = & interlace[iE*nbNodes];
2690 elemNodes.resize( nbNodes );
2691 for ( int iN = 0; iN < nbNodes; ++iN )
2692 elemNodes[iN] = srcNodes[ select[ iN ]];
2694 const SMDS_MeshElement* e;
2696 for ( int iE = 0; iE < nbElems; ++iE )
2697 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2698 elemQueue->push_back( e );
2702 //=======================================================================
2704 * Split bi-quadratic elements into linear ones without creation of additional nodes
2705 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2706 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2707 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2708 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2709 * will be split in order to keep the mesh conformal.
2710 * \param elems - elements to split
2712 //=======================================================================
2714 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2716 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2717 vector<const SMDS_MeshElement* > splitElems;
2718 list< const SMDS_MeshElement* > elemQueue;
2719 list< const SMDS_MeshElement* >::iterator elemIt;
2721 SMESHDS_Mesh * mesh = GetMeshDS();
2722 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2723 int nbElems, nbNodes;
2725 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2726 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2729 elemQueue.push_back( *elemSetIt );
2730 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2732 const SMDS_MeshElement* elem = *elemIt;
2733 switch( elem->GetEntityType() )
2735 case SMDSEntity_TriQuad_Hexa: // HEX27
2737 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2738 nbElems = nbNodes = 8;
2739 elemType = & hexaType;
2741 // get nodes for new elements
2742 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2743 { 1,9,20,8, 17,22,26,21 },
2744 { 2,10,20,9, 18,23,26,22 },
2745 { 3,11,20,10, 19,24,26,23 },
2746 { 16,21,26,24, 4,12,25,15 },
2747 { 17,22,26,21, 5,13,25,12 },
2748 { 18,23,26,22, 6,14,25,13 },
2749 { 19,24,26,23, 7,15,25,14 }};
2750 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2752 // add boundary faces to elemQueue
2753 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2754 { 4,5,6,7, 12,13,14,15, 25 },
2755 { 0,1,5,4, 8,17,12,16, 21 },
2756 { 1,2,6,5, 9,18,13,17, 22 },
2757 { 2,3,7,6, 10,19,14,18, 23 },
2758 { 3,0,4,7, 11,16,15,19, 24 }};
2759 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2761 // add boundary segments to elemQueue
2762 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2763 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2764 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2765 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2768 case SMDSEntity_BiQuad_Triangle: // TRIA7
2770 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2773 elemType = & quadType;
2775 // get nodes for new elements
2776 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2777 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2779 // add boundary segments to elemQueue
2780 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2781 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2784 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2786 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2789 elemType = & quadType;
2791 // get nodes for new elements
2792 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2793 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2795 // add boundary segments to elemQueue
2796 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2797 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2800 case SMDSEntity_Quad_Edge:
2802 if ( elemIt == elemQueue.begin() )
2803 continue; // an elem is in theElems
2804 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2807 elemType = & segType;
2809 // get nodes for new elements
2810 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2811 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2815 } // switch( elem->GetEntityType() )
2817 // Create new elements
2819 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2823 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2824 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2825 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2826 //elemType->SetID( -1 );
2828 for ( int iE = 0; iE < nbElems; ++iE )
2829 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2832 ReplaceElemInGroups( elem, splitElems, mesh );
2835 for ( size_t i = 0; i < splitElems.size(); ++i )
2836 subMesh->AddElement( splitElems[i] );
2841 //=======================================================================
2842 //function : AddToSameGroups
2843 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2844 //=======================================================================
2846 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2847 const SMDS_MeshElement* elemInGroups,
2848 SMESHDS_Mesh * aMesh)
2850 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2851 if (!groups.empty()) {
2852 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2853 for ( ; grIt != groups.end(); grIt++ ) {
2854 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2855 if ( group && group->Contains( elemInGroups ))
2856 group->SMDSGroup().Add( elemToAdd );
2862 //=======================================================================
2863 //function : RemoveElemFromGroups
2864 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2865 //=======================================================================
2866 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2867 SMESHDS_Mesh * aMesh)
2869 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2870 if (!groups.empty())
2872 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2873 for (; GrIt != groups.end(); GrIt++)
2875 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2876 if (!grp || grp->IsEmpty()) continue;
2877 grp->SMDSGroup().Remove(removeelem);
2882 //================================================================================
2884 * \brief Replace elemToRm by elemToAdd in the all groups
2886 //================================================================================
2888 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2889 const SMDS_MeshElement* elemToAdd,
2890 SMESHDS_Mesh * aMesh)
2892 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2893 if (!groups.empty()) {
2894 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2895 for ( ; grIt != groups.end(); grIt++ ) {
2896 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2897 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2898 group->SMDSGroup().Add( elemToAdd );
2903 //================================================================================
2905 * \brief Replace elemToRm by elemToAdd in the all groups
2907 //================================================================================
2909 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2910 const vector<const SMDS_MeshElement*>& elemToAdd,
2911 SMESHDS_Mesh * aMesh)
2913 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2914 if (!groups.empty())
2916 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2917 for ( ; grIt != groups.end(); grIt++ ) {
2918 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2919 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2920 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2921 group->SMDSGroup().Add( elemToAdd[ i ] );
2926 //=======================================================================
2927 //function : QuadToTri
2928 //purpose : Cut quadrangles into triangles.
2929 // theCrit is used to select a diagonal to cut
2930 //=======================================================================
2932 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2933 const bool the13Diag)
2935 myLastCreatedElems.Clear();
2936 myLastCreatedNodes.Clear();
2938 SMESHDS_Mesh * aMesh = GetMeshDS();
2940 Handle(Geom_Surface) surface;
2941 SMESH_MesherHelper helper( *GetMesh() );
2943 TIDSortedElemSet::iterator itElem;
2944 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2946 const SMDS_MeshElement* elem = *itElem;
2947 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2950 if ( elem->NbNodes() == 4 ) {
2951 // retrieve element nodes
2952 const SMDS_MeshNode* aNodes [4];
2953 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2955 while ( itN->more() )
2956 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2958 int aShapeId = FindShape( elem );
2959 const SMDS_MeshElement* newElem1 = 0;
2960 const SMDS_MeshElement* newElem2 = 0;
2962 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2963 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2966 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2967 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2969 myLastCreatedElems.Append(newElem1);
2970 myLastCreatedElems.Append(newElem2);
2971 // put a new triangle on the same shape and add to the same groups
2974 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2975 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2977 AddToSameGroups( newElem1, elem, aMesh );
2978 AddToSameGroups( newElem2, elem, aMesh );
2979 aMesh->RemoveElement( elem );
2982 // Quadratic quadrangle
2984 else if ( elem->NbNodes() >= 8 )
2986 // get surface elem is on
2987 int aShapeId = FindShape( elem );
2988 if ( aShapeId != helper.GetSubShapeID() ) {
2992 shape = aMesh->IndexToShape( aShapeId );
2993 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2994 TopoDS_Face face = TopoDS::Face( shape );
2995 surface = BRep_Tool::Surface( face );
2996 if ( !surface.IsNull() )
2997 helper.SetSubShape( shape );
3001 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3002 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3003 for ( int i = 0; itN->more(); ++i )
3004 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006 const SMDS_MeshNode* centrNode = aNodes[8];
3007 if ( centrNode == 0 )
3009 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3010 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3012 myLastCreatedNodes.Append(centrNode);
3015 // create a new element
3016 const SMDS_MeshElement* newElem1 = 0;
3017 const SMDS_MeshElement* newElem2 = 0;
3019 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3020 aNodes[6], aNodes[7], centrNode );
3021 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3022 centrNode, aNodes[4], aNodes[5] );
3025 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3026 aNodes[7], aNodes[4], centrNode );
3027 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3028 centrNode, aNodes[5], aNodes[6] );
3030 myLastCreatedElems.Append(newElem1);
3031 myLastCreatedElems.Append(newElem2);
3032 // put a new triangle on the same shape and add to the same groups
3035 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3036 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3038 AddToSameGroups( newElem1, elem, aMesh );
3039 AddToSameGroups( newElem2, elem, aMesh );
3040 aMesh->RemoveElement( elem );
3047 //=======================================================================
3048 //function : getAngle
3050 //=======================================================================
3052 double getAngle(const SMDS_MeshElement * tr1,
3053 const SMDS_MeshElement * tr2,
3054 const SMDS_MeshNode * n1,
3055 const SMDS_MeshNode * n2)
3057 double angle = 2. * M_PI; // bad angle
3060 SMESH::Controls::TSequenceOfXYZ P1, P2;
3061 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3062 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3065 if(!tr1->IsQuadratic())
3066 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3068 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3069 if ( N1.SquareMagnitude() <= gp::Resolution() )
3071 if(!tr2->IsQuadratic())
3072 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3074 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3075 if ( N2.SquareMagnitude() <= gp::Resolution() )
3078 // find the first diagonal node n1 in the triangles:
3079 // take in account a diagonal link orientation
3080 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3081 for ( int t = 0; t < 2; t++ ) {
3082 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3083 int i = 0, iDiag = -1;
3084 while ( it->more()) {
3085 const SMDS_MeshElement *n = it->next();
3086 if ( n == n1 || n == n2 ) {
3090 if ( i - iDiag == 1 )
3091 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3100 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3103 angle = N1.Angle( N2 );
3108 // =================================================
3109 // class generating a unique ID for a pair of nodes
3110 // and able to return nodes by that ID
3111 // =================================================
3115 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3116 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3119 long GetLinkID (const SMDS_MeshNode * n1,
3120 const SMDS_MeshNode * n2) const
3122 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3125 bool GetNodes (const long theLinkID,
3126 const SMDS_MeshNode* & theNode1,
3127 const SMDS_MeshNode* & theNode2) const
3129 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3130 if ( !theNode1 ) return false;
3131 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3132 if ( !theNode2 ) return false;
3138 const SMESHDS_Mesh* myMesh;
3143 //=======================================================================
3144 //function : TriToQuad
3145 //purpose : Fuse neighbour triangles into quadrangles.
3146 // theCrit is used to select a neighbour to fuse with.
3147 // theMaxAngle is a max angle between element normals at which
3148 // fusion is still performed.
3149 //=======================================================================
3151 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3152 SMESH::Controls::NumericalFunctorPtr theCrit,
3153 const double theMaxAngle)
3155 myLastCreatedElems.Clear();
3156 myLastCreatedNodes.Clear();
3158 if ( !theCrit.get() )
3161 SMESHDS_Mesh * aMesh = GetMeshDS();
3163 // Prepare data for algo: build
3164 // 1. map of elements with their linkIDs
3165 // 2. map of linkIDs with their elements
3167 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3168 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3169 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3170 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3172 TIDSortedElemSet::iterator itElem;
3173 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3175 const SMDS_MeshElement* elem = *itElem;
3176 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3177 bool IsTria = ( elem->NbCornerNodes()==3 );
3178 if (!IsTria) continue;
3180 // retrieve element nodes
3181 const SMDS_MeshNode* aNodes [4];
3182 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3185 aNodes[ i++ ] = itN->next();
3186 aNodes[ 3 ] = aNodes[ 0 ];
3189 for ( i = 0; i < 3; i++ ) {
3190 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3191 // check if elements sharing a link can be fused
3192 itLE = mapLi_listEl.find( link );
3193 if ( itLE != mapLi_listEl.end() ) {
3194 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3196 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3197 //if ( FindShape( elem ) != FindShape( elem2 ))
3198 // continue; // do not fuse triangles laying on different shapes
3199 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3200 continue; // avoid making badly shaped quads
3201 (*itLE).second.push_back( elem );
3204 mapLi_listEl[ link ].push_back( elem );
3206 mapEl_setLi [ elem ].insert( link );
3209 // Clean the maps from the links shared by a sole element, ie
3210 // links to which only one element is bound in mapLi_listEl
3212 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3213 int nbElems = (*itLE).second.size();
3214 if ( nbElems < 2 ) {
3215 const SMDS_MeshElement* elem = (*itLE).second.front();
3216 SMESH_TLink link = (*itLE).first;
3217 mapEl_setLi[ elem ].erase( link );
3218 if ( mapEl_setLi[ elem ].empty() )
3219 mapEl_setLi.erase( elem );
3223 // Algo: fuse triangles into quadrangles
3225 while ( ! mapEl_setLi.empty() ) {
3226 // Look for the start element:
3227 // the element having the least nb of shared links
3228 const SMDS_MeshElement* startElem = 0;
3230 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3231 int nbLinks = (*itEL).second.size();
3232 if ( nbLinks < minNbLinks ) {
3233 startElem = (*itEL).first;
3234 minNbLinks = nbLinks;
3235 if ( minNbLinks == 1 )
3240 // search elements to fuse starting from startElem or links of elements
3241 // fused earlyer - startLinks
3242 list< SMESH_TLink > startLinks;
3243 while ( startElem || !startLinks.empty() ) {
3244 while ( !startElem && !startLinks.empty() ) {
3245 // Get an element to start, by a link
3246 SMESH_TLink linkId = startLinks.front();
3247 startLinks.pop_front();
3248 itLE = mapLi_listEl.find( linkId );
3249 if ( itLE != mapLi_listEl.end() ) {
3250 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3251 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3252 for ( ; itE != listElem.end() ; itE++ )
3253 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3255 mapLi_listEl.erase( itLE );
3260 // Get candidates to be fused
3261 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3262 const SMESH_TLink *link12 = 0, *link13 = 0;
3264 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3265 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3266 ASSERT( !setLi.empty() );
3267 set< SMESH_TLink >::iterator itLi;
3268 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3270 const SMESH_TLink & link = (*itLi);
3271 itLE = mapLi_listEl.find( link );
3272 if ( itLE == mapLi_listEl.end() )
3275 const SMDS_MeshElement* elem = (*itLE).second.front();
3277 elem = (*itLE).second.back();
3278 mapLi_listEl.erase( itLE );
3279 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3290 // add other links of elem to list of links to re-start from
3291 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3292 set< SMESH_TLink >::iterator it;
3293 for ( it = links.begin(); it != links.end(); it++ ) {
3294 const SMESH_TLink& link2 = (*it);
3295 if ( link2 != link )
3296 startLinks.push_back( link2 );
3300 // Get nodes of possible quadrangles
3301 const SMDS_MeshNode *n12 [4], *n13 [4];
3302 bool Ok12 = false, Ok13 = false;
3303 const SMDS_MeshNode *linkNode1, *linkNode2;
3305 linkNode1 = link12->first;
3306 linkNode2 = link12->second;
3307 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3311 linkNode1 = link13->first;
3312 linkNode2 = link13->second;
3313 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3317 // Choose a pair to fuse
3318 if ( Ok12 && Ok13 ) {
3319 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3320 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3321 double aBadRate12 = getBadRate( &quad12, theCrit );
3322 double aBadRate13 = getBadRate( &quad13, theCrit );
3323 if ( aBadRate13 < aBadRate12 )
3330 // and remove fused elems and remove links from the maps
3331 mapEl_setLi.erase( tr1 );
3334 mapEl_setLi.erase( tr2 );
3335 mapLi_listEl.erase( *link12 );
3336 if ( tr1->NbNodes() == 3 )
3338 const SMDS_MeshElement* newElem = 0;
3339 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3340 myLastCreatedElems.Append(newElem);
3341 AddToSameGroups( newElem, tr1, aMesh );
3342 int aShapeId = tr1->getshapeId();
3344 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3345 aMesh->RemoveElement( tr1 );
3346 aMesh->RemoveElement( tr2 );
3349 vector< const SMDS_MeshNode* > N1;
3350 vector< const SMDS_MeshNode* > N2;
3351 getNodesFromTwoTria(tr1,tr2,N1,N2);
3352 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3353 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3354 // i.e. first nodes from both arrays form a new diagonal
3355 const SMDS_MeshNode* aNodes[8];
3364 const SMDS_MeshElement* newElem = 0;
3365 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3366 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3367 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3369 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3371 myLastCreatedElems.Append(newElem);
3372 AddToSameGroups( newElem, tr1, aMesh );
3373 int aShapeId = tr1->getshapeId();
3375 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3376 aMesh->RemoveElement( tr1 );
3377 aMesh->RemoveElement( tr2 );
3378 // remove middle node (9)
3379 if ( N1[4]->NbInverseElements() == 0 )
3380 aMesh->RemoveNode( N1[4] );
3381 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3382 aMesh->RemoveNode( N1[6] );
3383 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3384 aMesh->RemoveNode( N2[6] );
3389 mapEl_setLi.erase( tr3 );
3390 mapLi_listEl.erase( *link13 );
3391 if ( tr1->NbNodes() == 3 ) {
3392 const SMDS_MeshElement* newElem = 0;
3393 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3394 myLastCreatedElems.Append(newElem);
3395 AddToSameGroups( newElem, tr1, aMesh );
3396 int aShapeId = tr1->getshapeId();
3398 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3399 aMesh->RemoveElement( tr1 );
3400 aMesh->RemoveElement( tr3 );
3403 vector< const SMDS_MeshNode* > N1;
3404 vector< const SMDS_MeshNode* > N2;
3405 getNodesFromTwoTria(tr1,tr3,N1,N2);
3406 // now we receive following N1 and N2 (using numeration as above image)
3407 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3408 // i.e. first nodes from both arrays form a new diagonal
3409 const SMDS_MeshNode* aNodes[8];
3418 const SMDS_MeshElement* newElem = 0;
3419 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3420 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3421 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3423 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3425 myLastCreatedElems.Append(newElem);
3426 AddToSameGroups( newElem, tr1, aMesh );
3427 int aShapeId = tr1->getshapeId();
3429 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3430 aMesh->RemoveElement( tr1 );
3431 aMesh->RemoveElement( tr3 );
3432 // remove middle node (9)
3433 if ( N1[4]->NbInverseElements() == 0 )
3434 aMesh->RemoveNode( N1[4] );
3435 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3436 aMesh->RemoveNode( N1[6] );
3437 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3438 aMesh->RemoveNode( N2[6] );
3442 // Next element to fuse: the rejected one
3444 startElem = Ok12 ? tr3 : tr2;
3446 } // if ( startElem )
3447 } // while ( startElem || !startLinks.empty() )
3448 } // while ( ! mapEl_setLi.empty() )
3454 /*#define DUMPSO(txt) \
3455 // cout << txt << endl;
3456 //=============================================================================
3460 //=============================================================================
3461 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3465 int tmp = idNodes[ i1 ];
3466 idNodes[ i1 ] = idNodes[ i2 ];
3467 idNodes[ i2 ] = tmp;
3468 gp_Pnt Ptmp = P[ i1 ];
3471 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3474 //=======================================================================
3475 //function : SortQuadNodes
3476 //purpose : Set 4 nodes of a quadrangle face in a good order.
3477 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3479 //=======================================================================
3481 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3486 for ( i = 0; i < 4; i++ ) {
3487 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3489 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3492 gp_Vec V1(P[0], P[1]);
3493 gp_Vec V2(P[0], P[2]);
3494 gp_Vec V3(P[0], P[3]);
3496 gp_Vec Cross1 = V1 ^ V2;
3497 gp_Vec Cross2 = V2 ^ V3;
3500 if (Cross1.Dot(Cross2) < 0)
3505 if (Cross1.Dot(Cross2) < 0)
3509 swap ( i, i + 1, idNodes, P );
3511 // for ( int ii = 0; ii < 4; ii++ ) {
3512 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3513 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3519 //=======================================================================
3520 //function : SortHexaNodes
3521 //purpose : Set 8 nodes of a hexahedron in a good order.
3522 // Return success status
3523 //=======================================================================
3525 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3530 DUMPSO( "INPUT: ========================================");
3531 for ( i = 0; i < 8; i++ ) {
3532 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3533 if ( !n ) return false;
3534 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3535 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3537 DUMPSO( "========================================");
3540 set<int> faceNodes; // ids of bottom face nodes, to be found
3541 set<int> checkedId1; // ids of tried 2-nd nodes
3542 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3543 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3544 int iMin, iLoop1 = 0;
3546 // Loop to try the 2-nd nodes
3548 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3550 // Find not checked 2-nd node
3551 for ( i = 1; i < 8; i++ )
3552 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3553 int id1 = idNodes[i];
3554 swap ( 1, i, idNodes, P );
3555 checkedId1.insert ( id1 );
3559 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3560 // ie that all but meybe one (id3 which is on the same face) nodes
3561 // lay on the same side from the triangle plane.
3563 bool manyInPlane = false; // more than 4 nodes lay in plane
3565 while ( ++iLoop2 < 6 ) {
3567 // get 1-2-3 plane coeffs
3568 Standard_Real A, B, C, D;
3569 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3570 if ( N.SquareMagnitude() > gp::Resolution() )
3572 gp_Pln pln ( P[0], N );
3573 pln.Coefficients( A, B, C, D );
3575 // find the node (iMin) closest to pln
3576 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3578 for ( i = 3; i < 8; i++ ) {
3579 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3580 if ( fabs( dist[i] ) < minDist ) {
3581 minDist = fabs( dist[i] );
3584 if ( fabs( dist[i] ) <= tol )
3585 idInPln.insert( idNodes[i] );
3588 // there should not be more than 4 nodes in bottom plane
3589 if ( idInPln.size() > 1 )
3591 DUMPSO( "### idInPln.size() = " << idInPln.size());
3592 // idInPlane does not contain the first 3 nodes
3593 if ( manyInPlane || idInPln.size() == 5)
3594 return false; // all nodes in one plane
3597 // set the 1-st node to be not in plane
3598 for ( i = 3; i < 8; i++ ) {
3599 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3600 DUMPSO( "### Reset 0-th node");
3601 swap( 0, i, idNodes, P );
3606 // reset to re-check second nodes
3607 leastDist = DBL_MAX;
3611 break; // from iLoop2;
3614 // check that the other 4 nodes are on the same side
3615 bool sameSide = true;
3616 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3617 for ( i = 3; sameSide && i < 8; i++ ) {
3619 sameSide = ( isNeg == dist[i] <= 0.);
3622 // keep best solution
3623 if ( sameSide && minDist < leastDist ) {
3624 leastDist = minDist;
3626 faceNodes.insert( idNodes[ 1 ] );
3627 faceNodes.insert( idNodes[ 2 ] );
3628 faceNodes.insert( idNodes[ iMin ] );
3629 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3630 << " leastDist = " << leastDist);
3631 if ( leastDist <= DBL_MIN )
3636 // set next 3-d node to check
3637 int iNext = 2 + iLoop2;
3639 DUMPSO( "Try 2-nd");
3640 swap ( 2, iNext, idNodes, P );
3642 } // while ( iLoop2 < 6 )
3645 if ( faceNodes.empty() ) return false;
3647 // Put the faceNodes in proper places
3648 for ( i = 4; i < 8; i++ ) {
3649 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3650 // find a place to put
3652 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3654 DUMPSO( "Set faceNodes");
3655 swap ( iTo, i, idNodes, P );
3660 // Set nodes of the found bottom face in good order
3661 DUMPSO( " Found bottom face: ");
3662 i = SortQuadNodes( theMesh, idNodes );
3664 gp_Pnt Ptmp = P[ i ];
3669 // for ( int ii = 0; ii < 4; ii++ ) {
3670 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3671 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3674 // Gravity center of the top and bottom faces
3675 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3676 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3678 // Get direction from the bottom to the top face
3679 gp_Vec upDir ( aGCb, aGCt );
3680 Standard_Real upDirSize = upDir.Magnitude();
3681 if ( upDirSize <= gp::Resolution() ) return false;
3684 // Assure that the bottom face normal points up
3685 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3686 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3687 if ( Nb.Dot( upDir ) < 0 ) {
3688 DUMPSO( "Reverse bottom face");
3689 swap( 1, 3, idNodes, P );
3692 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3693 Standard_Real minDist = DBL_MAX;
3694 for ( i = 4; i < 8; i++ ) {
3695 // projection of P[i] to the plane defined by P[0] and upDir
3696 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3697 Standard_Real sqDist = P[0].SquareDistance( Pp );
3698 if ( sqDist < minDist ) {
3703 DUMPSO( "Set 4-th");
3704 swap ( 4, iMin, idNodes, P );
3706 // Set nodes of the top face in good order
3707 DUMPSO( "Sort top face");
3708 i = SortQuadNodes( theMesh, &idNodes[4] );
3711 gp_Pnt Ptmp = P[ i ];
3716 // Assure that direction of the top face normal is from the bottom face
3717 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3718 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3719 if ( Nt.Dot( upDir ) < 0 ) {
3720 DUMPSO( "Reverse top face");
3721 swap( 5, 7, idNodes, P );
3724 // DUMPSO( "OUTPUT: ========================================");
3725 // for ( i = 0; i < 8; i++ ) {
3726 // float *p = ugrid->GetPoint(idNodes[i]);
3727 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3733 //================================================================================
3735 * \brief Return nodes linked to the given one
3736 * \param theNode - the node
3737 * \param linkedNodes - the found nodes
3738 * \param type - the type of elements to check
3740 * Medium nodes are ignored
3742 //================================================================================
3744 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3745 TIDSortedElemSet & linkedNodes,
3746 SMDSAbs_ElementType type )
3748 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3749 while ( elemIt->more() )
3751 const SMDS_MeshElement* elem = elemIt->next();
3752 if(elem->GetType() == SMDSAbs_0DElement)
3755 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3756 if ( elem->GetType() == SMDSAbs_Volume )
3758 SMDS_VolumeTool vol( elem );
3759 while ( nodeIt->more() ) {
3760 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3761 if ( theNode != n && vol.IsLinked( theNode, n ))
3762 linkedNodes.insert( n );
3767 for ( int i = 0; nodeIt->more(); ++i ) {
3768 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3769 if ( n == theNode ) {
3770 int iBefore = i - 1;
3772 if ( elem->IsQuadratic() ) {
3773 int nb = elem->NbNodes() / 2;
3774 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3775 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3777 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3778 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3785 //=======================================================================
3786 //function : laplacianSmooth
3787 //purpose : pulls theNode toward the center of surrounding nodes directly
3788 // connected to that node along an element edge
3789 //=======================================================================
3791 void laplacianSmooth(const SMDS_MeshNode* theNode,
3792 const Handle(Geom_Surface)& theSurface,
3793 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3795 // find surrounding nodes
3797 TIDSortedElemSet nodeSet;
3798 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3800 // compute new coodrs
3802 double coord[] = { 0., 0., 0. };
3803 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3804 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3805 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3806 if ( theSurface.IsNull() ) { // smooth in 3D
3807 coord[0] += node->X();
3808 coord[1] += node->Y();
3809 coord[2] += node->Z();
3811 else { // smooth in 2D
3812 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3813 gp_XY* uv = theUVMap[ node ];
3814 coord[0] += uv->X();
3815 coord[1] += uv->Y();
3818 int nbNodes = nodeSet.size();
3821 coord[0] /= nbNodes;
3822 coord[1] /= nbNodes;
3824 if ( !theSurface.IsNull() ) {
3825 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3826 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3827 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3833 coord[2] /= nbNodes;
3837 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3840 //=======================================================================
3841 //function : centroidalSmooth
3842 //purpose : pulls theNode toward the element-area-weighted centroid of the
3843 // surrounding elements
3844 //=======================================================================
3846 void centroidalSmooth(const SMDS_MeshNode* theNode,
3847 const Handle(Geom_Surface)& theSurface,
3848 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3850 gp_XYZ aNewXYZ(0.,0.,0.);
3851 SMESH::Controls::Area anAreaFunc;
3852 double totalArea = 0.;
3857 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3858 while ( elemIt->more() )
3860 const SMDS_MeshElement* elem = elemIt->next();
3863 gp_XYZ elemCenter(0.,0.,0.);
3864 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3865 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3866 int nn = elem->NbNodes();
3867 if(elem->IsQuadratic()) nn = nn/2;
3869 //while ( itN->more() ) {
3871 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3873 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3874 aNodePoints.push_back( aP );
3875 if ( !theSurface.IsNull() ) { // smooth in 2D
3876 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3877 gp_XY* uv = theUVMap[ aNode ];
3878 aP.SetCoord( uv->X(), uv->Y(), 0. );
3882 double elemArea = anAreaFunc.GetValue( aNodePoints );
3883 totalArea += elemArea;
3885 aNewXYZ += elemCenter * elemArea;
3887 aNewXYZ /= totalArea;
3888 if ( !theSurface.IsNull() ) {
3889 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3890 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3895 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3898 //=======================================================================
3899 //function : getClosestUV
3900 //purpose : return UV of closest projection
3901 //=======================================================================
3903 static bool getClosestUV (Extrema_GenExtPS& projector,
3904 const gp_Pnt& point,
3907 projector.Perform( point );
3908 if ( projector.IsDone() ) {
3909 double u, v, minVal = DBL_MAX;
3910 for ( int i = projector.NbExt(); i > 0; i-- )
3911 if ( projector.SquareDistance( i ) < minVal ) {
3912 minVal = projector.SquareDistance( i );
3913 projector.Point( i ).Parameter( u, v );
3915 result.SetCoord( u, v );
3921 //=======================================================================
3923 //purpose : Smooth theElements during theNbIterations or until a worst
3924 // element has aspect ratio <= theTgtAspectRatio.
3925 // Aspect Ratio varies in range [1.0, inf].
3926 // If theElements is empty, the whole mesh is smoothed.
3927 // theFixedNodes contains additionally fixed nodes. Nodes built
3928 // on edges and boundary nodes are always fixed.
3929 //=======================================================================
3931 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3932 set<const SMDS_MeshNode*> & theFixedNodes,
3933 const SmoothMethod theSmoothMethod,
3934 const int theNbIterations,
3935 double theTgtAspectRatio,
3938 myLastCreatedElems.Clear();
3939 myLastCreatedNodes.Clear();
3941 if ( theTgtAspectRatio < 1.0 )
3942 theTgtAspectRatio = 1.0;
3944 const double disttol = 1.e-16;
3946 SMESH::Controls::AspectRatio aQualityFunc;
3948 SMESHDS_Mesh* aMesh = GetMeshDS();
3950 if ( theElems.empty() ) {
3951 // add all faces to theElems
3952 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3953 while ( fIt->more() ) {
3954 const SMDS_MeshElement* face = fIt->next();
3955 theElems.insert( theElems.end(), face );
3958 // get all face ids theElems are on
3959 set< int > faceIdSet;
3960 TIDSortedElemSet::iterator itElem;
3962 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3963 int fId = FindShape( *itElem );
3964 // check that corresponding submesh exists and a shape is face
3966 faceIdSet.find( fId ) == faceIdSet.end() &&
3967 aMesh->MeshElements( fId )) {
3968 TopoDS_Shape F = aMesh->IndexToShape( fId );
3969 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3970 faceIdSet.insert( fId );
3973 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3975 // ===============================================
3976 // smooth elements on each TopoDS_Face separately
3977 // ===============================================
3979 SMESH_MesherHelper helper( *GetMesh() );
3981 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3982 for ( ; fId != faceIdSet.rend(); ++fId )
3984 // get face surface and submesh
3985 Handle(Geom_Surface) surface;
3986 SMESHDS_SubMesh* faceSubMesh = 0;
3989 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3990 bool isUPeriodic = false, isVPeriodic = false;
3993 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3994 surface = BRep_Tool::Surface( face );
3995 faceSubMesh = aMesh->MeshElements( *fId );
3996 fToler2 = BRep_Tool::Tolerance( face );
3997 fToler2 *= fToler2 * 10.;
3998 isUPeriodic = surface->IsUPeriodic();
3999 // if ( isUPeriodic )
4000 // surface->UPeriod();
4001 isVPeriodic = surface->IsVPeriodic();
4002 // if ( isVPeriodic )
4003 // surface->VPeriod();
4004 surface->Bounds( u1, u2, v1, v2 );
4005 helper.SetSubShape( face );
4007 // ---------------------------------------------------------
4008 // for elements on a face, find movable and fixed nodes and
4009 // compute UV for them
4010 // ---------------------------------------------------------
4011 bool checkBoundaryNodes = false;
4012 bool isQuadratic = false;
4013 set<const SMDS_MeshNode*> setMovableNodes;
4014 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4015 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4016 list< const SMDS_MeshElement* > elemsOnFace;
4018 Extrema_GenExtPS projector;
4019 GeomAdaptor_Surface surfAdaptor;
4020 if ( !surface.IsNull() ) {
4021 surfAdaptor.Load( surface );
4022 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4024 int nbElemOnFace = 0;
4025 itElem = theElems.begin();
4026 // loop on not yet smoothed elements: look for elems on a face
4027 while ( itElem != theElems.end() )
4029 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4030 break; // all elements found
4032 const SMDS_MeshElement* elem = *itElem;
4033 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4034 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4038 elemsOnFace.push_back( elem );
4039 theElems.erase( itElem++ );
4043 isQuadratic = elem->IsQuadratic();
4045 // get movable nodes of elem
4046 const SMDS_MeshNode* node;
4047 SMDS_TypeOfPosition posType;
4048 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4049 int nn = 0, nbn = elem->NbNodes();
4050 if(elem->IsQuadratic())
4052 while ( nn++ < nbn ) {
4053 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4054 const SMDS_PositionPtr& pos = node->GetPosition();
4055 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4056 if (posType != SMDS_TOP_EDGE &&
4057 posType != SMDS_TOP_VERTEX &&
4058 theFixedNodes.find( node ) == theFixedNodes.end())
4060 // check if all faces around the node are on faceSubMesh
4061 // because a node on edge may be bound to face
4063 if ( faceSubMesh ) {
4064 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4065 while ( eIt->more() && all ) {
4066 const SMDS_MeshElement* e = eIt->next();
4067 all = faceSubMesh->Contains( e );
4071 setMovableNodes.insert( node );
4073 checkBoundaryNodes = true;
4075 if ( posType == SMDS_TOP_3DSPACE )
4076 checkBoundaryNodes = true;
4079 if ( surface.IsNull() )
4082 // get nodes to check UV
4083 list< const SMDS_MeshNode* > uvCheckNodes;
4084 const SMDS_MeshNode* nodeInFace = 0;
4085 itN = elem->nodesIterator();
4086 nn = 0; nbn = elem->NbNodes();
4087 if(elem->IsQuadratic())
4089 while ( nn++ < nbn ) {
4090 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4091 if ( node->GetPosition()->GetDim() == 2 )
4093 if ( uvMap.find( node ) == uvMap.end() )
4094 uvCheckNodes.push_back( node );
4095 // add nodes of elems sharing node
4096 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4097 // while ( eIt->more() ) {
4098 // const SMDS_MeshElement* e = eIt->next();
4099 // if ( e != elem ) {
4100 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4101 // while ( nIt->more() ) {
4102 // const SMDS_MeshNode* n =
4103 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4104 // if ( uvMap.find( n ) == uvMap.end() )
4105 // uvCheckNodes.push_back( n );
4111 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4112 for ( ; n != uvCheckNodes.end(); ++n ) {
4115 const SMDS_PositionPtr& pos = node->GetPosition();
4116 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4120 bool toCheck = true;
4121 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4123 // compute not existing UV
4124 bool project = ( posType == SMDS_TOP_3DSPACE );
4125 // double dist1 = DBL_MAX, dist2 = 0;
4126 // if ( posType != SMDS_TOP_3DSPACE ) {
4127 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4128 // project = dist1 > fToler2;
4130 if ( project ) { // compute new UV
4132 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4133 if ( !getClosestUV( projector, pNode, newUV )) {
4134 MESSAGE("Node Projection Failed " << node);
4138 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4140 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4142 // if ( posType != SMDS_TOP_3DSPACE )
4143 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4144 // if ( dist2 < dist1 )
4148 // store UV in the map
4149 listUV.push_back( uv );
4150 uvMap.insert( make_pair( node, &listUV.back() ));
4152 } // loop on not yet smoothed elements
4154 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4155 checkBoundaryNodes = true;
4157 // fix nodes on mesh boundary
4159 if ( checkBoundaryNodes ) {
4160 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4161 map< SMESH_TLink, int >::iterator link_nb;
4162 // put all elements links to linkNbMap
4163 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4164 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4165 const SMDS_MeshElement* elem = (*elemIt);
4166 int nbn = elem->NbCornerNodes();
4167 // loop on elem links: insert them in linkNbMap
4168 for ( int iN = 0; iN < nbn; ++iN ) {
4169 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4170 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4171 SMESH_TLink link( n1, n2 );
4172 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4176 // remove nodes that are in links encountered only once from setMovableNodes
4177 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4178 if ( link_nb->second == 1 ) {
4179 setMovableNodes.erase( link_nb->first.node1() );
4180 setMovableNodes.erase( link_nb->first.node2() );
4185 // -----------------------------------------------------
4186 // for nodes on seam edge, compute one more UV ( uvMap2 );
4187 // find movable nodes linked to nodes on seam and which
4188 // are to be smoothed using the second UV ( uvMap2 )
4189 // -----------------------------------------------------
4191 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4192 if ( !surface.IsNull() ) {
4193 TopExp_Explorer eExp( face, TopAbs_EDGE );
4194 for ( ; eExp.More(); eExp.Next() ) {
4195 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4196 if ( !BRep_Tool::IsClosed( edge, face ))
4198 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4199 if ( !sm ) continue;
4200 // find out which parameter varies for a node on seam
4203 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4204 if ( pcurve.IsNull() ) continue;
4205 uv1 = pcurve->Value( f );
4207 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4208 if ( pcurve.IsNull() ) continue;
4209 uv2 = pcurve->Value( f );
4210 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4212 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4213 std::swap( uv1, uv2 );
4214 // get nodes on seam and its vertices
4215 list< const SMDS_MeshNode* > seamNodes;
4216 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4217 while ( nSeamIt->more() ) {
4218 const SMDS_MeshNode* node = nSeamIt->next();
4219 if ( !isQuadratic || !IsMedium( node ))
4220 seamNodes.push_back( node );
4222 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4223 for ( ; vExp.More(); vExp.Next() ) {
4224 sm = aMesh->MeshElements( vExp.Current() );
4226 nSeamIt = sm->GetNodes();
4227 while ( nSeamIt->more() )
4228 seamNodes.push_back( nSeamIt->next() );
4231 // loop on nodes on seam
4232 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4233 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4234 const SMDS_MeshNode* nSeam = *noSeIt;
4235 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4236 if ( n_uv == uvMap.end() )
4239 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4240 // set the second UV
4241 listUV.push_back( *n_uv->second );
4242 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4243 if ( uvMap2.empty() )
4244 uvMap2 = uvMap; // copy the uvMap contents
4245 uvMap2[ nSeam ] = &listUV.back();
4247 // collect movable nodes linked to ones on seam in nodesNearSeam
4248 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4249 while ( eIt->more() ) {
4250 const SMDS_MeshElement* e = eIt->next();
4251 int nbUseMap1 = 0, nbUseMap2 = 0;
4252 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4253 int nn = 0, nbn = e->NbNodes();
4254 if(e->IsQuadratic()) nbn = nbn/2;
4255 while ( nn++ < nbn )
4257 const SMDS_MeshNode* n =
4258 static_cast<const SMDS_MeshNode*>( nIt->next() );
4260 setMovableNodes.find( n ) == setMovableNodes.end() )
4262 // add only nodes being closer to uv2 than to uv1
4263 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4264 // 0.5 * ( n->Y() + nSeam->Y() ),
4265 // 0.5 * ( n->Z() + nSeam->Z() ));
4267 // getClosestUV( projector, pMid, uv );
4268 double x = uvMap[ n ]->Coord( iPar );
4269 if ( Abs( uv1.Coord( iPar ) - x ) >
4270 Abs( uv2.Coord( iPar ) - x )) {
4271 nodesNearSeam.insert( n );
4277 // for centroidalSmooth all element nodes must
4278 // be on one side of a seam
4279 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4280 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4282 while ( nn++ < nbn ) {
4283 const SMDS_MeshNode* n =
4284 static_cast<const SMDS_MeshNode*>( nIt->next() );
4285 setMovableNodes.erase( n );
4289 } // loop on nodes on seam
4290 } // loop on edge of a face
4291 } // if ( !face.IsNull() )
4293 if ( setMovableNodes.empty() ) {
4294 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4295 continue; // goto next face
4303 double maxRatio = -1., maxDisplacement = -1.;
4304 set<const SMDS_MeshNode*>::iterator nodeToMove;
4305 for ( it = 0; it < theNbIterations; it++ ) {
4306 maxDisplacement = 0.;
4307 nodeToMove = setMovableNodes.begin();
4308 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4309 const SMDS_MeshNode* node = (*nodeToMove);
4310 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4313 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4314 if ( theSmoothMethod == LAPLACIAN )
4315 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4317 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4319 // node displacement
4320 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4321 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4322 if ( aDispl > maxDisplacement )
4323 maxDisplacement = aDispl;
4325 // no node movement => exit
4326 //if ( maxDisplacement < 1.e-16 ) {
4327 if ( maxDisplacement < disttol ) {
4328 MESSAGE("-- no node movement --");
4332 // check elements quality
4334 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4335 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4336 const SMDS_MeshElement* elem = (*elemIt);
4337 if ( !elem || elem->GetType() != SMDSAbs_Face )
4339 SMESH::Controls::TSequenceOfXYZ aPoints;
4340 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4341 double aValue = aQualityFunc.GetValue( aPoints );
4342 if ( aValue > maxRatio )
4346 if ( maxRatio <= theTgtAspectRatio ) {
4347 //MESSAGE("-- quality achieved --");
4350 if (it+1 == theNbIterations) {
4351 //MESSAGE("-- Iteration limit exceeded --");
4353 } // smoothing iterations
4355 // MESSAGE(" Face id: " << *fId <<
4356 // " Nb iterstions: " << it <<
4357 // " Displacement: " << maxDisplacement <<
4358 // " Aspect Ratio " << maxRatio);
4360 // ---------------------------------------
4361 // new nodes positions are computed,
4362 // record movement in DS and set new UV
4363 // ---------------------------------------
4364 nodeToMove = setMovableNodes.begin();
4365 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4366 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4367 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4368 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4369 if ( node_uv != uvMap.end() ) {
4370 gp_XY* uv = node_uv->second;
4372 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4376 // move medium nodes of quadratic elements
4379 vector<const SMDS_MeshNode*> nodes;
4381 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4382 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4384 const SMDS_MeshElement* QF = *elemIt;
4385 if ( QF->IsQuadratic() )
4387 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4388 SMDS_MeshElement::iterator() );
4389 nodes.push_back( nodes[0] );
4391 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4393 if ( !surface.IsNull() )
4395 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4396 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4397 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4398 xyz = surface->Value( uv.X(), uv.Y() );
4401 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4403 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4404 // we have to move a medium node
4405 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4411 } // loop on face ids
4417 //=======================================================================
4418 //function : isReverse
4419 //purpose : Return true if normal of prevNodes is not co-directied with
4420 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4421 // iNotSame is where prevNodes and nextNodes are different.
4422 // If result is true then future volume orientation is OK
4423 //=======================================================================
4425 bool isReverse(const SMDS_MeshElement* face,
4426 const vector<const SMDS_MeshNode*>& prevNodes,
4427 const vector<const SMDS_MeshNode*>& nextNodes,
4431 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4432 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4433 gp_XYZ extrDir( pN - pP ), faceNorm;
4434 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4436 return faceNorm * extrDir < 0.0;
4439 //================================================================================
4441 * \brief Assure that theElemSets[0] holds elements, not nodes
4443 //================================================================================
4445 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4447 if ( !theElemSets[0].empty() &&
4448 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4450 std::swap( theElemSets[0], theElemSets[1] );
4452 else if ( !theElemSets[1].empty() &&
4453 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4455 std::swap( theElemSets[0], theElemSets[1] );
4460 //=======================================================================
4462 * \brief Create elements by sweeping an element
4463 * \param elem - element to sweep
4464 * \param newNodesItVec - nodes generated from each node of the element
4465 * \param newElems - generated elements
4466 * \param nbSteps - number of sweeping steps
4467 * \param srcElements - to append elem for each generated element
4469 //=======================================================================
4471 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4472 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4473 list<const SMDS_MeshElement*>& newElems,
4474 const size_t nbSteps,
4475 SMESH_SequenceOfElemPtr& srcElements)
4477 SMESHDS_Mesh* aMesh = GetMeshDS();
4479 const int nbNodes = elem->NbNodes();
4480 const int nbCorners = elem->NbCornerNodes();
4481 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4482 polyhedron creation !!! */
4483 // Loop on elem nodes:
4484 // find new nodes and detect same nodes indices
4485 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4486 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4487 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4488 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4490 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4491 vector<int> sames(nbNodes);
4492 vector<bool> isSingleNode(nbNodes);
4494 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4495 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4496 const SMDS_MeshNode* node = nnIt->first;
4497 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4498 if ( listNewNodes.empty() )
4501 itNN [ iNode ] = listNewNodes.begin();
4502 prevNod[ iNode ] = node;
4503 nextNod[ iNode ] = listNewNodes.front();
4505 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4506 corner node of linear */
4507 if ( prevNod[ iNode ] != nextNod [ iNode ])
4508 nbDouble += !isSingleNode[iNode];
4510 if( iNode < nbCorners ) { // check corners only
4511 if ( prevNod[ iNode ] == nextNod [ iNode ])
4512 sames[nbSame++] = iNode;
4514 iNotSameNode = iNode;
4518 if ( nbSame == nbNodes || nbSame > 2) {
4519 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4523 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4525 // fix nodes order to have bottom normal external
4526 if ( baseType == SMDSEntity_Polygon )
4528 std::reverse( itNN.begin(), itNN.end() );
4529 std::reverse( prevNod.begin(), prevNod.end() );
4530 std::reverse( midlNod.begin(), midlNod.end() );
4531 std::reverse( nextNod.begin(), nextNod.end() );
4532 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4536 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4537 SMDS_MeshCell::applyInterlace( ind, itNN );
4538 SMDS_MeshCell::applyInterlace( ind, prevNod );
4539 SMDS_MeshCell::applyInterlace( ind, nextNod );
4540 SMDS_MeshCell::applyInterlace( ind, midlNod );
4541 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4544 sames[nbSame] = iNotSameNode;
4545 for ( int j = 0; j <= nbSame; ++j )
4546 for ( size_t i = 0; i < ind.size(); ++i )
4547 if ( ind[i] == sames[j] )
4552 iNotSameNode = sames[nbSame];
4556 else if ( elem->GetType() == SMDSAbs_Edge )
4558 // orient a new face same as adjacent one
4560 const SMDS_MeshElement* e;
4561 TIDSortedElemSet dummy;
4562 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4563 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4564 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4566 // there is an adjacent face, check order of nodes in it
4567 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4570 std::swap( itNN[0], itNN[1] );
4571 std::swap( prevNod[0], prevNod[1] );
4572 std::swap( nextNod[0], nextNod[1] );
4573 #if defined(__APPLE__)
4574 std::swap( isSingleNode[0], isSingleNode[1] );
4576 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4579 sames[0] = 1 - sames[0];
4580 iNotSameNode = 1 - iNotSameNode;
4585 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4587 iSameNode = sames[ nbSame-1 ];
4588 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4589 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4590 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4593 if ( baseType == SMDSEntity_Polygon )
4595 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4596 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4598 else if ( baseType == SMDSEntity_Quad_Polygon )
4600 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4601 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4604 // make new elements
4605 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4608 for ( iNode = 0; iNode < nbNodes; iNode++ )
4610 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4611 nextNod[ iNode ] = *itNN[ iNode ]++;
4614 SMDS_MeshElement* aNewElem = 0;
4615 /*if(!elem->IsPoly())*/ {
4616 switch ( baseType ) {
4618 case SMDSEntity_Node: { // sweep NODE
4619 if ( nbSame == 0 ) {
4620 if ( isSingleNode[0] )
4621 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4623 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4629 case SMDSEntity_Edge: { // sweep EDGE
4630 if ( nbDouble == 0 )
4632 if ( nbSame == 0 ) // ---> quadrangle
4633 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4634 nextNod[ 1 ], nextNod[ 0 ] );
4635 else // ---> triangle
4636 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4637 nextNod[ iNotSameNode ] );
4639 else // ---> polygon
4641 vector<const SMDS_MeshNode*> poly_nodes;
4642 poly_nodes.push_back( prevNod[0] );
4643 poly_nodes.push_back( prevNod[1] );
4644 if ( prevNod[1] != nextNod[1] )
4646 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4647 poly_nodes.push_back( nextNod[1] );
4649 if ( prevNod[0] != nextNod[0] )
4651 poly_nodes.push_back( nextNod[0] );
4652 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4654 switch ( poly_nodes.size() ) {
4656 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4659 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4660 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4663 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4668 case SMDSEntity_Triangle: // TRIANGLE --->
4670 if ( nbDouble > 0 ) break;
4671 if ( nbSame == 0 ) // ---> pentahedron
4672 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4673 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4675 else if ( nbSame == 1 ) // ---> pyramid
4676 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4677 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4678 nextNod[ iSameNode ]);
4680 else // 2 same nodes: ---> tetrahedron
4681 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4682 nextNod[ iNotSameNode ]);
4685 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4689 if ( nbDouble+nbSame == 2 )
4691 if(nbSame==0) { // ---> quadratic quadrangle
4692 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4693 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4695 else { //(nbSame==1) // ---> quadratic triangle
4697 return; // medium node on axis
4699 else if(sames[0]==0)
4700 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4701 prevNod[2], midlNod[1], nextNod[2] );
4703 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4704 prevNod[2], nextNod[2], midlNod[0]);
4707 else if ( nbDouble == 3 )
4709 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4710 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4711 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4718 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4719 if ( nbDouble > 0 ) break;
4721 if ( nbSame == 0 ) // ---> hexahedron
4722 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4723 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4725 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4726 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4727 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4728 nextNod[ iSameNode ]);
4729 newElems.push_back( aNewElem );
4730 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4731 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4732 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4734 else if ( nbSame == 2 ) { // ---> pentahedron
4735 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4736 // iBeforeSame is same too
4737 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4738 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4739 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4741 // iAfterSame is same too
4742 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4743 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4744 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4748 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4749 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4750 if ( nbDouble+nbSame != 3 ) break;
4752 // ---> pentahedron with 15 nodes
4753 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4754 nextNod[0], nextNod[1], nextNod[2],
4755 prevNod[3], prevNod[4], prevNod[5],
4756 nextNod[3], nextNod[4], nextNod[5],
4757 midlNod[0], midlNod[1], midlNod[2]);
4759 else if(nbSame==1) {
4760 // ---> 2d order pyramid of 13 nodes
4761 int apex = iSameNode;
4762 int i0 = ( apex + 1 ) % nbCorners;
4763 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4767 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4768 nextNod[i0], nextNod[i1], prevNod[apex],
4769 prevNod[i01], midlNod[i0],
4770 nextNod[i01], midlNod[i1],
4771 prevNod[i1a], prevNod[i0a],
4772 nextNod[i0a], nextNod[i1a]);
4774 else if(nbSame==2) {
4775 // ---> 2d order tetrahedron of 10 nodes
4776 int n1 = iNotSameNode;
4777 int n2 = ( n1 + 1 ) % nbCorners;
4778 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4782 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4783 prevNod[n12], prevNod[n23], prevNod[n31],
4784 midlNod[n1], nextNod[n12], nextNod[n31]);
4788 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4790 if ( nbDouble != 4 ) break;
4791 // ---> hexahedron with 20 nodes
4792 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4793 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4794 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4795 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4796 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4798 else if(nbSame==1) {
4799 // ---> pyramid + pentahedron - can not be created since it is needed
4800 // additional middle node at the center of face
4801 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4804 else if( nbSame == 2 ) {
4805 if ( nbDouble != 2 ) break;
4806 // ---> 2d order Pentahedron with 15 nodes
4808 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4809 // iBeforeSame is same too
4816 // iAfterSame is same too
4826 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4827 prevNod[n4], prevNod[n5], nextNod[n5],
4828 prevNod[n12], midlNod[n2], nextNod[n12],
4829 prevNod[n45], midlNod[n5], nextNod[n45],
4830 prevNod[n14], prevNod[n25], nextNod[n25]);
4834 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4836 if( nbSame == 0 && nbDouble == 9 ) {
4837 // ---> tri-quadratic hexahedron with 27 nodes
4838 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4839 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4840 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4841 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4842 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4843 prevNod[8], // bottom center
4844 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4845 nextNod[8], // top center
4846 midlNod[8]);// elem center
4854 case SMDSEntity_Polygon: { // sweep POLYGON
4856 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4857 // ---> hexagonal prism
4858 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4859 prevNod[3], prevNod[4], prevNod[5],
4860 nextNod[0], nextNod[1], nextNod[2],
4861 nextNod[3], nextNod[4], nextNod[5]);
4865 case SMDSEntity_Ball:
4870 } // switch ( baseType )
4873 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4875 if ( baseType != SMDSEntity_Polygon )
4877 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4878 SMDS_MeshCell::applyInterlace( ind, prevNod );
4879 SMDS_MeshCell::applyInterlace( ind, nextNod );
4880 SMDS_MeshCell::applyInterlace( ind, midlNod );
4881 SMDS_MeshCell::applyInterlace( ind, itNN );
4882 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4883 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4885 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4886 vector<int> quantities (nbNodes + 2);
4887 polyedre_nodes.clear();
4891 for (int inode = 0; inode < nbNodes; inode++)
4892 polyedre_nodes.push_back( prevNod[inode] );
4893 quantities.push_back( nbNodes );
4896 polyedre_nodes.push_back( nextNod[0] );
4897 for (int inode = nbNodes; inode-1; --inode )
4898 polyedre_nodes.push_back( nextNod[inode-1] );
4899 quantities.push_back( nbNodes );
4907 const int iQuad = elem->IsQuadratic();
4908 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4910 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4911 int inextface = (iface+1+iQuad) % nbNodes;
4912 int imid = (iface+1) % nbNodes;
4913 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4914 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4915 polyedre_nodes.push_back( prevNod[iface] ); // 1
4916 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4918 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4919 polyedre_nodes.push_back( nextNod[iface] ); // 2
4921 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4922 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4924 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4925 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4927 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4928 if ( nbFaceNodes > 2 )
4929 quantities.push_back( nbFaceNodes );
4930 else // degenerated face
4931 polyedre_nodes.resize( prevNbNodes );
4933 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4935 } // try to create a polyherdal prism
4938 newElems.push_back( aNewElem );
4939 myLastCreatedElems.Append(aNewElem);
4940 srcElements.Append( elem );
4943 // set new prev nodes
4944 for ( iNode = 0; iNode < nbNodes; iNode++ )
4945 prevNod[ iNode ] = nextNod[ iNode ];
4950 //=======================================================================
4952 * \brief Create 1D and 2D elements around swept elements
4953 * \param mapNewNodes - source nodes and ones generated from them
4954 * \param newElemsMap - source elements and ones generated from them
4955 * \param elemNewNodesMap - nodes generated from each node of each element
4956 * \param elemSet - all swept elements
4957 * \param nbSteps - number of sweeping steps
4958 * \param srcElements - to append elem for each generated element
4960 //=======================================================================
4962 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4963 TTElemOfElemListMap & newElemsMap,
4964 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4965 TIDSortedElemSet& elemSet,
4967 SMESH_SequenceOfElemPtr& srcElements)
4969 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4970 SMESHDS_Mesh* aMesh = GetMeshDS();
4972 // Find nodes belonging to only one initial element - sweep them into edges.
4974 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4975 for ( ; nList != mapNewNodes.end(); nList++ )
4977 const SMDS_MeshNode* node =
4978 static_cast<const SMDS_MeshNode*>( nList->first );
4979 if ( newElemsMap.count( node ))
4980 continue; // node was extruded into edge
4981 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4982 int nbInitElems = 0;
4983 const SMDS_MeshElement* el = 0;
4984 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4985 while ( eIt->more() && nbInitElems < 2 ) {
4986 const SMDS_MeshElement* e = eIt->next();
4987 SMDSAbs_ElementType type = e->GetType();
4988 if ( type == SMDSAbs_Volume ||
4992 if ( type > highType ) {
4999 if ( nbInitElems == 1 ) {
5000 bool NotCreateEdge = el && el->IsMediumNode(node);
5001 if(!NotCreateEdge) {
5002 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5003 list<const SMDS_MeshElement*> newEdges;
5004 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5009 // Make a ceiling for each element ie an equal element of last new nodes.
5010 // Find free links of faces - make edges and sweep them into faces.
5012 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5014 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5015 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5016 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5018 const SMDS_MeshElement* elem = itElem->first;
5019 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5021 if(itElem->second.size()==0) continue;
5023 const bool isQuadratic = elem->IsQuadratic();
5025 if ( elem->GetType() == SMDSAbs_Edge ) {
5026 // create a ceiling edge
5027 if ( !isQuadratic ) {
5028 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5029 vecNewNodes[ 1 ]->second.back())) {
5030 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5031 vecNewNodes[ 1 ]->second.back()));
5032 srcElements.Append( elem );
5036 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5037 vecNewNodes[ 1 ]->second.back(),
5038 vecNewNodes[ 2 ]->second.back())) {
5039 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5040 vecNewNodes[ 1 ]->second.back(),
5041 vecNewNodes[ 2 ]->second.back()));
5042 srcElements.Append( elem );
5046 if ( elem->GetType() != SMDSAbs_Face )
5049 bool hasFreeLinks = false;
5051 TIDSortedElemSet avoidSet;
5052 avoidSet.insert( elem );
5054 set<const SMDS_MeshNode*> aFaceLastNodes;
5055 int iNode, nbNodes = vecNewNodes.size();
5056 if ( !isQuadratic ) {
5057 // loop on the face nodes
5058 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5059 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5060 // look for free links of the face
5061 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5062 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5063 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5064 // check if a link n1-n2 is free
5065 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5066 hasFreeLinks = true;
5067 // make a new edge and a ceiling for a new edge
5068 const SMDS_MeshElement* edge;
5069 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5070 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5071 srcElements.Append( myLastCreatedElems.Last() );
5073 n1 = vecNewNodes[ iNode ]->second.back();
5074 n2 = vecNewNodes[ iNext ]->second.back();
5075 if ( !aMesh->FindEdge( n1, n2 )) {
5076 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5077 srcElements.Append( edge );
5082 else { // elem is quadratic face
5083 int nbn = nbNodes/2;
5084 for ( iNode = 0; iNode < nbn; iNode++ ) {
5085 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5086 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5087 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5088 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5089 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5090 // check if a link is free
5091 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5092 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5093 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5094 hasFreeLinks = true;
5095 // make an edge and a ceiling for a new edge
5097 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5098 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5099 srcElements.Append( elem );
5101 n1 = vecNewNodes[ iNode ]->second.back();
5102 n2 = vecNewNodes[ iNext ]->second.back();
5103 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5104 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5105 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5106 srcElements.Append( elem );
5110 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5111 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5115 // sweep free links into faces
5117 if ( hasFreeLinks ) {
5118 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5119 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5121 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5122 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5123 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5124 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5125 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5127 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5128 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5129 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5131 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5132 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5133 std::advance( v, volNb );
5134 // find indices of free faces of a volume and their source edges
5135 list< int > freeInd;
5136 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5137 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5138 int iF, nbF = vTool.NbFaces();
5139 for ( iF = 0; iF < nbF; iF ++ ) {
5140 if (vTool.IsFreeFace( iF ) &&
5141 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5142 initNodeSet != faceNodeSet) // except an initial face
5144 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5146 if ( faceNodeSet == initNodeSetNoCenter )
5148 freeInd.push_back( iF );
5149 // find source edge of a free face iF
5150 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5151 vector<const SMDS_MeshNode*>::iterator lastCommom;
5152 commonNodes.resize( nbNodes, 0 );
5153 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5154 initNodeSet.begin(), initNodeSet.end(),
5155 commonNodes.begin());
5156 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5157 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5159 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5161 if ( !srcEdges.back() )
5163 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5164 << iF << " of volume #" << vTool.ID() << endl;
5169 if ( freeInd.empty() )
5172 // create wall faces for all steps;
5173 // if such a face has been already created by sweep of edge,
5174 // assure that its orientation is OK
5175 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5177 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5178 vTool.SetExternalNormal();
5179 const int nextShift = vTool.IsForward() ? +1 : -1;
5180 list< int >::iterator ind = freeInd.begin();
5181 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5182 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5184 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5185 int nbn = vTool.NbFaceNodes( *ind );
5186 const SMDS_MeshElement * f = 0;
5187 if ( nbn == 3 ) ///// triangle
5189 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5191 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5193 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5195 nodes[ 1 + nextShift ] };
5197 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5199 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5203 else if ( nbn == 4 ) ///// quadrangle
5205 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5207 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5209 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5210 nodes[ 2 ], nodes[ 2+nextShift ] };
5212 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5214 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5215 newOrder[ 2 ], newOrder[ 3 ]));
5218 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5220 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5222 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5224 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5226 nodes[2 + 2*nextShift],
5227 nodes[3 - 2*nextShift],
5229 nodes[3 + 2*nextShift]};
5231 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5233 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5241 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5243 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5244 nodes[1], nodes[3], nodes[5], nodes[7] );
5246 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5248 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5249 nodes[4 - 2*nextShift],
5251 nodes[4 + 2*nextShift],
5253 nodes[5 - 2*nextShift],
5255 nodes[5 + 2*nextShift] };
5257 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5259 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5260 newOrder[ 2 ], newOrder[ 3 ],
5261 newOrder[ 4 ], newOrder[ 5 ],
5262 newOrder[ 6 ], newOrder[ 7 ]));
5265 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5267 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5268 SMDSAbs_Face, /*noMedium=*/false);
5270 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5272 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5273 nodes[4 - 2*nextShift],
5275 nodes[4 + 2*nextShift],
5277 nodes[5 - 2*nextShift],
5279 nodes[5 + 2*nextShift],
5282 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5284 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5285 newOrder[ 2 ], newOrder[ 3 ],
5286 newOrder[ 4 ], newOrder[ 5 ],
5287 newOrder[ 6 ], newOrder[ 7 ],
5291 else //////// polygon
5293 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5294 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5296 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5298 if ( !vTool.IsForward() )
5299 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5301 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5303 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5307 while ( srcElements.Length() < myLastCreatedElems.Length() )
5308 srcElements.Append( *srcEdge );
5310 } // loop on free faces
5312 // go to the next volume
5314 while ( iVol++ < nbVolumesByStep ) v++;
5317 } // loop on volumes of one step
5318 } // sweep free links into faces
5320 // Make a ceiling face with a normal external to a volume
5322 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5323 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5324 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5326 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5327 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5328 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5332 lastVol.SetExternalNormal();
5333 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5334 const int nbn = lastVol.NbFaceNodes( iF );
5335 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5336 if ( !hasFreeLinks ||
5337 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5339 const vector<int>& interlace =
5340 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5341 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5343 AddElement( nodeVec, anyFace.Init( elem ));
5345 while ( srcElements.Length() < myLastCreatedElems.Length() )
5346 srcElements.Append( elem );
5349 } // loop on swept elements
5352 //=======================================================================
5353 //function : RotationSweep
5355 //=======================================================================
5357 SMESH_MeshEditor::PGroupIDs
5358 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5359 const gp_Ax1& theAxis,
5360 const double theAngle,
5361 const int theNbSteps,
5362 const double theTol,
5363 const bool theMakeGroups,
5364 const bool theMakeWalls)
5366 myLastCreatedElems.Clear();
5367 myLastCreatedNodes.Clear();
5369 // source elements for each generated one
5370 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5373 aTrsf.SetRotation( theAxis, theAngle );
5375 aTrsf2.SetRotation( theAxis, theAngle/2. );
5377 gp_Lin aLine( theAxis );
5378 double aSqTol = theTol * theTol;
5380 SMESHDS_Mesh* aMesh = GetMeshDS();
5382 TNodeOfNodeListMap mapNewNodes;
5383 TElemOfVecOfNnlmiMap mapElemNewNodes;
5384 TTElemOfElemListMap newElemsMap;
5386 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5387 myMesh->NbFaces(ORDER_QUADRATIC) +
5388 myMesh->NbVolumes(ORDER_QUADRATIC) );
5389 // loop on theElemSets
5390 setElemsFirst( theElemSets );
5391 TIDSortedElemSet::iterator itElem;
5392 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5394 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5395 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5396 const SMDS_MeshElement* elem = *itElem;
5397 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5399 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5400 newNodesItVec.reserve( elem->NbNodes() );
5402 // loop on elem nodes
5403 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5404 while ( itN->more() )
5406 const SMDS_MeshNode* node = cast2Node( itN->next() );
5408 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5410 aXYZ.Coord( coord[0], coord[1], coord[2] );
5411 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5413 // check if a node has been already sweeped
5414 TNodeOfNodeListMapItr nIt =
5415 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5416 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5417 if ( listNewNodes.empty() )
5419 // check if we are to create medium nodes between corner ones
5420 bool needMediumNodes = false;
5421 if ( isQuadraticMesh )
5423 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5424 while (it->more() && !needMediumNodes )
5426 const SMDS_MeshElement* invElem = it->next();
5427 if ( invElem != elem && !theElems.count( invElem )) continue;
5428 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5429 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5430 needMediumNodes = true;
5435 const SMDS_MeshNode * newNode = node;
5436 for ( int i = 0; i < theNbSteps; i++ ) {
5438 if ( needMediumNodes ) // create a medium node
5440 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5441 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5442 myLastCreatedNodes.Append(newNode);
5443 srcNodes.Append( node );
5444 listNewNodes.push_back( newNode );
5445 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5448 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5450 // create a corner node
5451 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5452 myLastCreatedNodes.Append(newNode);
5453 srcNodes.Append( node );
5454 listNewNodes.push_back( newNode );
5457 listNewNodes.push_back( newNode );
5458 // if ( needMediumNodes )
5459 // listNewNodes.push_back( newNode );
5463 newNodesItVec.push_back( nIt );
5465 // make new elements
5466 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5471 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5473 PGroupIDs newGroupIDs;
5474 if ( theMakeGroups )
5475 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5480 //=======================================================================
5481 //function : ExtrusParam
5482 //purpose : standard construction
5483 //=======================================================================
5485 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5486 const int theNbSteps,
5487 const std::list<double>& theScales,
5488 const gp_XYZ* theBasePoint,
5490 const double theTolerance):
5492 myBaseP( Precision::Infinite(), 0, 0 ),
5493 myFlags( theFlags ),
5494 myTolerance( theTolerance ),
5495 myElemsToUse( NULL )
5497 mySteps = new TColStd_HSequenceOfReal;
5498 const double stepSize = theStep.Magnitude();
5499 for (int i=1; i<=theNbSteps; i++ )
5500 mySteps->Append( stepSize );
5502 int nbScales = theScales.size();
5505 if ( IsLinearVariation() && nbScales < theNbSteps )
5507 myScales.reserve( theNbSteps );
5508 std::list<double>::const_iterator scale = theScales.begin();
5509 double prevScale = 1.0;
5510 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5512 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5513 int stDelta = Max( 1, iStep - myScales.size());
5514 double scDelta = ( *scale - prevScale ) / stDelta;
5515 for ( int iStep = 0; iStep < stDelta; ++iStep )
5517 myScales.push_back( prevScale + scDelta );
5518 prevScale = myScales.back();
5525 myScales.assign( theScales.begin(), theScales.end() );
5530 myBaseP = *theBasePoint;
5533 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5534 ( theTolerance > 0 ))
5536 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5540 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5544 //=======================================================================
5545 //function : ExtrusParam
5546 //purpose : steps are given explicitly
5547 //=======================================================================
5549 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5550 Handle(TColStd_HSequenceOfReal) theSteps,
5552 const double theTolerance):
5554 mySteps( theSteps ),
5555 myFlags( theFlags ),
5556 myTolerance( theTolerance ),
5557 myElemsToUse( NULL )
5559 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5560 ( theTolerance > 0 ))
5562 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5566 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5570 //=======================================================================
5571 //function : ExtrusParam
5572 //purpose : for extrusion by normal
5573 //=======================================================================
5575 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5576 const int theNbSteps,
5580 mySteps( new TColStd_HSequenceOfReal ),
5581 myFlags( theFlags ),
5583 myElemsToUse( NULL )
5585 for (int i = 0; i < theNbSteps; i++ )
5586 mySteps->Append( theStepSize );
5590 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5594 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5598 //=======================================================================
5599 //function : ExtrusParam::SetElementsToUse
5600 //purpose : stores elements to use for extrusion by normal, depending on
5601 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5602 // define myBaseP for scaling
5603 //=======================================================================
5605 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5606 const TIDSortedElemSet& nodes )
5608 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5610 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5612 myBaseP.SetCoord( 0.,0.,0. );
5613 TIDSortedElemSet newNodes;
5615 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5616 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5618 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5619 TIDSortedElemSet::const_iterator itElem = elements.begin();
5620 for ( ; itElem != elements.end(); itElem++ )
5622 const SMDS_MeshElement* elem = *itElem;
5623 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5624 while ( itN->more() ) {
5625 const SMDS_MeshElement* node = itN->next();
5626 if ( newNodes.insert( node ).second )
5627 myBaseP += SMESH_TNodeXYZ( node );
5631 myBaseP /= newNodes.size();
5635 //=======================================================================
5636 //function : ExtrusParam::beginStepIter
5637 //purpose : prepare iteration on steps
5638 //=======================================================================
5640 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5642 myWithMediumNodes = withMediumNodes;
5646 //=======================================================================
5647 //function : ExtrusParam::moreSteps
5648 //purpose : are there more steps?
5649 //=======================================================================
5651 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5653 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5655 //=======================================================================
5656 //function : ExtrusParam::nextStep
5657 //purpose : returns the next step
5658 //=======================================================================
5660 double SMESH_MeshEditor::ExtrusParam::nextStep()
5663 if ( !myCurSteps.empty() )
5665 res = myCurSteps.back();
5666 myCurSteps.pop_back();
5668 else if ( myNextStep <= mySteps->Length() )
5670 myCurSteps.push_back( mySteps->Value( myNextStep ));
5672 if ( myWithMediumNodes )
5674 myCurSteps.back() /= 2.;
5675 myCurSteps.push_back( myCurSteps.back() );
5682 //=======================================================================
5683 //function : ExtrusParam::makeNodesByDir
5684 //purpose : create nodes for standard extrusion
5685 //=======================================================================
5687 int SMESH_MeshEditor::ExtrusParam::
5688 makeNodesByDir( SMESHDS_Mesh* mesh,
5689 const SMDS_MeshNode* srcNode,
5690 std::list<const SMDS_MeshNode*> & newNodes,
5691 const bool makeMediumNodes)
5693 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5696 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5698 p += myDir.XYZ() * nextStep();
5699 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5700 newNodes.push_back( newNode );
5703 if ( !myScales.empty() )
5705 if ( makeMediumNodes && myMediumScales.empty() )
5707 myMediumScales.resize( myScales.size() );
5708 double prevFactor = 1.;
5709 for ( size_t i = 0; i < myScales.size(); ++i )
5711 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5712 prevFactor = myScales[i];
5715 typedef std::vector<double>::iterator ScaleIt;
5716 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5718 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5720 gp_XYZ center = myBaseP;
5721 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5723 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5725 center += myDir.XYZ() * nextStep();
5727 iSc += int( makeMediumNodes );
5728 ScaleIt& scale = scales[ iSc % 2 ];
5730 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5731 xyz = ( *scale * ( xyz - center )) + center;
5732 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5740 //=======================================================================
5741 //function : ExtrusParam::makeNodesByDirAndSew
5742 //purpose : create nodes for standard extrusion with sewing
5743 //=======================================================================
5745 int SMESH_MeshEditor::ExtrusParam::
5746 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5747 const SMDS_MeshNode* srcNode,
5748 std::list<const SMDS_MeshNode*> & newNodes,
5749 const bool makeMediumNodes)
5751 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5754 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5756 P1 += myDir.XYZ() * nextStep();
5758 // try to search in sequence of existing nodes
5759 // if myNodes.Length()>0 we 'nave to use given sequence
5760 // else - use all nodes of mesh
5761 const SMDS_MeshNode * node = 0;
5762 if ( myNodes.Length() > 0 ) {
5764 for(i=1; i<=myNodes.Length(); i++) {
5765 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5766 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5768 node = myNodes.Value(i);
5774 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5775 while(itn->more()) {
5776 SMESH_TNodeXYZ P2( itn->next() );
5777 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5786 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5788 newNodes.push_back( node );
5795 //=======================================================================
5796 //function : ExtrusParam::makeNodesByNormal2D
5797 //purpose : create nodes for extrusion using normals of faces
5798 //=======================================================================
5800 int SMESH_MeshEditor::ExtrusParam::
5801 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5802 const SMDS_MeshNode* srcNode,
5803 std::list<const SMDS_MeshNode*> & newNodes,
5804 const bool makeMediumNodes)
5806 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5808 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5810 // get normals to faces sharing srcNode
5811 vector< gp_XYZ > norms, baryCenters;
5812 gp_XYZ norm, avgNorm( 0,0,0 );
5813 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5814 while ( faceIt->more() )
5816 const SMDS_MeshElement* face = faceIt->next();
5817 if ( myElemsToUse && !myElemsToUse->count( face ))
5819 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5821 norms.push_back( norm );
5823 if ( !alongAvgNorm )
5827 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5828 bc += SMESH_TNodeXYZ( nIt->next() );
5829 baryCenters.push_back( bc / nbN );
5834 if ( norms.empty() ) return 0;
5836 double normSize = avgNorm.Modulus();
5837 if ( normSize < std::numeric_limits<double>::min() )
5840 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5843 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5846 avgNorm /= normSize;
5849 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5852 double stepSize = nextStep();
5854 if ( norms.size() > 1 )
5856 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5858 // translate plane of a face
5859 baryCenters[ iF ] += norms[ iF ] * stepSize;
5861 // find point of intersection of the face plane located at baryCenters[ iF ]
5862 // and avgNorm located at pNew
5863 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5864 double dot = ( norms[ iF ] * avgNorm );
5865 if ( dot < std::numeric_limits<double>::min() )
5866 dot = stepSize * 1e-3;
5867 double step = -( norms[ iF ] * pNew + d ) / dot;
5868 pNew += step * avgNorm;
5873 pNew += stepSize * avgNorm;
5877 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5878 newNodes.push_back( newNode );
5883 //=======================================================================
5884 //function : ExtrusParam::makeNodesByNormal1D
5885 //purpose : create nodes for extrusion using normals of edges
5886 //=======================================================================
5888 int SMESH_MeshEditor::ExtrusParam::
5889 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5890 const SMDS_MeshNode* srcNode,
5891 std::list<const SMDS_MeshNode*> & newNodes,
5892 const bool makeMediumNodes)
5894 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5898 //=======================================================================
5899 //function : ExtrusionSweep
5901 //=======================================================================
5903 SMESH_MeshEditor::PGroupIDs
5904 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5905 const gp_Vec& theStep,
5906 const int theNbSteps,
5907 TTElemOfElemListMap& newElemsMap,
5909 const double theTolerance)
5911 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5912 return ExtrusionSweep( theElems, aParams, newElemsMap );
5916 //=======================================================================
5917 //function : ExtrusionSweep
5919 //=======================================================================
5921 SMESH_MeshEditor::PGroupIDs
5922 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5923 ExtrusParam& theParams,
5924 TTElemOfElemListMap& newElemsMap)
5926 myLastCreatedElems.Clear();
5927 myLastCreatedNodes.Clear();
5929 // source elements for each generated one
5930 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5932 setElemsFirst( theElemSets );
5933 const int nbSteps = theParams.NbSteps();
5934 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5936 TNodeOfNodeListMap mapNewNodes;
5937 TElemOfVecOfNnlmiMap mapElemNewNodes;
5939 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5940 myMesh->NbFaces(ORDER_QUADRATIC) +
5941 myMesh->NbVolumes(ORDER_QUADRATIC) );
5943 TIDSortedElemSet::iterator itElem;
5944 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5946 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5947 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5949 // check element type
5950 const SMDS_MeshElement* elem = *itElem;
5951 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5954 const size_t nbNodes = elem->NbNodes();
5955 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5956 newNodesItVec.reserve( nbNodes );
5958 // loop on elem nodes
5959 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5960 while ( itN->more() )
5962 // check if a node has been already sweeped
5963 const SMDS_MeshNode* node = cast2Node( itN->next() );
5964 TNodeOfNodeListMap::iterator nIt =
5965 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5966 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5967 if ( listNewNodes.empty() )
5971 // check if we are to create medium nodes between corner ones
5972 bool needMediumNodes = false;
5973 if ( isQuadraticMesh )
5975 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5976 while (it->more() && !needMediumNodes )
5978 const SMDS_MeshElement* invElem = it->next();
5979 if ( invElem != elem && !theElems.count( invElem )) continue;
5980 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5981 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5982 needMediumNodes = true;
5985 // create nodes for all steps
5986 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5988 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5989 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5991 myLastCreatedNodes.Append( *newNodesIt );
5992 srcNodes.Append( node );
5997 break; // newNodesItVec will be shorter than nbNodes
6000 newNodesItVec.push_back( nIt );
6002 // make new elements
6003 if ( newNodesItVec.size() == nbNodes )
6004 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6008 if ( theParams.ToMakeBoundary() ) {
6009 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6011 PGroupIDs newGroupIDs;
6012 if ( theParams.ToMakeGroups() )
6013 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6018 //=======================================================================
6019 //function : ExtrusionAlongTrack
6021 //=======================================================================
6022 SMESH_MeshEditor::Extrusion_Error
6023 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6024 SMESH_subMesh* theTrack,
6025 const SMDS_MeshNode* theN1,
6026 const bool theHasAngles,
6027 list<double>& theAngles,
6028 const bool theLinearVariation,
6029 const bool theHasRefPoint,
6030 const gp_Pnt& theRefPoint,
6031 const bool theMakeGroups)
6033 myLastCreatedElems.Clear();
6034 myLastCreatedNodes.Clear();
6037 std::list<double> aPrms;
6038 TIDSortedElemSet::iterator itElem;
6041 TopoDS_Edge aTrackEdge;
6042 TopoDS_Vertex aV1, aV2;
6044 SMDS_ElemIteratorPtr aItE;
6045 SMDS_NodeIteratorPtr aItN;
6046 SMDSAbs_ElementType aTypeE;
6048 TNodeOfNodeListMap mapNewNodes;
6051 aNbE = theElements[0].size() + theElements[1].size();
6054 return EXTR_NO_ELEMENTS;
6056 // 1.1 Track Pattern
6059 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6061 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6062 theHasAngles, theAngles, theLinearVariation,
6063 theHasRefPoint, theRefPoint, theMakeGroups );
6065 aItE = pSubMeshDS->GetElements();
6066 while ( aItE->more() ) {
6067 const SMDS_MeshElement* pE = aItE->next();
6068 aTypeE = pE->GetType();
6069 // Pattern must contain links only
6070 if ( aTypeE != SMDSAbs_Edge )
6071 return EXTR_PATH_NOT_EDGE;
6074 list<SMESH_MeshEditor_PathPoint> fullList;
6076 const TopoDS_Shape& aS = theTrack->GetSubShape();
6077 // Sub-shape for the Pattern must be an Edge or Wire
6078 if( aS.ShapeType() == TopAbs_EDGE ) {
6079 aTrackEdge = TopoDS::Edge( aS );
6080 // the Edge must not be degenerated
6081 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6082 return EXTR_BAD_PATH_SHAPE;
6083 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6084 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6085 const SMDS_MeshNode* aN1 = aItN->next();
6086 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6087 const SMDS_MeshNode* aN2 = aItN->next();
6088 // starting node must be aN1 or aN2
6089 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6090 return EXTR_BAD_STARTING_NODE;
6091 aItN = pSubMeshDS->GetNodes();
6092 while ( aItN->more() ) {
6093 const SMDS_MeshNode* pNode = aItN->next();
6094 const SMDS_EdgePosition* pEPos =
6095 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6096 double aT = pEPos->GetUParameter();
6097 aPrms.push_back( aT );
6099 //Extrusion_Error err =
6100 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6101 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6102 list< SMESH_subMesh* > LSM;
6103 TopTools_SequenceOfShape Edges;
6104 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6105 while(itSM->more()) {
6106 SMESH_subMesh* SM = itSM->next();
6108 const TopoDS_Shape& aS = SM->GetSubShape();
6111 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6112 int startNid = theN1->GetID();
6113 TColStd_MapOfInteger UsedNums;
6115 int NbEdges = Edges.Length();
6117 for(; i<=NbEdges; i++) {
6119 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6120 for(; itLSM!=LSM.end(); itLSM++) {
6122 if(UsedNums.Contains(k)) continue;
6123 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6124 SMESH_subMesh* locTrack = *itLSM;
6125 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6126 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6127 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6128 const SMDS_MeshNode* aN1 = aItN->next();
6129 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6130 const SMDS_MeshNode* aN2 = aItN->next();
6131 // starting node must be aN1 or aN2
6132 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6133 // 2. Collect parameters on the track edge
6135 aItN = locMeshDS->GetNodes();
6136 while ( aItN->more() ) {
6137 const SMDS_MeshNode* pNode = aItN->next();
6138 const SMDS_EdgePosition* pEPos =
6139 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6140 double aT = pEPos->GetUParameter();
6141 aPrms.push_back( aT );
6143 list<SMESH_MeshEditor_PathPoint> LPP;
6144 //Extrusion_Error err =
6145 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6146 LLPPs.push_back(LPP);
6148 // update startN for search following egde
6149 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6150 else startNid = aN1->GetID();
6154 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6155 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6156 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6157 for(; itPP!=firstList.end(); itPP++) {
6158 fullList.push_back( *itPP );
6160 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6161 fullList.pop_back();
6163 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6164 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6165 itPP = currList.begin();
6166 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6167 gp_Dir D1 = PP1.Tangent();
6168 gp_Dir D2 = PP2.Tangent();
6169 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6170 (D1.Z()+D2.Z())/2 ) );
6171 PP1.SetTangent(Dnew);
6172 fullList.push_back(PP1);
6174 for(; itPP!=firstList.end(); itPP++) {
6175 fullList.push_back( *itPP );
6177 PP1 = fullList.back();
6178 fullList.pop_back();
6180 // if wire not closed
6181 fullList.push_back(PP1);
6185 return EXTR_BAD_PATH_SHAPE;
6188 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6189 theHasRefPoint, theRefPoint, theMakeGroups);
6193 //=======================================================================
6194 //function : ExtrusionAlongTrack
6196 //=======================================================================
6197 SMESH_MeshEditor::Extrusion_Error
6198 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6199 SMESH_Mesh* theTrack,
6200 const SMDS_MeshNode* theN1,
6201 const bool theHasAngles,
6202 list<double>& theAngles,
6203 const bool theLinearVariation,
6204 const bool theHasRefPoint,
6205 const gp_Pnt& theRefPoint,
6206 const bool theMakeGroups)
6208 myLastCreatedElems.Clear();
6209 myLastCreatedNodes.Clear();
6212 std::list<double> aPrms;
6213 TIDSortedElemSet::iterator itElem;
6216 TopoDS_Edge aTrackEdge;
6217 TopoDS_Vertex aV1, aV2;
6219 SMDS_ElemIteratorPtr aItE;
6220 SMDS_NodeIteratorPtr aItN;
6221 SMDSAbs_ElementType aTypeE;
6223 TNodeOfNodeListMap mapNewNodes;
6226 aNbE = theElements[0].size() + theElements[1].size();
6229 return EXTR_NO_ELEMENTS;
6231 // 1.1 Track Pattern
6234 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6236 aItE = pMeshDS->elementsIterator();
6237 while ( aItE->more() ) {
6238 const SMDS_MeshElement* pE = aItE->next();
6239 aTypeE = pE->GetType();
6240 // Pattern must contain links only
6241 if ( aTypeE != SMDSAbs_Edge )
6242 return EXTR_PATH_NOT_EDGE;
6245 list<SMESH_MeshEditor_PathPoint> fullList;
6247 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6249 if ( !theTrack->HasShapeToMesh() ) {
6250 //Mesh without shape
6251 const SMDS_MeshNode* currentNode = NULL;
6252 const SMDS_MeshNode* prevNode = theN1;
6253 std::vector<const SMDS_MeshNode*> aNodesList;
6254 aNodesList.push_back(theN1);
6255 int nbEdges = 0, conn=0;
6256 const SMDS_MeshElement* prevElem = NULL;
6257 const SMDS_MeshElement* currentElem = NULL;
6258 int totalNbEdges = theTrack->NbEdges();
6259 SMDS_ElemIteratorPtr nIt;
6262 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6263 return EXTR_BAD_STARTING_NODE;
6266 conn = nbEdgeConnectivity(theN1);
6268 return EXTR_PATH_NOT_EDGE;
6270 aItE = theN1->GetInverseElementIterator();
6271 prevElem = aItE->next();
6272 currentElem = prevElem;
6274 if(totalNbEdges == 1 ) {
6275 nIt = currentElem->nodesIterator();
6276 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6277 if(currentNode == prevNode)
6278 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6279 aNodesList.push_back(currentNode);
6281 nIt = currentElem->nodesIterator();
6282 while( nIt->more() ) {
6283 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6284 if(currentNode == prevNode)
6285 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6286 aNodesList.push_back(currentNode);
6288 //case of the closed mesh
6289 if(currentNode == theN1) {
6294 conn = nbEdgeConnectivity(currentNode);
6296 return EXTR_PATH_NOT_EDGE;
6297 }else if( conn == 1 && nbEdges > 0 ) {
6302 prevNode = currentNode;
6303 aItE = currentNode->GetInverseElementIterator();
6304 currentElem = aItE->next();
6305 if( currentElem == prevElem)
6306 currentElem = aItE->next();
6307 nIt = currentElem->nodesIterator();
6308 prevElem = currentElem;
6314 if(nbEdges != totalNbEdges)
6315 return EXTR_PATH_NOT_EDGE;
6317 TopTools_SequenceOfShape Edges;
6318 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6319 int startNid = theN1->GetID();
6320 for ( size_t i = 1; i < aNodesList.size(); i++ )
6322 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6323 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6324 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6325 list<SMESH_MeshEditor_PathPoint> LPP;
6327 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6328 LLPPs.push_back(LPP);
6329 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6330 else startNid = aNodesList[i-1]->GetID();
6333 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6334 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6335 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6336 for(; itPP!=firstList.end(); itPP++) {
6337 fullList.push_back( *itPP );
6340 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6341 SMESH_MeshEditor_PathPoint PP2;
6342 fullList.pop_back();
6344 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6345 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6346 itPP = currList.begin();
6347 PP2 = currList.front();
6348 gp_Dir D1 = PP1.Tangent();
6349 gp_Dir D2 = PP2.Tangent();
6350 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6351 PP1.SetTangent(Dnew);
6352 fullList.push_back(PP1);
6354 for(; itPP!=currList.end(); itPP++) {
6355 fullList.push_back( *itPP );
6357 PP1 = fullList.back();
6358 fullList.pop_back();
6360 fullList.push_back(PP1);
6362 } // Sub-shape for the Pattern must be an Edge or Wire
6363 else if ( aS.ShapeType() == TopAbs_EDGE )
6365 aTrackEdge = TopoDS::Edge( aS );
6366 // the Edge must not be degenerated
6367 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6368 return EXTR_BAD_PATH_SHAPE;
6369 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6370 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6371 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6372 // starting node must be aN1 or aN2
6373 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6374 return EXTR_BAD_STARTING_NODE;
6375 aItN = pMeshDS->nodesIterator();
6376 while ( aItN->more() ) {
6377 const SMDS_MeshNode* pNode = aItN->next();
6378 if( pNode==aN1 || pNode==aN2 ) continue;
6379 const SMDS_EdgePosition* pEPos =
6380 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6381 double aT = pEPos->GetUParameter();
6382 aPrms.push_back( aT );
6384 //Extrusion_Error err =
6385 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6387 else if( aS.ShapeType() == TopAbs_WIRE ) {
6388 list< SMESH_subMesh* > LSM;
6389 TopTools_SequenceOfShape Edges;
6390 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6391 for(; eExp.More(); eExp.Next()) {
6392 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6393 if( SMESH_Algo::isDegenerated(E) ) continue;
6394 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6400 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6401 TopoDS_Vertex aVprev;
6402 TColStd_MapOfInteger UsedNums;
6403 int NbEdges = Edges.Length();
6405 for(; i<=NbEdges; i++) {
6407 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6408 for(; itLSM!=LSM.end(); itLSM++) {
6410 if(UsedNums.Contains(k)) continue;
6411 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6412 SMESH_subMesh* locTrack = *itLSM;
6413 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6414 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6415 bool aN1isOK = false, aN2isOK = false;
6416 if ( aVprev.IsNull() ) {
6417 // if previous vertex is not yet defined, it means that we in the beginning of wire
6418 // and we have to find initial vertex corresponding to starting node theN1
6419 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6420 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6421 // starting node must be aN1 or aN2
6422 aN1isOK = ( aN1 && aN1 == theN1 );
6423 aN2isOK = ( aN2 && aN2 == theN1 );
6426 // we have specified ending vertex of the previous edge on the previous iteration
6427 // and we have just to check that it corresponds to any vertex in current segment
6428 aN1isOK = aVprev.IsSame( aV1 );
6429 aN2isOK = aVprev.IsSame( aV2 );
6431 if ( !aN1isOK && !aN2isOK ) continue;
6432 // 2. Collect parameters on the track edge
6434 aItN = locMeshDS->GetNodes();
6435 while ( aItN->more() ) {
6436 const SMDS_MeshNode* pNode = aItN->next();
6437 const SMDS_EdgePosition* pEPos =
6438 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6439 double aT = pEPos->GetUParameter();
6440 aPrms.push_back( aT );
6442 list<SMESH_MeshEditor_PathPoint> LPP;
6443 //Extrusion_Error err =
6444 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6445 LLPPs.push_back(LPP);
6447 // update startN for search following egde
6448 if ( aN1isOK ) aVprev = aV2;
6453 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6454 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6455 fullList.splice( fullList.end(), firstList );
6457 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6458 fullList.pop_back();
6460 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6461 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6462 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6463 gp_Dir D1 = PP1.Tangent();
6464 gp_Dir D2 = PP2.Tangent();
6465 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6466 PP1.SetTangent(Dnew);
6467 fullList.push_back(PP1);
6468 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6469 PP1 = fullList.back();
6470 fullList.pop_back();
6472 // if wire not closed
6473 fullList.push_back(PP1);
6477 return EXTR_BAD_PATH_SHAPE;
6480 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6481 theHasRefPoint, theRefPoint, theMakeGroups);
6485 //=======================================================================
6486 //function : makeEdgePathPoints
6487 //purpose : auxiliary for ExtrusionAlongTrack
6488 //=======================================================================
6489 SMESH_MeshEditor::Extrusion_Error
6490 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6491 const TopoDS_Edge& aTrackEdge,
6493 list<SMESH_MeshEditor_PathPoint>& LPP)
6495 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6497 aTolVec2=aTolVec*aTolVec;
6499 TopoDS_Vertex aV1, aV2;
6500 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6501 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6502 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6503 // 2. Collect parameters on the track edge
6504 aPrms.push_front( aT1 );
6505 aPrms.push_back( aT2 );
6508 if( FirstIsStart ) {
6519 SMESH_MeshEditor_PathPoint aPP;
6520 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6521 std::list<double>::iterator aItD = aPrms.begin();
6522 for(; aItD != aPrms.end(); ++aItD) {
6526 aC3D->D1( aT, aP3D, aVec );
6527 aL2 = aVec.SquareMagnitude();
6528 if ( aL2 < aTolVec2 )
6529 return EXTR_CANT_GET_TANGENT;
6530 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6532 aPP.SetTangent( aTgt );
6533 aPP.SetParameter( aT );
6540 //=======================================================================
6541 //function : makeExtrElements
6542 //purpose : auxiliary for ExtrusionAlongTrack
6543 //=======================================================================
6544 SMESH_MeshEditor::Extrusion_Error
6545 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6546 list<SMESH_MeshEditor_PathPoint>& fullList,
6547 const bool theHasAngles,
6548 list<double>& theAngles,
6549 const bool theLinearVariation,
6550 const bool theHasRefPoint,
6551 const gp_Pnt& theRefPoint,
6552 const bool theMakeGroups)
6554 const int aNbTP = fullList.size();
6557 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6558 linearAngleVariation(aNbTP-1, theAngles);
6560 // fill vector of path points with angles
6561 vector<SMESH_MeshEditor_PathPoint> aPPs;
6562 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6563 list<double>::iterator itAngles = theAngles.begin();
6564 aPPs.push_back( *itPP++ );
6565 for( ; itPP != fullList.end(); itPP++) {
6566 aPPs.push_back( *itPP );
6567 if ( theHasAngles && itAngles != theAngles.end() )
6568 aPPs.back().SetAngle( *itAngles++ );
6571 TNodeOfNodeListMap mapNewNodes;
6572 TElemOfVecOfNnlmiMap mapElemNewNodes;
6573 TTElemOfElemListMap newElemsMap;
6574 TIDSortedElemSet::iterator itElem;
6575 // source elements for each generated one
6576 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6578 // 3. Center of rotation aV0
6579 gp_Pnt aV0 = theRefPoint;
6580 if ( !theHasRefPoint )
6582 gp_XYZ aGC( 0.,0.,0. );
6583 TIDSortedElemSet newNodes;
6585 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6587 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6588 itElem = theElements.begin();
6589 for ( ; itElem != theElements.end(); itElem++ )
6591 const SMDS_MeshElement* elem = *itElem;
6592 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6593 while ( itN->more() ) {
6594 const SMDS_MeshElement* node = itN->next();
6595 if ( newNodes.insert( node ).second )
6596 aGC += SMESH_TNodeXYZ( node );
6600 aGC /= newNodes.size();
6602 } // if (!theHasRefPoint) {
6604 // 4. Processing the elements
6605 SMESHDS_Mesh* aMesh = GetMeshDS();
6606 list<const SMDS_MeshNode*> emptyList;
6608 setElemsFirst( theElemSets );
6609 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6611 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6612 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6614 const SMDS_MeshElement* elem = *itElem;
6616 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6617 newNodesItVec.reserve( elem->NbNodes() );
6619 // loop on elem nodes
6621 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6622 while ( itN->more() )
6625 // check if a node has been already processed
6626 const SMDS_MeshNode* node = cast2Node( itN->next() );
6627 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6628 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6629 if ( listNewNodes.empty() )
6632 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6633 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6634 gp_Ax1 anAx1, anAxT1T0;
6635 gp_Dir aDT1x, aDT0x, aDT1T0;
6640 aPN0 = SMESH_TNodeXYZ( node );
6642 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6644 aDT0x= aPP0.Tangent();
6646 for ( int j = 1; j < aNbTP; ++j ) {
6647 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6649 aDT1x = aPP1.Tangent();
6650 aAngle1x = aPP1.Angle();
6652 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6654 gp_Vec aV01x( aP0x, aP1x );
6655 aTrsf.SetTranslation( aV01x );
6658 aV1x = aV0x.Transformed( aTrsf );
6659 aPN1 = aPN0.Transformed( aTrsf );
6661 // rotation 1 [ T1,T0 ]
6662 aAngleT1T0=-aDT1x.Angle( aDT0x );
6663 if (fabs(aAngleT1T0) > aTolAng)
6666 anAxT1T0.SetLocation( aV1x );
6667 anAxT1T0.SetDirection( aDT1T0 );
6668 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6670 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6674 if ( theHasAngles ) {
6675 anAx1.SetLocation( aV1x );
6676 anAx1.SetDirection( aDT1x );
6677 aTrsfRot.SetRotation( anAx1, aAngle1x );
6679 aPN1 = aPN1.Transformed( aTrsfRot );
6683 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6685 // create additional node
6686 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6687 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6688 myLastCreatedNodes.Append(newNode);
6689 srcNodes.Append( node );
6690 listNewNodes.push_back( newNode );
6692 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6693 myLastCreatedNodes.Append(newNode);
6694 srcNodes.Append( node );
6695 listNewNodes.push_back( newNode );
6703 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6705 // if current elem is quadratic and current node is not medium
6706 // we have to check - may be it is needed to insert additional nodes
6707 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6708 if ((int) listNewNodes.size() == aNbTP-1 )
6710 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6711 gp_XYZ P(node->X(), node->Y(), node->Z());
6712 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6714 for(i=0; i<aNbTP-1; i++) {
6715 const SMDS_MeshNode* N = *it;
6716 double x = ( N->X() + P.X() )/2.;
6717 double y = ( N->Y() + P.Y() )/2.;
6718 double z = ( N->Z() + P.Z() )/2.;
6719 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6720 srcNodes.Append( node );
6721 myLastCreatedNodes.Append(newN);
6724 P = gp_XYZ(N->X(),N->Y(),N->Z());
6726 listNewNodes.clear();
6727 for(i=0; i<2*(aNbTP-1); i++) {
6728 listNewNodes.push_back(aNodes[i]);
6733 newNodesItVec.push_back( nIt );
6736 // make new elements
6737 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6741 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6743 if ( theMakeGroups )
6744 generateGroups( srcNodes, srcElems, "extruded");
6750 //=======================================================================
6751 //function : linearAngleVariation
6752 //purpose : spread values over nbSteps
6753 //=======================================================================
6755 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6756 list<double>& Angles)
6758 int nbAngles = Angles.size();
6759 if( nbSteps > nbAngles && nbAngles > 0 )
6761 vector<double> theAngles(nbAngles);
6762 theAngles.assign( Angles.begin(), Angles.end() );
6765 double rAn2St = double( nbAngles ) / double( nbSteps );
6766 double angPrev = 0, angle;
6767 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6769 double angCur = rAn2St * ( iSt+1 );
6770 double angCurFloor = floor( angCur );
6771 double angPrevFloor = floor( angPrev );
6772 if ( angPrevFloor == angCurFloor )
6773 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6775 int iP = int( angPrevFloor );
6776 double angPrevCeil = ceil(angPrev);
6777 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6779 int iC = int( angCurFloor );
6780 if ( iC < nbAngles )
6781 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6783 iP = int( angPrevCeil );
6785 angle += theAngles[ iC ];
6787 res.push_back(angle);
6795 //================================================================================
6797 * \brief Move or copy theElements applying theTrsf to their nodes
6798 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6799 * \param theTrsf - transformation to apply
6800 * \param theCopy - if true, create translated copies of theElems
6801 * \param theMakeGroups - if true and theCopy, create translated groups
6802 * \param theTargetMesh - mesh to copy translated elements into
6803 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6805 //================================================================================
6807 SMESH_MeshEditor::PGroupIDs
6808 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6809 const gp_Trsf& theTrsf,
6811 const bool theMakeGroups,
6812 SMESH_Mesh* theTargetMesh)
6814 myLastCreatedElems.Clear();
6815 myLastCreatedNodes.Clear();
6817 bool needReverse = false;
6818 string groupPostfix;
6819 switch ( theTrsf.Form() ) {
6822 groupPostfix = "mirrored";
6825 groupPostfix = "mirrored";
6829 groupPostfix = "mirrored";
6832 groupPostfix = "rotated";
6834 case gp_Translation:
6835 groupPostfix = "translated";
6838 groupPostfix = "scaled";
6840 case gp_CompoundTrsf: // different scale by axis
6841 groupPostfix = "scaled";
6844 needReverse = false;
6845 groupPostfix = "transformed";
6848 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6849 SMESHDS_Mesh* aMesh = GetMeshDS();
6851 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6852 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6853 SMESH_MeshEditor::ElemFeatures elemType;
6855 // map old node to new one
6856 TNodeNodeMap nodeMap;
6858 // elements sharing moved nodes; those of them which have all
6859 // nodes mirrored but are not in theElems are to be reversed
6860 TIDSortedElemSet inverseElemSet;
6862 // source elements for each generated one
6863 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6865 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6866 TIDSortedElemSet orphanNode;
6868 if ( theElems.empty() ) // transform the whole mesh
6871 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6872 while ( eIt->more() ) theElems.insert( eIt->next() );
6874 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6875 while ( nIt->more() )
6877 const SMDS_MeshNode* node = nIt->next();
6878 if ( node->NbInverseElements() == 0)
6879 orphanNode.insert( node );
6883 // loop on elements to transform nodes : first orphan nodes then elems
6884 TIDSortedElemSet::iterator itElem;
6885 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6886 for (int i=0; i<2; i++)
6887 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6889 const SMDS_MeshElement* elem = *itElem;
6893 // loop on elem nodes
6895 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6896 while ( itN->more() )
6898 const SMDS_MeshNode* node = cast2Node( itN->next() );
6899 // check if a node has been already transformed
6900 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6901 nodeMap.insert( make_pair ( node, node ));
6902 if ( !n2n_isnew.second )
6905 node->GetXYZ( coord );
6906 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6907 if ( theTargetMesh ) {
6908 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6909 n2n_isnew.first->second = newNode;
6910 myLastCreatedNodes.Append(newNode);
6911 srcNodes.Append( node );
6913 else if ( theCopy ) {
6914 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6915 n2n_isnew.first->second = newNode;
6916 myLastCreatedNodes.Append(newNode);
6917 srcNodes.Append( node );
6920 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6921 // node position on shape becomes invalid
6922 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6923 ( SMDS_SpacePosition::originSpacePosition() );
6926 // keep inverse elements
6927 if ( !theCopy && !theTargetMesh && needReverse ) {
6928 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6929 while ( invElemIt->more() ) {
6930 const SMDS_MeshElement* iel = invElemIt->next();
6931 inverseElemSet.insert( iel );
6935 } // loop on elems in { &orphanNode, &theElems };
6937 // either create new elements or reverse mirrored ones
6938 if ( !theCopy && !needReverse && !theTargetMesh )
6941 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6943 // Replicate or reverse elements
6945 std::vector<int> iForw;
6946 vector<const SMDS_MeshNode*> nodes;
6947 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6949 const SMDS_MeshElement* elem = *itElem;
6950 if ( !elem ) continue;
6952 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6953 size_t nbNodes = elem->NbNodes();
6954 if ( geomType == SMDSGeom_NONE ) continue; // node
6956 nodes.resize( nbNodes );
6958 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6960 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6964 bool allTransformed = true;
6965 int nbFaces = aPolyedre->NbFaces();
6966 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6968 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6969 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6971 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6972 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6973 if ( nodeMapIt == nodeMap.end() )
6974 allTransformed = false; // not all nodes transformed
6976 nodes.push_back((*nodeMapIt).second);
6978 if ( needReverse && allTransformed )
6979 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6981 if ( !allTransformed )
6982 continue; // not all nodes transformed
6984 else // ----------------------- the rest element types
6986 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6987 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6988 const vector<int>& i = needReverse ? iRev : iForw;
6990 // find transformed nodes
6992 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6993 while ( itN->more() ) {
6994 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6995 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6996 if ( nodeMapIt == nodeMap.end() )
6997 break; // not all nodes transformed
6998 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
7000 if ( iNode != nbNodes )
7001 continue; // not all nodes transformed
7005 // copy in this or a new mesh
7006 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7007 srcElems.Append( elem );
7010 // reverse element as it was reversed by transformation
7012 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7015 } // loop on elements
7017 if ( editor && editor != this )
7018 myLastCreatedElems = editor->myLastCreatedElems;
7020 PGroupIDs newGroupIDs;
7022 if ( ( theMakeGroups && theCopy ) ||
7023 ( theMakeGroups && theTargetMesh ) )
7024 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7029 //=======================================================================
7031 * \brief Create groups of elements made during transformation
7032 * \param nodeGens - nodes making corresponding myLastCreatedNodes
7033 * \param elemGens - elements making corresponding myLastCreatedElems
7034 * \param postfix - to append to names of new groups
7035 * \param targetMesh - mesh to create groups in
7036 * \param topPresent - is there "top" elements that are created by sweeping
7038 //=======================================================================
7040 SMESH_MeshEditor::PGroupIDs
7041 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7042 const SMESH_SequenceOfElemPtr& elemGens,
7043 const std::string& postfix,
7044 SMESH_Mesh* targetMesh,
7045 const bool topPresent)
7047 PGroupIDs newGroupIDs( new list<int> );
7048 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7050 // Sort existing groups by types and collect their names
7052 // containers to store an old group and generated new ones;
7053 // 1st new group is for result elems of different type than a source one;
7054 // 2nd new group is for same type result elems ("top" group at extrusion)
7056 using boost::make_tuple;
7057 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7058 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7059 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7061 set< string > groupNames;
7063 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7064 if ( !groupIt->more() ) return newGroupIDs;
7066 int newGroupID = mesh->GetGroupIds().back()+1;
7067 while ( groupIt->more() )
7069 SMESH_Group * group = groupIt->next();
7070 if ( !group ) continue;
7071 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7072 if ( !groupDS || groupDS->IsEmpty() ) continue;
7073 groupNames.insert ( group->GetName() );
7074 groupDS->SetStoreName( group->GetName() );
7075 const SMDSAbs_ElementType type = groupDS->GetType();
7076 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7077 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7078 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7079 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7082 // Loop on nodes and elements to add them in new groups
7084 vector< const SMDS_MeshElement* > resultElems;
7085 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7087 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7088 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7089 if ( gens.Length() != elems.Length() )
7090 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7092 // loop on created elements
7093 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7095 const SMDS_MeshElement* sourceElem = gens( iElem );
7096 if ( !sourceElem ) {
7097 MESSAGE("generateGroups(): NULL source element");
7100 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7101 if ( groupsOldNew.empty() ) { // no groups of this type at all
7102 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7103 ++iElem; // skip all elements made by sourceElem
7106 // collect all elements made by the iElem-th sourceElem
7107 resultElems.clear();
7108 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7109 if ( resElem != sourceElem )
7110 resultElems.push_back( resElem );
7111 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7112 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7113 if ( resElem != sourceElem )
7114 resultElems.push_back( resElem );
7116 const SMDS_MeshElement* topElem = 0;
7117 if ( isNodes ) // there must be a top element
7119 topElem = resultElems.back();
7120 resultElems.pop_back();
7124 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7125 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7126 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7128 topElem = *resElemIt;
7129 *resElemIt = 0; // erase *resElemIt
7133 // add resultElems to groups originted from ones the sourceElem belongs to
7134 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7135 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7137 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7138 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7140 // fill in a new group
7141 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7142 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7143 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7145 newGroup.Add( *resElemIt );
7147 // fill a "top" group
7150 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7151 newTopGroup.Add( topElem );
7155 } // loop on created elements
7156 }// loop on nodes and elements
7158 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7160 list<int> topGrouIds;
7161 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7163 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7164 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7165 orderedOldNewGroups[i]->get<2>() };
7166 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7168 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7169 if ( newGroupDS->IsEmpty() )
7171 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7176 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7179 const bool isTop = ( topPresent &&
7180 newGroupDS->GetType() == oldGroupDS->GetType() &&
7183 string name = oldGroupDS->GetStoreName();
7184 { // remove trailing whitespaces (issue 22599)
7185 size_t size = name.size();
7186 while ( size > 1 && isspace( name[ size-1 ]))
7188 if ( size != name.size() )
7190 name.resize( size );
7191 oldGroupDS->SetStoreName( name.c_str() );
7194 if ( !targetMesh ) {
7195 string suffix = ( isTop ? "top": postfix.c_str() );
7199 while ( !groupNames.insert( name ).second ) // name exists
7200 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7205 newGroupDS->SetStoreName( name.c_str() );
7207 // make a SMESH_Groups
7208 mesh->AddGroup( newGroupDS );
7210 topGrouIds.push_back( newGroupDS->GetID() );
7212 newGroupIDs->push_back( newGroupDS->GetID() );
7216 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7221 //================================================================================
7223 * * \brief Return list of group of nodes close to each other within theTolerance
7224 * * Search among theNodes or in the whole mesh if theNodes is empty using
7225 * * an Octree algorithm
7226 * \param [in,out] theNodes - the nodes to treat
7227 * \param [in] theTolerance - the tolerance
7228 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7229 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7230 * corner and medium nodes in separate groups
7232 //================================================================================
7234 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7235 const double theTolerance,
7236 TListOfListOfNodes & theGroupsOfNodes,
7237 bool theSeparateCornersAndMedium)
7239 myLastCreatedElems.Clear();
7240 myLastCreatedNodes.Clear();
7242 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7243 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7244 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7245 theSeparateCornersAndMedium = false;
7247 TIDSortedNodeSet& corners = theNodes;
7248 TIDSortedNodeSet medium;
7250 if ( theNodes.empty() ) // get all nodes in the mesh
7252 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7253 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7254 if ( theSeparateCornersAndMedium )
7255 while ( nIt->more() )
7257 const SMDS_MeshNode* n = nIt->next();
7258 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7259 nodeSet->insert( nodeSet->end(), n );
7262 while ( nIt->more() )
7263 theNodes.insert( theNodes.end(), nIt->next() );
7265 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7267 TIDSortedNodeSet::iterator nIt = corners.begin();
7268 while ( nIt != corners.end() )
7269 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7271 medium.insert( medium.end(), *nIt );
7272 corners.erase( nIt++ );
7280 if ( !corners.empty() )
7281 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7282 if ( !medium.empty() )
7283 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7286 //=======================================================================
7287 //function : SimplifyFace
7288 //purpose : split a chain of nodes into several closed chains
7289 //=======================================================================
7291 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7292 vector<const SMDS_MeshNode *>& poly_nodes,
7293 vector<int>& quantities) const
7295 int nbNodes = faceNodes.size();
7296 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7300 size_t prevNbQuant = quantities.size();
7302 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7303 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7304 map< const SMDS_MeshNode*, int >::iterator nInd;
7306 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7307 simpleNodes.push_back( faceNodes[0] );
7308 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7310 if ( faceNodes[ iCur ] != simpleNodes.back() )
7312 int index = simpleNodes.size();
7313 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7314 int prevIndex = nInd->second;
7315 if ( prevIndex < index )
7318 int loopLen = index - prevIndex;
7321 // store the sub-loop
7322 quantities.push_back( loopLen );
7323 for ( int i = prevIndex; i < index; i++ )
7324 poly_nodes.push_back( simpleNodes[ i ]);
7326 simpleNodes.resize( prevIndex+1 );
7330 simpleNodes.push_back( faceNodes[ iCur ]);
7335 if ( simpleNodes.size() > 2 )
7337 quantities.push_back( simpleNodes.size() );
7338 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7341 return quantities.size() - prevNbQuant;
7344 //=======================================================================
7345 //function : MergeNodes
7346 //purpose : In each group, the cdr of nodes are substituted by the first one
7348 //=======================================================================
7350 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7351 const bool theAvoidMakingHoles)
7353 myLastCreatedElems.Clear();
7354 myLastCreatedNodes.Clear();
7356 SMESHDS_Mesh* mesh = GetMeshDS();
7358 TNodeNodeMap nodeNodeMap; // node to replace - new node
7359 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7360 list< int > rmElemIds, rmNodeIds;
7361 vector< ElemFeatures > newElemDefs;
7363 // Fill nodeNodeMap and elems
7365 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7366 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7368 list<const SMDS_MeshNode*>& nodes = *grIt;
7369 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7370 const SMDS_MeshNode* nToKeep = *nIt;
7371 for ( ++nIt; nIt != nodes.end(); nIt++ )
7373 const SMDS_MeshNode* nToRemove = *nIt;
7374 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7375 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7376 while ( invElemIt->more() ) {
7377 const SMDS_MeshElement* elem = invElemIt->next();
7383 // Apply recursive replacements (BUG 0020185)
7384 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7385 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7387 const SMDS_MeshNode* nToKeep = nnIt->second;
7388 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7389 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7390 nToKeep = nnIt_i->second;
7391 nnIt->second = nToKeep;
7394 if ( theAvoidMakingHoles )
7396 // find elements whose topology changes
7398 vector<const SMDS_MeshElement*> pbElems;
7399 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7400 for ( ; eIt != elems.end(); ++eIt )
7402 const SMDS_MeshElement* elem = *eIt;
7403 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7404 while ( itN->more() )
7406 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7407 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7408 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7410 // several nodes of elem stick
7411 pbElems.push_back( elem );
7416 // exclude from merge nodes causing spoiling element
7417 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7419 bool nodesExcluded = false;
7420 for ( size_t i = 0; i < pbElems.size(); ++i )
7422 size_t prevNbMergeNodes = nodeNodeMap.size();
7423 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7424 prevNbMergeNodes < nodeNodeMap.size() )
7425 nodesExcluded = true;
7427 if ( !nodesExcluded )
7432 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7434 const SMDS_MeshNode* nToRemove = nnIt->first;
7435 const SMDS_MeshNode* nToKeep = nnIt->second;
7436 if ( nToRemove != nToKeep )
7438 rmNodeIds.push_back( nToRemove->GetID() );
7439 AddToSameGroups( nToKeep, nToRemove, mesh );
7440 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7441 // w/o creating node in place of merged ones.
7442 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7443 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7444 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7445 sm->SetIsAlwaysComputed( true );
7449 // Change element nodes or remove an element
7451 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7452 for ( ; eIt != elems.end(); eIt++ )
7454 const SMDS_MeshElement* elem = *eIt;
7455 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7457 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7459 rmElemIds.push_back( elem->GetID() );
7461 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7463 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7464 & newElemDefs[i].myNodes[0],
7465 newElemDefs[i].myNodes.size() ))
7469 newElemDefs[i].SetID( elem->GetID() );
7470 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7471 if ( !keepElem ) rmElemIds.pop_back();
7475 newElemDefs[i].SetID( -1 );
7477 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7478 if ( sm && newElem )
7479 sm->AddElement( newElem );
7480 if ( elem != newElem )
7481 ReplaceElemInGroups( elem, newElem, mesh );
7486 // Remove bad elements, then equal nodes (order important)
7487 Remove( rmElemIds, /*isNodes=*/false );
7488 Remove( rmNodeIds, /*isNodes=*/true );
7493 //=======================================================================
7494 //function : applyMerge
7495 //purpose : Compute new connectivity of an element after merging nodes
7496 // \param [in] elems - the element
7497 // \param [out] newElemDefs - definition(s) of result element(s)
7498 // \param [inout] nodeNodeMap - nodes to merge
7499 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7500 // after merging (but not degenerated), removes nodes causing
7501 // the invalidity from \a nodeNodeMap.
7502 // \return bool - true if the element should be removed
7503 //=======================================================================
7505 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7506 vector< ElemFeatures >& newElemDefs,
7507 TNodeNodeMap& nodeNodeMap,
7508 const bool avoidMakingHoles )
7510 bool toRemove = false; // to remove elem
7511 int nbResElems = 1; // nb new elements
7513 newElemDefs.resize(nbResElems);
7514 newElemDefs[0].Init( elem );
7515 newElemDefs[0].myNodes.clear();
7517 set<const SMDS_MeshNode*> nodeSet;
7518 vector< const SMDS_MeshNode*> curNodes;
7519 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7522 const int nbNodes = elem->NbNodes();
7523 SMDSAbs_EntityType entity = elem->GetEntityType();
7525 curNodes.resize( nbNodes );
7526 uniqueNodes.resize( nbNodes );
7527 iRepl.resize( nbNodes );
7528 int iUnique = 0, iCur = 0, nbRepl = 0;
7530 // Get new seq of nodes
7532 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7533 while ( itN->more() )
7535 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7537 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7538 if ( nnIt != nodeNodeMap.end() ) {
7541 curNodes[ iCur ] = n;
7542 bool isUnique = nodeSet.insert( n ).second;
7544 uniqueNodes[ iUnique++ ] = n;
7546 iRepl[ nbRepl++ ] = iCur;
7550 // Analyse element topology after replacement
7552 int nbUniqueNodes = nodeSet.size();
7553 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7558 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7560 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7561 int nbCorners = nbNodes / 2;
7562 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7564 int iNext = ( iCur + 1 ) % nbCorners;
7565 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7567 int iMedium = iCur + nbCorners;
7568 vector< const SMDS_MeshNode* >::iterator i =
7569 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7571 curNodes[ iMedium ]);
7572 if ( i != uniqueNodes.end() )
7575 for ( ; i+1 != uniqueNodes.end(); ++i )
7584 case SMDSEntity_Polygon:
7585 case SMDSEntity_Quad_Polygon: // Polygon
7587 ElemFeatures* elemType = & newElemDefs[0];
7588 const bool isQuad = elemType->myIsQuad;
7590 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7591 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7593 // a polygon can divide into several elements
7594 vector<const SMDS_MeshNode *> polygons_nodes;
7595 vector<int> quantities;
7596 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7597 newElemDefs.resize( nbResElems );
7598 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7600 ElemFeatures* elemType = & newElemDefs[iface];
7601 if ( iface ) elemType->Init( elem );
7603 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7604 int nbNewNodes = quantities[iface];
7605 face_nodes.assign( polygons_nodes.begin() + inode,
7606 polygons_nodes.begin() + inode + nbNewNodes );
7607 inode += nbNewNodes;
7608 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7610 bool isValid = ( nbNewNodes % 2 == 0 );
7611 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7612 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7613 elemType->SetQuad( isValid );
7614 if ( isValid ) // put medium nodes after corners
7615 SMDS_MeshCell::applyInterlaceRev
7616 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7617 nbNewNodes ), face_nodes );
7619 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7621 nbUniqueNodes = newElemDefs[0].myNodes.size();
7625 case SMDSEntity_Polyhedra: // Polyhedral volume
7627 if ( nbUniqueNodes >= 4 )
7629 // each face has to be analyzed in order to check volume validity
7630 if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7632 int nbFaces = aPolyedre->NbFaces();
7634 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7635 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7636 vector<const SMDS_MeshNode *> faceNodes;
7640 for (int iface = 1; iface <= nbFaces; iface++)
7642 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7643 faceNodes.resize( nbFaceNodes );
7644 for (int inode = 1; inode <= nbFaceNodes; inode++)
7646 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7647 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7648 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7649 faceNode = (*nnIt).second;
7650 faceNodes[inode - 1] = faceNode;
7652 SimplifyFace(faceNodes, poly_nodes, quantities);
7655 if ( quantities.size() > 3 )
7657 // TODO: remove coincident faces
7659 nbUniqueNodes = newElemDefs[0].myNodes.size();
7667 // TODO not all the possible cases are solved. Find something more generic?
7668 case SMDSEntity_Edge: //////// EDGE
7669 case SMDSEntity_Triangle: //// TRIANGLE
7670 case SMDSEntity_Quad_Triangle:
7671 case SMDSEntity_Tetra:
7672 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7676 case SMDSEntity_Quad_Edge:
7680 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7682 if ( nbUniqueNodes < 3 )
7684 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7685 toRemove = true; // opposite nodes stick
7690 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7699 if ( nbUniqueNodes == 6 &&
7701 ( nbRepl == 1 || iRepl[1] >= 4 ))
7707 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7716 if ( nbUniqueNodes == 7 &&
7718 ( nbRepl == 1 || iRepl[1] != 8 ))
7724 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7726 if ( nbUniqueNodes == 4 ) {
7727 // ---------------------------------> tetrahedron
7728 if ( curNodes[3] == curNodes[4] &&
7729 curNodes[3] == curNodes[5] ) {
7733 else if ( curNodes[0] == curNodes[1] &&
7734 curNodes[0] == curNodes[2] ) {
7735 // bottom nodes stick: set a top before
7736 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7737 uniqueNodes[ 0 ] = curNodes [ 5 ];
7738 uniqueNodes[ 1 ] = curNodes [ 4 ];
7739 uniqueNodes[ 2 ] = curNodes [ 3 ];
7742 else if (( curNodes[0] == curNodes[3] ) +
7743 ( curNodes[1] == curNodes[4] ) +
7744 ( curNodes[2] == curNodes[5] ) == 2 ) {
7745 // a lateral face turns into a line
7749 else if ( nbUniqueNodes == 5 ) {
7750 // PENTAHEDRON --------------------> pyramid
7751 if ( curNodes[0] == curNodes[3] )
7753 uniqueNodes[ 0 ] = curNodes[ 1 ];
7754 uniqueNodes[ 1 ] = curNodes[ 4 ];
7755 uniqueNodes[ 2 ] = curNodes[ 5 ];
7756 uniqueNodes[ 3 ] = curNodes[ 2 ];
7757 uniqueNodes[ 4 ] = curNodes[ 0 ];
7760 if ( curNodes[1] == curNodes[4] )
7762 uniqueNodes[ 0 ] = curNodes[ 0 ];
7763 uniqueNodes[ 1 ] = curNodes[ 2 ];
7764 uniqueNodes[ 2 ] = curNodes[ 5 ];
7765 uniqueNodes[ 3 ] = curNodes[ 3 ];
7766 uniqueNodes[ 4 ] = curNodes[ 1 ];
7769 if ( curNodes[2] == curNodes[5] )
7771 uniqueNodes[ 0 ] = curNodes[ 0 ];
7772 uniqueNodes[ 1 ] = curNodes[ 3 ];
7773 uniqueNodes[ 2 ] = curNodes[ 4 ];
7774 uniqueNodes[ 3 ] = curNodes[ 1 ];
7775 uniqueNodes[ 4 ] = curNodes[ 2 ];
7781 case SMDSEntity_Hexa:
7783 //////////////////////////////////// HEXAHEDRON
7784 SMDS_VolumeTool hexa (elem);
7785 hexa.SetExternalNormal();
7786 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7787 //////////////////////// HEX ---> tetrahedron
7788 for ( int iFace = 0; iFace < 6; iFace++ ) {
7789 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7790 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7791 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7792 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7793 // one face turns into a point ...
7794 int pickInd = ind[ 0 ];
7795 int iOppFace = hexa.GetOppFaceIndex( iFace );
7796 ind = hexa.GetFaceNodesIndices( iOppFace );
7798 uniqueNodes.clear();
7799 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7800 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7803 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7805 if ( nbStick == 1 ) {
7806 // ... and the opposite one - into a triangle.
7808 uniqueNodes.push_back( curNodes[ pickInd ]);
7815 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7816 //////////////////////// HEX ---> prism
7817 int nbTria = 0, iTria[3];
7818 const int *ind; // indices of face nodes
7819 // look for triangular faces
7820 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7821 ind = hexa.GetFaceNodesIndices( iFace );
7822 TIDSortedNodeSet faceNodes;
7823 for ( iCur = 0; iCur < 4; iCur++ )
7824 faceNodes.insert( curNodes[ind[iCur]] );
7825 if ( faceNodes.size() == 3 )
7826 iTria[ nbTria++ ] = iFace;
7828 // check if triangles are opposite
7829 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7831 // set nodes of the bottom triangle
7832 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7834 for ( iCur = 0; iCur < 4; iCur++ )
7835 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7836 indB.push_back( ind[iCur] );
7837 if ( !hexa.IsForward() )
7838 std::swap( indB[0], indB[2] );
7839 for ( iCur = 0; iCur < 3; iCur++ )
7840 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7841 // set nodes of the top triangle
7842 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7843 for ( iCur = 0; iCur < 3; ++iCur )
7844 for ( int j = 0; j < 4; ++j )
7845 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7847 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7854 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7855 //////////////////// HEXAHEDRON ---> pyramid
7856 for ( int iFace = 0; iFace < 6; iFace++ ) {
7857 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7858 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7859 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7860 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7861 // one face turns into a point ...
7862 int iOppFace = hexa.GetOppFaceIndex( iFace );
7863 ind = hexa.GetFaceNodesIndices( iOppFace );
7864 uniqueNodes.clear();
7865 for ( iCur = 0; iCur < 4; iCur++ ) {
7866 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7869 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7871 if ( uniqueNodes.size() == 4 ) {
7872 // ... and the opposite one is a quadrangle
7874 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7875 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7883 if ( toRemove && nbUniqueNodes > 4 ) {
7884 ////////////////// HEXAHEDRON ---> polyhedron
7885 hexa.SetExternalNormal();
7886 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7887 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7888 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7889 quantities.reserve( 6 ); quantities.clear();
7890 for ( int iFace = 0; iFace < 6; iFace++ )
7892 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7893 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7894 curNodes[ind[1]] == curNodes[ind[3]] )
7897 break; // opposite nodes stick
7900 for ( iCur = 0; iCur < 4; iCur++ )
7902 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7903 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7905 if ( nodeSet.size() < 3 )
7906 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7908 quantities.push_back( nodeSet.size() );
7910 if ( quantities.size() >= 4 )
7913 nbUniqueNodes = poly_nodes.size();
7914 newElemDefs[0].SetPoly(true);
7918 } // case HEXAHEDRON
7923 } // switch ( entity )
7925 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7927 // erase from nodeNodeMap nodes whose merge spoils elem
7928 vector< const SMDS_MeshNode* > noMergeNodes;
7929 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7930 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7931 nodeNodeMap.erase( noMergeNodes[i] );
7934 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7936 uniqueNodes.resize( nbUniqueNodes );
7938 if ( !toRemove && nbResElems == 0 )
7941 newElemDefs.resize( nbResElems );
7947 // ========================================================
7948 // class : SortableElement
7949 // purpose : allow sorting elements basing on their nodes
7950 // ========================================================
7951 class SortableElement : public set <const SMDS_MeshElement*>
7955 SortableElement( const SMDS_MeshElement* theElem )
7958 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7959 while ( nodeIt->more() )
7960 this->insert( nodeIt->next() );
7963 const SMDS_MeshElement* Get() const
7967 mutable const SMDS_MeshElement* myElem;
7970 //=======================================================================
7971 //function : FindEqualElements
7972 //purpose : Return list of group of elements built on the same nodes.
7973 // Search among theElements or in the whole mesh if theElements is empty
7974 //=======================================================================
7976 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7977 TListOfListOfElementsID & theGroupsOfElementsID)
7979 myLastCreatedElems.Clear();
7980 myLastCreatedNodes.Clear();
7982 typedef map< SortableElement, int > TMapOfNodeSet;
7983 typedef list<int> TGroupOfElems;
7985 if ( theElements.empty() )
7986 { // get all elements in the mesh
7987 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7988 while ( eIt->more() )
7989 theElements.insert( theElements.end(), eIt->next() );
7992 vector< TGroupOfElems > arrayOfGroups;
7993 TGroupOfElems groupOfElems;
7994 TMapOfNodeSet mapOfNodeSet;
7996 TIDSortedElemSet::iterator elemIt = theElements.begin();
7997 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7999 const SMDS_MeshElement* curElem = *elemIt;
8000 SortableElement SE(curElem);
8002 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8003 if ( !pp.second ) { // one more coincident elem
8004 TMapOfNodeSet::iterator& itSE = pp.first;
8005 int ind = (*itSE).second;
8006 arrayOfGroups[ind].push_back( curElem->GetID() );
8009 arrayOfGroups.push_back( groupOfElems );
8010 arrayOfGroups.back().push_back( curElem->GetID() );
8015 groupOfElems.clear();
8016 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8017 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8019 if ( groupIt->size() > 1 ) {
8020 //groupOfElems.sort(); -- theElements is sorted already
8021 theGroupsOfElementsID.push_back( groupOfElems );
8022 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8027 //=======================================================================
8028 //function : MergeElements
8029 //purpose : In each given group, substitute all elements by the first one.
8030 //=======================================================================
8032 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8034 myLastCreatedElems.Clear();
8035 myLastCreatedNodes.Clear();
8037 typedef list<int> TListOfIDs;
8038 TListOfIDs rmElemIds; // IDs of elems to remove
8040 SMESHDS_Mesh* aMesh = GetMeshDS();
8042 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8043 while ( groupsIt != theGroupsOfElementsID.end() ) {
8044 TListOfIDs& aGroupOfElemID = *groupsIt;
8045 aGroupOfElemID.sort();
8046 int elemIDToKeep = aGroupOfElemID.front();
8047 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8048 aGroupOfElemID.pop_front();
8049 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8050 while ( idIt != aGroupOfElemID.end() ) {
8051 int elemIDToRemove = *idIt;
8052 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8053 // add the kept element in groups of removed one (PAL15188)
8054 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8055 rmElemIds.push_back( elemIDToRemove );
8061 Remove( rmElemIds, false );
8064 //=======================================================================
8065 //function : MergeEqualElements
8066 //purpose : Remove all but one of elements built on the same nodes.
8067 //=======================================================================
8069 void SMESH_MeshEditor::MergeEqualElements()
8071 TIDSortedElemSet aMeshElements; /* empty input ==
8072 to merge equal elements in the whole mesh */
8073 TListOfListOfElementsID aGroupsOfElementsID;
8074 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8075 MergeElements(aGroupsOfElementsID);
8078 //=======================================================================
8079 //function : findAdjacentFace
8081 //=======================================================================
8083 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8084 const SMDS_MeshNode* n2,
8085 const SMDS_MeshElement* elem)
8087 TIDSortedElemSet elemSet, avoidSet;
8089 avoidSet.insert ( elem );
8090 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8093 //=======================================================================
8094 //function : findSegment
8095 //purpose : Return a mesh segment by two nodes one of which can be medium
8096 //=======================================================================
8098 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8099 const SMDS_MeshNode* n2)
8101 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8102 while ( it->more() )
8104 const SMDS_MeshElement* seg = it->next();
8105 if ( seg->GetNodeIndex( n2 ) >= 0 )
8111 //=======================================================================
8112 //function : FindFreeBorder
8114 //=======================================================================
8116 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8118 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8119 const SMDS_MeshNode* theSecondNode,
8120 const SMDS_MeshNode* theLastNode,
8121 list< const SMDS_MeshNode* > & theNodes,
8122 list< const SMDS_MeshElement* >& theFaces)
8124 if ( !theFirstNode || !theSecondNode )
8126 // find border face between theFirstNode and theSecondNode
8127 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8131 theFaces.push_back( curElem );
8132 theNodes.push_back( theFirstNode );
8133 theNodes.push_back( theSecondNode );
8135 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8136 TIDSortedElemSet foundElems;
8137 bool needTheLast = ( theLastNode != 0 );
8139 while ( nStart != theLastNode ) {
8140 if ( nStart == theFirstNode )
8141 return !needTheLast;
8143 // find all free border faces sharing form nStart
8145 list< const SMDS_MeshElement* > curElemList;
8146 list< const SMDS_MeshNode* > nStartList;
8147 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8148 while ( invElemIt->more() ) {
8149 const SMDS_MeshElement* e = invElemIt->next();
8150 if ( e == curElem || foundElems.insert( e ).second ) {
8152 int iNode = 0, nbNodes = e->NbNodes();
8153 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8155 if ( e->IsQuadratic() ) {
8156 const SMDS_VtkFace* F =
8157 dynamic_cast<const SMDS_VtkFace*>(e);
8158 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8159 // use special nodes iterator
8160 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8161 while( anIter->more() ) {
8162 nodes[ iNode++ ] = cast2Node(anIter->next());
8166 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8167 while ( nIt->more() )
8168 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8170 nodes[ iNode ] = nodes[ 0 ];
8172 for ( iNode = 0; iNode < nbNodes; iNode++ )
8173 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8174 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8175 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8177 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8178 curElemList.push_back( e );
8182 // analyse the found
8184 int nbNewBorders = curElemList.size();
8185 if ( nbNewBorders == 0 ) {
8186 // no free border furthermore
8187 return !needTheLast;
8189 else if ( nbNewBorders == 1 ) {
8190 // one more element found
8192 nStart = nStartList.front();
8193 curElem = curElemList.front();
8194 theFaces.push_back( curElem );
8195 theNodes.push_back( nStart );
8198 // several continuations found
8199 list< const SMDS_MeshElement* >::iterator curElemIt;
8200 list< const SMDS_MeshNode* >::iterator nStartIt;
8201 // check if one of them reached the last node
8202 if ( needTheLast ) {
8203 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8204 curElemIt!= curElemList.end();
8205 curElemIt++, nStartIt++ )
8206 if ( *nStartIt == theLastNode ) {
8207 theFaces.push_back( *curElemIt );
8208 theNodes.push_back( *nStartIt );
8212 // find the best free border by the continuations
8213 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8214 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8215 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8216 curElemIt!= curElemList.end();
8217 curElemIt++, nStartIt++ )
8219 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8220 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8221 // find one more free border
8222 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8226 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8227 // choice: clear a worse one
8228 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8229 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8230 contNodes[ iWorse ].clear();
8231 contFaces[ iWorse ].clear();
8234 if ( contNodes[0].empty() && contNodes[1].empty() )
8237 // append the best free border
8238 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8239 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8240 theNodes.pop_back(); // remove nIgnore
8241 theNodes.pop_back(); // remove nStart
8242 theFaces.pop_back(); // remove curElem
8243 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8244 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8245 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8246 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8249 } // several continuations found
8250 } // while ( nStart != theLastNode )
8255 //=======================================================================
8256 //function : CheckFreeBorderNodes
8257 //purpose : Return true if the tree nodes are on a free border
8258 //=======================================================================
8260 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8261 const SMDS_MeshNode* theNode2,
8262 const SMDS_MeshNode* theNode3)
8264 list< const SMDS_MeshNode* > nodes;
8265 list< const SMDS_MeshElement* > faces;
8266 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8269 //=======================================================================
8270 //function : SewFreeBorder
8272 //warning : for border-to-side sewing theSideSecondNode is considered as
8273 // the last side node and theSideThirdNode is not used
8274 //=======================================================================
8276 SMESH_MeshEditor::Sew_Error
8277 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8278 const SMDS_MeshNode* theBordSecondNode,
8279 const SMDS_MeshNode* theBordLastNode,
8280 const SMDS_MeshNode* theSideFirstNode,
8281 const SMDS_MeshNode* theSideSecondNode,
8282 const SMDS_MeshNode* theSideThirdNode,
8283 const bool theSideIsFreeBorder,
8284 const bool toCreatePolygons,
8285 const bool toCreatePolyedrs)
8287 myLastCreatedElems.Clear();
8288 myLastCreatedNodes.Clear();
8290 Sew_Error aResult = SEW_OK;
8292 // ====================================
8293 // find side nodes and elements
8294 // ====================================
8296 list< const SMDS_MeshNode* > nSide[ 2 ];
8297 list< const SMDS_MeshElement* > eSide[ 2 ];
8298 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8299 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8303 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8304 nSide[0], eSide[0])) {
8305 MESSAGE(" Free Border 1 not found " );
8306 aResult = SEW_BORDER1_NOT_FOUND;
8308 if (theSideIsFreeBorder) {
8311 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8312 nSide[1], eSide[1])) {
8313 MESSAGE(" Free Border 2 not found " );
8314 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8317 if ( aResult != SEW_OK )
8320 if (!theSideIsFreeBorder) {
8324 // -------------------------------------------------------------------------
8326 // 1. If nodes to merge are not coincident, move nodes of the free border
8327 // from the coord sys defined by the direction from the first to last
8328 // nodes of the border to the correspondent sys of the side 2
8329 // 2. On the side 2, find the links most co-directed with the correspondent
8330 // links of the free border
8331 // -------------------------------------------------------------------------
8333 // 1. Since sewing may break if there are volumes to split on the side 2,
8334 // we won't move nodes but just compute new coordinates for them
8335 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8336 TNodeXYZMap nBordXYZ;
8337 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8338 list< const SMDS_MeshNode* >::iterator nBordIt;
8340 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8341 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8342 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8343 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8344 double tol2 = 1.e-8;
8345 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8346 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8347 // Need node movement.
8349 // find X and Z axes to create trsf
8350 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8352 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8354 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8357 gp_Ax3 toBordAx( Pb1, Zb, X );
8358 gp_Ax3 fromSideAx( Ps1, Zs, X );
8359 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8361 gp_Trsf toBordSys, fromSide2Sys;
8362 toBordSys.SetTransformation( toBordAx );
8363 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8364 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8367 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8368 const SMDS_MeshNode* n = *nBordIt;
8369 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8370 toBordSys.Transforms( xyz );
8371 fromSide2Sys.Transforms( xyz );
8372 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8376 // just insert nodes XYZ in the nBordXYZ map
8377 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8378 const SMDS_MeshNode* n = *nBordIt;
8379 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8383 // 2. On the side 2, find the links most co-directed with the correspondent
8384 // links of the free border
8386 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8387 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8388 sideNodes.push_back( theSideFirstNode );
8390 bool hasVolumes = false;
8391 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8392 set<long> foundSideLinkIDs, checkedLinkIDs;
8393 SMDS_VolumeTool volume;
8394 //const SMDS_MeshNode* faceNodes[ 4 ];
8396 const SMDS_MeshNode* sideNode;
8397 const SMDS_MeshElement* sideElem = 0;
8398 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8399 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8400 nBordIt = bordNodes.begin();
8402 // border node position and border link direction to compare with
8403 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8404 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8405 // choose next side node by link direction or by closeness to
8406 // the current border node:
8407 bool searchByDir = ( *nBordIt != theBordLastNode );
8409 // find the next node on the Side 2
8411 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8413 checkedLinkIDs.clear();
8414 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8416 // loop on inverse elements of current node (prevSideNode) on the Side 2
8417 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8418 while ( invElemIt->more() )
8420 const SMDS_MeshElement* elem = invElemIt->next();
8421 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8422 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8423 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8424 bool isVolume = volume.Set( elem );
8425 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8426 if ( isVolume ) // --volume
8428 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8429 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8430 if(elem->IsQuadratic()) {
8431 const SMDS_VtkFace* F =
8432 dynamic_cast<const SMDS_VtkFace*>(elem);
8433 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8434 // use special nodes iterator
8435 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8436 while( anIter->more() ) {
8437 nodes[ iNode ] = cast2Node(anIter->next());
8438 if ( nodes[ iNode++ ] == prevSideNode )
8439 iPrevNode = iNode - 1;
8443 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8444 while ( nIt->more() ) {
8445 nodes[ iNode ] = cast2Node( nIt->next() );
8446 if ( nodes[ iNode++ ] == prevSideNode )
8447 iPrevNode = iNode - 1;
8450 // there are 2 links to check
8455 // loop on links, to be precise, on the second node of links
8456 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8457 const SMDS_MeshNode* n = nodes[ iNode ];
8459 if ( !volume.IsLinked( n, prevSideNode ))
8463 if ( iNode ) // a node before prevSideNode
8464 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8465 else // a node after prevSideNode
8466 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8468 // check if this link was already used
8469 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8470 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8471 if (!isJustChecked &&
8472 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8474 // test a link geometrically
8475 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8476 bool linkIsBetter = false;
8477 double dot = 0.0, dist = 0.0;
8478 if ( searchByDir ) { // choose most co-directed link
8479 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8480 linkIsBetter = ( dot > maxDot );
8482 else { // choose link with the node closest to bordPos
8483 dist = ( nextXYZ - bordPos ).SquareModulus();
8484 linkIsBetter = ( dist < minDist );
8486 if ( linkIsBetter ) {
8495 } // loop on inverse elements of prevSideNode
8498 MESSAGE(" Can't find path by links of the Side 2 ");
8499 return SEW_BAD_SIDE_NODES;
8501 sideNodes.push_back( sideNode );
8502 sideElems.push_back( sideElem );
8503 foundSideLinkIDs.insert ( linkID );
8504 prevSideNode = sideNode;
8506 if ( *nBordIt == theBordLastNode )
8507 searchByDir = false;
8509 // find the next border link to compare with
8510 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8511 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8512 // move to next border node if sideNode is before forward border node (bordPos)
8513 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8514 prevBordNode = *nBordIt;
8516 bordPos = nBordXYZ[ *nBordIt ];
8517 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8518 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8522 while ( sideNode != theSideSecondNode );
8524 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8525 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8526 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8528 } // end nodes search on the side 2
8530 // ============================
8531 // sew the border to the side 2
8532 // ============================
8534 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8535 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8537 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8538 if ( toMergeConformal && toCreatePolygons )
8540 // do not merge quadrangles if polygons are OK (IPAL0052824)
8541 eIt[0] = eSide[0].begin();
8542 eIt[1] = eSide[1].begin();
8543 bool allQuads[2] = { true, true };
8544 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8545 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8546 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8548 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8551 TListOfListOfNodes nodeGroupsToMerge;
8552 if (( toMergeConformal ) ||
8553 ( theSideIsFreeBorder && !theSideThirdNode )) {
8555 // all nodes are to be merged
8557 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8558 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8559 nIt[0]++, nIt[1]++ )
8561 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8562 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8563 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8568 // insert new nodes into the border and the side to get equal nb of segments
8570 // get normalized parameters of nodes on the borders
8571 vector< double > param[ 2 ];
8572 param[0].resize( maxNbNodes );
8573 param[1].resize( maxNbNodes );
8575 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8576 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8577 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8578 const SMDS_MeshNode* nPrev = *nIt;
8579 double bordLength = 0;
8580 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8581 const SMDS_MeshNode* nCur = *nIt;
8582 gp_XYZ segment (nCur->X() - nPrev->X(),
8583 nCur->Y() - nPrev->Y(),
8584 nCur->Z() - nPrev->Z());
8585 double segmentLen = segment.Modulus();
8586 bordLength += segmentLen;
8587 param[ iBord ][ iNode ] = bordLength;
8590 // normalize within [0,1]
8591 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8592 param[ iBord ][ iNode ] /= bordLength;
8596 // loop on border segments
8597 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8598 int i[ 2 ] = { 0, 0 };
8599 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8600 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8602 TElemOfNodeListMap insertMap;
8603 TElemOfNodeListMap::iterator insertMapIt;
8605 // key: elem to insert nodes into
8606 // value: 2 nodes to insert between + nodes to be inserted
8608 bool next[ 2 ] = { false, false };
8610 // find min adjacent segment length after sewing
8611 double nextParam = 10., prevParam = 0;
8612 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8613 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8614 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8615 if ( i[ iBord ] > 0 )
8616 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8618 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8619 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8620 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8622 // choose to insert or to merge nodes
8623 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8624 if ( Abs( du ) <= minSegLen * 0.2 ) {
8627 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8628 const SMDS_MeshNode* n0 = *nIt[0];
8629 const SMDS_MeshNode* n1 = *nIt[1];
8630 nodeGroupsToMerge.back().push_back( n1 );
8631 nodeGroupsToMerge.back().push_back( n0 );
8632 // position of node of the border changes due to merge
8633 param[ 0 ][ i[0] ] += du;
8634 // move n1 for the sake of elem shape evaluation during insertion.
8635 // n1 will be removed by MergeNodes() anyway
8636 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8637 next[0] = next[1] = true;
8642 int intoBord = ( du < 0 ) ? 0 : 1;
8643 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8644 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8645 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8646 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8647 if ( intoBord == 1 ) {
8648 // move node of the border to be on a link of elem of the side
8649 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8650 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8651 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8652 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8653 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8655 insertMapIt = insertMap.find( elem );
8656 bool notFound = ( insertMapIt == insertMap.end() );
8657 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8659 // insert into another link of the same element:
8660 // 1. perform insertion into the other link of the elem
8661 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8662 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8663 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8664 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8665 // 2. perform insertion into the link of adjacent faces
8666 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8667 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8669 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8670 InsertNodesIntoLink( seg, n12, n22, nodeList );
8672 if (toCreatePolyedrs) {
8673 // perform insertion into the links of adjacent volumes
8674 UpdateVolumes(n12, n22, nodeList);
8676 // 3. find an element appeared on n1 and n2 after the insertion
8677 insertMap.erase( elem );
8678 elem = findAdjacentFace( n1, n2, 0 );
8680 if ( notFound || otherLink ) {
8681 // add element and nodes of the side into the insertMap
8682 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8683 (*insertMapIt).second.push_back( n1 );
8684 (*insertMapIt).second.push_back( n2 );
8686 // add node to be inserted into elem
8687 (*insertMapIt).second.push_back( nIns );
8688 next[ 1 - intoBord ] = true;
8691 // go to the next segment
8692 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8693 if ( next[ iBord ] ) {
8694 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8696 nPrev[ iBord ] = *nIt[ iBord ];
8697 nIt[ iBord ]++; i[ iBord ]++;
8701 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8703 // perform insertion of nodes into elements
8705 for (insertMapIt = insertMap.begin();
8706 insertMapIt != insertMap.end();
8709 const SMDS_MeshElement* elem = (*insertMapIt).first;
8710 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8711 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8712 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8714 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8716 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8717 InsertNodesIntoLink( seg, n1, n2, nodeList );
8720 if ( !theSideIsFreeBorder ) {
8721 // look for and insert nodes into the faces adjacent to elem
8722 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8723 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8726 if (toCreatePolyedrs) {
8727 // perform insertion into the links of adjacent volumes
8728 UpdateVolumes(n1, n2, nodeList);
8731 } // end: insert new nodes
8733 MergeNodes ( nodeGroupsToMerge );
8736 // Remove coincident segments
8739 TIDSortedElemSet segments;
8740 SMESH_SequenceOfElemPtr newFaces;
8741 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8743 if ( !myLastCreatedElems(i) ) continue;
8744 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8745 segments.insert( segments.end(), myLastCreatedElems(i) );
8747 newFaces.Append( myLastCreatedElems(i) );
8749 // get segments adjacent to merged nodes
8750 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8751 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8753 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8754 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8755 while ( segIt->more() )
8756 segments.insert( segIt->next() );
8760 TListOfListOfElementsID equalGroups;
8761 if ( !segments.empty() )
8762 FindEqualElements( segments, equalGroups );
8763 if ( !equalGroups.empty() )
8765 // remove from segments those that will be removed
8766 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8767 for ( ; itGroups != equalGroups.end(); ++itGroups )
8769 list< int >& group = *itGroups;
8770 list< int >::iterator id = group.begin();
8771 for ( ++id; id != group.end(); ++id )
8772 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8773 segments.erase( seg );
8775 // remove equal segments
8776 MergeElements( equalGroups );
8778 // restore myLastCreatedElems
8779 myLastCreatedElems = newFaces;
8780 TIDSortedElemSet::iterator seg = segments.begin();
8781 for ( ; seg != segments.end(); ++seg )
8782 myLastCreatedElems.Append( *seg );
8788 //=======================================================================
8789 //function : InsertNodesIntoLink
8790 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8791 // and theBetweenNode2 and split theElement
8792 //=======================================================================
8794 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8795 const SMDS_MeshNode* theBetweenNode1,
8796 const SMDS_MeshNode* theBetweenNode2,
8797 list<const SMDS_MeshNode*>& theNodesToInsert,
8798 const bool toCreatePoly)
8800 if ( !theElement ) return;
8802 SMESHDS_Mesh *aMesh = GetMeshDS();
8803 vector<const SMDS_MeshElement*> newElems;
8805 if ( theElement->GetType() == SMDSAbs_Edge )
8807 theNodesToInsert.push_front( theBetweenNode1 );
8808 theNodesToInsert.push_back ( theBetweenNode2 );
8809 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8810 const SMDS_MeshNode* n1 = *n;
8811 for ( ++n; n != theNodesToInsert.end(); ++n )
8813 const SMDS_MeshNode* n2 = *n;
8814 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8815 AddToSameGroups( seg, theElement, aMesh );
8817 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8820 theNodesToInsert.pop_front();
8821 theNodesToInsert.pop_back();
8823 if ( theElement->IsQuadratic() ) // add a not split part
8825 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8826 theElement->end_nodes() );
8827 int iOther = 0, nbN = nodes.size();
8828 for ( ; iOther < nbN; ++iOther )
8829 if ( nodes[iOther] != theBetweenNode1 &&
8830 nodes[iOther] != theBetweenNode2 )
8834 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8835 AddToSameGroups( seg, theElement, aMesh );
8837 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8839 else if ( iOther == 2 )
8841 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8842 AddToSameGroups( seg, theElement, aMesh );
8844 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8847 // treat new elements
8848 for ( size_t i = 0; i < newElems.size(); ++i )
8851 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8852 myLastCreatedElems.Append( newElems[i] );
8854 ReplaceElemInGroups( theElement, newElems, aMesh );
8855 aMesh->RemoveElement( theElement );
8858 } // if ( theElement->GetType() == SMDSAbs_Edge )
8860 const SMDS_MeshElement* theFace = theElement;
8861 if ( theFace->GetType() != SMDSAbs_Face ) return;
8863 // find indices of 2 link nodes and of the rest nodes
8864 int iNode = 0, il1, il2, i3, i4;
8865 il1 = il2 = i3 = i4 = -1;
8866 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8868 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8869 while ( nodeIt->more() ) {
8870 const SMDS_MeshNode* n = nodeIt->next();
8871 if ( n == theBetweenNode1 )
8873 else if ( n == theBetweenNode2 )
8879 nodes[ iNode++ ] = n;
8881 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8884 // arrange link nodes to go one after another regarding the face orientation
8885 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8886 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8891 aNodesToInsert.reverse();
8893 // check that not link nodes of a quadrangles are in good order
8894 int nbFaceNodes = theFace->NbNodes();
8895 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8901 if (toCreatePoly || theFace->IsPoly()) {
8904 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8906 // add nodes of face up to first node of link
8909 if ( theFace->IsQuadratic() ) {
8910 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8911 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8912 // use special nodes iterator
8913 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8914 while( anIter->more() && !isFLN ) {
8915 const SMDS_MeshNode* n = cast2Node(anIter->next());
8916 poly_nodes[iNode++] = n;
8917 if (n == nodes[il1]) {
8921 // add nodes to insert
8922 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8923 for (; nIt != aNodesToInsert.end(); nIt++) {
8924 poly_nodes[iNode++] = *nIt;
8926 // add nodes of face starting from last node of link
8927 while ( anIter->more() ) {
8928 poly_nodes[iNode++] = cast2Node(anIter->next());
8932 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8933 while ( nodeIt->more() && !isFLN ) {
8934 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8935 poly_nodes[iNode++] = n;
8936 if (n == nodes[il1]) {
8940 // add nodes to insert
8941 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8942 for (; nIt != aNodesToInsert.end(); nIt++) {
8943 poly_nodes[iNode++] = *nIt;
8945 // add nodes of face starting from last node of link
8946 while ( nodeIt->more() ) {
8947 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8948 poly_nodes[iNode++] = n;
8953 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8956 else if ( !theFace->IsQuadratic() )
8958 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8959 int nbLinkNodes = 2 + aNodesToInsert.size();
8960 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8961 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8962 linkNodes[ 0 ] = nodes[ il1 ];
8963 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8964 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8965 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8966 linkNodes[ iNode++ ] = *nIt;
8968 // decide how to split a quadrangle: compare possible variants
8969 // and choose which of splits to be a quadrangle
8970 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8971 if ( nbFaceNodes == 3 ) {
8972 iBestQuad = nbSplits;
8975 else if ( nbFaceNodes == 4 ) {
8976 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8977 double aBestRate = DBL_MAX;
8978 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8980 double aBadRate = 0;
8981 // evaluate elements quality
8982 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8983 if ( iSplit == iQuad ) {
8984 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8988 aBadRate += getBadRate( &quad, aCrit );
8991 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8993 nodes[ iSplit < iQuad ? i4 : i3 ]);
8994 aBadRate += getBadRate( &tria, aCrit );
8998 if ( aBadRate < aBestRate ) {
9000 aBestRate = aBadRate;
9005 // create new elements
9007 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9009 if ( iSplit == iBestQuad )
9010 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9015 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9017 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9020 const SMDS_MeshNode* newNodes[ 4 ];
9021 newNodes[ 0 ] = linkNodes[ i1 ];
9022 newNodes[ 1 ] = linkNodes[ i2 ];
9023 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9024 newNodes[ 3 ] = nodes[ i4 ];
9025 if (iSplit == iBestQuad)
9026 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9028 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9030 } // end if(!theFace->IsQuadratic())
9032 else { // theFace is quadratic
9033 // we have to split theFace on simple triangles and one simple quadrangle
9035 int nbshift = tmp*2;
9036 // shift nodes in nodes[] by nbshift
9038 for(i=0; i<nbshift; i++) {
9039 const SMDS_MeshNode* n = nodes[0];
9040 for(j=0; j<nbFaceNodes-1; j++) {
9041 nodes[j] = nodes[j+1];
9043 nodes[nbFaceNodes-1] = n;
9045 il1 = il1 - nbshift;
9046 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9047 // n0 n1 n2 n0 n1 n2
9048 // +-----+-----+ +-----+-----+
9057 // create new elements
9059 if ( nbFaceNodes == 6 ) { // quadratic triangle
9060 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9061 if ( theFace->IsMediumNode(nodes[il1]) ) {
9062 // create quadrangle
9063 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9069 // create quadrangle
9070 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9076 else { // nbFaceNodes==8 - quadratic quadrangle
9077 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9078 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9079 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9080 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9081 // create quadrangle
9082 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9088 // create quadrangle
9089 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9095 // create needed triangles using n1,n2,n3 and inserted nodes
9096 int nbn = 2 + aNodesToInsert.size();
9097 vector<const SMDS_MeshNode*> aNodes(nbn);
9098 aNodes[0 ] = nodes[n1];
9099 aNodes[nbn-1] = nodes[n2];
9100 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9101 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9102 aNodes[iNode++] = *nIt;
9104 for ( i = 1; i < nbn; i++ )
9105 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9108 // remove the old face
9109 for ( size_t i = 0; i < newElems.size(); ++i )
9112 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9113 myLastCreatedElems.Append( newElems[i] );
9115 ReplaceElemInGroups( theFace, newElems, aMesh );
9116 aMesh->RemoveElement(theFace);
9118 } // InsertNodesIntoLink()
9120 //=======================================================================
9121 //function : UpdateVolumes
9123 //=======================================================================
9125 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9126 const SMDS_MeshNode* theBetweenNode2,
9127 list<const SMDS_MeshNode*>& theNodesToInsert)
9129 myLastCreatedElems.Clear();
9130 myLastCreatedNodes.Clear();
9132 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9133 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9134 const SMDS_MeshElement* elem = invElemIt->next();
9136 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9137 SMDS_VolumeTool aVolume (elem);
9138 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9141 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9142 int iface, nbFaces = aVolume.NbFaces();
9143 vector<const SMDS_MeshNode *> poly_nodes;
9144 vector<int> quantities (nbFaces);
9146 for (iface = 0; iface < nbFaces; iface++) {
9147 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9148 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9149 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9151 for (int inode = 0; inode < nbFaceNodes; inode++) {
9152 poly_nodes.push_back(faceNodes[inode]);
9154 if (nbInserted == 0) {
9155 if (faceNodes[inode] == theBetweenNode1) {
9156 if (faceNodes[inode + 1] == theBetweenNode2) {
9157 nbInserted = theNodesToInsert.size();
9159 // add nodes to insert
9160 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9161 for (; nIt != theNodesToInsert.end(); nIt++) {
9162 poly_nodes.push_back(*nIt);
9166 else if (faceNodes[inode] == theBetweenNode2) {
9167 if (faceNodes[inode + 1] == theBetweenNode1) {
9168 nbInserted = theNodesToInsert.size();
9170 // add nodes to insert in reversed order
9171 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9173 for (; nIt != theNodesToInsert.begin(); nIt--) {
9174 poly_nodes.push_back(*nIt);
9176 poly_nodes.push_back(*nIt);
9183 quantities[iface] = nbFaceNodes + nbInserted;
9186 // Replace the volume
9187 SMESHDS_Mesh *aMesh = GetMeshDS();
9189 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9191 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9192 myLastCreatedElems.Append( newElem );
9193 ReplaceElemInGroups( elem, newElem, aMesh );
9195 aMesh->RemoveElement( elem );
9201 //================================================================================
9203 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9205 //================================================================================
9207 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9208 vector<const SMDS_MeshNode *> & nodes,
9209 vector<int> & nbNodeInFaces )
9212 nbNodeInFaces.clear();
9213 SMDS_VolumeTool vTool ( elem );
9214 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9216 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9217 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9218 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9223 //=======================================================================
9225 * \brief Convert elements contained in a sub-mesh to quadratic
9226 * \return int - nb of checked elements
9228 //=======================================================================
9230 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9231 SMESH_MesherHelper& theHelper,
9232 const bool theForce3d)
9234 //MESSAGE("convertElemToQuadratic");
9236 if( !theSm ) return nbElem;
9238 vector<int> nbNodeInFaces;
9239 vector<const SMDS_MeshNode *> nodes;
9240 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9241 while(ElemItr->more())
9244 const SMDS_MeshElement* elem = ElemItr->next();
9245 if( !elem ) continue;
9247 // analyse a necessity of conversion
9248 const SMDSAbs_ElementType aType = elem->GetType();
9249 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9251 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9252 bool hasCentralNodes = false;
9253 if ( elem->IsQuadratic() )
9256 switch ( aGeomType ) {
9257 case SMDSEntity_Quad_Triangle:
9258 case SMDSEntity_Quad_Quadrangle:
9259 case SMDSEntity_Quad_Hexa:
9260 case SMDSEntity_Quad_Penta:
9261 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9263 case SMDSEntity_BiQuad_Triangle:
9264 case SMDSEntity_BiQuad_Quadrangle:
9265 case SMDSEntity_TriQuad_Hexa:
9266 case SMDSEntity_BiQuad_Penta:
9267 alreadyOK = theHelper.GetIsBiQuadratic();
9268 hasCentralNodes = true;
9273 // take into account already present medium nodes
9275 case SMDSAbs_Volume:
9276 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9278 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9280 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9286 // get elem data needed to re-create it
9288 const int id = elem->GetID();
9289 const int nbNodes = elem->NbCornerNodes();
9290 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9291 if ( aGeomType == SMDSEntity_Polyhedra )
9292 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9293 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9294 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9296 // remove a linear element
9297 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9299 // remove central nodes of biquadratic elements (biquad->quad conversion)
9300 if ( hasCentralNodes )
9301 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9302 if ( nodes[i]->NbInverseElements() == 0 )
9303 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9305 const SMDS_MeshElement* NewElem = 0;
9311 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9319 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9322 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9325 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9329 case SMDSAbs_Volume :
9333 case SMDSEntity_Tetra:
9334 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9336 case SMDSEntity_Pyramid:
9337 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9339 case SMDSEntity_Penta:
9340 case SMDSEntity_Quad_Penta:
9341 case SMDSEntity_BiQuad_Penta:
9342 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9344 case SMDSEntity_Hexa:
9345 case SMDSEntity_Quad_Hexa:
9346 case SMDSEntity_TriQuad_Hexa:
9347 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9348 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9350 case SMDSEntity_Hexagonal_Prism:
9352 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9359 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9360 if( NewElem && NewElem->getshapeId() < 1 )
9361 theSm->AddElement( NewElem );
9365 //=======================================================================
9366 //function : ConvertToQuadratic
9368 //=======================================================================
9370 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9372 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9373 SMESHDS_Mesh* meshDS = GetMeshDS();
9375 SMESH_MesherHelper aHelper(*myMesh);
9377 aHelper.SetIsQuadratic( true );
9378 aHelper.SetIsBiQuadratic( theToBiQuad );
9379 aHelper.SetElementsOnShape(true);
9380 aHelper.ToFixNodeParameters( true );
9382 // convert elements assigned to sub-meshes
9383 int nbCheckedElems = 0;
9384 if ( myMesh->HasShapeToMesh() )
9386 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9388 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9389 while ( smIt->more() ) {
9390 SMESH_subMesh* sm = smIt->next();
9391 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9392 aHelper.SetSubShape( sm->GetSubShape() );
9393 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9399 // convert elements NOT assigned to sub-meshes
9400 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9401 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9403 aHelper.SetElementsOnShape(false);
9404 SMESHDS_SubMesh *smDS = 0;
9407 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9408 while( aEdgeItr->more() )
9410 const SMDS_MeshEdge* edge = aEdgeItr->next();
9411 if ( !edge->IsQuadratic() )
9413 int id = edge->GetID();
9414 const SMDS_MeshNode* n1 = edge->GetNode(0);
9415 const SMDS_MeshNode* n2 = edge->GetNode(1);
9417 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9419 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9420 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9424 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9429 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9430 while( aFaceItr->more() )
9432 const SMDS_MeshFace* face = aFaceItr->next();
9433 if ( !face ) continue;
9435 const SMDSAbs_EntityType type = face->GetEntityType();
9439 case SMDSEntity_Quad_Triangle:
9440 case SMDSEntity_Quad_Quadrangle:
9441 alreadyOK = !theToBiQuad;
9442 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9444 case SMDSEntity_BiQuad_Triangle:
9445 case SMDSEntity_BiQuad_Quadrangle:
9446 alreadyOK = theToBiQuad;
9447 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9449 default: alreadyOK = false;
9454 const int id = face->GetID();
9455 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9457 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9459 SMDS_MeshFace * NewFace = 0;
9462 case SMDSEntity_Triangle:
9463 case SMDSEntity_Quad_Triangle:
9464 case SMDSEntity_BiQuad_Triangle:
9465 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9466 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9467 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9470 case SMDSEntity_Quadrangle:
9471 case SMDSEntity_Quad_Quadrangle:
9472 case SMDSEntity_BiQuad_Quadrangle:
9473 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9474 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9475 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9479 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9481 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9485 vector<int> nbNodeInFaces;
9486 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9487 while(aVolumeItr->more())
9489 const SMDS_MeshVolume* volume = aVolumeItr->next();
9490 if ( !volume ) continue;
9492 const SMDSAbs_EntityType type = volume->GetEntityType();
9493 if ( volume->IsQuadratic() )
9498 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9499 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9500 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9501 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9502 default: alreadyOK = true;
9506 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9510 const int id = volume->GetID();
9511 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9512 if ( type == SMDSEntity_Polyhedra )
9513 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9514 else if ( type == SMDSEntity_Hexagonal_Prism )
9515 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9517 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9519 SMDS_MeshVolume * NewVolume = 0;
9522 case SMDSEntity_Tetra:
9523 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9525 case SMDSEntity_Hexa:
9526 case SMDSEntity_Quad_Hexa:
9527 case SMDSEntity_TriQuad_Hexa:
9528 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9529 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9530 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9531 if ( nodes[i]->NbInverseElements() == 0 )
9532 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9534 case SMDSEntity_Pyramid:
9535 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9536 nodes[3], nodes[4], id, theForce3d);
9538 case SMDSEntity_Penta:
9539 case SMDSEntity_Quad_Penta:
9540 case SMDSEntity_BiQuad_Penta:
9541 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9542 nodes[3], nodes[4], nodes[5], id, theForce3d);
9543 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9544 if ( nodes[i]->NbInverseElements() == 0 )
9545 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9547 case SMDSEntity_Hexagonal_Prism:
9549 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9551 ReplaceElemInGroups(volume, NewVolume, meshDS);
9556 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9557 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9558 // aHelper.FixQuadraticElements(myError);
9559 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9563 //================================================================================
9565 * \brief Makes given elements quadratic
9566 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9567 * \param theElements - elements to make quadratic
9569 //================================================================================
9571 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9572 TIDSortedElemSet& theElements,
9573 const bool theToBiQuad)
9575 if ( theElements.empty() ) return;
9577 // we believe that all theElements are of the same type
9578 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9580 // get all nodes shared by theElements
9581 TIDSortedNodeSet allNodes;
9582 TIDSortedElemSet::iterator eIt = theElements.begin();
9583 for ( ; eIt != theElements.end(); ++eIt )
9584 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9586 // complete theElements with elements of lower dim whose all nodes are in allNodes
9588 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9589 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9590 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9591 for ( ; nIt != allNodes.end(); ++nIt )
9593 const SMDS_MeshNode* n = *nIt;
9594 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9595 while ( invIt->more() )
9597 const SMDS_MeshElement* e = invIt->next();
9598 const SMDSAbs_ElementType type = e->GetType();
9599 if ( e->IsQuadratic() )
9601 quadAdjacentElems[ type ].insert( e );
9604 switch ( e->GetEntityType() ) {
9605 case SMDSEntity_Quad_Triangle:
9606 case SMDSEntity_Quad_Quadrangle:
9607 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9608 case SMDSEntity_BiQuad_Triangle:
9609 case SMDSEntity_BiQuad_Quadrangle:
9610 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9611 default: alreadyOK = true;
9616 if ( type >= elemType )
9617 continue; // same type or more complex linear element
9619 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9620 continue; // e is already checked
9624 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9625 while ( nodeIt->more() && allIn )
9626 allIn = allNodes.count( nodeIt->next() );
9628 theElements.insert(e );
9632 SMESH_MesherHelper helper(*myMesh);
9633 helper.SetIsQuadratic( true );
9634 helper.SetIsBiQuadratic( theToBiQuad );
9636 // add links of quadratic adjacent elements to the helper
9638 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9639 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9640 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9642 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9644 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9645 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9646 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9648 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9650 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9651 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9652 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9654 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9657 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9659 SMESHDS_Mesh* meshDS = GetMeshDS();
9660 SMESHDS_SubMesh* smDS = 0;
9661 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9663 const SMDS_MeshElement* elem = *eIt;
9666 int nbCentralNodes = 0;
9667 switch ( elem->GetEntityType() ) {
9668 // linear convertible
9669 case SMDSEntity_Edge:
9670 case SMDSEntity_Triangle:
9671 case SMDSEntity_Quadrangle:
9672 case SMDSEntity_Tetra:
9673 case SMDSEntity_Pyramid:
9674 case SMDSEntity_Hexa:
9675 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9676 // quadratic that can become bi-quadratic
9677 case SMDSEntity_Quad_Triangle:
9678 case SMDSEntity_Quad_Quadrangle:
9679 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9681 case SMDSEntity_BiQuad_Triangle:
9682 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9683 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9685 default: alreadyOK = true;
9687 if ( alreadyOK ) continue;
9689 const SMDSAbs_ElementType type = elem->GetType();
9690 const int id = elem->GetID();
9691 const int nbNodes = elem->NbCornerNodes();
9692 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9694 helper.SetSubShape( elem->getshapeId() );
9696 if ( !smDS || !smDS->Contains( elem ))
9697 smDS = meshDS->MeshElements( elem->getshapeId() );
9698 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9700 SMDS_MeshElement * newElem = 0;
9703 case 4: // cases for most frequently used element types go first (for optimization)
9704 if ( type == SMDSAbs_Volume )
9705 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9707 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9710 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9711 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9714 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9717 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9720 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9721 nodes[4], id, theForce3d);
9724 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9725 nodes[4], nodes[5], id, theForce3d);
9729 ReplaceElemInGroups( elem, newElem, meshDS);
9730 if( newElem && smDS )
9731 smDS->AddElement( newElem );
9733 // remove central nodes
9734 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9735 if ( nodes[i]->NbInverseElements() == 0 )
9736 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9738 } // loop on theElements
9741 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9742 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9743 // helper.FixQuadraticElements( myError );
9744 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9748 //=======================================================================
9750 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9751 * \return int - nb of checked elements
9753 //=======================================================================
9755 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9756 SMDS_ElemIteratorPtr theItr,
9757 const int theShapeID)
9760 SMESHDS_Mesh* meshDS = GetMeshDS();
9761 ElemFeatures elemType;
9762 vector<const SMDS_MeshNode *> nodes;
9764 while( theItr->more() )
9766 const SMDS_MeshElement* elem = theItr->next();
9768 if( elem && elem->IsQuadratic())
9771 int nbCornerNodes = elem->NbCornerNodes();
9772 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9774 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9776 //remove a quadratic element
9777 if ( !theSm || !theSm->Contains( elem ))
9778 theSm = meshDS->MeshElements( elem->getshapeId() );
9779 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9781 // remove medium nodes
9782 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9783 if ( nodes[i]->NbInverseElements() == 0 )
9784 meshDS->RemoveFreeNode( nodes[i], theSm );
9786 // add a linear element
9787 nodes.resize( nbCornerNodes );
9788 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9789 ReplaceElemInGroups(elem, newElem, meshDS);
9790 if( theSm && newElem )
9791 theSm->AddElement( newElem );
9797 //=======================================================================
9798 //function : ConvertFromQuadratic
9800 //=======================================================================
9802 bool SMESH_MeshEditor::ConvertFromQuadratic()
9804 int nbCheckedElems = 0;
9805 if ( myMesh->HasShapeToMesh() )
9807 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9809 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9810 while ( smIt->more() ) {
9811 SMESH_subMesh* sm = smIt->next();
9812 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9813 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9819 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9820 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9822 SMESHDS_SubMesh *aSM = 0;
9823 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9831 //================================================================================
9833 * \brief Return true if all medium nodes of the element are in the node set
9835 //================================================================================
9837 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9839 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9840 if ( !nodeSet.count( elem->GetNode(i) ))
9846 //================================================================================
9848 * \brief Makes given elements linear
9850 //================================================================================
9852 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9854 if ( theElements.empty() ) return;
9856 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9857 set<int> mediumNodeIDs;
9858 TIDSortedElemSet::iterator eIt = theElements.begin();
9859 for ( ; eIt != theElements.end(); ++eIt )
9861 const SMDS_MeshElement* e = *eIt;
9862 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9863 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9866 // replace given elements by linear ones
9867 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9868 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9870 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9871 // except those elements sharing medium nodes of quadratic element whose medium nodes
9872 // are not all in mediumNodeIDs
9874 // get remaining medium nodes
9875 TIDSortedNodeSet mediumNodes;
9876 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9877 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9878 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9879 mediumNodes.insert( mediumNodes.end(), n );
9881 // find more quadratic elements to convert
9882 TIDSortedElemSet moreElemsToConvert;
9883 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9884 for ( ; nIt != mediumNodes.end(); ++nIt )
9886 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9887 while ( invIt->more() )
9889 const SMDS_MeshElement* e = invIt->next();
9890 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9892 // find a more complex element including e and
9893 // whose medium nodes are not in mediumNodes
9894 bool complexFound = false;
9895 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9897 SMDS_ElemIteratorPtr invIt2 =
9898 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9899 while ( invIt2->more() )
9901 const SMDS_MeshElement* eComplex = invIt2->next();
9902 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9904 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9905 if ( nbCommonNodes == e->NbNodes())
9907 complexFound = true;
9908 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9914 if ( !complexFound )
9915 moreElemsToConvert.insert( e );
9919 elemIt = elemSetIterator( moreElemsToConvert );
9920 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9923 //=======================================================================
9924 //function : SewSideElements
9926 //=======================================================================
9928 SMESH_MeshEditor::Sew_Error
9929 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9930 TIDSortedElemSet& theSide2,
9931 const SMDS_MeshNode* theFirstNode1,
9932 const SMDS_MeshNode* theFirstNode2,
9933 const SMDS_MeshNode* theSecondNode1,
9934 const SMDS_MeshNode* theSecondNode2)
9936 myLastCreatedElems.Clear();
9937 myLastCreatedNodes.Clear();
9939 if ( theSide1.size() != theSide2.size() )
9940 return SEW_DIFF_NB_OF_ELEMENTS;
9942 Sew_Error aResult = SEW_OK;
9944 // 1. Build set of faces representing each side
9945 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9946 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9948 // =======================================================================
9949 // 1. Build set of faces representing each side:
9950 // =======================================================================
9951 // a. build set of nodes belonging to faces
9952 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9953 // c. create temporary faces representing side of volumes if correspondent
9954 // face does not exist
9956 SMESHDS_Mesh* aMesh = GetMeshDS();
9957 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9958 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9959 TIDSortedElemSet faceSet1, faceSet2;
9960 set<const SMDS_MeshElement*> volSet1, volSet2;
9961 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9962 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9963 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9964 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9965 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9966 int iSide, iFace, iNode;
9968 list<const SMDS_MeshElement* > tempFaceList;
9969 for ( iSide = 0; iSide < 2; iSide++ ) {
9970 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9971 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9972 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9973 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9974 set<const SMDS_MeshElement*>::iterator vIt;
9975 TIDSortedElemSet::iterator eIt;
9976 set<const SMDS_MeshNode*>::iterator nIt;
9978 // check that given nodes belong to given elements
9979 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9980 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9981 int firstIndex = -1, secondIndex = -1;
9982 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9983 const SMDS_MeshElement* elem = *eIt;
9984 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9985 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9986 if ( firstIndex > -1 && secondIndex > -1 ) break;
9988 if ( firstIndex < 0 || secondIndex < 0 ) {
9989 // we can simply return until temporary faces created
9990 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9993 // -----------------------------------------------------------
9994 // 1a. Collect nodes of existing faces
9995 // and build set of face nodes in order to detect missing
9996 // faces corresponding to sides of volumes
9997 // -----------------------------------------------------------
9999 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10001 // loop on the given element of a side
10002 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10003 //const SMDS_MeshElement* elem = *eIt;
10004 const SMDS_MeshElement* elem = *eIt;
10005 if ( elem->GetType() == SMDSAbs_Face ) {
10006 faceSet->insert( elem );
10007 set <const SMDS_MeshNode*> faceNodeSet;
10008 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10009 while ( nodeIt->more() ) {
10010 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10011 nodeSet->insert( n );
10012 faceNodeSet.insert( n );
10014 setOfFaceNodeSet.insert( faceNodeSet );
10016 else if ( elem->GetType() == SMDSAbs_Volume )
10017 volSet->insert( elem );
10019 // ------------------------------------------------------------------------------
10020 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10021 // ------------------------------------------------------------------------------
10023 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10024 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10025 while ( fIt->more() ) { // loop on faces sharing a node
10026 const SMDS_MeshElement* f = fIt->next();
10027 if ( faceSet->find( f ) == faceSet->end() ) {
10028 // check if all nodes are in nodeSet and
10029 // complete setOfFaceNodeSet if they are
10030 set <const SMDS_MeshNode*> faceNodeSet;
10031 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10032 bool allInSet = true;
10033 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10034 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10035 if ( nodeSet->find( n ) == nodeSet->end() )
10038 faceNodeSet.insert( n );
10041 faceSet->insert( f );
10042 setOfFaceNodeSet.insert( faceNodeSet );
10048 // -------------------------------------------------------------------------
10049 // 1c. Create temporary faces representing sides of volumes if correspondent
10050 // face does not exist
10051 // -------------------------------------------------------------------------
10053 if ( !volSet->empty() ) {
10054 //int nodeSetSize = nodeSet->size();
10056 // loop on given volumes
10057 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10058 SMDS_VolumeTool vol (*vIt);
10059 // loop on volume faces: find free faces
10060 // --------------------------------------
10061 list<const SMDS_MeshElement* > freeFaceList;
10062 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10063 if ( !vol.IsFreeFace( iFace ))
10065 // check if there is already a face with same nodes in a face set
10066 const SMDS_MeshElement* aFreeFace = 0;
10067 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10068 int nbNodes = vol.NbFaceNodes( iFace );
10069 set <const SMDS_MeshNode*> faceNodeSet;
10070 vol.GetFaceNodes( iFace, faceNodeSet );
10071 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10073 // no such a face is given but it still can exist, check it
10074 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10075 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10077 if ( !aFreeFace ) {
10078 // create a temporary face
10079 if ( nbNodes == 3 ) {
10080 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10081 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10083 else if ( nbNodes == 4 ) {
10084 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10085 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10088 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10089 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10090 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10093 tempFaceList.push_back( aFreeFace );
10097 freeFaceList.push_back( aFreeFace );
10099 } // loop on faces of a volume
10101 // choose one of several free faces of a volume
10102 // --------------------------------------------
10103 if ( freeFaceList.size() > 1 ) {
10104 // choose a face having max nb of nodes shared by other elems of a side
10105 int maxNbNodes = -1;
10106 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10107 while ( fIt != freeFaceList.end() ) { // loop on free faces
10108 int nbSharedNodes = 0;
10109 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10110 while ( nodeIt->more() ) { // loop on free face nodes
10111 const SMDS_MeshNode* n =
10112 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10113 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10114 while ( invElemIt->more() ) {
10115 const SMDS_MeshElement* e = invElemIt->next();
10116 nbSharedNodes += faceSet->count( e );
10117 nbSharedNodes += elemSet->count( e );
10120 if ( nbSharedNodes > maxNbNodes ) {
10121 maxNbNodes = nbSharedNodes;
10122 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10124 else if ( nbSharedNodes == maxNbNodes ) {
10128 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10131 if ( freeFaceList.size() > 1 )
10133 // could not choose one face, use another way
10134 // choose a face most close to the bary center of the opposite side
10135 gp_XYZ aBC( 0., 0., 0. );
10136 set <const SMDS_MeshNode*> addedNodes;
10137 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10138 eIt = elemSet2->begin();
10139 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10140 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10141 while ( nodeIt->more() ) { // loop on free face nodes
10142 const SMDS_MeshNode* n =
10143 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10144 if ( addedNodes.insert( n ).second )
10145 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10148 aBC /= addedNodes.size();
10149 double minDist = DBL_MAX;
10150 fIt = freeFaceList.begin();
10151 while ( fIt != freeFaceList.end() ) { // loop on free faces
10153 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10154 while ( nodeIt->more() ) { // loop on free face nodes
10155 const SMDS_MeshNode* n =
10156 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10157 gp_XYZ p( n->X(),n->Y(),n->Z() );
10158 dist += ( aBC - p ).SquareModulus();
10160 if ( dist < minDist ) {
10162 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10165 fIt = freeFaceList.erase( fIt++ );
10168 } // choose one of several free faces of a volume
10170 if ( freeFaceList.size() == 1 ) {
10171 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10172 faceSet->insert( aFreeFace );
10173 // complete a node set with nodes of a found free face
10174 // for ( iNode = 0; iNode < ; iNode++ )
10175 // nodeSet->insert( fNodes[ iNode ] );
10178 } // loop on volumes of a side
10180 // // complete a set of faces if new nodes in a nodeSet appeared
10181 // // ----------------------------------------------------------
10182 // if ( nodeSetSize != nodeSet->size() ) {
10183 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10184 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10185 // while ( fIt->more() ) { // loop on faces sharing a node
10186 // const SMDS_MeshElement* f = fIt->next();
10187 // if ( faceSet->find( f ) == faceSet->end() ) {
10188 // // check if all nodes are in nodeSet and
10189 // // complete setOfFaceNodeSet if they are
10190 // set <const SMDS_MeshNode*> faceNodeSet;
10191 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10192 // bool allInSet = true;
10193 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10194 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10195 // if ( nodeSet->find( n ) == nodeSet->end() )
10196 // allInSet = false;
10198 // faceNodeSet.insert( n );
10200 // if ( allInSet ) {
10201 // faceSet->insert( f );
10202 // setOfFaceNodeSet.insert( faceNodeSet );
10208 } // Create temporary faces, if there are volumes given
10211 if ( faceSet1.size() != faceSet2.size() ) {
10212 // delete temporary faces: they are in reverseElements of actual nodes
10213 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10214 // while ( tmpFaceIt->more() )
10215 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10216 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10217 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10218 // aMesh->RemoveElement(*tmpFaceIt);
10219 MESSAGE("Diff nb of faces");
10220 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10223 // ============================================================
10224 // 2. Find nodes to merge:
10225 // bind a node to remove to a node to put instead
10226 // ============================================================
10228 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10229 if ( theFirstNode1 != theFirstNode2 )
10230 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10231 if ( theSecondNode1 != theSecondNode2 )
10232 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10234 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10235 set< long > linkIdSet; // links to process
10236 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10238 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10239 list< NLink > linkList[2];
10240 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10241 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10242 // loop on links in linkList; find faces by links and append links
10243 // of the found faces to linkList
10244 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10245 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10247 NLink link[] = { *linkIt[0], *linkIt[1] };
10248 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10249 if ( !linkIdSet.count( linkID ) )
10252 // by links, find faces in the face sets,
10253 // and find indices of link nodes in the found faces;
10254 // in a face set, there is only one or no face sharing a link
10255 // ---------------------------------------------------------------
10257 const SMDS_MeshElement* face[] = { 0, 0 };
10258 vector<const SMDS_MeshNode*> fnodes[2];
10259 int iLinkNode[2][2];
10260 TIDSortedElemSet avoidSet;
10261 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10262 const SMDS_MeshNode* n1 = link[iSide].first;
10263 const SMDS_MeshNode* n2 = link[iSide].second;
10264 //cout << "Side " << iSide << " ";
10265 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10266 // find a face by two link nodes
10267 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10268 *faceSetPtr[ iSide ], avoidSet,
10269 &iLinkNode[iSide][0],
10270 &iLinkNode[iSide][1] );
10271 if ( face[ iSide ])
10273 //cout << " F " << face[ iSide]->GetID() <<endl;
10274 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10275 // put face nodes to fnodes
10276 if ( face[ iSide ]->IsQuadratic() )
10278 // use interlaced nodes iterator
10279 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10280 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10281 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10282 while ( nIter->more() )
10283 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10287 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10288 face[ iSide ]->end_nodes() );
10290 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10294 // check similarity of elements of the sides
10295 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10296 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10297 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10298 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10301 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10303 break; // do not return because it's necessary to remove tmp faces
10306 // set nodes to merge
10307 // -------------------
10309 if ( face[0] && face[1] ) {
10310 const int nbNodes = face[0]->NbNodes();
10311 if ( nbNodes != face[1]->NbNodes() ) {
10312 MESSAGE("Diff nb of face nodes");
10313 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10314 break; // do not return because it s necessary to remove tmp faces
10316 bool reverse[] = { false, false }; // order of nodes in the link
10317 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10318 // analyse link orientation in faces
10319 int i1 = iLinkNode[ iSide ][ 0 ];
10320 int i2 = iLinkNode[ iSide ][ 1 ];
10321 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10323 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10324 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10325 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10327 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10328 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10331 // add other links of the faces to linkList
10332 // -----------------------------------------
10334 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10335 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10336 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10337 if ( !iter_isnew.second ) { // already in a set: no need to process
10338 linkIdSet.erase( iter_isnew.first );
10340 else // new in set == encountered for the first time: add
10342 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10343 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10344 linkList[0].push_back ( NLink( n1, n2 ));
10345 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10350 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10353 } // loop on link lists
10355 if ( aResult == SEW_OK &&
10356 ( //linkIt[0] != linkList[0].end() ||
10357 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10358 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10359 " " << (faceSetPtr[1]->empty()));
10360 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10363 // ====================================================================
10364 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10365 // ====================================================================
10367 // delete temporary faces
10368 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10369 // while ( tmpFaceIt->more() )
10370 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10371 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10372 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10373 aMesh->RemoveElement(*tmpFaceIt);
10375 if ( aResult != SEW_OK)
10378 list< int > nodeIDsToRemove;
10379 vector< const SMDS_MeshNode*> nodes;
10380 ElemFeatures elemType;
10382 // loop on nodes replacement map
10383 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10384 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10385 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10387 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10388 nodeIDsToRemove.push_back( nToRemove->GetID() );
10389 // loop on elements sharing nToRemove
10390 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10391 while ( invElemIt->more() ) {
10392 const SMDS_MeshElement* e = invElemIt->next();
10393 // get a new suite of nodes: make replacement
10394 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10395 nodes.resize( nbNodes );
10396 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10397 while ( nIt->more() ) {
10398 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10399 nnIt = nReplaceMap.find( n );
10400 if ( nnIt != nReplaceMap.end() ) {
10402 n = (*nnIt).second;
10406 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10407 // elemIDsToRemove.push_back( e->GetID() );
10411 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10412 aMesh->RemoveElement( e );
10414 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10416 AddToSameGroups( newElem, e, aMesh );
10417 if ( int aShapeId = e->getshapeId() )
10418 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10424 Remove( nodeIDsToRemove, true );
10429 //================================================================================
10431 * \brief Find corresponding nodes in two sets of faces
10432 * \param theSide1 - first face set
10433 * \param theSide2 - second first face
10434 * \param theFirstNode1 - a boundary node of set 1
10435 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10436 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10437 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10438 * \param nReplaceMap - output map of corresponding nodes
10439 * \return bool - is a success or not
10441 //================================================================================
10444 //#define DEBUG_MATCHING_NODES
10447 SMESH_MeshEditor::Sew_Error
10448 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10449 set<const SMDS_MeshElement*>& theSide2,
10450 const SMDS_MeshNode* theFirstNode1,
10451 const SMDS_MeshNode* theFirstNode2,
10452 const SMDS_MeshNode* theSecondNode1,
10453 const SMDS_MeshNode* theSecondNode2,
10454 TNodeNodeMap & nReplaceMap)
10456 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10458 nReplaceMap.clear();
10459 if ( theFirstNode1 != theFirstNode2 )
10460 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10461 if ( theSecondNode1 != theSecondNode2 )
10462 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10464 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10465 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10467 list< NLink > linkList[2];
10468 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10469 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10471 // loop on links in linkList; find faces by links and append links
10472 // of the found faces to linkList
10473 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10474 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10475 NLink link[] = { *linkIt[0], *linkIt[1] };
10476 if ( linkSet.find( link[0] ) == linkSet.end() )
10479 // by links, find faces in the face sets,
10480 // and find indices of link nodes in the found faces;
10481 // in a face set, there is only one or no face sharing a link
10482 // ---------------------------------------------------------------
10484 const SMDS_MeshElement* face[] = { 0, 0 };
10485 list<const SMDS_MeshNode*> notLinkNodes[2];
10486 //bool reverse[] = { false, false }; // order of notLinkNodes
10488 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10490 const SMDS_MeshNode* n1 = link[iSide].first;
10491 const SMDS_MeshNode* n2 = link[iSide].second;
10492 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10493 set< const SMDS_MeshElement* > facesOfNode1;
10494 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10496 // during a loop of the first node, we find all faces around n1,
10497 // during a loop of the second node, we find one face sharing both n1 and n2
10498 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10499 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10500 while ( fIt->more() ) { // loop on faces sharing a node
10501 const SMDS_MeshElement* f = fIt->next();
10502 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10503 ! facesOfNode1.insert( f ).second ) // f encounters twice
10505 if ( face[ iSide ] ) {
10506 MESSAGE( "2 faces per link " );
10507 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10510 faceSet->erase( f );
10512 // get not link nodes
10513 int nbN = f->NbNodes();
10514 if ( f->IsQuadratic() )
10516 nbNodes[ iSide ] = nbN;
10517 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10518 int i1 = f->GetNodeIndex( n1 );
10519 int i2 = f->GetNodeIndex( n2 );
10520 int iEnd = nbN, iBeg = -1, iDelta = 1;
10521 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10523 std::swap( iEnd, iBeg ); iDelta = -1;
10528 if ( i == iEnd ) i = iBeg + iDelta;
10529 if ( i == i1 ) break;
10530 nodes.push_back ( f->GetNode( i ) );
10536 // check similarity of elements of the sides
10537 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10538 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10539 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10540 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10543 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10547 // set nodes to merge
10548 // -------------------
10550 if ( face[0] && face[1] ) {
10551 if ( nbNodes[0] != nbNodes[1] ) {
10552 MESSAGE("Diff nb of face nodes");
10553 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10555 #ifdef DEBUG_MATCHING_NODES
10556 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10557 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10558 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10560 int nbN = nbNodes[0];
10562 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10563 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10564 for ( int i = 0 ; i < nbN - 2; ++i ) {
10565 #ifdef DEBUG_MATCHING_NODES
10566 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10568 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10572 // add other links of the face 1 to linkList
10573 // -----------------------------------------
10575 const SMDS_MeshElement* f0 = face[0];
10576 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10577 for ( int i = 0; i < nbN; i++ )
10579 const SMDS_MeshNode* n2 = f0->GetNode( i );
10580 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10581 linkSet.insert( SMESH_TLink( n1, n2 ));
10582 if ( !iter_isnew.second ) { // already in a set: no need to process
10583 linkSet.erase( iter_isnew.first );
10585 else // new in set == encountered for the first time: add
10587 #ifdef DEBUG_MATCHING_NODES
10588 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10589 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10591 linkList[0].push_back ( NLink( n1, n2 ));
10592 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10597 } // loop on link lists
10602 //================================================================================
10604 * \brief Create elements equal (on same nodes) to given ones
10605 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10606 * elements of the uppest dimension are duplicated.
10608 //================================================================================
10610 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10612 ClearLastCreated();
10613 SMESHDS_Mesh* mesh = GetMeshDS();
10615 // get an element type and an iterator over elements
10617 SMDSAbs_ElementType type = SMDSAbs_All;
10618 SMDS_ElemIteratorPtr elemIt;
10619 vector< const SMDS_MeshElement* > allElems;
10620 if ( theElements.empty() )
10622 if ( mesh->NbNodes() == 0 )
10624 // get most complex type
10625 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10626 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10627 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10629 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10630 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10635 // put all elements in the vector <allElems>
10636 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10637 elemIt = mesh->elementsIterator( type );
10638 while ( elemIt->more() )
10639 allElems.push_back( elemIt->next());
10640 elemIt = elemSetIterator( allElems );
10644 type = (*theElements.begin())->GetType();
10645 elemIt = elemSetIterator( theElements );
10648 // duplicate elements
10650 ElemFeatures elemType;
10652 vector< const SMDS_MeshNode* > nodes;
10653 while ( elemIt->more() )
10655 const SMDS_MeshElement* elem = elemIt->next();
10656 if ( elem->GetType() != type )
10659 elemType.Init( elem, /*basicOnly=*/false );
10660 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10662 AddElement( nodes, elemType );
10666 //================================================================================
10668 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10669 \param theElems - the list of elements (edges or faces) to be replicated
10670 The nodes for duplication could be found from these elements
10671 \param theNodesNot - list of nodes to NOT replicate
10672 \param theAffectedElems - the list of elements (cells and edges) to which the
10673 replicated nodes should be associated to.
10674 \return TRUE if operation has been completed successfully, FALSE otherwise
10676 //================================================================================
10678 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10679 const TIDSortedElemSet& theNodesNot,
10680 const TIDSortedElemSet& theAffectedElems )
10682 myLastCreatedElems.Clear();
10683 myLastCreatedNodes.Clear();
10685 if ( theElems.size() == 0 )
10688 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10693 TNodeNodeMap anOldNodeToNewNode;
10694 // duplicate elements and nodes
10695 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10696 // replce nodes by duplications
10697 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10701 //================================================================================
10703 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10704 \param theMeshDS - mesh instance
10705 \param theElems - the elements replicated or modified (nodes should be changed)
10706 \param theNodesNot - nodes to NOT replicate
10707 \param theNodeNodeMap - relation of old node to new created node
10708 \param theIsDoubleElem - flag os to replicate element or modify
10709 \return TRUE if operation has been completed successfully, FALSE otherwise
10711 //================================================================================
10713 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10714 const TIDSortedElemSet& theElems,
10715 const TIDSortedElemSet& theNodesNot,
10716 TNodeNodeMap& theNodeNodeMap,
10717 const bool theIsDoubleElem )
10719 // iterate through element and duplicate them (by nodes duplication)
10721 std::vector<const SMDS_MeshNode*> newNodes;
10722 ElemFeatures elemType;
10724 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10725 for ( ; elemItr != theElems.end(); ++elemItr )
10727 const SMDS_MeshElement* anElem = *elemItr;
10731 // duplicate nodes to duplicate element
10732 bool isDuplicate = false;
10733 newNodes.resize( anElem->NbNodes() );
10734 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10736 while ( anIter->more() )
10738 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10739 const SMDS_MeshNode* aNewNode = aCurrNode;
10740 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10741 if ( n2n != theNodeNodeMap.end() )
10743 aNewNode = n2n->second;
10745 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10748 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10749 copyPosition( aCurrNode, aNewNode );
10750 theNodeNodeMap[ aCurrNode ] = aNewNode;
10751 myLastCreatedNodes.Append( aNewNode );
10753 isDuplicate |= (aCurrNode != aNewNode);
10754 newNodes[ ind++ ] = aNewNode;
10756 if ( !isDuplicate )
10759 if ( theIsDoubleElem )
10760 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10762 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10769 //================================================================================
10771 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10772 \param theNodes - identifiers of nodes to be doubled
10773 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10774 nodes. If list of element identifiers is empty then nodes are doubled but
10775 they not assigned to elements
10776 \return TRUE if operation has been completed successfully, FALSE otherwise
10778 //================================================================================
10780 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10781 const std::list< int >& theListOfModifiedElems )
10783 myLastCreatedElems.Clear();
10784 myLastCreatedNodes.Clear();
10786 if ( theListOfNodes.size() == 0 )
10789 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10793 // iterate through nodes and duplicate them
10795 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10797 std::list< int >::const_iterator aNodeIter;
10798 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10800 int aCurr = *aNodeIter;
10801 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10807 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10810 copyPosition( aNode, aNewNode );
10811 anOldNodeToNewNode[ aNode ] = aNewNode;
10812 myLastCreatedNodes.Append( aNewNode );
10816 // Create map of new nodes for modified elements
10818 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10820 std::list< int >::const_iterator anElemIter;
10821 for ( anElemIter = theListOfModifiedElems.begin();
10822 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10824 int aCurr = *anElemIter;
10825 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10829 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10831 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10833 while ( anIter->more() )
10835 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10836 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10838 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10839 aNodeArr[ ind++ ] = aNewNode;
10842 aNodeArr[ ind++ ] = aCurrNode;
10844 anElemToNodes[ anElem ] = aNodeArr;
10847 // Change nodes of elements
10849 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10850 anElemToNodesIter = anElemToNodes.begin();
10851 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10853 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10854 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10857 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10866 //================================================================================
10868 \brief Check if element located inside shape
10869 \return TRUE if IN or ON shape, FALSE otherwise
10871 //================================================================================
10873 template<class Classifier>
10874 bool isInside(const SMDS_MeshElement* theElem,
10875 Classifier& theClassifier,
10876 const double theTol)
10878 gp_XYZ centerXYZ (0, 0, 0);
10879 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10880 while (aNodeItr->more())
10881 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10883 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10884 theClassifier.Perform(aPnt, theTol);
10885 TopAbs_State aState = theClassifier.State();
10886 return (aState == TopAbs_IN || aState == TopAbs_ON );
10889 //================================================================================
10891 * \brief Classifier of the 3D point on the TopoDS_Face
10892 * with interaface suitable for isInside()
10894 //================================================================================
10896 struct _FaceClassifier
10898 Extrema_ExtPS _extremum;
10899 BRepAdaptor_Surface _surface;
10900 TopAbs_State _state;
10902 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10904 _extremum.Initialize( _surface,
10905 _surface.FirstUParameter(), _surface.LastUParameter(),
10906 _surface.FirstVParameter(), _surface.LastVParameter(),
10907 _surface.Tolerance(), _surface.Tolerance() );
10909 void Perform(const gp_Pnt& aPnt, double theTol)
10912 _state = TopAbs_OUT;
10913 _extremum.Perform(aPnt);
10914 if ( _extremum.IsDone() )
10915 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10916 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10918 TopAbs_State State() const
10925 //================================================================================
10927 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10928 This method is the first step of DoubleNodeElemGroupsInRegion.
10929 \param theElems - list of groups of elements (edges or faces) to be replicated
10930 \param theNodesNot - list of groups of nodes not to replicated
10931 \param theShape - shape to detect affected elements (element which geometric center
10932 located on or inside shape). If the shape is null, detection is done on faces orientations
10933 (select elements with a gravity center on the side given by faces normals).
10934 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10935 The replicated nodes should be associated to affected elements.
10936 \return groups of affected elements
10937 \sa DoubleNodeElemGroupsInRegion()
10939 //================================================================================
10941 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10942 const TIDSortedElemSet& theNodesNot,
10943 const TopoDS_Shape& theShape,
10944 TIDSortedElemSet& theAffectedElems)
10946 if ( theShape.IsNull() )
10948 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10949 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10950 std::set<const SMDS_MeshElement*> edgesToCheck;
10951 alreadyCheckedNodes.clear();
10952 alreadyCheckedElems.clear();
10953 edgesToCheck.clear();
10955 // --- iterates on elements to be replicated and get elements by back references from their nodes
10957 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10958 for ( ; elemItr != theElems.end(); ++elemItr )
10960 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10961 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10964 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10965 std::set<const SMDS_MeshNode*> nodesElem;
10967 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10968 while ( nodeItr->more() )
10970 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10971 nodesElem.insert(aNode);
10973 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10974 for (; nodit != nodesElem.end(); nodit++)
10976 const SMDS_MeshNode* aNode = *nodit;
10977 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10979 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10981 alreadyCheckedNodes.insert(aNode);
10982 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10983 while ( backElemItr->more() )
10985 const SMDS_MeshElement* curElem = backElemItr->next();
10986 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10988 if (theElems.find(curElem) != theElems.end())
10990 alreadyCheckedElems.insert(curElem);
10991 double x=0, y=0, z=0;
10993 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10994 while ( nodeItr2->more() )
10996 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10997 x += anotherNode->X();
10998 y += anotherNode->Y();
10999 z += anotherNode->Z();
11003 p.SetCoord( x/nb -aNode->X(),
11005 z/nb -aNode->Z() );
11008 theAffectedElems.insert( curElem );
11010 else if (curElem->GetType() == SMDSAbs_Edge)
11011 edgesToCheck.insert(curElem);
11015 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11016 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11017 for( ; eit != edgesToCheck.end(); eit++)
11019 bool onside = true;
11020 const SMDS_MeshElement* anEdge = *eit;
11021 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11022 while ( nodeItr->more() )
11024 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11025 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11033 theAffectedElems.insert(anEdge);
11039 const double aTol = Precision::Confusion();
11040 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11041 auto_ptr<_FaceClassifier> aFaceClassifier;
11042 if ( theShape.ShapeType() == TopAbs_SOLID )
11044 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11045 bsc3d->PerformInfinitePoint(aTol);
11047 else if (theShape.ShapeType() == TopAbs_FACE )
11049 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11052 // iterates on indicated elements and get elements by back references from their nodes
11053 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11054 for ( ; elemItr != theElems.end(); ++elemItr )
11056 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11059 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11060 while ( nodeItr->more() )
11062 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11063 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11065 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11066 while ( backElemItr->more() )
11068 const SMDS_MeshElement* curElem = backElemItr->next();
11069 if ( curElem && theElems.find(curElem) == theElems.end() &&
11071 isInside( curElem, *bsc3d, aTol ) :
11072 isInside( curElem, *aFaceClassifier, aTol )))
11073 theAffectedElems.insert( curElem );
11081 //================================================================================
11083 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11084 \param theElems - group of of elements (edges or faces) to be replicated
11085 \param theNodesNot - group of nodes not to replicate
11086 \param theShape - shape to detect affected elements (element which geometric center
11087 located on or inside shape).
11088 The replicated nodes should be associated to affected elements.
11089 \return TRUE if operation has been completed successfully, FALSE otherwise
11091 //================================================================================
11093 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11094 const TIDSortedElemSet& theNodesNot,
11095 const TopoDS_Shape& theShape )
11097 if ( theShape.IsNull() )
11100 const double aTol = Precision::Confusion();
11101 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11102 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11103 if ( theShape.ShapeType() == TopAbs_SOLID )
11105 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11106 bsc3d->PerformInfinitePoint(aTol);
11108 else if (theShape.ShapeType() == TopAbs_FACE )
11110 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11113 // iterates on indicated elements and get elements by back references from their nodes
11114 TIDSortedElemSet anAffected;
11115 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11116 for ( ; elemItr != theElems.end(); ++elemItr )
11118 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11122 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11123 while ( nodeItr->more() )
11125 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11126 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11128 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11129 while ( backElemItr->more() )
11131 const SMDS_MeshElement* curElem = backElemItr->next();
11132 if ( curElem && theElems.find(curElem) == theElems.end() &&
11134 isInside( curElem, *bsc3d, aTol ) :
11135 isInside( curElem, *aFaceClassifier, aTol )))
11136 anAffected.insert( curElem );
11140 return DoubleNodes( theElems, theNodesNot, anAffected );
11144 * \brief compute an oriented angle between two planes defined by four points.
11145 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11146 * @param p0 base of the rotation axe
11147 * @param p1 extremity of the rotation axe
11148 * @param g1 belongs to the first plane
11149 * @param g2 belongs to the second plane
11151 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11153 gp_Vec vref(p0, p1);
11156 gp_Vec n1 = vref.Crossed(v1);
11157 gp_Vec n2 = vref.Crossed(v2);
11159 return n2.AngleWithRef(n1, vref);
11161 catch ( Standard_Failure ) {
11163 return Max( v1.Magnitude(), v2.Magnitude() );
11167 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11168 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11169 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11170 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11171 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11172 * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
11173 * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
11174 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11175 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11176 * \param theElems - list of groups of volumes, where a group of volume is a set of
11177 * SMDS_MeshElements sorted by Id.
11178 * \param createJointElems - if TRUE, create the elements
11179 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11180 * the boundary between \a theDomains and the rest mesh
11181 * \return TRUE if operation has been completed successfully, FALSE otherwise
11183 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11184 bool createJointElems,
11185 bool onAllBoundaries)
11187 // MESSAGE("----------------------------------------------");
11188 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11189 // MESSAGE("----------------------------------------------");
11191 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11192 meshDS->BuildDownWardConnectivity(true);
11194 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11196 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11197 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11198 // build the list of nodes shared by 2 or more domains, with their domain indexes
11200 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11201 std::map<int,int>celldom; // cell vtkId --> domain
11202 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11203 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11204 faceDomains.clear();
11206 cellDomains.clear();
11207 nodeDomains.clear();
11208 std::map<int,int> emptyMap;
11209 std::set<int> emptySet;
11212 //MESSAGE(".. Number of domains :"<<theElems.size());
11214 TIDSortedElemSet theRestDomElems;
11215 const int iRestDom = -1;
11216 const int idom0 = onAllBoundaries ? iRestDom : 0;
11217 const int nbDomains = theElems.size();
11219 // Check if the domains do not share an element
11220 for (int idom = 0; idom < nbDomains-1; idom++)
11222 // MESSAGE("... Check of domain #" << idom);
11223 const TIDSortedElemSet& domain = theElems[idom];
11224 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11225 for (; elemItr != domain.end(); ++elemItr)
11227 const SMDS_MeshElement* anElem = *elemItr;
11228 int idombisdeb = idom + 1 ;
11229 // check if the element belongs to a domain further in the list
11230 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11232 const TIDSortedElemSet& domainbis = theElems[idombis];
11233 if ( domainbis.count( anElem ))
11235 MESSAGE(".... Domain #" << idom);
11236 MESSAGE(".... Domain #" << idombis);
11237 throw SALOME_Exception("The domains are not disjoint.");
11244 for (int idom = 0; idom < nbDomains; idom++)
11247 // --- build a map (face to duplicate --> volume to modify)
11248 // with all the faces shared by 2 domains (group of elements)
11249 // and corresponding volume of this domain, for each shared face.
11250 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11252 //MESSAGE("... Neighbors of domain #" << idom);
11253 const TIDSortedElemSet& domain = theElems[idom];
11254 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11255 for (; elemItr != domain.end(); ++elemItr)
11257 const SMDS_MeshElement* anElem = *elemItr;
11260 int vtkId = anElem->getVtkId();
11261 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11262 int neighborsVtkIds[NBMAXNEIGHBORS];
11263 int downIds[NBMAXNEIGHBORS];
11264 unsigned char downTypes[NBMAXNEIGHBORS];
11265 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11266 for (int n = 0; n < nbNeighbors; n++)
11268 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11269 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11270 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11273 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11275 // MESSAGE("Domain " << idombis);
11276 const TIDSortedElemSet& domainbis = theElems[idombis];
11277 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11279 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11281 DownIdType face(downIds[n], downTypes[n]);
11282 if (!faceDomains[face].count(idom))
11284 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11285 celldom[vtkId] = idom;
11286 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11290 theRestDomElems.insert( elem );
11291 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11292 celldom[neighborsVtkIds[n]] = iRestDom;
11300 //MESSAGE("Number of shared faces " << faceDomains.size());
11301 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11303 // --- explore the shared faces domain by domain,
11304 // explore the nodes of the face and see if they belong to a cell in the domain,
11305 // which has only a node or an edge on the border (not a shared face)
11307 for (int idomain = idom0; idomain < nbDomains; idomain++)
11309 //MESSAGE("Domain " << idomain);
11310 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11311 itface = faceDomains.begin();
11312 for (; itface != faceDomains.end(); ++itface)
11314 const std::map<int, int>& domvol = itface->second;
11315 if (!domvol.count(idomain))
11317 DownIdType face = itface->first;
11318 //MESSAGE(" --- face " << face.cellId);
11319 std::set<int> oldNodes;
11321 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11322 std::set<int>::iterator itn = oldNodes.begin();
11323 for (; itn != oldNodes.end(); ++itn)
11326 //MESSAGE(" node " << oldId);
11327 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11328 for (int i=0; i<l.ncells; i++)
11330 int vtkId = l.cells[i];
11331 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11332 if (!domain.count(anElem))
11334 int vtkType = grid->GetCellType(vtkId);
11335 int downId = grid->CellIdToDownId(vtkId);
11338 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11339 continue; // not OK at this stage of the algorithm:
11340 //no cells created after BuildDownWardConnectivity
11342 DownIdType aCell(downId, vtkType);
11343 cellDomains[aCell][idomain] = vtkId;
11344 celldom[vtkId] = idomain;
11345 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11351 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11352 // for each shared face, get the nodes
11353 // for each node, for each domain of the face, create a clone of the node
11355 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11356 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11357 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11359 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11360 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11361 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11363 //MESSAGE(".. Duplication of the nodes");
11364 for (int idomain = idom0; idomain < nbDomains; idomain++)
11366 itface = faceDomains.begin();
11367 for (; itface != faceDomains.end(); ++itface)
11369 const std::map<int, int>& domvol = itface->second;
11370 if (!domvol.count(idomain))
11372 DownIdType face = itface->first;
11373 //MESSAGE(" --- face " << face.cellId);
11374 std::set<int> oldNodes;
11376 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11377 std::set<int>::iterator itn = oldNodes.begin();
11378 for (; itn != oldNodes.end(); ++itn)
11381 if (nodeDomains[oldId].empty())
11383 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11384 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11386 std::map<int, int>::const_iterator itdom = domvol.begin();
11387 for (; itdom != domvol.end(); ++itdom)
11389 int idom = itdom->first;
11390 //MESSAGE(" domain " << idom);
11391 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11393 if (nodeDomains[oldId].size() >= 2) // a multiple node
11395 vector<int> orderedDoms;
11396 //MESSAGE("multiple node " << oldId);
11397 if (mutipleNodes.count(oldId))
11398 orderedDoms = mutipleNodes[oldId];
11401 map<int,int>::iterator it = nodeDomains[oldId].begin();
11402 for (; it != nodeDomains[oldId].end(); ++it)
11403 orderedDoms.push_back(it->first);
11405 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11406 //stringstream txt;
11407 //for (int i=0; i<orderedDoms.size(); i++)
11408 // txt << orderedDoms[i] << " ";
11409 //MESSAGE("orderedDoms " << txt.str());
11410 mutipleNodes[oldId] = orderedDoms;
11412 double *coords = grid->GetPoint(oldId);
11413 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11414 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11415 int newId = newNode->getVtkId();
11416 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11417 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11424 //MESSAGE(".. Creation of elements");
11425 for (int idomain = idom0; idomain < nbDomains; idomain++)
11427 itface = faceDomains.begin();
11428 for (; itface != faceDomains.end(); ++itface)
11430 std::map<int, int> domvol = itface->second;
11431 if (!domvol.count(idomain))
11433 DownIdType face = itface->first;
11434 //MESSAGE(" --- face " << face.cellId);
11435 std::set<int> oldNodes;
11437 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11438 int nbMultipleNodes = 0;
11439 std::set<int>::iterator itn = oldNodes.begin();
11440 for (; itn != oldNodes.end(); ++itn)
11443 if (mutipleNodes.count(oldId))
11446 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11448 //MESSAGE("multiple Nodes detected on a shared face");
11449 int downId = itface->first.cellId;
11450 unsigned char cellType = itface->first.cellType;
11451 // --- shared edge or shared face ?
11452 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11455 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11456 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11457 if (mutipleNodes.count(nodes[i]))
11458 if (!mutipleNodesToFace.count(nodes[i]))
11459 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11461 else // shared face (between two volumes)
11463 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11464 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11465 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11466 for (int ie =0; ie < nbEdges; ie++)
11469 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11470 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11472 vector<int> vn0 = mutipleNodes[nodes[0]];
11473 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11475 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11476 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11477 if ( vn0[i0] == vn1[i1] )
11478 doms.push_back( vn0[ i0 ]);
11479 if ( doms.size() > 2 )
11481 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11482 double *coords = grid->GetPoint(nodes[0]);
11483 gp_Pnt p0(coords[0], coords[1], coords[2]);
11484 coords = grid->GetPoint(nodes[nbNodes - 1]);
11485 gp_Pnt p1(coords[0], coords[1], coords[2]);
11487 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11488 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11489 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11490 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11491 for ( size_t id = 0; id < doms.size(); id++ )
11493 int idom = doms[id];
11494 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11495 for ( int ivol = 0; ivol < nbvol; ivol++ )
11497 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11498 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11499 if (domain.count(elem))
11501 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11502 domvol[idom] = svol;
11503 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11505 vtkIdType npts = 0;
11506 vtkIdType* pts = 0;
11507 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11508 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11511 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11512 angleDom[idom] = 0;
11516 gp_Pnt g(values[0], values[1], values[2]);
11517 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11518 //MESSAGE(" angle=" << angleDom[idom]);
11524 map<double, int> sortedDom; // sort domains by angle
11525 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11526 sortedDom[ia->second] = ia->first;
11527 vector<int> vnodes;
11529 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11531 vdom.push_back(ib->second);
11532 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11534 for (int ino = 0; ino < nbNodes; ino++)
11535 vnodes.push_back(nodes[ino]);
11536 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11545 // --- iterate on shared faces (volumes to modify, face to extrude)
11546 // get node id's of the face (id SMDS = id VTK)
11547 // create flat element with old and new nodes if requested
11549 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11550 // (domain1 X domain2) = domain1 + MAXINT*domain2
11552 std::map<int, std::map<long,int> > nodeQuadDomains;
11553 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11555 //MESSAGE(".. Creation of elements: simple junction");
11556 if (createJointElems)
11559 string joints2DName = "joints2D";
11560 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11561 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11562 string joints3DName = "joints3D";
11563 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11564 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11566 itface = faceDomains.begin();
11567 for (; itface != faceDomains.end(); ++itface)
11569 DownIdType face = itface->first;
11570 std::set<int> oldNodes;
11571 std::set<int>::iterator itn;
11573 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11575 std::map<int, int> domvol = itface->second;
11576 std::map<int, int>::iterator itdom = domvol.begin();
11577 int dom1 = itdom->first;
11578 int vtkVolId = itdom->second;
11580 int dom2 = itdom->first;
11581 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11583 stringstream grpname;
11586 grpname << dom1 << "_" << dom2;
11588 grpname << dom2 << "_" << dom1;
11589 string namegrp = grpname.str();
11590 if (!mapOfJunctionGroups.count(namegrp))
11591 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11592 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11594 sgrp->Add(vol->GetID());
11595 if (vol->GetType() == SMDSAbs_Volume)
11596 joints3DGrp->Add(vol->GetID());
11597 else if (vol->GetType() == SMDSAbs_Face)
11598 joints2DGrp->Add(vol->GetID());
11602 // --- create volumes on multiple domain intersection if requested
11603 // iterate on mutipleNodesToFace
11604 // iterate on edgesMultiDomains
11606 //MESSAGE(".. Creation of elements: multiple junction");
11607 if (createJointElems)
11609 // --- iterate on mutipleNodesToFace
11611 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11612 for (; itn != mutipleNodesToFace.end(); ++itn)
11614 int node = itn->first;
11615 vector<int> orderDom = itn->second;
11616 vector<vtkIdType> orderedNodes;
11617 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11618 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11619 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11621 stringstream grpname;
11623 grpname << 0 << "_" << 0;
11625 string namegrp = grpname.str();
11626 if (!mapOfJunctionGroups.count(namegrp))
11627 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11628 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11630 sgrp->Add(face->GetID());
11633 // --- iterate on edgesMultiDomains
11635 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11636 for (; ite != edgesMultiDomains.end(); ++ite)
11638 vector<int> nodes = ite->first;
11639 vector<int> orderDom = ite->second;
11640 vector<vtkIdType> orderedNodes;
11641 if (nodes.size() == 2)
11643 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11644 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11645 if ( orderDom.size() == 3 )
11646 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11647 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11649 for (int idom = orderDom.size()-1; idom >=0; idom--)
11650 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11651 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11654 string namegrp = "jointsMultiples";
11655 if (!mapOfJunctionGroups.count(namegrp))
11656 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11657 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11659 sgrp->Add(vol->GetID());
11663 //INFOS("Quadratic multiple joints not implemented");
11664 // TODO quadratic nodes
11669 // --- list the explicit faces and edges of the mesh that need to be modified,
11670 // i.e. faces and edges built with one or more duplicated nodes.
11671 // associate these faces or edges to their corresponding domain.
11672 // only the first domain found is kept when a face or edge is shared
11674 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11675 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11676 faceOrEdgeDom.clear();
11679 //MESSAGE(".. Modification of elements");
11680 for (int idomain = idom0; idomain < nbDomains; idomain++)
11682 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11683 for (; itnod != nodeDomains.end(); ++itnod)
11685 int oldId = itnod->first;
11686 //MESSAGE(" node " << oldId);
11687 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11688 for (int i = 0; i < l.ncells; i++)
11690 int vtkId = l.cells[i];
11691 int vtkType = grid->GetCellType(vtkId);
11692 int downId = grid->CellIdToDownId(vtkId);
11694 continue; // new cells: not to be modified
11695 DownIdType aCell(downId, vtkType);
11696 int volParents[1000];
11697 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11698 for (int j = 0; j < nbvol; j++)
11699 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11700 if (!feDom.count(vtkId))
11702 feDom[vtkId] = idomain;
11703 faceOrEdgeDom[aCell] = emptyMap;
11704 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11705 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11706 // << " type " << vtkType << " downId " << downId);
11712 // --- iterate on shared faces (volumes to modify, face to extrude)
11713 // get node id's of the face
11714 // replace old nodes by new nodes in volumes, and update inverse connectivity
11716 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11717 for (int m=0; m<3; m++)
11719 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11720 itface = (*amap).begin();
11721 for (; itface != (*amap).end(); ++itface)
11723 DownIdType face = itface->first;
11724 std::set<int> oldNodes;
11725 std::set<int>::iterator itn;
11727 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11728 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11729 std::map<int, int> localClonedNodeIds;
11731 std::map<int, int> domvol = itface->second;
11732 std::map<int, int>::iterator itdom = domvol.begin();
11733 for (; itdom != domvol.end(); ++itdom)
11735 int idom = itdom->first;
11736 int vtkVolId = itdom->second;
11737 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11738 localClonedNodeIds.clear();
11739 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11742 if (nodeDomains[oldId].count(idom))
11744 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11745 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11748 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11753 // Remove empty groups (issue 0022812)
11754 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11755 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11757 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11758 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11761 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11762 grid->DeleteLinks();
11770 * \brief Double nodes on some external faces and create flat elements.
11771 * Flat elements are mainly used by some types of mechanic calculations.
11773 * Each group of the list must be constituted of faces.
11774 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11775 * @param theElems - list of groups of faces, where a group of faces is a set of
11776 * SMDS_MeshElements sorted by Id.
11777 * @return TRUE if operation has been completed successfully, FALSE otherwise
11779 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11781 // MESSAGE("-------------------------------------------------");
11782 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11783 // MESSAGE("-------------------------------------------------");
11785 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11787 // --- For each group of faces
11788 // duplicate the nodes, create a flat element based on the face
11789 // replace the nodes of the faces by their clones
11791 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11792 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11793 clonedNodes.clear();
11794 intermediateNodes.clear();
11795 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11796 mapOfJunctionGroups.clear();
11798 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11800 const TIDSortedElemSet& domain = theElems[idom];
11801 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11802 for ( ; elemItr != domain.end(); ++elemItr )
11804 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11805 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11808 // MESSAGE("aFace=" << aFace->GetID());
11809 bool isQuad = aFace->IsQuadratic();
11810 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11812 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11814 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11815 while (nodeIt->more())
11817 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11818 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11820 ln2.push_back(node);
11822 ln0.push_back(node);
11824 const SMDS_MeshNode* clone = 0;
11825 if (!clonedNodes.count(node))
11827 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11828 copyPosition( node, clone );
11829 clonedNodes[node] = clone;
11832 clone = clonedNodes[node];
11835 ln3.push_back(clone);
11837 ln1.push_back(clone);
11839 const SMDS_MeshNode* inter = 0;
11840 if (isQuad && (!isMedium))
11842 if (!intermediateNodes.count(node))
11844 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11845 copyPosition( node, inter );
11846 intermediateNodes[node] = inter;
11849 inter = intermediateNodes[node];
11850 ln4.push_back(inter);
11854 // --- extrude the face
11856 vector<const SMDS_MeshNode*> ln;
11857 SMDS_MeshVolume* vol = 0;
11858 vtkIdType aType = aFace->GetVtkType();
11862 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11863 // MESSAGE("vol prism " << vol->GetID());
11864 ln.push_back(ln1[0]);
11865 ln.push_back(ln1[1]);
11866 ln.push_back(ln1[2]);
11869 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11870 // MESSAGE("vol hexa " << vol->GetID());
11871 ln.push_back(ln1[0]);
11872 ln.push_back(ln1[1]);
11873 ln.push_back(ln1[2]);
11874 ln.push_back(ln1[3]);
11876 case VTK_QUADRATIC_TRIANGLE:
11877 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11878 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11879 // MESSAGE("vol quad prism " << vol->GetID());
11880 ln.push_back(ln1[0]);
11881 ln.push_back(ln1[1]);
11882 ln.push_back(ln1[2]);
11883 ln.push_back(ln3[0]);
11884 ln.push_back(ln3[1]);
11885 ln.push_back(ln3[2]);
11887 case VTK_QUADRATIC_QUAD:
11888 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11889 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11890 // ln4[0], ln4[1], ln4[2], ln4[3]);
11891 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11892 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11893 ln4[0], ln4[1], ln4[2], ln4[3]);
11894 // MESSAGE("vol quad hexa " << vol->GetID());
11895 ln.push_back(ln1[0]);
11896 ln.push_back(ln1[1]);
11897 ln.push_back(ln1[2]);
11898 ln.push_back(ln1[3]);
11899 ln.push_back(ln3[0]);
11900 ln.push_back(ln3[1]);
11901 ln.push_back(ln3[2]);
11902 ln.push_back(ln3[3]);
11912 stringstream grpname;
11916 string namegrp = grpname.str();
11917 if (!mapOfJunctionGroups.count(namegrp))
11918 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11919 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11921 sgrp->Add(vol->GetID());
11924 // --- modify the face
11926 aFace->ChangeNodes(&ln[0], ln.size());
11933 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11934 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11935 * groups of faces to remove inside the object, (idem edges).
11936 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11938 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11939 const TopoDS_Shape& theShape,
11940 SMESH_NodeSearcher* theNodeSearcher,
11941 const char* groupName,
11942 std::vector<double>& nodesCoords,
11943 std::vector<std::vector<int> >& listOfListOfNodes)
11945 // MESSAGE("--------------------------------");
11946 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11947 // MESSAGE("--------------------------------");
11949 // --- zone of volumes to remove is given :
11950 // 1 either by a geom shape (one or more vertices) and a radius,
11951 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11952 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11953 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11954 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11955 // defined by it's name.
11957 SMESHDS_GroupBase* groupDS = 0;
11958 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11959 while ( groupIt->more() )
11962 SMESH_Group * group = groupIt->next();
11963 if ( !group ) continue;
11964 groupDS = group->GetGroupDS();
11965 if ( !groupDS || groupDS->IsEmpty() ) continue;
11966 std::string grpName = group->GetName();
11967 //MESSAGE("grpName=" << grpName);
11968 if (grpName == groupName)
11974 bool isNodeGroup = false;
11975 bool isNodeCoords = false;
11978 if (groupDS->GetType() != SMDSAbs_Node)
11980 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11983 if (nodesCoords.size() > 0)
11984 isNodeCoords = true; // a list o nodes given by their coordinates
11985 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11987 // --- define groups to build
11989 int idg; // --- group of SMDS volumes
11990 string grpvName = groupName;
11991 grpvName += "_vol";
11992 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11995 MESSAGE("group not created " << grpvName);
11998 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12000 int idgs; // --- group of SMDS faces on the skin
12001 string grpsName = groupName;
12002 grpsName += "_skin";
12003 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12006 MESSAGE("group not created " << grpsName);
12009 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12011 int idgi; // --- group of SMDS faces internal (several shapes)
12012 string grpiName = groupName;
12013 grpiName += "_internalFaces";
12014 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12017 MESSAGE("group not created " << grpiName);
12020 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12022 int idgei; // --- group of SMDS faces internal (several shapes)
12023 string grpeiName = groupName;
12024 grpeiName += "_internalEdges";
12025 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12028 MESSAGE("group not created " << grpeiName);
12031 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12033 // --- build downward connectivity
12035 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12036 meshDS->BuildDownWardConnectivity(true);
12037 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12039 // --- set of volumes detected inside
12041 std::set<int> setOfInsideVol;
12042 std::set<int> setOfVolToCheck;
12044 std::vector<gp_Pnt> gpnts;
12047 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12049 //MESSAGE("group of nodes provided");
12050 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12051 while ( elemIt->more() )
12053 const SMDS_MeshElement* elem = elemIt->next();
12056 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12059 SMDS_MeshElement* vol = 0;
12060 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12061 while (volItr->more())
12063 vol = (SMDS_MeshElement*)volItr->next();
12064 setOfInsideVol.insert(vol->getVtkId());
12065 sgrp->Add(vol->GetID());
12069 else if (isNodeCoords)
12071 //MESSAGE("list of nodes coordinates provided");
12074 while ( i < nodesCoords.size()-2 )
12076 double x = nodesCoords[i++];
12077 double y = nodesCoords[i++];
12078 double z = nodesCoords[i++];
12079 gp_Pnt p = gp_Pnt(x, y ,z);
12080 gpnts.push_back(p);
12081 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12085 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12087 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12088 TopTools_IndexedMapOfShape vertexMap;
12089 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12090 gp_Pnt p = gp_Pnt(0,0,0);
12091 if (vertexMap.Extent() < 1)
12094 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12096 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12097 p = BRep_Tool::Pnt(vertex);
12098 gpnts.push_back(p);
12099 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12103 if (gpnts.size() > 0)
12105 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12106 //MESSAGE("startNode->nodeId " << nodeId);
12108 double radius2 = radius*radius;
12109 //MESSAGE("radius2 " << radius2);
12111 // --- volumes on start node
12113 setOfVolToCheck.clear();
12114 SMDS_MeshElement* startVol = 0;
12115 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12116 while (volItr->more())
12118 startVol = (SMDS_MeshElement*)volItr->next();
12119 setOfVolToCheck.insert(startVol->getVtkId());
12121 if (setOfVolToCheck.empty())
12123 MESSAGE("No volumes found");
12127 // --- starting with central volumes then their neighbors, check if they are inside
12128 // or outside the domain, until no more new neighbor volume is inside.
12129 // Fill the group of inside volumes
12131 std::map<int, double> mapOfNodeDistance2;
12132 mapOfNodeDistance2.clear();
12133 std::set<int> setOfOutsideVol;
12134 while (!setOfVolToCheck.empty())
12136 std::set<int>::iterator it = setOfVolToCheck.begin();
12138 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12139 bool volInside = false;
12140 vtkIdType npts = 0;
12141 vtkIdType* pts = 0;
12142 grid->GetCellPoints(vtkId, npts, pts);
12143 for (int i=0; i<npts; i++)
12145 double distance2 = 0;
12146 if (mapOfNodeDistance2.count(pts[i]))
12148 distance2 = mapOfNodeDistance2[pts[i]];
12149 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12153 double *coords = grid->GetPoint(pts[i]);
12154 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12156 for ( size_t j = 0; j < gpnts.size(); j++ )
12158 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12159 if (d2 < distance2)
12162 if (distance2 < radius2)
12166 mapOfNodeDistance2[pts[i]] = distance2;
12167 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12169 if (distance2 < radius2)
12171 volInside = true; // one or more nodes inside the domain
12172 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12178 setOfInsideVol.insert(vtkId);
12179 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12180 int neighborsVtkIds[NBMAXNEIGHBORS];
12181 int downIds[NBMAXNEIGHBORS];
12182 unsigned char downTypes[NBMAXNEIGHBORS];
12183 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12184 for (int n = 0; n < nbNeighbors; n++)
12185 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12186 setOfVolToCheck.insert(neighborsVtkIds[n]);
12190 setOfOutsideVol.insert(vtkId);
12191 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12193 setOfVolToCheck.erase(vtkId);
12197 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12198 // If yes, add the volume to the inside set
12200 bool addedInside = true;
12201 std::set<int> setOfVolToReCheck;
12202 while (addedInside)
12204 //MESSAGE(" --------------------------- re check");
12205 addedInside = false;
12206 std::set<int>::iterator itv = setOfInsideVol.begin();
12207 for (; itv != setOfInsideVol.end(); ++itv)
12210 int neighborsVtkIds[NBMAXNEIGHBORS];
12211 int downIds[NBMAXNEIGHBORS];
12212 unsigned char downTypes[NBMAXNEIGHBORS];
12213 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12214 for (int n = 0; n < nbNeighbors; n++)
12215 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12216 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12218 setOfVolToCheck = setOfVolToReCheck;
12219 setOfVolToReCheck.clear();
12220 while (!setOfVolToCheck.empty())
12222 std::set<int>::iterator it = setOfVolToCheck.begin();
12224 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12226 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12227 int countInside = 0;
12228 int neighborsVtkIds[NBMAXNEIGHBORS];
12229 int downIds[NBMAXNEIGHBORS];
12230 unsigned char downTypes[NBMAXNEIGHBORS];
12231 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12232 for (int n = 0; n < nbNeighbors; n++)
12233 if (setOfInsideVol.count(neighborsVtkIds[n]))
12235 //MESSAGE("countInside " << countInside);
12236 if (countInside > 1)
12238 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12239 setOfInsideVol.insert(vtkId);
12240 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12241 addedInside = true;
12244 setOfVolToReCheck.insert(vtkId);
12246 setOfVolToCheck.erase(vtkId);
12250 // --- map of Downward faces at the boundary, inside the global volume
12251 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12252 // fill group of SMDS faces inside the volume (when several volume shapes)
12253 // fill group of SMDS faces on the skin of the global volume (if skin)
12255 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12256 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12257 std::set<int>::iterator it = setOfInsideVol.begin();
12258 for (; it != setOfInsideVol.end(); ++it)
12261 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12262 int neighborsVtkIds[NBMAXNEIGHBORS];
12263 int downIds[NBMAXNEIGHBORS];
12264 unsigned char downTypes[NBMAXNEIGHBORS];
12265 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12266 for (int n = 0; n < nbNeighbors; n++)
12268 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12269 if (neighborDim == 3)
12271 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12273 DownIdType face(downIds[n], downTypes[n]);
12274 boundaryFaces[face] = vtkId;
12276 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12277 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12278 if (vtkFaceId >= 0)
12280 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12281 // find also the smds edges on this face
12282 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12283 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12284 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12285 for (int i = 0; i < nbEdges; i++)
12287 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12288 if (vtkEdgeId >= 0)
12289 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12293 else if (neighborDim == 2) // skin of the volume
12295 DownIdType face(downIds[n], downTypes[n]);
12296 skinFaces[face] = vtkId;
12297 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12298 if (vtkFaceId >= 0)
12299 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12304 // --- identify the edges constituting the wire of each subshape on the skin
12305 // define polylines with the nodes of edges, equivalent to wires
12306 // project polylines on subshapes, and partition, to get geom faces
12308 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12309 std::set<int> emptySet;
12311 std::set<int> shapeIds;
12313 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12314 while (itelem->more())
12316 const SMDS_MeshElement *elem = itelem->next();
12317 int shapeId = elem->getshapeId();
12318 int vtkId = elem->getVtkId();
12319 if (!shapeIdToVtkIdSet.count(shapeId))
12321 shapeIdToVtkIdSet[shapeId] = emptySet;
12322 shapeIds.insert(shapeId);
12324 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12327 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12328 std::set<DownIdType, DownIdCompare> emptyEdges;
12329 emptyEdges.clear();
12331 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12332 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12334 int shapeId = itShape->first;
12335 //MESSAGE(" --- Shape ID --- "<< shapeId);
12336 shapeIdToEdges[shapeId] = emptyEdges;
12338 std::vector<int> nodesEdges;
12340 std::set<int>::iterator its = itShape->second.begin();
12341 for (; its != itShape->second.end(); ++its)
12344 //MESSAGE(" " << vtkId);
12345 int neighborsVtkIds[NBMAXNEIGHBORS];
12346 int downIds[NBMAXNEIGHBORS];
12347 unsigned char downTypes[NBMAXNEIGHBORS];
12348 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12349 for (int n = 0; n < nbNeighbors; n++)
12351 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12353 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12354 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12355 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12357 DownIdType edge(downIds[n], downTypes[n]);
12358 if (!shapeIdToEdges[shapeId].count(edge))
12360 shapeIdToEdges[shapeId].insert(edge);
12362 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12363 nodesEdges.push_back(vtkNodeId[0]);
12364 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12365 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12371 std::list<int> order;
12373 if (nodesEdges.size() > 0)
12375 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12376 nodesEdges[0] = -1;
12377 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12378 nodesEdges[1] = -1; // do not reuse this edge
12382 int nodeTofind = order.back(); // try first to push back
12384 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12385 if (nodesEdges[i] == nodeTofind)
12387 if ( i == (int) nodesEdges.size() )
12388 found = false; // no follower found on back
12391 if (i%2) // odd ==> use the previous one
12392 if (nodesEdges[i-1] < 0)
12396 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12397 nodesEdges[i-1] = -1;
12399 else // even ==> use the next one
12400 if (nodesEdges[i+1] < 0)
12404 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12405 nodesEdges[i+1] = -1;
12410 // try to push front
12412 nodeTofind = order.front(); // try to push front
12413 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12414 if ( nodesEdges[i] == nodeTofind )
12416 if ( i == (int)nodesEdges.size() )
12418 found = false; // no predecessor found on front
12421 if (i%2) // odd ==> use the previous one
12422 if (nodesEdges[i-1] < 0)
12426 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12427 nodesEdges[i-1] = -1;
12429 else // even ==> use the next one
12430 if (nodesEdges[i+1] < 0)
12434 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12435 nodesEdges[i+1] = -1;
12441 std::vector<int> nodes;
12442 nodes.push_back(shapeId);
12443 std::list<int>::iterator itl = order.begin();
12444 for (; itl != order.end(); itl++)
12446 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12447 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12449 listOfListOfNodes.push_back(nodes);
12452 // partition geom faces with blocFissure
12453 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12454 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12460 //================================================================================
12462 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12463 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12464 * \return TRUE if operation has been completed successfully, FALSE otherwise
12466 //================================================================================
12468 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12470 // iterates on volume elements and detect all free faces on them
12471 SMESHDS_Mesh* aMesh = GetMeshDS();
12475 ElemFeatures faceType( SMDSAbs_Face );
12476 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12477 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12480 const SMDS_MeshVolume* volume = vIt->next();
12481 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12482 vTool.SetExternalNormal();
12483 const int iQuad = volume->IsQuadratic();
12484 faceType.SetQuad( iQuad );
12485 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12487 if (!vTool.IsFreeFace(iface))
12490 vector<const SMDS_MeshNode *> nodes;
12491 int nbFaceNodes = vTool.NbFaceNodes(iface);
12492 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12494 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12495 nodes.push_back(faceNodes[inode]);
12497 if (iQuad) // add medium nodes
12499 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12500 nodes.push_back(faceNodes[inode]);
12501 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12502 nodes.push_back(faceNodes[8]);
12504 // add new face based on volume nodes
12505 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12507 nbExisted++; // face already exsist
12511 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12516 return ( nbFree == ( nbExisted + nbCreated ));
12521 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12523 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12525 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12528 //================================================================================
12530 * \brief Creates missing boundary elements
12531 * \param elements - elements whose boundary is to be checked
12532 * \param dimension - defines type of boundary elements to create
12533 * \param group - a group to store created boundary elements in
12534 * \param targetMesh - a mesh to store created boundary elements in
12535 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12536 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12537 * boundary elements will be copied into the targetMesh
12538 * \param toAddExistingBondary - if true, not only new but also pre-existing
12539 * boundary elements will be added into the new group
12540 * \param aroundElements - if true, elements will be created on boundary of given
12541 * elements else, on boundary of the whole mesh.
12542 * \return nb of added boundary elements
12544 //================================================================================
12546 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12547 Bnd_Dimension dimension,
12548 SMESH_Group* group/*=0*/,
12549 SMESH_Mesh* targetMesh/*=0*/,
12550 bool toCopyElements/*=false*/,
12551 bool toCopyExistingBoundary/*=false*/,
12552 bool toAddExistingBondary/*= false*/,
12553 bool aroundElements/*= false*/)
12555 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12556 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12557 // hope that all elements are of the same type, do not check them all
12558 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12559 throw SALOME_Exception(LOCALIZED("wrong element type"));
12562 toCopyElements = toCopyExistingBoundary = false;
12564 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12565 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12566 int nbAddedBnd = 0;
12568 // editor adding present bnd elements and optionally holding elements to add to the group
12569 SMESH_MeshEditor* presentEditor;
12570 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12571 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12573 SMESH_MesherHelper helper( *myMesh );
12574 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12575 SMDS_VolumeTool vTool;
12576 TIDSortedElemSet avoidSet;
12577 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12580 typedef vector<const SMDS_MeshNode*> TConnectivity;
12581 TConnectivity tgtNodes;
12582 ElemFeatures elemKind( missType ), elemToCopy;
12584 vector<const SMDS_MeshElement*> presentBndElems;
12585 vector<TConnectivity> missingBndElems;
12586 vector<int> freeFacets;
12587 TConnectivity nodes, elemNodes;
12589 SMDS_ElemIteratorPtr eIt;
12590 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12591 else eIt = elemSetIterator( elements );
12593 while (eIt->more())
12595 const SMDS_MeshElement* elem = eIt->next();
12596 const int iQuad = elem->IsQuadratic();
12597 elemKind.SetQuad( iQuad );
12599 // ------------------------------------------------------------------------------------
12600 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12601 // ------------------------------------------------------------------------------------
12602 presentBndElems.clear();
12603 missingBndElems.clear();
12604 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12605 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12607 const SMDS_MeshElement* otherVol = 0;
12608 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12610 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12611 ( !aroundElements || elements.count( otherVol )))
12613 freeFacets.push_back( iface );
12615 if ( missType == SMDSAbs_Face )
12616 vTool.SetExternalNormal();
12617 for ( size_t i = 0; i < freeFacets.size(); ++i )
12619 int iface = freeFacets[i];
12620 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12621 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12622 if ( missType == SMDSAbs_Edge ) // boundary edges
12624 nodes.resize( 2+iQuad );
12625 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12627 for ( size_t j = 0; j < nodes.size(); ++j )
12628 nodes[ j ] = nn[ i+j ];
12629 if ( const SMDS_MeshElement* edge =
12630 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12631 presentBndElems.push_back( edge );
12633 missingBndElems.push_back( nodes );
12636 else // boundary face
12639 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12640 nodes.push_back( nn[inode] ); // add corner nodes
12642 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12643 nodes.push_back( nn[inode] ); // add medium nodes
12644 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12646 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12648 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12649 SMDSAbs_Face, /*noMedium=*/false ))
12650 presentBndElems.push_back( f );
12652 missingBndElems.push_back( nodes );
12654 if ( targetMesh != myMesh )
12656 // add 1D elements on face boundary to be added to a new mesh
12657 const SMDS_MeshElement* edge;
12658 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12661 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12663 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12664 if ( edge && avoidSet.insert( edge ).second )
12665 presentBndElems.push_back( edge );
12671 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12673 avoidSet.clear(), avoidSet.insert( elem );
12674 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12675 SMDS_MeshElement::iterator() );
12676 elemNodes.push_back( elemNodes[0] );
12677 nodes.resize( 2 + iQuad );
12678 const int nbLinks = elem->NbCornerNodes();
12679 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12681 nodes[0] = elemNodes[iN];
12682 nodes[1] = elemNodes[iN+1+iQuad];
12683 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12684 continue; // not free link
12686 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12687 if ( const SMDS_MeshElement* edge =
12688 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12689 presentBndElems.push_back( edge );
12691 missingBndElems.push_back( nodes );
12695 // ---------------------------------
12696 // 2. Add missing boundary elements
12697 // ---------------------------------
12698 if ( targetMesh != myMesh )
12699 // instead of making a map of nodes in this mesh and targetMesh,
12700 // we create nodes with same IDs.
12701 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12703 TConnectivity& srcNodes = missingBndElems[i];
12704 tgtNodes.resize( srcNodes.size() );
12705 for ( inode = 0; inode < srcNodes.size(); ++inode )
12706 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12707 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12709 /*noMedium=*/false))
12711 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12715 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12717 TConnectivity& nodes = missingBndElems[ i ];
12718 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12720 /*noMedium=*/false))
12722 SMDS_MeshElement* newElem =
12723 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12724 nbAddedBnd += bool( newElem );
12726 // try to set a new element to a shape
12727 if ( myMesh->HasShapeToMesh() )
12730 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12731 const size_t nbN = nodes.size() / (iQuad+1 );
12732 for ( inode = 0; inode < nbN && ok; ++inode )
12734 pair<int, TopAbs_ShapeEnum> i_stype =
12735 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12736 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12737 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12739 if ( ok && mediumShapes.size() > 1 )
12741 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12742 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12743 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12745 if (( ok = ( stype_i->first != stype_i_0.first )))
12746 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12747 aMesh->IndexToShape( stype_i_0.second ));
12750 if ( ok && mediumShapes.begin()->first == missShapeType )
12751 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12755 // ----------------------------------
12756 // 3. Copy present boundary elements
12757 // ----------------------------------
12758 if ( toCopyExistingBoundary )
12759 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12761 const SMDS_MeshElement* e = presentBndElems[i];
12762 tgtNodes.resize( e->NbNodes() );
12763 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12764 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12765 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12767 else // store present elements to add them to a group
12768 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12770 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12773 } // loop on given elements
12775 // ---------------------------------------------
12776 // 4. Fill group with boundary elements
12777 // ---------------------------------------------
12780 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12781 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12782 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12784 tgtEditor.myLastCreatedElems.Clear();
12785 tgtEditor2.myLastCreatedElems.Clear();
12787 // -----------------------
12788 // 5. Copy given elements
12789 // -----------------------
12790 if ( toCopyElements && targetMesh != myMesh )
12792 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12793 else eIt = elemSetIterator( elements );
12794 while (eIt->more())
12796 const SMDS_MeshElement* elem = eIt->next();
12797 tgtNodes.resize( elem->NbNodes() );
12798 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12799 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12800 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12802 tgtEditor.myLastCreatedElems.Clear();
12808 //================================================================================
12810 * \brief Copy node position and set \a to node on the same geometry
12812 //================================================================================
12814 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12815 const SMDS_MeshNode* to )
12817 if ( !from || !to ) return;
12819 SMDS_PositionPtr pos = from->GetPosition();
12820 if ( !pos || from->getshapeId() < 1 ) return;
12822 switch ( pos->GetTypeOfPosition() )
12824 case SMDS_TOP_3DSPACE: break;
12826 case SMDS_TOP_FACE:
12828 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12829 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12830 fPos->GetUParameter(), fPos->GetVParameter() );
12833 case SMDS_TOP_EDGE:
12835 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12836 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12837 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12840 case SMDS_TOP_VERTEX:
12842 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12845 case SMDS_TOP_UNSPEC:
12850 namespace // utils for MakePolyLine
12852 //================================================================================
12854 * \brief Sequence of found points and a current point data
12858 std::vector< gp_XYZ > myPoints;
12861 int mySrcPntInd; //!< start point index
12862 const SMDS_MeshElement* myFace;
12863 SMESH_NodeXYZ myNode1;
12864 SMESH_NodeXYZ myNode2;
12869 TIDSortedElemSet myElemSet, myAvoidSet;
12871 Path(): myLength(0.0), myFace(0) {}
12873 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
12874 const SMDS_MeshElement* face,
12875 const gp_XYZ& plnNorm,
12876 const gp_XYZ& plnOrig );
12878 void AddPoint( const gp_XYZ& p );
12880 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
12882 bool ReachSamePoint( const Path& other );
12884 static void Remove( std::vector< Path > & paths, size_t& i );
12887 //================================================================================
12889 * \brief Return true if this Path meats another
12891 //================================================================================
12893 bool Path::ReachSamePoint( const Path& other )
12895 return ( mySrcPntInd != other.mySrcPntInd &&
12896 myFace == other.myFace );
12899 //================================================================================
12901 * \brief Remove a path from a vector
12903 //================================================================================
12905 void Path::Remove( std::vector< Path > & paths, size_t& i )
12907 if ( paths.size() > 1 )
12909 size_t j = paths.size() - 1; // last item to be removed
12912 paths[ i ].myPoints.swap( paths[ j ].myPoints );
12913 paths[ i ].myLength = paths[ j ].myLength;
12914 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
12915 paths[ i ].myFace = paths[ j ].myFace;
12916 paths[ i ].myNode1 = paths[ j ].myNode1;
12917 paths[ i ].myNode2 = paths[ j ].myNode2;
12918 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
12919 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
12920 paths[ i ].myDot1 = paths[ j ].myDot1;
12921 paths[ i ].myDot2 = paths[ j ].myDot2;
12929 //================================================================================
12931 * \brief Store a point that is at a node of a face if the face is intersected by plane.
12932 * Return false if the node is a sole intersection point of the face and the plane
12934 //================================================================================
12936 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
12937 const SMDS_MeshElement* face,
12938 const gp_XYZ& plnNorm,
12939 const gp_XYZ& plnOrig )
12941 if ( face == myFace )
12943 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
12944 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
12945 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
12946 myNode1.Set( face->GetNode( ind3 ));
12947 myNode2.Set( face->GetNode( myNodeInd2 ));
12949 myDot1 = plnNorm * ( myNode1 - plnOrig );
12950 myDot2 = plnNorm * ( myNode2 - plnOrig );
12952 bool ok = ( myDot1 * myDot2 < 0 );
12953 if ( !ok && myDot1 * myDot2 == 0 )
12955 ok = ( myDot1 != myDot2 );
12956 if ( ok && myFace )
12957 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
12963 AddPoint( cornerNode );
12968 //================================================================================
12970 * \brief Store a point and update myLength
12972 //================================================================================
12974 void Path::AddPoint( const gp_XYZ& p )
12976 if ( !myPoints.empty() )
12977 myLength += ( p - myPoints.back() ).Modulus();
12980 myPoints.push_back( p );
12983 //================================================================================
12985 * \brief Try to find the next point
12986 * \param [in] plnNorm - cutting plane normal
12987 * \param [in] plnOrig - cutting plane origin
12989 //================================================================================
12991 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
12993 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
12994 if ( myNodeInd2 == nodeInd3 )
12995 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
12997 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
12998 double dot3 = plnNorm * ( node3 - plnOrig );
13000 if ( dot3 * myDot1 < 0. )
13003 myNodeInd2 = nodeInd3;
13006 else if ( dot3 * myDot2 < 0. )
13009 myNodeInd1 = nodeInd3;
13012 else if ( dot3 == 0. )
13014 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13015 while ( fIt->more() )
13016 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13020 else if ( myDot2 == 0. )
13022 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13023 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13024 while ( fIt->more() )
13025 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13030 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13031 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13033 myAvoidSet.clear();
13034 myAvoidSet.insert( myFace );
13035 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13036 myElemSet, myAvoidSet,
13037 &myNodeInd1, &myNodeInd2 );
13041 //================================================================================
13043 * \brief Compute a path between two points of PolySegment
13045 struct PolyPathCompute
13047 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13048 std::vector< Path >& myPaths; //!< path of each of segments to compute
13049 SMESH_Mesh* myMesh;
13050 mutable std::vector< std::string > myErrors;
13052 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13053 std::vector< Path >& thePaths,
13054 SMESH_Mesh* theMesh):
13055 mySegments( theSegments ),
13056 myPaths( thePaths ),
13058 myErrors( theSegments.size() )
13061 #undef SMESH_CAUGHT
13062 #define SMESH_CAUGHT myErrors[i] =
13063 void operator() ( const int i ) const
13066 const_cast< PolyPathCompute* >( this )->Compute( i );
13067 SMESH_CATCH( SMESH::returnError );
13069 #undef SMESH_CAUGHT
13070 //================================================================================
13072 * \brief Compute a path of a given segment
13074 //================================================================================
13076 void Compute( const int iSeg )
13078 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13080 // get a cutting plane
13082 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13083 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13084 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13085 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13087 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13088 gp_XYZ plnOrig = p2;
13090 // find paths connecting the 2 end points of polySeg
13092 std::vector< Path > paths; paths.reserve(10);
13094 // initialize paths
13096 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13099 path.mySrcPntInd = iP;
13100 size_t nbPaths = paths.size();
13102 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13104 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13105 polySeg.myNode2[ iP ],
13109 &path.myNodeInd2 )))
13111 path.myNode1.Set( polySeg.myNode1[ iP ]);
13112 path.myNode2.Set( polySeg.myNode2[ iP ]);
13113 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13114 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13115 path.myPoints.clear();
13116 path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13117 path.myAvoidSet.insert( path.myFace );
13118 paths.push_back( path );
13120 if ( nbPaths == paths.size() )
13121 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13122 << " in a PolySegment " << iSeg );
13124 else // an end point is at node
13126 std::set<const SMDS_MeshNode* > nodes;
13127 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13128 while ( fIt->more() )
13130 path.myPoints.clear();
13131 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13133 if (( path.myDot1 * path.myDot2 != 0 ) ||
13134 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13135 paths.push_back( path );
13140 // look for a one-segment path
13141 for ( size_t i = 0; i < nbPaths; ++i )
13142 for ( size_t j = nbPaths; j < paths.size(); ++j )
13143 if ( paths[i].myFace == paths[j].myFace )
13145 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13146 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13153 myPaths[ iSeg ].myLength = 1e100;
13155 while ( paths.size() >= 2 )
13157 for ( size_t i = 0; i < paths.size(); ++i )
13159 Path& path = paths[ i ];
13160 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13161 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13163 Path::Remove( paths, i );
13167 // join paths that reach same point
13168 for ( size_t j = 0; j < paths.size(); ++j )
13170 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13172 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13173 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13174 if ( fullLength < myPaths[ iSeg ].myLength )
13176 myPaths[ iSeg ].myLength = fullLength;
13177 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13178 allPoints.swap( paths[i].myPoints );
13179 allPoints.insert( allPoints.end(),
13180 paths[j].myPoints.rbegin(),
13181 paths[j].myPoints.rend() );
13183 Path::Remove( paths, i );
13184 Path::Remove( paths, j );
13188 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13189 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13192 if ( myPaths[ iSeg ].myPoints.empty() )
13193 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13195 } // PolyPathCompute::Compute()
13197 }; // struct PolyPathCompute
13201 //=======================================================================
13202 //function : MakePolyLine
13203 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13204 // the initial mesh
13205 //=======================================================================
13207 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13208 SMESHDS_Group* theGroup,
13209 SMESH_ElementSearcher* theSearcher)
13211 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13213 SMESH_ElementSearcher* searcher = theSearcher;
13214 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13217 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13218 delSearcher._obj = searcher;
13221 // get cutting planes
13223 std::vector< bool > isVectorOK( theSegments.size(), true );
13224 const double planarCoef = 0.333; // plane height in planar case
13226 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13228 PolySegment& polySeg = theSegments[ iSeg ];
13230 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13231 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13232 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13233 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13235 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13237 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13238 if ( !isVectorOK[ iSeg ])
13240 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13241 const SMDS_MeshElement* face;
13242 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13243 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13246 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13248 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13249 polySeg.myVector * faceNorm < Precision::Confusion() )
13251 polySeg.myVector = faceNorm;
13252 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13257 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13261 // assure that inverse elements are constructed, avoid their concurrent building in threads
13262 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13266 PolyPathCompute algo( theSegments, segPaths, myMesh );
13267 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13269 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13270 if ( !algo.myErrors[ iSeg ].empty() )
13271 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13273 // create an 1D mesh
13275 const SMDS_MeshNode *n, *nPrev = 0;
13276 SMESHDS_Mesh* mesh = GetMeshDS();
13278 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13280 const Path& path = segPaths[iSeg];
13281 if ( path.myPoints.size() < 2 )
13284 double tol = path.myLength / path.myPoints.size() / 1000.;
13285 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13287 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13288 myLastCreatedNodes.Append( nPrev );
13290 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13292 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13293 myLastCreatedNodes.Append( n );
13295 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13296 myLastCreatedElems.Append( elem );
13298 theGroup->Add( elem );
13305 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13306 if ( isVectorOK[ iSeg ])
13308 // find the most distance point of a path
13309 double maxDist = 0;
13310 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13312 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13313 if ( dist > maxDist )
13316 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13319 if ( maxDist < Precision::Confusion() ) // planar case
13320 theSegments[iSeg].myMidProjPoint =
13321 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13323 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );