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 <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101 #include <OSD_Parallel.hxx>
103 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
108 using namespace SMESH::Controls;
112 template < class ELEM_SET >
113 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
115 typedef SMDS_SetIterator
116 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
117 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
121 //=======================================================================
122 //function : SMESH_MeshEditor
124 //=======================================================================
126 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
127 :myMesh( theMesh ) // theMesh may be NULL
131 //================================================================================
133 * \brief Return mesh DS
135 //================================================================================
137 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
139 return myMesh->GetMeshDS();
143 //================================================================================
145 * \brief Clears myLastCreatedNodes and myLastCreatedElems
147 //================================================================================
149 void SMESH_MeshEditor::ClearLastCreated()
151 myLastCreatedNodes.Clear();
152 myLastCreatedElems.Clear();
155 //================================================================================
157 * \brief Initializes members by an existing element
158 * \param [in] elem - the source element
159 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
161 //================================================================================
163 SMESH_MeshEditor::ElemFeatures&
164 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
168 myType = elem->GetType();
169 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
171 myIsPoly = elem->IsPoly();
174 myIsQuad = elem->IsQuadratic();
175 if ( myType == SMDSAbs_Volume && !basicOnly )
177 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
178 myPolyhedQuantities.swap( quant );
182 else if ( myType == SMDSAbs_Ball && !basicOnly )
184 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
190 //=======================================================================
194 //=======================================================================
197 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
198 const ElemFeatures& features)
200 SMDS_MeshElement* e = 0;
201 int nbnode = node.size();
202 SMESHDS_Mesh* mesh = GetMeshDS();
203 const int ID = features.myID;
205 switch ( features.myType ) {
207 if ( !features.myIsPoly ) {
209 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2] );
212 else if (nbnode == 4) {
213 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
214 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
216 else if (nbnode == 6) {
217 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], ID);
219 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
222 else if (nbnode == 7) {
223 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], ID);
225 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6] );
228 else if (nbnode == 8) {
229 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
230 node[4], node[5], node[6], node[7], ID);
231 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
232 node[4], node[5], node[6], node[7] );
234 else if (nbnode == 9) {
235 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
236 node[4], node[5], node[6], node[7], node[8], ID);
237 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
238 node[4], node[5], node[6], node[7], node[8] );
241 else if ( !features.myIsQuad )
243 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
244 else e = mesh->AddPolygonalFace (node );
246 else if ( nbnode % 2 == 0 ) // just a protection
248 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
249 else e = mesh->AddQuadPolygonalFace (node );
254 if ( !features.myIsPoly ) {
256 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
257 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
259 else if (nbnode == 5) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
265 else if (nbnode == 6) {
266 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
267 node[4], node[5], ID);
268 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
271 else if (nbnode == 8) {
272 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7], ID);
274 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7] );
277 else if (nbnode == 10) {
278 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
279 node[4], node[5], node[6], node[7],
280 node[8], node[9], ID);
281 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
282 node[4], node[5], node[6], node[7],
285 else if (nbnode == 12) {
286 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10], node[11], ID);
289 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10], node[11] );
293 else if (nbnode == 13) {
294 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
295 node[4], node[5], node[6], node[7],
296 node[8], node[9], node[10],node[11],
298 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
299 node[4], node[5], node[6], node[7],
300 node[8], node[9], node[10],node[11],
303 else if (nbnode == 15) {
304 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],ID);
308 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
309 node[4], node[5], node[6], node[7],
310 node[8], node[9], node[10],node[11],
311 node[12],node[13],node[14] );
313 else if (nbnode == 20) {
314 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
315 node[4], node[5], node[6], node[7],
316 node[8], node[9], node[10],node[11],
317 node[12],node[13],node[14],node[15],
318 node[16],node[17],node[18],node[19],ID);
319 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
320 node[4], node[5], node[6], node[7],
321 node[8], node[9], node[10],node[11],
322 node[12],node[13],node[14],node[15],
323 node[16],node[17],node[18],node[19] );
325 else if (nbnode == 27) {
326 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
327 node[4], node[5], node[6], node[7],
328 node[8], node[9], node[10],node[11],
329 node[12],node[13],node[14],node[15],
330 node[16],node[17],node[18],node[19],
331 node[20],node[21],node[22],node[23],
332 node[24],node[25],node[26], ID);
333 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
334 node[4], node[5], node[6], node[7],
335 node[8], node[9], node[10],node[11],
336 node[12],node[13],node[14],node[15],
337 node[16],node[17],node[18],node[19],
338 node[20],node[21],node[22],node[23],
339 node[24],node[25],node[26] );
342 else if ( !features.myIsQuad )
344 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
345 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
349 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
350 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
356 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
357 else e = mesh->AddEdge (node[0], node[1] );
359 else if ( nbnode == 3 ) {
360 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
361 else e = mesh->AddEdge (node[0], node[1], node[2] );
365 case SMDSAbs_0DElement:
367 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
368 else e = mesh->Add0DElement (node[0] );
373 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
374 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
378 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
379 else e = mesh->AddBall (node[0], features.myBallDiameter );
384 if ( e ) myLastCreatedElems.Append( e );
388 //=======================================================================
392 //=======================================================================
394 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
395 const ElemFeatures& features)
397 vector<const SMDS_MeshNode*> nodes;
398 nodes.reserve( nodeIDs.size() );
399 vector<int>::const_iterator id = nodeIDs.begin();
400 while ( id != nodeIDs.end() ) {
401 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
402 nodes.push_back( node );
406 return AddElement( nodes, features );
409 //=======================================================================
411 //purpose : Remove a node or an element.
412 // Modify a compute state of sub-meshes which become empty
413 //=======================================================================
415 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
418 myLastCreatedElems.Clear();
419 myLastCreatedNodes.Clear();
421 SMESHDS_Mesh* aMesh = GetMeshDS();
422 set< SMESH_subMesh *> smmap;
425 list<int>::const_iterator it = theIDs.begin();
426 for ( ; it != theIDs.end(); it++ ) {
427 const SMDS_MeshElement * elem;
429 elem = aMesh->FindNode( *it );
431 elem = aMesh->FindElement( *it );
435 // Notify VERTEX sub-meshes about modification
437 const SMDS_MeshNode* node = cast2Node( elem );
438 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
439 if ( int aShapeID = node->getshapeId() )
440 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
443 // Find sub-meshes to notify about modification
444 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
445 // while ( nodeIt->more() ) {
446 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
447 // const SMDS_PositionPtr& aPosition = node->GetPosition();
448 // if ( aPosition.get() ) {
449 // if ( int aShapeID = aPosition->GetShapeId() ) {
450 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
451 // smmap.insert( sm );
458 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
460 aMesh->RemoveElement( elem );
464 // Notify sub-meshes about modification
465 if ( !smmap.empty() ) {
466 set< SMESH_subMesh *>::iterator smIt;
467 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
468 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
471 // // Check if the whole mesh becomes empty
472 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
473 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
478 //================================================================================
480 * \brief Create 0D elements on all nodes of the given object.
481 * \param elements - Elements on whose nodes to create 0D elements; if empty,
482 * the all mesh is treated
483 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
484 * \param duplicateElements - to add one more 0D element to a node or not
486 //================================================================================
488 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
489 TIDSortedElemSet& all0DElems,
490 const bool duplicateElements )
492 SMDS_ElemIteratorPtr elemIt;
493 if ( elements.empty() )
495 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
499 elemIt = elemSetIterator( elements );
502 while ( elemIt->more() )
504 const SMDS_MeshElement* e = elemIt->next();
505 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
506 while ( nodeIt->more() )
508 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
509 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
510 if ( duplicateElements || !it0D->more() )
512 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
513 all0DElems.insert( myLastCreatedElems.Last() );
515 while ( it0D->more() )
516 all0DElems.insert( it0D->next() );
521 //=======================================================================
522 //function : FindShape
523 //purpose : Return an index of the shape theElem is on
524 // or zero if a shape not found
525 //=======================================================================
527 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
529 myLastCreatedElems.Clear();
530 myLastCreatedNodes.Clear();
532 SMESHDS_Mesh * aMesh = GetMeshDS();
533 if ( aMesh->ShapeToMesh().IsNull() )
536 int aShapeID = theElem->getshapeId();
540 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
541 if ( sm->Contains( theElem ))
544 if ( theElem->GetType() == SMDSAbs_Node ) {
545 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
548 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
551 TopoDS_Shape aShape; // the shape a node of theElem is on
552 if ( theElem->GetType() != SMDSAbs_Node )
554 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
555 while ( nodeIt->more() ) {
556 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
557 if ((aShapeID = node->getshapeId()) > 0) {
558 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
559 if ( sm->Contains( theElem ))
561 if ( aShape.IsNull() )
562 aShape = aMesh->IndexToShape( aShapeID );
568 // None of nodes is on a proper shape,
569 // find the shape among ancestors of aShape on which a node is
570 if ( !aShape.IsNull() ) {
571 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
572 for ( ; ancIt.More(); ancIt.Next() ) {
573 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
574 if ( sm && sm->Contains( theElem ))
575 return aMesh->ShapeToIndex( ancIt.Value() );
580 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
581 while ( const SMESHDS_SubMesh* sm = smIt->next() )
582 if ( sm->Contains( theElem ))
589 //=======================================================================
590 //function : IsMedium
592 //=======================================================================
594 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
595 const SMDSAbs_ElementType typeToCheck)
597 bool isMedium = false;
598 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
599 while (it->more() && !isMedium ) {
600 const SMDS_MeshElement* elem = it->next();
601 isMedium = elem->IsMediumNode(node);
606 //=======================================================================
607 //function : shiftNodesQuadTria
608 //purpose : Shift nodes in the array corresponded to quadratic triangle
609 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
610 //=======================================================================
612 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
614 const SMDS_MeshNode* nd1 = aNodes[0];
615 aNodes[0] = aNodes[1];
616 aNodes[1] = aNodes[2];
618 const SMDS_MeshNode* nd2 = aNodes[3];
619 aNodes[3] = aNodes[4];
620 aNodes[4] = aNodes[5];
624 //=======================================================================
625 //function : nbEdgeConnectivity
626 //purpose : return number of the edges connected with the theNode.
627 // if theEdges has connections with the other type of the
628 // elements, return -1
629 //=======================================================================
631 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
633 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
635 // while(elemIt->more()) {
640 return theNode->NbInverseElements();
643 //=======================================================================
644 //function : getNodesFromTwoTria
646 //=======================================================================
648 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
649 const SMDS_MeshElement * theTria2,
650 vector< const SMDS_MeshNode*>& N1,
651 vector< const SMDS_MeshNode*>& N2)
653 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
654 if ( N1.size() < 6 ) return false;
655 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
656 if ( N2.size() < 6 ) return false;
658 int sames[3] = {-1,-1,-1};
670 if(nbsames!=2) return false;
672 shiftNodesQuadTria(N1);
674 shiftNodesQuadTria(N1);
677 i = sames[0] + sames[1] + sames[2];
679 shiftNodesQuadTria(N2);
681 // now we receive following N1 and N2 (using numeration as in the image below)
682 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
683 // i.e. first nodes from both arrays form a new diagonal
687 //=======================================================================
688 //function : InverseDiag
689 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
690 // but having other common link.
691 // Return False if args are improper
692 //=======================================================================
694 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
695 const SMDS_MeshElement * theTria2 )
697 myLastCreatedElems.Clear();
698 myLastCreatedNodes.Clear();
700 if (!theTria1 || !theTria2)
703 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
704 if (!F1) return false;
705 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
706 if (!F2) return false;
707 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
708 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
710 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
711 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
715 // put nodes in array and find out indices of the same ones
716 const SMDS_MeshNode* aNodes [6];
717 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
719 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
720 while ( it->more() ) {
721 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
723 if ( i > 2 ) // theTria2
724 // find same node of theTria1
725 for ( int j = 0; j < 3; j++ )
726 if ( aNodes[ i ] == aNodes[ j ]) {
735 return false; // theTria1 is not a triangle
736 it = theTria2->nodesIterator();
738 if ( i == 6 && it->more() )
739 return false; // theTria2 is not a triangle
742 // find indices of 1,2 and of A,B in theTria1
743 int iA = -1, iB = 0, i1 = 0, i2 = 0;
744 for ( i = 0; i < 6; i++ ) {
745 if ( sameInd [ i ] == -1 ) {
750 if ( iA >= 0) iB = i;
754 // nodes 1 and 2 should not be the same
755 if ( aNodes[ i1 ] == aNodes[ i2 ] )
759 aNodes[ iA ] = aNodes[ i2 ];
761 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
763 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
764 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
768 } // end if(F1 && F2)
770 // check case of quadratic faces
771 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
772 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
774 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
775 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
779 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
780 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
788 vector< const SMDS_MeshNode* > N1;
789 vector< const SMDS_MeshNode* > N2;
790 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
792 // now we receive following N1 and N2 (using numeration as above image)
793 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
794 // i.e. first nodes from both arrays determ new diagonal
796 vector< const SMDS_MeshNode*> N1new( N1.size() );
797 vector< const SMDS_MeshNode*> N2new( N2.size() );
798 N1new.back() = N1.back(); // central node of biquadratic
799 N2new.back() = N2.back();
800 N1new[0] = N1[0]; N2new[0] = N1[0];
801 N1new[1] = N2[0]; N2new[1] = N1[1];
802 N1new[2] = N2[1]; N2new[2] = N2[0];
803 N1new[3] = N1[4]; N2new[3] = N1[3];
804 N1new[4] = N2[3]; N2new[4] = N2[5];
805 N1new[5] = N1[5]; N2new[5] = N1[4];
806 // change nodes in faces
807 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
808 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
810 // move the central node of biquadratic triangle
811 SMESH_MesherHelper helper( *GetMesh() );
812 for ( int is2nd = 0; is2nd < 2; ++is2nd )
814 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
815 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
816 if ( nodes.size() < 7 )
818 helper.SetSubShape( tria->getshapeId() );
819 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
823 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
824 SMESH_TNodeXYZ( nodes[4] ) +
825 SMESH_TNodeXYZ( nodes[5] )) / 3.;
830 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
831 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
832 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
834 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
835 xyz = S->Value( uv.X(), uv.Y() );
836 xyz.Transform( loc );
837 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
838 nodes[6]->getshapeId() > 0 )
839 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
841 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
846 //=======================================================================
847 //function : findTriangles
848 //purpose : find triangles sharing theNode1-theNode2 link
849 //=======================================================================
851 static bool findTriangles(const SMDS_MeshNode * theNode1,
852 const SMDS_MeshNode * theNode2,
853 const SMDS_MeshElement*& theTria1,
854 const SMDS_MeshElement*& theTria2)
856 if ( !theNode1 || !theNode2 ) return false;
858 theTria1 = theTria2 = 0;
860 set< const SMDS_MeshElement* > emap;
861 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
863 const SMDS_MeshElement* elem = it->next();
864 if ( elem->NbCornerNodes() == 3 )
867 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
869 const SMDS_MeshElement* elem = it->next();
870 if ( emap.count( elem )) {
878 // theTria1 must be element with minimum ID
879 if ( theTria2->GetID() < theTria1->GetID() )
880 std::swap( theTria2, theTria1 );
888 //=======================================================================
889 //function : InverseDiag
890 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
891 // with ones built on the same 4 nodes but having other common link.
892 // Return false if proper faces not found
893 //=======================================================================
895 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
896 const SMDS_MeshNode * theNode2)
898 myLastCreatedElems.Clear();
899 myLastCreatedNodes.Clear();
901 const SMDS_MeshElement *tr1, *tr2;
902 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
905 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
906 if (!F1) return false;
907 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
908 if (!F2) return false;
909 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
910 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
912 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
913 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
917 // put nodes in array
918 // and find indices of 1,2 and of A in tr1 and of B in tr2
919 int i, iA1 = 0, i1 = 0;
920 const SMDS_MeshNode* aNodes1 [3];
921 SMDS_ElemIteratorPtr it;
922 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
923 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
924 if ( aNodes1[ i ] == theNode1 )
925 iA1 = i; // node A in tr1
926 else if ( aNodes1[ i ] != theNode2 )
930 const SMDS_MeshNode* aNodes2 [3];
931 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
932 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
933 if ( aNodes2[ i ] == theNode2 )
934 iB2 = i; // node B in tr2
935 else if ( aNodes2[ i ] != theNode1 )
939 // nodes 1 and 2 should not be the same
940 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
944 aNodes1[ iA1 ] = aNodes2[ i2 ];
946 aNodes2[ iB2 ] = aNodes1[ i1 ];
948 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
949 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
954 // check case of quadratic faces
955 return InverseDiag(tr1,tr2);
958 //=======================================================================
959 //function : getQuadrangleNodes
960 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
961 // fusion of triangles tr1 and tr2 having shared link on
962 // theNode1 and theNode2
963 //=======================================================================
965 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
966 const SMDS_MeshNode * theNode1,
967 const SMDS_MeshNode * theNode2,
968 const SMDS_MeshElement * tr1,
969 const SMDS_MeshElement * tr2 )
971 if( tr1->NbNodes() != tr2->NbNodes() )
973 // find the 4-th node to insert into tr1
974 const SMDS_MeshNode* n4 = 0;
975 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
977 while ( !n4 && i<3 ) {
978 const SMDS_MeshNode * n = cast2Node( it->next() );
980 bool isDiag = ( n == theNode1 || n == theNode2 );
984 // Make an array of nodes to be in a quadrangle
985 int iNode = 0, iFirstDiag = -1;
986 it = tr1->nodesIterator();
989 const SMDS_MeshNode * n = cast2Node( it->next() );
991 bool isDiag = ( n == theNode1 || n == theNode2 );
993 if ( iFirstDiag < 0 )
995 else if ( iNode - iFirstDiag == 1 )
996 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
998 else if ( n == n4 ) {
999 return false; // tr1 and tr2 should not have all the same nodes
1001 theQuadNodes[ iNode++ ] = n;
1003 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1004 theQuadNodes[ iNode ] = n4;
1009 //=======================================================================
1010 //function : DeleteDiag
1011 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1012 // with a quadrangle built on the same 4 nodes.
1013 // Return false if proper faces not found
1014 //=======================================================================
1016 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1017 const SMDS_MeshNode * theNode2)
1019 myLastCreatedElems.Clear();
1020 myLastCreatedNodes.Clear();
1022 const SMDS_MeshElement *tr1, *tr2;
1023 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1026 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1027 if (!F1) return false;
1028 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1029 if (!F2) return false;
1030 SMESHDS_Mesh * aMesh = GetMeshDS();
1032 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1033 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1035 const SMDS_MeshNode* aNodes [ 4 ];
1036 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1039 const SMDS_MeshElement* newElem = 0;
1040 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1041 myLastCreatedElems.Append(newElem);
1042 AddToSameGroups( newElem, tr1, aMesh );
1043 int aShapeId = tr1->getshapeId();
1046 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1048 aMesh->RemoveElement( tr1 );
1049 aMesh->RemoveElement( tr2 );
1054 // check case of quadratic faces
1055 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1057 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1061 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1062 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1070 vector< const SMDS_MeshNode* > N1;
1071 vector< const SMDS_MeshNode* > N2;
1072 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1074 // now we receive following N1 and N2 (using numeration as above image)
1075 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1076 // i.e. first nodes from both arrays determ new diagonal
1078 const SMDS_MeshNode* aNodes[8];
1088 const SMDS_MeshElement* newElem = 0;
1089 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1090 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1091 myLastCreatedElems.Append(newElem);
1092 AddToSameGroups( newElem, tr1, aMesh );
1093 int aShapeId = tr1->getshapeId();
1096 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1098 aMesh->RemoveElement( tr1 );
1099 aMesh->RemoveElement( tr2 );
1101 // remove middle node (9)
1102 GetMeshDS()->RemoveNode( N1[4] );
1107 //=======================================================================
1108 //function : Reorient
1109 //purpose : Reverse theElement orientation
1110 //=======================================================================
1112 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1114 myLastCreatedElems.Clear();
1115 myLastCreatedNodes.Clear();
1119 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1120 if ( !it || !it->more() )
1123 const SMDSAbs_ElementType type = theElem->GetType();
1124 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1127 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1128 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1130 const SMDS_VtkVolume* aPolyedre =
1131 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1133 MESSAGE("Warning: bad volumic element");
1136 const int nbFaces = aPolyedre->NbFaces();
1137 vector<const SMDS_MeshNode *> poly_nodes;
1138 vector<int> quantities (nbFaces);
1140 // reverse each face of the polyedre
1141 for (int iface = 1; iface <= nbFaces; iface++) {
1142 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1143 quantities[iface - 1] = nbFaceNodes;
1145 for (inode = nbFaceNodes; inode >= 1; inode--) {
1146 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1147 poly_nodes.push_back(curNode);
1150 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1152 else // other elements
1154 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1155 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1156 if ( interlace.empty() )
1158 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1162 SMDS_MeshCell::applyInterlace( interlace, nodes );
1164 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1169 //================================================================================
1171 * \brief Reorient faces.
1172 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1173 * \param theDirection - desired direction of normal of \a theFace
1174 * \param theFace - one of \a theFaces that should be oriented according to
1175 * \a theDirection and whose orientation defines orientation of other faces
1176 * \return number of reoriented faces.
1178 //================================================================================
1180 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1181 const gp_Dir& theDirection,
1182 const SMDS_MeshElement * theFace)
1185 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1187 if ( theFaces.empty() )
1189 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1190 while ( fIt->more() )
1191 theFaces.insert( theFaces.end(), fIt->next() );
1194 // orient theFace according to theDirection
1196 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1197 if ( normal * theDirection.XYZ() < 0 )
1198 nbReori += Reorient( theFace );
1200 // Orient other faces
1202 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1203 TIDSortedElemSet avoidSet;
1204 set< SMESH_TLink > checkedLinks;
1205 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1207 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1208 theFaces.erase( theFace );
1209 startFaces.insert( theFace );
1211 int nodeInd1, nodeInd2;
1212 const SMDS_MeshElement* otherFace;
1213 vector< const SMDS_MeshElement* > facesNearLink;
1214 vector< std::pair< int, int > > nodeIndsOfFace;
1216 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1217 while ( !startFaces.empty() )
1219 startFace = startFaces.begin();
1220 theFace = *startFace;
1221 startFaces.erase( startFace );
1222 if ( !visitedFaces.insert( theFace ).second )
1226 avoidSet.insert(theFace);
1228 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1230 const int nbNodes = theFace->NbCornerNodes();
1231 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1233 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1234 linkIt_isNew = checkedLinks.insert( link );
1235 if ( !linkIt_isNew.second )
1237 // link has already been checked and won't be encountered more
1238 // if the group (theFaces) is manifold
1239 //checkedLinks.erase( linkIt_isNew.first );
1243 facesNearLink.clear();
1244 nodeIndsOfFace.clear();
1245 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1247 &nodeInd1, &nodeInd2 )))
1248 if ( otherFace != theFace)
1250 facesNearLink.push_back( otherFace );
1251 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1252 avoidSet.insert( otherFace );
1254 if ( facesNearLink.size() > 1 )
1256 // NON-MANIFOLD mesh shell !
1257 // select a face most co-directed with theFace,
1258 // other faces won't be visited this time
1260 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1261 double proj, maxProj = -1;
1262 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1263 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1264 if (( proj = Abs( NF * NOF )) > maxProj ) {
1266 otherFace = facesNearLink[i];
1267 nodeInd1 = nodeIndsOfFace[i].first;
1268 nodeInd2 = nodeIndsOfFace[i].second;
1271 // not to visit rejected faces
1272 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1273 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1274 visitedFaces.insert( facesNearLink[i] );
1276 else if ( facesNearLink.size() == 1 )
1278 otherFace = facesNearLink[0];
1279 nodeInd1 = nodeIndsOfFace.back().first;
1280 nodeInd2 = nodeIndsOfFace.back().second;
1282 if ( otherFace && otherFace != theFace)
1284 // link must be reverse in otherFace if orientation ot otherFace
1285 // is same as that of theFace
1286 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1288 nbReori += Reorient( otherFace );
1290 startFaces.insert( otherFace );
1293 std::swap( link.first, link.second ); // reverse the link
1299 //================================================================================
1301 * \brief Reorient faces basing on orientation of adjacent volumes.
1302 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1303 * \param theVolumes - reference volumes.
1304 * \param theOutsideNormal - to orient faces to have their normal
1305 * pointing either \a outside or \a inside the adjacent volumes.
1306 * \return number of reoriented faces.
1308 //================================================================================
1310 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1311 TIDSortedElemSet & theVolumes,
1312 const bool theOutsideNormal)
1316 SMDS_ElemIteratorPtr faceIt;
1317 if ( theFaces.empty() )
1318 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1320 faceIt = elemSetIterator( theFaces );
1322 vector< const SMDS_MeshNode* > faceNodes;
1323 TIDSortedElemSet checkedVolumes;
1324 set< const SMDS_MeshNode* > faceNodesSet;
1325 SMDS_VolumeTool volumeTool;
1327 while ( faceIt->more() ) // loop on given faces
1329 const SMDS_MeshElement* face = faceIt->next();
1330 if ( face->GetType() != SMDSAbs_Face )
1333 const size_t nbCornersNodes = face->NbCornerNodes();
1334 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1336 checkedVolumes.clear();
1337 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1338 while ( vIt->more() )
1340 const SMDS_MeshElement* volume = vIt->next();
1342 if ( !checkedVolumes.insert( volume ).second )
1344 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1347 // is volume adjacent?
1348 bool allNodesCommon = true;
1349 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1350 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1351 if ( !allNodesCommon )
1354 // get nodes of a corresponding volume facet
1355 faceNodesSet.clear();
1356 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1357 volumeTool.Set( volume );
1358 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1359 if ( facetID < 0 ) continue;
1360 volumeTool.SetExternalNormal();
1361 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1363 // compare order of faceNodes and facetNodes
1364 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1366 for ( int i = 0; i < 2; ++i )
1368 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1369 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1370 if ( faceNodes[ iN ] == n )
1376 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1377 if ( isOutside != theOutsideNormal )
1378 nbReori += Reorient( face );
1380 } // loop on given faces
1385 //=======================================================================
1386 //function : getBadRate
1388 //=======================================================================
1390 static double getBadRate (const SMDS_MeshElement* theElem,
1391 SMESH::Controls::NumericalFunctorPtr& theCrit)
1393 SMESH::Controls::TSequenceOfXYZ P;
1394 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1396 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1397 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1400 //=======================================================================
1401 //function : QuadToTri
1402 //purpose : Cut quadrangles into triangles.
1403 // theCrit is used to select a diagonal to cut
1404 //=======================================================================
1406 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1407 SMESH::Controls::NumericalFunctorPtr theCrit)
1409 myLastCreatedElems.Clear();
1410 myLastCreatedNodes.Clear();
1412 if ( !theCrit.get() )
1415 SMESHDS_Mesh * aMesh = GetMeshDS();
1417 Handle(Geom_Surface) surface;
1418 SMESH_MesherHelper helper( *GetMesh() );
1420 TIDSortedElemSet::iterator itElem;
1421 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1423 const SMDS_MeshElement* elem = *itElem;
1424 if ( !elem || elem->GetType() != SMDSAbs_Face )
1426 if ( elem->NbCornerNodes() != 4 )
1429 // retrieve element nodes
1430 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1432 // compare two sets of possible triangles
1433 double aBadRate1, aBadRate2; // to what extent a set is bad
1434 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1435 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1436 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1438 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1439 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1440 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1442 const int aShapeId = FindShape( elem );
1443 const SMDS_MeshElement* newElem1 = 0;
1444 const SMDS_MeshElement* newElem2 = 0;
1446 if ( !elem->IsQuadratic() ) // split liner quadrangle
1448 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1449 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1450 if ( aBadRate1 <= aBadRate2 ) {
1451 // tr1 + tr2 is better
1452 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1453 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1456 // tr3 + tr4 is better
1457 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1458 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1461 else // split quadratic quadrangle
1463 helper.SetIsQuadratic( true );
1464 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1466 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1467 if ( aNodes.size() == 9 )
1469 helper.SetIsBiQuadratic( true );
1470 if ( aBadRate1 <= aBadRate2 )
1471 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1473 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1475 // create a new element
1476 if ( aBadRate1 <= aBadRate2 ) {
1477 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1478 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1481 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1482 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1486 // care of a new element
1488 myLastCreatedElems.Append(newElem1);
1489 myLastCreatedElems.Append(newElem2);
1490 AddToSameGroups( newElem1, elem, aMesh );
1491 AddToSameGroups( newElem2, elem, aMesh );
1493 // put a new triangle on the same shape
1495 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1496 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1498 aMesh->RemoveElement( elem );
1503 //=======================================================================
1505 * \brief Split each of given quadrangles into 4 triangles.
1506 * \param theElems - The faces to be splitted. If empty all faces are split.
1508 //=======================================================================
1510 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1512 myLastCreatedElems.Clear();
1513 myLastCreatedNodes.Clear();
1515 SMESH_MesherHelper helper( *GetMesh() );
1516 helper.SetElementsOnShape( true );
1518 SMDS_ElemIteratorPtr faceIt;
1519 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1520 else faceIt = elemSetIterator( theElems );
1523 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1525 vector< const SMDS_MeshNode* > nodes;
1526 SMESHDS_SubMesh* subMeshDS = 0;
1528 Handle(Geom_Surface) surface;
1529 TopLoc_Location loc;
1531 while ( faceIt->more() )
1533 const SMDS_MeshElement* quad = faceIt->next();
1534 if ( !quad || quad->NbCornerNodes() != 4 )
1537 // get a surface the quad is on
1539 if ( quad->getshapeId() < 1 )
1542 helper.SetSubShape( 0 );
1545 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1547 helper.SetSubShape( quad->getshapeId() );
1548 if ( !helper.GetSubShape().IsNull() &&
1549 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1551 F = TopoDS::Face( helper.GetSubShape() );
1552 surface = BRep_Tool::Surface( F, loc );
1553 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1557 helper.SetSubShape( 0 );
1562 // create a central node
1564 const SMDS_MeshNode* nCentral;
1565 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1567 if ( nodes.size() == 9 )
1569 nCentral = nodes.back();
1576 for ( ; iN < nodes.size(); ++iN )
1577 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1579 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1580 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1582 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1583 xyz[0], xyz[1], xyz[2], xyz[3],
1584 xyz[4], xyz[5], xyz[6], xyz[7] );
1588 for ( ; iN < nodes.size(); ++iN )
1589 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1591 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1592 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1594 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1595 uv[0], uv[1], uv[2], uv[3],
1596 uv[4], uv[5], uv[6], uv[7] );
1598 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1602 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1603 uv[8].X(), uv[8].Y() );
1604 myLastCreatedNodes.Append( nCentral );
1607 // create 4 triangles
1609 helper.SetIsQuadratic ( nodes.size() > 4 );
1610 helper.SetIsBiQuadratic( nodes.size() == 9 );
1611 if ( helper.GetIsQuadratic() )
1612 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1614 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1616 for ( int i = 0; i < 4; ++i )
1618 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1621 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1622 myLastCreatedElems.Append( tria );
1627 //=======================================================================
1628 //function : BestSplit
1629 //purpose : Find better diagonal for cutting.
1630 //=======================================================================
1632 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1633 SMESH::Controls::NumericalFunctorPtr theCrit)
1635 myLastCreatedElems.Clear();
1636 myLastCreatedNodes.Clear();
1641 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1644 if( theQuad->NbNodes()==4 ||
1645 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1647 // retrieve element nodes
1648 const SMDS_MeshNode* aNodes [4];
1649 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1651 //while (itN->more())
1653 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1655 // compare two sets of possible triangles
1656 double aBadRate1, aBadRate2; // to what extent a set is bad
1657 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1658 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1659 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1661 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1662 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1663 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1664 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1665 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1666 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1667 return 1; // diagonal 1-3
1669 return 2; // diagonal 2-4
1676 // Methods of splitting volumes into tetra
1678 const int theHexTo5_1[5*4+1] =
1680 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1682 const int theHexTo5_2[5*4+1] =
1684 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1686 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1688 const int theHexTo6_1[6*4+1] =
1690 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
1692 const int theHexTo6_2[6*4+1] =
1694 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
1696 const int theHexTo6_3[6*4+1] =
1698 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
1700 const int theHexTo6_4[6*4+1] =
1702 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
1704 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1706 const int thePyraTo2_1[2*4+1] =
1708 0, 1, 2, 4, 0, 2, 3, 4, -1
1710 const int thePyraTo2_2[2*4+1] =
1712 1, 2, 3, 4, 1, 3, 0, 4, -1
1714 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1716 const int thePentaTo3_1[3*4+1] =
1718 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1720 const int thePentaTo3_2[3*4+1] =
1722 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1724 const int thePentaTo3_3[3*4+1] =
1726 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1728 const int thePentaTo3_4[3*4+1] =
1730 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1732 const int thePentaTo3_5[3*4+1] =
1734 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1736 const int thePentaTo3_6[3*4+1] =
1738 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1740 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1741 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1743 // Methods of splitting hexahedron into prisms
1745 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1747 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
1749 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1751 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
1753 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1755 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
1758 const int theHexTo2Prisms_BT_1[6*2+1] =
1760 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1762 const int theHexTo2Prisms_BT_2[6*2+1] =
1764 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1766 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1768 const int theHexTo2Prisms_LR_1[6*2+1] =
1770 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1772 const int theHexTo2Prisms_LR_2[6*2+1] =
1774 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1776 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1778 const int theHexTo2Prisms_FB_1[6*2+1] =
1780 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1782 const int theHexTo2Prisms_FB_2[6*2+1] =
1784 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1786 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1789 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1792 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1793 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1794 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1795 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1801 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1802 bool _baryNode; //!< additional node is to be created at cell barycenter
1803 bool _ownConn; //!< to delete _connectivity in destructor
1804 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1806 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1807 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1808 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1809 bool hasFacet( const TTriangleFacet& facet ) const
1811 if ( _nbCorners == 4 )
1813 const int* tetConn = _connectivity;
1814 for ( ; tetConn[0] >= 0; tetConn += 4 )
1815 if (( facet.contains( tetConn[0] ) +
1816 facet.contains( tetConn[1] ) +
1817 facet.contains( tetConn[2] ) +
1818 facet.contains( tetConn[3] )) == 3 )
1821 else // prism, _nbCorners == 6
1823 const int* prismConn = _connectivity;
1824 for ( ; prismConn[0] >= 0; prismConn += 6 )
1826 if (( facet.contains( prismConn[0] ) &&
1827 facet.contains( prismConn[1] ) &&
1828 facet.contains( prismConn[2] ))
1830 ( facet.contains( prismConn[3] ) &&
1831 facet.contains( prismConn[4] ) &&
1832 facet.contains( prismConn[5] )))
1840 //=======================================================================
1842 * \brief return TSplitMethod for the given element to split into tetrahedra
1844 //=======================================================================
1846 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1848 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1850 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1851 // an edge and a face barycenter; tertaherdons are based on triangles and
1852 // a volume barycenter
1853 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1855 // Find out how adjacent volumes are split
1857 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1858 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1859 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1861 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1862 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1863 if ( nbNodes < 4 ) continue;
1865 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1866 const int* nInd = vol.GetFaceNodesIndices( iF );
1869 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1870 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1871 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1872 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1876 int iCom = 0; // common node of triangle faces to split into
1877 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1879 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1880 nInd[ iQ * ( (iCom+1)%nbNodes )],
1881 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1882 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1883 nInd[ iQ * ( (iCom+2)%nbNodes )],
1884 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1885 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1887 triaSplits.push_back( t012 );
1888 triaSplits.push_back( t023 );
1893 if ( !triaSplits.empty() )
1894 hasAdjacentSplits = true;
1897 // Among variants of split method select one compliant with adjacent volumes
1899 TSplitMethod method;
1900 if ( !vol.Element()->IsPoly() && !is24TetMode )
1902 int nbVariants = 2, nbTet = 0;
1903 const int** connVariants = 0;
1904 switch ( vol.Element()->GetEntityType() )
1906 case SMDSEntity_Hexa:
1907 case SMDSEntity_Quad_Hexa:
1908 case SMDSEntity_TriQuad_Hexa:
1909 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1910 connVariants = theHexTo5, nbTet = 5;
1912 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1914 case SMDSEntity_Pyramid:
1915 case SMDSEntity_Quad_Pyramid:
1916 connVariants = thePyraTo2; nbTet = 2;
1918 case SMDSEntity_Penta:
1919 case SMDSEntity_Quad_Penta:
1920 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1925 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1927 // check method compliancy with adjacent tetras,
1928 // all found splits must be among facets of tetras described by this method
1929 method = TSplitMethod( nbTet, connVariants[variant] );
1930 if ( hasAdjacentSplits && method._nbSplits > 0 )
1932 bool facetCreated = true;
1933 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1935 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1936 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1937 facetCreated = method.hasFacet( *facet );
1939 if ( !facetCreated )
1940 method = TSplitMethod(0); // incompatible method
1944 if ( method._nbSplits < 1 )
1946 // No standard method is applicable, use a generic solution:
1947 // each facet of a volume is split into triangles and
1948 // each of triangles and a volume barycenter form a tetrahedron.
1950 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1952 int* connectivity = new int[ maxTetConnSize + 1 ];
1953 method._connectivity = connectivity;
1954 method._ownConn = true;
1955 method._baryNode = !isHex27; // to create central node or not
1958 int baryCenInd = vol.NbNodes() - int( isHex27 );
1959 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1961 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1962 const int* nInd = vol.GetFaceNodesIndices( iF );
1963 // find common node of triangle facets of tetra to create
1964 int iCommon = 0; // index in linear numeration
1965 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1966 if ( !triaSplits.empty() )
1969 const TTriangleFacet* facet = &triaSplits.front();
1970 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1971 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1972 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1975 else if ( nbNodes > 3 && !is24TetMode )
1977 // find the best method of splitting into triangles by aspect ratio
1978 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1979 map< double, int > badness2iCommon;
1980 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1981 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1982 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1985 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1987 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1988 nodes[ iQ*((iLast-1)%nbNodes)],
1989 nodes[ iQ*((iLast )%nbNodes)]);
1990 badness += getBadRate( &tria, aspectRatio );
1992 badness2iCommon.insert( make_pair( badness, iCommon ));
1994 // use iCommon with lowest badness
1995 iCommon = badness2iCommon.begin()->second;
1997 if ( iCommon >= nbNodes )
1998 iCommon = 0; // something wrong
2000 // fill connectivity of tetrahedra based on a current face
2001 int nbTet = nbNodes - 2;
2002 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2007 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2008 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2012 method._faceBaryNode[ iF ] = 0;
2013 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2016 for ( int i = 0; i < nbTet; ++i )
2018 int i1 = i, i2 = (i+1) % nbNodes;
2019 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2020 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2021 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2022 connectivity[ connSize++ ] = faceBaryCenInd;
2023 connectivity[ connSize++ ] = baryCenInd;
2028 for ( int i = 0; i < nbTet; ++i )
2030 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2031 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2032 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2033 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2034 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2035 connectivity[ connSize++ ] = baryCenInd;
2038 method._nbSplits += nbTet;
2040 } // loop on volume faces
2042 connectivity[ connSize++ ] = -1;
2044 } // end of generic solution
2048 //=======================================================================
2050 * \brief return TSplitMethod to split haxhedron into prisms
2052 //=======================================================================
2054 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2055 const int methodFlags,
2056 const int facetToSplit)
2058 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2060 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2062 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2064 static TSplitMethod to4methods[4]; // order BT, LR, FB
2065 if ( to4methods[iF]._nbSplits == 0 )
2069 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2070 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2071 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2074 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2075 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2079 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2080 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2081 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2083 default: return to4methods[3];
2085 to4methods[iF]._nbSplits = 4;
2086 to4methods[iF]._nbCorners = 6;
2088 return to4methods[iF];
2090 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2092 TSplitMethod method;
2094 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2096 const int nbVariants = 2, nbSplits = 2;
2097 const int** connVariants = 0;
2099 case 0: connVariants = theHexTo2Prisms_BT; break;
2100 case 1: connVariants = theHexTo2Prisms_LR; break;
2101 case 2: connVariants = theHexTo2Prisms_FB; break;
2102 default: return method;
2105 // look for prisms adjacent via facetToSplit and an opposite one
2106 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2108 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2109 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2110 if ( nbNodes != 4 ) return method;
2112 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2113 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2114 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2116 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2123 // there are adjacent prism
2124 for ( int variant = 0; variant < nbVariants; ++variant )
2126 // check method compliancy with adjacent prisms,
2127 // the found prism facets must be among facets of prisms described by current method
2128 method._nbSplits = nbSplits;
2129 method._nbCorners = 6;
2130 method._connectivity = connVariants[ variant ];
2131 if ( method.hasFacet( *t ))
2136 // No adjacent prisms. Select a variant with a best aspect ratio.
2138 double badness[2] = { 0., 0. };
2139 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2140 const SMDS_MeshNode** nodes = vol.GetNodes();
2141 for ( int variant = 0; variant < nbVariants; ++variant )
2142 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2144 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2145 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2147 method._connectivity = connVariants[ variant ];
2148 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2149 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2150 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2152 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2155 badness[ variant ] += getBadRate( &tria, aspectRatio );
2157 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2159 method._nbSplits = nbSplits;
2160 method._nbCorners = 6;
2161 method._connectivity = connVariants[ iBetter ];
2166 //================================================================================
2168 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2170 //================================================================================
2172 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2173 const SMDSAbs_GeometryType geom ) const
2175 // find the tetrahedron including the three nodes of facet
2176 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2177 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2178 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2179 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2180 while ( volIt1->more() )
2182 const SMDS_MeshElement* v = volIt1->next();
2183 if ( v->GetGeomType() != geom )
2185 const int lastCornerInd = v->NbCornerNodes() - 1;
2186 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2187 continue; // medium node not allowed
2188 const int ind2 = v->GetNodeIndex( n2 );
2189 if ( ind2 < 0 || lastCornerInd < ind2 )
2191 const int ind3 = v->GetNodeIndex( n3 );
2192 if ( ind3 < 0 || lastCornerInd < ind3 )
2199 //=======================================================================
2201 * \brief A key of a face of volume
2203 //=======================================================================
2205 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2207 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2209 TIDSortedNodeSet sortedNodes;
2210 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2211 int nbNodes = vol.NbFaceNodes( iF );
2212 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2213 for ( int i = 0; i < nbNodes; i += iQ )
2214 sortedNodes.insert( fNodes[i] );
2215 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2216 first.first = (*(n++))->GetID();
2217 first.second = (*(n++))->GetID();
2218 second.first = (*(n++))->GetID();
2219 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2224 //=======================================================================
2225 //function : SplitVolumes
2226 //purpose : Split volume elements into tetrahedra or prisms.
2227 // If facet ID < 0, element is split into tetrahedra,
2228 // else a hexahedron is split into prisms so that the given facet is
2229 // split into triangles
2230 //=======================================================================
2232 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2233 const int theMethodFlags)
2235 SMDS_VolumeTool volTool;
2236 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2237 fHelper.ToFixNodeParameters( true );
2239 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2240 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2242 SMESH_SequenceOfElemPtr newNodes, newElems;
2244 // map face of volume to it's baricenrtic node
2245 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2247 vector<const SMDS_MeshElement* > splitVols;
2249 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2250 for ( ; elem2facet != theElems.end(); ++elem2facet )
2252 const SMDS_MeshElement* elem = elem2facet->first;
2253 const int facetToSplit = elem2facet->second;
2254 if ( elem->GetType() != SMDSAbs_Volume )
2256 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2257 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2260 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2262 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2263 getTetraSplitMethod( volTool, theMethodFlags ) :
2264 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2265 if ( splitMethod._nbSplits < 1 ) continue;
2267 // find submesh to add new tetras to
2268 if ( !subMesh || !subMesh->Contains( elem ))
2270 int shapeID = FindShape( elem );
2271 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2272 subMesh = GetMeshDS()->MeshElements( shapeID );
2275 if ( elem->IsQuadratic() )
2278 // add quadratic links to the helper
2279 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2281 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2282 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2283 for ( int iN = 0; iN < nbN; iN += iQ )
2284 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2286 helper.SetIsQuadratic( true );
2291 helper.SetIsQuadratic( false );
2293 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2294 volTool.GetNodes() + elem->NbNodes() );
2295 helper.SetElementsOnShape( true );
2296 if ( splitMethod._baryNode )
2298 // make a node at barycenter
2299 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2300 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2301 nodes.push_back( gcNode );
2302 newNodes.Append( gcNode );
2304 if ( !splitMethod._faceBaryNode.empty() )
2306 // make or find baricentric nodes of faces
2307 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2308 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2310 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2311 volFace2BaryNode.insert
2312 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2315 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2316 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2318 nodes.push_back( iF_n->second = f_n->second );
2323 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2324 const int* volConn = splitMethod._connectivity;
2325 if ( splitMethod._nbCorners == 4 ) // tetra
2326 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2327 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2328 nodes[ volConn[1] ],
2329 nodes[ volConn[2] ],
2330 nodes[ volConn[3] ]));
2332 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2333 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2334 nodes[ volConn[1] ],
2335 nodes[ volConn[2] ],
2336 nodes[ volConn[3] ],
2337 nodes[ volConn[4] ],
2338 nodes[ volConn[5] ]));
2340 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2342 // Split faces on sides of the split volume
2344 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2345 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2347 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2348 if ( nbNodes < 4 ) continue;
2350 // find an existing face
2351 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2352 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2353 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2354 /*noMedium=*/false))
2357 helper.SetElementsOnShape( false );
2358 vector< const SMDS_MeshElement* > triangles;
2360 // find submesh to add new triangles in
2361 if ( !fSubMesh || !fSubMesh->Contains( face ))
2363 int shapeID = FindShape( face );
2364 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2366 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2367 if ( iF_n != splitMethod._faceBaryNode.end() )
2369 const SMDS_MeshNode *baryNode = iF_n->second;
2370 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2372 const SMDS_MeshNode* n1 = fNodes[iN];
2373 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2374 const SMDS_MeshNode *n3 = baryNode;
2375 if ( !volTool.IsFaceExternal( iF ))
2377 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2379 if ( fSubMesh ) // update position of the bary node on geometry
2382 subMesh->RemoveNode( baryNode, false );
2383 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2384 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2385 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2387 fHelper.SetSubShape( s );
2388 gp_XY uv( 1e100, 1e100 );
2390 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2391 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2394 // node is too far from the surface
2395 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2396 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2397 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2404 // among possible triangles create ones described by split method
2405 const int* nInd = volTool.GetFaceNodesIndices( iF );
2406 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2407 int iCom = 0; // common node of triangle faces to split into
2408 list< TTriangleFacet > facets;
2409 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2411 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2412 nInd[ iQ * ( (iCom+1)%nbNodes )],
2413 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2414 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2415 nInd[ iQ * ( (iCom+2)%nbNodes )],
2416 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2417 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2419 facets.push_back( t012 );
2420 facets.push_back( t023 );
2421 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2422 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2423 nInd[ iQ * ((iLast-1)%nbNodes )],
2424 nInd[ iQ * ((iLast )%nbNodes )]));
2428 list< TTriangleFacet >::iterator facet = facets.begin();
2429 if ( facet == facets.end() )
2431 for ( ; facet != facets.end(); ++facet )
2433 if ( !volTool.IsFaceExternal( iF ))
2434 swap( facet->_n2, facet->_n3 );
2435 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2436 volNodes[ facet->_n2 ],
2437 volNodes[ facet->_n3 ]));
2440 for ( size_t i = 0; i < triangles.size(); ++i )
2442 if ( !triangles[ i ]) continue;
2444 fSubMesh->AddElement( triangles[ i ]);
2445 newElems.Append( triangles[ i ]);
2447 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2448 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2450 } // while a face based on facet nodes exists
2451 } // loop on volume faces to split them into triangles
2453 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2455 if ( geomType == SMDSEntity_TriQuad_Hexa )
2457 // remove medium nodes that could become free
2458 for ( int i = 20; i < volTool.NbNodes(); ++i )
2459 if ( volNodes[i]->NbInverseElements() == 0 )
2460 GetMeshDS()->RemoveNode( volNodes[i] );
2462 } // loop on volumes to split
2464 myLastCreatedNodes = newNodes;
2465 myLastCreatedElems = newElems;
2468 //=======================================================================
2469 //function : GetHexaFacetsToSplit
2470 //purpose : For hexahedra that will be split into prisms, finds facets to
2471 // split into triangles. Only hexahedra adjacent to the one closest
2472 // to theFacetNormal.Location() are returned.
2473 //param [in,out] theHexas - the hexahedra
2474 //param [in] theFacetNormal - facet normal
2475 //param [out] theFacets - the hexahedra and found facet IDs
2476 //=======================================================================
2478 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2479 const gp_Ax1& theFacetNormal,
2480 TFacetOfElem & theFacets)
2482 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2484 // Find a hexa closest to the location of theFacetNormal
2486 const SMDS_MeshElement* startHex;
2488 // get SMDS_ElemIteratorPtr on theHexas
2489 typedef const SMDS_MeshElement* TValue;
2490 typedef TIDSortedElemSet::iterator TSetIterator;
2491 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2492 typedef SMDS_MeshElement::GeomFilter TFilter;
2493 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2494 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2495 ( new TElemSetIter( theHexas.begin(),
2497 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2499 SMESH_ElementSearcher* searcher =
2500 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2502 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2507 throw SALOME_Exception( THIS_METHOD "startHex not found");
2510 // Select a facet of startHex by theFacetNormal
2512 SMDS_VolumeTool vTool( startHex );
2513 double norm[3], dot, maxDot = 0;
2515 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2516 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2518 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2526 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2528 // Fill theFacets starting from facetID of startHex
2530 // facets used for searching of volumes adjacent to already treated ones
2531 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2532 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2533 TFacetMap facetsToCheck;
2535 set<const SMDS_MeshNode*> facetNodes;
2536 const SMDS_MeshElement* curHex;
2538 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2542 // move in two directions from startHex via facetID
2543 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2546 int curFacet = facetID;
2547 if ( is2nd ) // do not treat startHex twice
2549 vTool.Set( curHex );
2550 if ( vTool.IsFreeFace( curFacet, &curHex ))
2556 vTool.GetFaceNodes( curFacet, facetNodes );
2557 vTool.Set( curHex );
2558 curFacet = vTool.GetFaceIndex( facetNodes );
2563 // store a facet to split
2564 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2566 theFacets.insert( make_pair( curHex, -1 ));
2569 if ( !allHex && !theHexas.count( curHex ))
2572 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2573 theFacets.insert( make_pair( curHex, curFacet ));
2574 if ( !facetIt2isNew.second )
2577 // remember not-to-split facets in facetsToCheck
2578 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2579 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2581 if ( iF == curFacet && iF == oppFacet )
2583 TVolumeFaceKey facetKey ( vTool, iF );
2584 TElemFacets elemFacet( facetIt2isNew.first, iF );
2585 pair< TFacetMap::iterator, bool > it2isnew =
2586 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2587 if ( !it2isnew.second )
2588 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2590 // pass to a volume adjacent via oppFacet
2591 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2597 // get a new curFacet
2598 vTool.GetFaceNodes( oppFacet, facetNodes );
2599 vTool.Set( curHex );
2600 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2603 } // move in two directions from startHex via facetID
2605 // Find a new startHex by facetsToCheck
2609 TFacetMap::iterator fIt = facetsToCheck.begin();
2610 while ( !startHex && fIt != facetsToCheck.end() )
2612 const TElemFacets& elemFacets = fIt->second;
2613 const SMDS_MeshElement* hex = elemFacets.first->first;
2614 int splitFacet = elemFacets.first->second;
2615 int lateralFacet = elemFacets.second;
2616 facetsToCheck.erase( fIt );
2617 fIt = facetsToCheck.begin();
2620 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2621 curHex->GetGeomType() != SMDSGeom_HEXA )
2623 if ( !allHex && !theHexas.count( curHex ))
2628 // find a facet of startHex to split
2630 set<const SMDS_MeshNode*> lateralNodes;
2631 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2632 vTool.GetFaceNodes( splitFacet, facetNodes );
2633 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2634 vTool.Set( startHex );
2635 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2637 // look for a facet of startHex having common nodes with facetNodes
2638 // but not lateralFacet
2639 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2641 if ( iF == lateralFacet )
2643 int nbCommonNodes = 0;
2644 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2645 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2646 nbCommonNodes += facetNodes.count( nn[ iN ]);
2648 if ( nbCommonNodes >= 2 )
2655 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2657 } // while ( startHex )
2664 //================================================================================
2666 * \brief Selects nodes of several elements according to a given interlace
2667 * \param [in] srcNodes - nodes to select from
2668 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2669 * \param [in] interlace - indices of nodes for all elements
2670 * \param [in] nbElems - nb of elements
2671 * \param [in] nbNodes - nb of nodes in each element
2672 * \param [in] mesh - the mesh
2673 * \param [out] elemQueue - a list to push elements found by the selected nodes
2674 * \param [in] type - type of elements to look for
2676 //================================================================================
2678 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2679 vector< const SMDS_MeshNode* >* tgtNodesVec,
2680 const int* interlace,
2683 SMESHDS_Mesh* mesh = 0,
2684 list< const SMDS_MeshElement* >* elemQueue=0,
2685 SMDSAbs_ElementType type=SMDSAbs_All)
2687 for ( int iE = 0; iE < nbElems; ++iE )
2689 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2690 const int* select = & interlace[iE*nbNodes];
2691 elemNodes.resize( nbNodes );
2692 for ( int iN = 0; iN < nbNodes; ++iN )
2693 elemNodes[iN] = srcNodes[ select[ iN ]];
2695 const SMDS_MeshElement* e;
2697 for ( int iE = 0; iE < nbElems; ++iE )
2698 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2699 elemQueue->push_back( e );
2703 //=======================================================================
2705 * Split bi-quadratic elements into linear ones without creation of additional nodes
2706 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2707 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2708 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2709 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2710 * will be split in order to keep the mesh conformal.
2711 * \param elems - elements to split
2713 //=======================================================================
2715 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2717 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2718 vector<const SMDS_MeshElement* > splitElems;
2719 list< const SMDS_MeshElement* > elemQueue;
2720 list< const SMDS_MeshElement* >::iterator elemIt;
2722 SMESHDS_Mesh * mesh = GetMeshDS();
2723 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2724 int nbElems, nbNodes;
2726 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2727 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2730 elemQueue.push_back( *elemSetIt );
2731 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2733 const SMDS_MeshElement* elem = *elemIt;
2734 switch( elem->GetEntityType() )
2736 case SMDSEntity_TriQuad_Hexa: // HEX27
2738 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2739 nbElems = nbNodes = 8;
2740 elemType = & hexaType;
2742 // get nodes for new elements
2743 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2744 { 1,9,20,8, 17,22,26,21 },
2745 { 2,10,20,9, 18,23,26,22 },
2746 { 3,11,20,10, 19,24,26,23 },
2747 { 16,21,26,24, 4,12,25,15 },
2748 { 17,22,26,21, 5,13,25,12 },
2749 { 18,23,26,22, 6,14,25,13 },
2750 { 19,24,26,23, 7,15,25,14 }};
2751 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2753 // add boundary faces to elemQueue
2754 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2755 { 4,5,6,7, 12,13,14,15, 25 },
2756 { 0,1,5,4, 8,17,12,16, 21 },
2757 { 1,2,6,5, 9,18,13,17, 22 },
2758 { 2,3,7,6, 10,19,14,18, 23 },
2759 { 3,0,4,7, 11,16,15,19, 24 }};
2760 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2762 // add boundary segments to elemQueue
2763 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2764 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2765 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2766 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2769 case SMDSEntity_BiQuad_Triangle: // TRIA7
2771 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2774 elemType = & quadType;
2776 // get nodes for new elements
2777 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2778 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2780 // add boundary segments to elemQueue
2781 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2782 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2785 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2787 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2790 elemType = & quadType;
2792 // get nodes for new elements
2793 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2794 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2796 // add boundary segments to elemQueue
2797 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2798 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2801 case SMDSEntity_Quad_Edge:
2803 if ( elemIt == elemQueue.begin() )
2804 continue; // an elem is in theElems
2805 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2808 elemType = & segType;
2810 // get nodes for new elements
2811 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2812 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2816 } // switch( elem->GetEntityType() )
2818 // Create new elements
2820 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2824 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2825 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2826 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2827 //elemType->SetID( -1 );
2829 for ( int iE = 0; iE < nbElems; ++iE )
2830 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2833 ReplaceElemInGroups( elem, splitElems, mesh );
2836 for ( size_t i = 0; i < splitElems.size(); ++i )
2837 subMesh->AddElement( splitElems[i] );
2842 //=======================================================================
2843 //function : AddToSameGroups
2844 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2845 //=======================================================================
2847 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2848 const SMDS_MeshElement* elemInGroups,
2849 SMESHDS_Mesh * aMesh)
2851 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2852 if (!groups.empty()) {
2853 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2854 for ( ; grIt != groups.end(); grIt++ ) {
2855 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2856 if ( group && group->Contains( elemInGroups ))
2857 group->SMDSGroup().Add( elemToAdd );
2863 //=======================================================================
2864 //function : RemoveElemFromGroups
2865 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2866 //=======================================================================
2867 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2868 SMESHDS_Mesh * aMesh)
2870 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2871 if (!groups.empty())
2873 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2874 for (; GrIt != groups.end(); GrIt++)
2876 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2877 if (!grp || grp->IsEmpty()) continue;
2878 grp->SMDSGroup().Remove(removeelem);
2883 //================================================================================
2885 * \brief Replace elemToRm by elemToAdd in the all groups
2887 //================================================================================
2889 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2890 const SMDS_MeshElement* elemToAdd,
2891 SMESHDS_Mesh * aMesh)
2893 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2894 if (!groups.empty()) {
2895 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2896 for ( ; grIt != groups.end(); grIt++ ) {
2897 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2898 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2899 group->SMDSGroup().Add( elemToAdd );
2904 //================================================================================
2906 * \brief Replace elemToRm by elemToAdd in the all groups
2908 //================================================================================
2910 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2911 const vector<const SMDS_MeshElement*>& elemToAdd,
2912 SMESHDS_Mesh * aMesh)
2914 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2915 if (!groups.empty())
2917 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2918 for ( ; grIt != groups.end(); grIt++ ) {
2919 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2920 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2921 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2922 group->SMDSGroup().Add( elemToAdd[ i ] );
2927 //=======================================================================
2928 //function : QuadToTri
2929 //purpose : Cut quadrangles into triangles.
2930 // theCrit is used to select a diagonal to cut
2931 //=======================================================================
2933 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2934 const bool the13Diag)
2936 myLastCreatedElems.Clear();
2937 myLastCreatedNodes.Clear();
2939 SMESHDS_Mesh * aMesh = GetMeshDS();
2941 Handle(Geom_Surface) surface;
2942 SMESH_MesherHelper helper( *GetMesh() );
2944 TIDSortedElemSet::iterator itElem;
2945 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2947 const SMDS_MeshElement* elem = *itElem;
2948 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2951 if ( elem->NbNodes() == 4 ) {
2952 // retrieve element nodes
2953 const SMDS_MeshNode* aNodes [4];
2954 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2956 while ( itN->more() )
2957 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2959 int aShapeId = FindShape( elem );
2960 const SMDS_MeshElement* newElem1 = 0;
2961 const SMDS_MeshElement* newElem2 = 0;
2963 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2964 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2967 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2968 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2970 myLastCreatedElems.Append(newElem1);
2971 myLastCreatedElems.Append(newElem2);
2972 // put a new triangle on the same shape and add to the same groups
2975 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2976 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2978 AddToSameGroups( newElem1, elem, aMesh );
2979 AddToSameGroups( newElem2, elem, aMesh );
2980 aMesh->RemoveElement( elem );
2983 // Quadratic quadrangle
2985 else if ( elem->NbNodes() >= 8 )
2987 // get surface elem is on
2988 int aShapeId = FindShape( elem );
2989 if ( aShapeId != helper.GetSubShapeID() ) {
2993 shape = aMesh->IndexToShape( aShapeId );
2994 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2995 TopoDS_Face face = TopoDS::Face( shape );
2996 surface = BRep_Tool::Surface( face );
2997 if ( !surface.IsNull() )
2998 helper.SetSubShape( shape );
3002 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3003 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3004 for ( int i = 0; itN->more(); ++i )
3005 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3007 const SMDS_MeshNode* centrNode = aNodes[8];
3008 if ( centrNode == 0 )
3010 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3011 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3013 myLastCreatedNodes.Append(centrNode);
3016 // create a new element
3017 const SMDS_MeshElement* newElem1 = 0;
3018 const SMDS_MeshElement* newElem2 = 0;
3020 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3021 aNodes[6], aNodes[7], centrNode );
3022 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3023 centrNode, aNodes[4], aNodes[5] );
3026 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3027 aNodes[7], aNodes[4], centrNode );
3028 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3029 centrNode, aNodes[5], aNodes[6] );
3031 myLastCreatedElems.Append(newElem1);
3032 myLastCreatedElems.Append(newElem2);
3033 // put a new triangle on the same shape and add to the same groups
3036 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3037 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3039 AddToSameGroups( newElem1, elem, aMesh );
3040 AddToSameGroups( newElem2, elem, aMesh );
3041 aMesh->RemoveElement( elem );
3048 //=======================================================================
3049 //function : getAngle
3051 //=======================================================================
3053 double getAngle(const SMDS_MeshElement * tr1,
3054 const SMDS_MeshElement * tr2,
3055 const SMDS_MeshNode * n1,
3056 const SMDS_MeshNode * n2)
3058 double angle = 2. * M_PI; // bad angle
3061 SMESH::Controls::TSequenceOfXYZ P1, P2;
3062 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3063 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3066 if(!tr1->IsQuadratic())
3067 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3069 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3070 if ( N1.SquareMagnitude() <= gp::Resolution() )
3072 if(!tr2->IsQuadratic())
3073 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3075 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3076 if ( N2.SquareMagnitude() <= gp::Resolution() )
3079 // find the first diagonal node n1 in the triangles:
3080 // take in account a diagonal link orientation
3081 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3082 for ( int t = 0; t < 2; t++ ) {
3083 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3084 int i = 0, iDiag = -1;
3085 while ( it->more()) {
3086 const SMDS_MeshElement *n = it->next();
3087 if ( n == n1 || n == n2 ) {
3091 if ( i - iDiag == 1 )
3092 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3101 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3104 angle = N1.Angle( N2 );
3109 // =================================================
3110 // class generating a unique ID for a pair of nodes
3111 // and able to return nodes by that ID
3112 // =================================================
3116 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3117 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3120 long GetLinkID (const SMDS_MeshNode * n1,
3121 const SMDS_MeshNode * n2) const
3123 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3126 bool GetNodes (const long theLinkID,
3127 const SMDS_MeshNode* & theNode1,
3128 const SMDS_MeshNode* & theNode2) const
3130 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3131 if ( !theNode1 ) return false;
3132 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3133 if ( !theNode2 ) return false;
3139 const SMESHDS_Mesh* myMesh;
3144 //=======================================================================
3145 //function : TriToQuad
3146 //purpose : Fuse neighbour triangles into quadrangles.
3147 // theCrit is used to select a neighbour to fuse with.
3148 // theMaxAngle is a max angle between element normals at which
3149 // fusion is still performed.
3150 //=======================================================================
3152 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3153 SMESH::Controls::NumericalFunctorPtr theCrit,
3154 const double theMaxAngle)
3156 myLastCreatedElems.Clear();
3157 myLastCreatedNodes.Clear();
3159 if ( !theCrit.get() )
3162 SMESHDS_Mesh * aMesh = GetMeshDS();
3164 // Prepare data for algo: build
3165 // 1. map of elements with their linkIDs
3166 // 2. map of linkIDs with their elements
3168 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3169 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3170 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3171 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3173 TIDSortedElemSet::iterator itElem;
3174 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3176 const SMDS_MeshElement* elem = *itElem;
3177 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3178 bool IsTria = ( elem->NbCornerNodes()==3 );
3179 if (!IsTria) continue;
3181 // retrieve element nodes
3182 const SMDS_MeshNode* aNodes [4];
3183 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3186 aNodes[ i++ ] = itN->next();
3187 aNodes[ 3 ] = aNodes[ 0 ];
3190 for ( i = 0; i < 3; i++ ) {
3191 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3192 // check if elements sharing a link can be fused
3193 itLE = mapLi_listEl.find( link );
3194 if ( itLE != mapLi_listEl.end() ) {
3195 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3197 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3198 //if ( FindShape( elem ) != FindShape( elem2 ))
3199 // continue; // do not fuse triangles laying on different shapes
3200 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3201 continue; // avoid making badly shaped quads
3202 (*itLE).second.push_back( elem );
3205 mapLi_listEl[ link ].push_back( elem );
3207 mapEl_setLi [ elem ].insert( link );
3210 // Clean the maps from the links shared by a sole element, ie
3211 // links to which only one element is bound in mapLi_listEl
3213 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3214 int nbElems = (*itLE).second.size();
3215 if ( nbElems < 2 ) {
3216 const SMDS_MeshElement* elem = (*itLE).second.front();
3217 SMESH_TLink link = (*itLE).first;
3218 mapEl_setLi[ elem ].erase( link );
3219 if ( mapEl_setLi[ elem ].empty() )
3220 mapEl_setLi.erase( elem );
3224 // Algo: fuse triangles into quadrangles
3226 while ( ! mapEl_setLi.empty() ) {
3227 // Look for the start element:
3228 // the element having the least nb of shared links
3229 const SMDS_MeshElement* startElem = 0;
3231 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3232 int nbLinks = (*itEL).second.size();
3233 if ( nbLinks < minNbLinks ) {
3234 startElem = (*itEL).first;
3235 minNbLinks = nbLinks;
3236 if ( minNbLinks == 1 )
3241 // search elements to fuse starting from startElem or links of elements
3242 // fused earlyer - startLinks
3243 list< SMESH_TLink > startLinks;
3244 while ( startElem || !startLinks.empty() ) {
3245 while ( !startElem && !startLinks.empty() ) {
3246 // Get an element to start, by a link
3247 SMESH_TLink linkId = startLinks.front();
3248 startLinks.pop_front();
3249 itLE = mapLi_listEl.find( linkId );
3250 if ( itLE != mapLi_listEl.end() ) {
3251 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3252 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3253 for ( ; itE != listElem.end() ; itE++ )
3254 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3256 mapLi_listEl.erase( itLE );
3261 // Get candidates to be fused
3262 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3263 const SMESH_TLink *link12 = 0, *link13 = 0;
3265 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3266 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3267 ASSERT( !setLi.empty() );
3268 set< SMESH_TLink >::iterator itLi;
3269 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3271 const SMESH_TLink & link = (*itLi);
3272 itLE = mapLi_listEl.find( link );
3273 if ( itLE == mapLi_listEl.end() )
3276 const SMDS_MeshElement* elem = (*itLE).second.front();
3278 elem = (*itLE).second.back();
3279 mapLi_listEl.erase( itLE );
3280 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3291 // add other links of elem to list of links to re-start from
3292 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3293 set< SMESH_TLink >::iterator it;
3294 for ( it = links.begin(); it != links.end(); it++ ) {
3295 const SMESH_TLink& link2 = (*it);
3296 if ( link2 != link )
3297 startLinks.push_back( link2 );
3301 // Get nodes of possible quadrangles
3302 const SMDS_MeshNode *n12 [4], *n13 [4];
3303 bool Ok12 = false, Ok13 = false;
3304 const SMDS_MeshNode *linkNode1, *linkNode2;
3306 linkNode1 = link12->first;
3307 linkNode2 = link12->second;
3308 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3312 linkNode1 = link13->first;
3313 linkNode2 = link13->second;
3314 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3318 // Choose a pair to fuse
3319 if ( Ok12 && Ok13 ) {
3320 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3321 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3322 double aBadRate12 = getBadRate( &quad12, theCrit );
3323 double aBadRate13 = getBadRate( &quad13, theCrit );
3324 if ( aBadRate13 < aBadRate12 )
3331 // and remove fused elems and remove links from the maps
3332 mapEl_setLi.erase( tr1 );
3335 mapEl_setLi.erase( tr2 );
3336 mapLi_listEl.erase( *link12 );
3337 if ( tr1->NbNodes() == 3 )
3339 const SMDS_MeshElement* newElem = 0;
3340 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3341 myLastCreatedElems.Append(newElem);
3342 AddToSameGroups( newElem, tr1, aMesh );
3343 int aShapeId = tr1->getshapeId();
3345 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3346 aMesh->RemoveElement( tr1 );
3347 aMesh->RemoveElement( tr2 );
3350 vector< const SMDS_MeshNode* > N1;
3351 vector< const SMDS_MeshNode* > N2;
3352 getNodesFromTwoTria(tr1,tr2,N1,N2);
3353 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3354 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3355 // i.e. first nodes from both arrays form a new diagonal
3356 const SMDS_MeshNode* aNodes[8];
3365 const SMDS_MeshElement* newElem = 0;
3366 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3367 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3368 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3370 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3371 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3372 myLastCreatedElems.Append(newElem);
3373 AddToSameGroups( newElem, tr1, aMesh );
3374 int aShapeId = tr1->getshapeId();
3376 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3377 aMesh->RemoveElement( tr1 );
3378 aMesh->RemoveElement( tr2 );
3379 // remove middle node (9)
3380 if ( N1[4]->NbInverseElements() == 0 )
3381 aMesh->RemoveNode( N1[4] );
3382 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3383 aMesh->RemoveNode( N1[6] );
3384 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3385 aMesh->RemoveNode( N2[6] );
3390 mapEl_setLi.erase( tr3 );
3391 mapLi_listEl.erase( *link13 );
3392 if ( tr1->NbNodes() == 3 ) {
3393 const SMDS_MeshElement* newElem = 0;
3394 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3395 myLastCreatedElems.Append(newElem);
3396 AddToSameGroups( newElem, tr1, aMesh );
3397 int aShapeId = tr1->getshapeId();
3399 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3400 aMesh->RemoveElement( tr1 );
3401 aMesh->RemoveElement( tr3 );
3404 vector< const SMDS_MeshNode* > N1;
3405 vector< const SMDS_MeshNode* > N2;
3406 getNodesFromTwoTria(tr1,tr3,N1,N2);
3407 // now we receive following N1 and N2 (using numeration as above image)
3408 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3409 // i.e. first nodes from both arrays form a new diagonal
3410 const SMDS_MeshNode* aNodes[8];
3419 const SMDS_MeshElement* newElem = 0;
3420 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3421 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3422 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3424 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3425 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3426 myLastCreatedElems.Append(newElem);
3427 AddToSameGroups( newElem, tr1, aMesh );
3428 int aShapeId = tr1->getshapeId();
3430 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3431 aMesh->RemoveElement( tr1 );
3432 aMesh->RemoveElement( tr3 );
3433 // remove middle node (9)
3434 if ( N1[4]->NbInverseElements() == 0 )
3435 aMesh->RemoveNode( N1[4] );
3436 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3437 aMesh->RemoveNode( N1[6] );
3438 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3439 aMesh->RemoveNode( N2[6] );
3443 // Next element to fuse: the rejected one
3445 startElem = Ok12 ? tr3 : tr2;
3447 } // if ( startElem )
3448 } // while ( startElem || !startLinks.empty() )
3449 } // while ( ! mapEl_setLi.empty() )
3455 /*#define DUMPSO(txt) \
3456 // cout << txt << endl;
3457 //=============================================================================
3461 //=============================================================================
3462 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3466 int tmp = idNodes[ i1 ];
3467 idNodes[ i1 ] = idNodes[ i2 ];
3468 idNodes[ i2 ] = tmp;
3469 gp_Pnt Ptmp = P[ i1 ];
3472 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3475 //=======================================================================
3476 //function : SortQuadNodes
3477 //purpose : Set 4 nodes of a quadrangle face in a good order.
3478 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3480 //=======================================================================
3482 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3487 for ( i = 0; i < 4; i++ ) {
3488 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3490 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3493 gp_Vec V1(P[0], P[1]);
3494 gp_Vec V2(P[0], P[2]);
3495 gp_Vec V3(P[0], P[3]);
3497 gp_Vec Cross1 = V1 ^ V2;
3498 gp_Vec Cross2 = V2 ^ V3;
3501 if (Cross1.Dot(Cross2) < 0)
3506 if (Cross1.Dot(Cross2) < 0)
3510 swap ( i, i + 1, idNodes, P );
3512 // for ( int ii = 0; ii < 4; ii++ ) {
3513 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3514 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3520 //=======================================================================
3521 //function : SortHexaNodes
3522 //purpose : Set 8 nodes of a hexahedron in a good order.
3523 // Return success status
3524 //=======================================================================
3526 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3531 DUMPSO( "INPUT: ========================================");
3532 for ( i = 0; i < 8; i++ ) {
3533 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3534 if ( !n ) return false;
3535 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3536 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3538 DUMPSO( "========================================");
3541 set<int> faceNodes; // ids of bottom face nodes, to be found
3542 set<int> checkedId1; // ids of tried 2-nd nodes
3543 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3544 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3545 int iMin, iLoop1 = 0;
3547 // Loop to try the 2-nd nodes
3549 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3551 // Find not checked 2-nd node
3552 for ( i = 1; i < 8; i++ )
3553 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3554 int id1 = idNodes[i];
3555 swap ( 1, i, idNodes, P );
3556 checkedId1.insert ( id1 );
3560 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3561 // ie that all but meybe one (id3 which is on the same face) nodes
3562 // lay on the same side from the triangle plane.
3564 bool manyInPlane = false; // more than 4 nodes lay in plane
3566 while ( ++iLoop2 < 6 ) {
3568 // get 1-2-3 plane coeffs
3569 Standard_Real A, B, C, D;
3570 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3571 if ( N.SquareMagnitude() > gp::Resolution() )
3573 gp_Pln pln ( P[0], N );
3574 pln.Coefficients( A, B, C, D );
3576 // find the node (iMin) closest to pln
3577 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3579 for ( i = 3; i < 8; i++ ) {
3580 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3581 if ( fabs( dist[i] ) < minDist ) {
3582 minDist = fabs( dist[i] );
3585 if ( fabs( dist[i] ) <= tol )
3586 idInPln.insert( idNodes[i] );
3589 // there should not be more than 4 nodes in bottom plane
3590 if ( idInPln.size() > 1 )
3592 DUMPSO( "### idInPln.size() = " << idInPln.size());
3593 // idInPlane does not contain the first 3 nodes
3594 if ( manyInPlane || idInPln.size() == 5)
3595 return false; // all nodes in one plane
3598 // set the 1-st node to be not in plane
3599 for ( i = 3; i < 8; i++ ) {
3600 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3601 DUMPSO( "### Reset 0-th node");
3602 swap( 0, i, idNodes, P );
3607 // reset to re-check second nodes
3608 leastDist = DBL_MAX;
3612 break; // from iLoop2;
3615 // check that the other 4 nodes are on the same side
3616 bool sameSide = true;
3617 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3618 for ( i = 3; sameSide && i < 8; i++ ) {
3620 sameSide = ( isNeg == dist[i] <= 0.);
3623 // keep best solution
3624 if ( sameSide && minDist < leastDist ) {
3625 leastDist = minDist;
3627 faceNodes.insert( idNodes[ 1 ] );
3628 faceNodes.insert( idNodes[ 2 ] );
3629 faceNodes.insert( idNodes[ iMin ] );
3630 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3631 << " leastDist = " << leastDist);
3632 if ( leastDist <= DBL_MIN )
3637 // set next 3-d node to check
3638 int iNext = 2 + iLoop2;
3640 DUMPSO( "Try 2-nd");
3641 swap ( 2, iNext, idNodes, P );
3643 } // while ( iLoop2 < 6 )
3646 if ( faceNodes.empty() ) return false;
3648 // Put the faceNodes in proper places
3649 for ( i = 4; i < 8; i++ ) {
3650 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3651 // find a place to put
3653 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3655 DUMPSO( "Set faceNodes");
3656 swap ( iTo, i, idNodes, P );
3661 // Set nodes of the found bottom face in good order
3662 DUMPSO( " Found bottom face: ");
3663 i = SortQuadNodes( theMesh, idNodes );
3665 gp_Pnt Ptmp = P[ i ];
3670 // for ( int ii = 0; ii < 4; ii++ ) {
3671 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3672 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3675 // Gravity center of the top and bottom faces
3676 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3677 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3679 // Get direction from the bottom to the top face
3680 gp_Vec upDir ( aGCb, aGCt );
3681 Standard_Real upDirSize = upDir.Magnitude();
3682 if ( upDirSize <= gp::Resolution() ) return false;
3685 // Assure that the bottom face normal points up
3686 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3687 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3688 if ( Nb.Dot( upDir ) < 0 ) {
3689 DUMPSO( "Reverse bottom face");
3690 swap( 1, 3, idNodes, P );
3693 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3694 Standard_Real minDist = DBL_MAX;
3695 for ( i = 4; i < 8; i++ ) {
3696 // projection of P[i] to the plane defined by P[0] and upDir
3697 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3698 Standard_Real sqDist = P[0].SquareDistance( Pp );
3699 if ( sqDist < minDist ) {
3704 DUMPSO( "Set 4-th");
3705 swap ( 4, iMin, idNodes, P );
3707 // Set nodes of the top face in good order
3708 DUMPSO( "Sort top face");
3709 i = SortQuadNodes( theMesh, &idNodes[4] );
3712 gp_Pnt Ptmp = P[ i ];
3717 // Assure that direction of the top face normal is from the bottom face
3718 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3719 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3720 if ( Nt.Dot( upDir ) < 0 ) {
3721 DUMPSO( "Reverse top face");
3722 swap( 5, 7, idNodes, P );
3725 // DUMPSO( "OUTPUT: ========================================");
3726 // for ( i = 0; i < 8; i++ ) {
3727 // float *p = ugrid->GetPoint(idNodes[i]);
3728 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3734 //================================================================================
3736 * \brief Return nodes linked to the given one
3737 * \param theNode - the node
3738 * \param linkedNodes - the found nodes
3739 * \param type - the type of elements to check
3741 * Medium nodes are ignored
3743 //================================================================================
3745 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3746 TIDSortedElemSet & linkedNodes,
3747 SMDSAbs_ElementType type )
3749 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3750 while ( elemIt->more() )
3752 const SMDS_MeshElement* elem = elemIt->next();
3753 if(elem->GetType() == SMDSAbs_0DElement)
3756 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3757 if ( elem->GetType() == SMDSAbs_Volume )
3759 SMDS_VolumeTool vol( elem );
3760 while ( nodeIt->more() ) {
3761 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3762 if ( theNode != n && vol.IsLinked( theNode, n ))
3763 linkedNodes.insert( n );
3768 for ( int i = 0; nodeIt->more(); ++i ) {
3769 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3770 if ( n == theNode ) {
3771 int iBefore = i - 1;
3773 if ( elem->IsQuadratic() ) {
3774 int nb = elem->NbNodes() / 2;
3775 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3776 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3778 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3779 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3786 //=======================================================================
3787 //function : laplacianSmooth
3788 //purpose : pulls theNode toward the center of surrounding nodes directly
3789 // connected to that node along an element edge
3790 //=======================================================================
3792 void laplacianSmooth(const SMDS_MeshNode* theNode,
3793 const Handle(Geom_Surface)& theSurface,
3794 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3796 // find surrounding nodes
3798 TIDSortedElemSet nodeSet;
3799 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3801 // compute new coodrs
3803 double coord[] = { 0., 0., 0. };
3804 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3805 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3806 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3807 if ( theSurface.IsNull() ) { // smooth in 3D
3808 coord[0] += node->X();
3809 coord[1] += node->Y();
3810 coord[2] += node->Z();
3812 else { // smooth in 2D
3813 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3814 gp_XY* uv = theUVMap[ node ];
3815 coord[0] += uv->X();
3816 coord[1] += uv->Y();
3819 int nbNodes = nodeSet.size();
3822 coord[0] /= nbNodes;
3823 coord[1] /= nbNodes;
3825 if ( !theSurface.IsNull() ) {
3826 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3827 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3828 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3834 coord[2] /= nbNodes;
3838 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3841 //=======================================================================
3842 //function : centroidalSmooth
3843 //purpose : pulls theNode toward the element-area-weighted centroid of the
3844 // surrounding elements
3845 //=======================================================================
3847 void centroidalSmooth(const SMDS_MeshNode* theNode,
3848 const Handle(Geom_Surface)& theSurface,
3849 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3851 gp_XYZ aNewXYZ(0.,0.,0.);
3852 SMESH::Controls::Area anAreaFunc;
3853 double totalArea = 0.;
3858 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3859 while ( elemIt->more() )
3861 const SMDS_MeshElement* elem = elemIt->next();
3864 gp_XYZ elemCenter(0.,0.,0.);
3865 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3866 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3867 int nn = elem->NbNodes();
3868 if(elem->IsQuadratic()) nn = nn/2;
3870 //while ( itN->more() ) {
3872 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3874 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3875 aNodePoints.push_back( aP );
3876 if ( !theSurface.IsNull() ) { // smooth in 2D
3877 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3878 gp_XY* uv = theUVMap[ aNode ];
3879 aP.SetCoord( uv->X(), uv->Y(), 0. );
3883 double elemArea = anAreaFunc.GetValue( aNodePoints );
3884 totalArea += elemArea;
3886 aNewXYZ += elemCenter * elemArea;
3888 aNewXYZ /= totalArea;
3889 if ( !theSurface.IsNull() ) {
3890 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3891 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3896 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3899 //=======================================================================
3900 //function : getClosestUV
3901 //purpose : return UV of closest projection
3902 //=======================================================================
3904 static bool getClosestUV (Extrema_GenExtPS& projector,
3905 const gp_Pnt& point,
3908 projector.Perform( point );
3909 if ( projector.IsDone() ) {
3910 double u, v, minVal = DBL_MAX;
3911 for ( int i = projector.NbExt(); i > 0; i-- )
3912 if ( projector.SquareDistance( i ) < minVal ) {
3913 minVal = projector.SquareDistance( i );
3914 projector.Point( i ).Parameter( u, v );
3916 result.SetCoord( u, v );
3922 //=======================================================================
3924 //purpose : Smooth theElements during theNbIterations or until a worst
3925 // element has aspect ratio <= theTgtAspectRatio.
3926 // Aspect Ratio varies in range [1.0, inf].
3927 // If theElements is empty, the whole mesh is smoothed.
3928 // theFixedNodes contains additionally fixed nodes. Nodes built
3929 // on edges and boundary nodes are always fixed.
3930 //=======================================================================
3932 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3933 set<const SMDS_MeshNode*> & theFixedNodes,
3934 const SmoothMethod theSmoothMethod,
3935 const int theNbIterations,
3936 double theTgtAspectRatio,
3939 myLastCreatedElems.Clear();
3940 myLastCreatedNodes.Clear();
3942 if ( theTgtAspectRatio < 1.0 )
3943 theTgtAspectRatio = 1.0;
3945 const double disttol = 1.e-16;
3947 SMESH::Controls::AspectRatio aQualityFunc;
3949 SMESHDS_Mesh* aMesh = GetMeshDS();
3951 if ( theElems.empty() ) {
3952 // add all faces to theElems
3953 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3954 while ( fIt->more() ) {
3955 const SMDS_MeshElement* face = fIt->next();
3956 theElems.insert( theElems.end(), face );
3959 // get all face ids theElems are on
3960 set< int > faceIdSet;
3961 TIDSortedElemSet::iterator itElem;
3963 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3964 int fId = FindShape( *itElem );
3965 // check that corresponding submesh exists and a shape is face
3967 faceIdSet.find( fId ) == faceIdSet.end() &&
3968 aMesh->MeshElements( fId )) {
3969 TopoDS_Shape F = aMesh->IndexToShape( fId );
3970 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3971 faceIdSet.insert( fId );
3974 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3976 // ===============================================
3977 // smooth elements on each TopoDS_Face separately
3978 // ===============================================
3980 SMESH_MesherHelper helper( *GetMesh() );
3982 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3983 for ( ; fId != faceIdSet.rend(); ++fId )
3985 // get face surface and submesh
3986 Handle(Geom_Surface) surface;
3987 SMESHDS_SubMesh* faceSubMesh = 0;
3990 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3991 bool isUPeriodic = false, isVPeriodic = false;
3994 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3995 surface = BRep_Tool::Surface( face );
3996 faceSubMesh = aMesh->MeshElements( *fId );
3997 fToler2 = BRep_Tool::Tolerance( face );
3998 fToler2 *= fToler2 * 10.;
3999 isUPeriodic = surface->IsUPeriodic();
4000 // if ( isUPeriodic )
4001 // surface->UPeriod();
4002 isVPeriodic = surface->IsVPeriodic();
4003 // if ( isVPeriodic )
4004 // surface->VPeriod();
4005 surface->Bounds( u1, u2, v1, v2 );
4006 helper.SetSubShape( face );
4008 // ---------------------------------------------------------
4009 // for elements on a face, find movable and fixed nodes and
4010 // compute UV for them
4011 // ---------------------------------------------------------
4012 bool checkBoundaryNodes = false;
4013 bool isQuadratic = false;
4014 set<const SMDS_MeshNode*> setMovableNodes;
4015 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4016 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4017 list< const SMDS_MeshElement* > elemsOnFace;
4019 Extrema_GenExtPS projector;
4020 GeomAdaptor_Surface surfAdaptor;
4021 if ( !surface.IsNull() ) {
4022 surfAdaptor.Load( surface );
4023 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4025 int nbElemOnFace = 0;
4026 itElem = theElems.begin();
4027 // loop on not yet smoothed elements: look for elems on a face
4028 while ( itElem != theElems.end() )
4030 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4031 break; // all elements found
4033 const SMDS_MeshElement* elem = *itElem;
4034 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4035 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4039 elemsOnFace.push_back( elem );
4040 theElems.erase( itElem++ );
4044 isQuadratic = elem->IsQuadratic();
4046 // get movable nodes of elem
4047 const SMDS_MeshNode* node;
4048 SMDS_TypeOfPosition posType;
4049 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4050 int nn = 0, nbn = elem->NbNodes();
4051 if(elem->IsQuadratic())
4053 while ( nn++ < nbn ) {
4054 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4055 const SMDS_PositionPtr& pos = node->GetPosition();
4056 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4057 if (posType != SMDS_TOP_EDGE &&
4058 posType != SMDS_TOP_VERTEX &&
4059 theFixedNodes.find( node ) == theFixedNodes.end())
4061 // check if all faces around the node are on faceSubMesh
4062 // because a node on edge may be bound to face
4064 if ( faceSubMesh ) {
4065 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4066 while ( eIt->more() && all ) {
4067 const SMDS_MeshElement* e = eIt->next();
4068 all = faceSubMesh->Contains( e );
4072 setMovableNodes.insert( node );
4074 checkBoundaryNodes = true;
4076 if ( posType == SMDS_TOP_3DSPACE )
4077 checkBoundaryNodes = true;
4080 if ( surface.IsNull() )
4083 // get nodes to check UV
4084 list< const SMDS_MeshNode* > uvCheckNodes;
4085 const SMDS_MeshNode* nodeInFace = 0;
4086 itN = elem->nodesIterator();
4087 nn = 0; nbn = elem->NbNodes();
4088 if(elem->IsQuadratic())
4090 while ( nn++ < nbn ) {
4091 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4092 if ( node->GetPosition()->GetDim() == 2 )
4094 if ( uvMap.find( node ) == uvMap.end() )
4095 uvCheckNodes.push_back( node );
4096 // add nodes of elems sharing node
4097 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4098 // while ( eIt->more() ) {
4099 // const SMDS_MeshElement* e = eIt->next();
4100 // if ( e != elem ) {
4101 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4102 // while ( nIt->more() ) {
4103 // const SMDS_MeshNode* n =
4104 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4105 // if ( uvMap.find( n ) == uvMap.end() )
4106 // uvCheckNodes.push_back( n );
4112 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4113 for ( ; n != uvCheckNodes.end(); ++n ) {
4116 const SMDS_PositionPtr& pos = node->GetPosition();
4117 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4121 bool toCheck = true;
4122 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4124 // compute not existing UV
4125 bool project = ( posType == SMDS_TOP_3DSPACE );
4126 // double dist1 = DBL_MAX, dist2 = 0;
4127 // if ( posType != SMDS_TOP_3DSPACE ) {
4128 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4129 // project = dist1 > fToler2;
4131 if ( project ) { // compute new UV
4133 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4134 if ( !getClosestUV( projector, pNode, newUV )) {
4135 MESSAGE("Node Projection Failed " << node);
4139 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4141 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4143 // if ( posType != SMDS_TOP_3DSPACE )
4144 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4145 // if ( dist2 < dist1 )
4149 // store UV in the map
4150 listUV.push_back( uv );
4151 uvMap.insert( make_pair( node, &listUV.back() ));
4153 } // loop on not yet smoothed elements
4155 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4156 checkBoundaryNodes = true;
4158 // fix nodes on mesh boundary
4160 if ( checkBoundaryNodes ) {
4161 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4162 map< SMESH_TLink, int >::iterator link_nb;
4163 // put all elements links to linkNbMap
4164 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4165 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4166 const SMDS_MeshElement* elem = (*elemIt);
4167 int nbn = elem->NbCornerNodes();
4168 // loop on elem links: insert them in linkNbMap
4169 for ( int iN = 0; iN < nbn; ++iN ) {
4170 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4171 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4172 SMESH_TLink link( n1, n2 );
4173 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4177 // remove nodes that are in links encountered only once from setMovableNodes
4178 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4179 if ( link_nb->second == 1 ) {
4180 setMovableNodes.erase( link_nb->first.node1() );
4181 setMovableNodes.erase( link_nb->first.node2() );
4186 // -----------------------------------------------------
4187 // for nodes on seam edge, compute one more UV ( uvMap2 );
4188 // find movable nodes linked to nodes on seam and which
4189 // are to be smoothed using the second UV ( uvMap2 )
4190 // -----------------------------------------------------
4192 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4193 if ( !surface.IsNull() ) {
4194 TopExp_Explorer eExp( face, TopAbs_EDGE );
4195 for ( ; eExp.More(); eExp.Next() ) {
4196 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4197 if ( !BRep_Tool::IsClosed( edge, face ))
4199 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4200 if ( !sm ) continue;
4201 // find out which parameter varies for a node on seam
4204 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4205 if ( pcurve.IsNull() ) continue;
4206 uv1 = pcurve->Value( f );
4208 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4209 if ( pcurve.IsNull() ) continue;
4210 uv2 = pcurve->Value( f );
4211 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4213 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4214 std::swap( uv1, uv2 );
4215 // get nodes on seam and its vertices
4216 list< const SMDS_MeshNode* > seamNodes;
4217 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4218 while ( nSeamIt->more() ) {
4219 const SMDS_MeshNode* node = nSeamIt->next();
4220 if ( !isQuadratic || !IsMedium( node ))
4221 seamNodes.push_back( node );
4223 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4224 for ( ; vExp.More(); vExp.Next() ) {
4225 sm = aMesh->MeshElements( vExp.Current() );
4227 nSeamIt = sm->GetNodes();
4228 while ( nSeamIt->more() )
4229 seamNodes.push_back( nSeamIt->next() );
4232 // loop on nodes on seam
4233 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4234 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4235 const SMDS_MeshNode* nSeam = *noSeIt;
4236 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4237 if ( n_uv == uvMap.end() )
4240 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4241 // set the second UV
4242 listUV.push_back( *n_uv->second );
4243 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4244 if ( uvMap2.empty() )
4245 uvMap2 = uvMap; // copy the uvMap contents
4246 uvMap2[ nSeam ] = &listUV.back();
4248 // collect movable nodes linked to ones on seam in nodesNearSeam
4249 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4250 while ( eIt->more() ) {
4251 const SMDS_MeshElement* e = eIt->next();
4252 int nbUseMap1 = 0, nbUseMap2 = 0;
4253 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4254 int nn = 0, nbn = e->NbNodes();
4255 if(e->IsQuadratic()) nbn = nbn/2;
4256 while ( nn++ < nbn )
4258 const SMDS_MeshNode* n =
4259 static_cast<const SMDS_MeshNode*>( nIt->next() );
4261 setMovableNodes.find( n ) == setMovableNodes.end() )
4263 // add only nodes being closer to uv2 than to uv1
4264 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4265 // 0.5 * ( n->Y() + nSeam->Y() ),
4266 // 0.5 * ( n->Z() + nSeam->Z() ));
4268 // getClosestUV( projector, pMid, uv );
4269 double x = uvMap[ n ]->Coord( iPar );
4270 if ( Abs( uv1.Coord( iPar ) - x ) >
4271 Abs( uv2.Coord( iPar ) - x )) {
4272 nodesNearSeam.insert( n );
4278 // for centroidalSmooth all element nodes must
4279 // be on one side of a seam
4280 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4281 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4283 while ( nn++ < nbn ) {
4284 const SMDS_MeshNode* n =
4285 static_cast<const SMDS_MeshNode*>( nIt->next() );
4286 setMovableNodes.erase( n );
4290 } // loop on nodes on seam
4291 } // loop on edge of a face
4292 } // if ( !face.IsNull() )
4294 if ( setMovableNodes.empty() ) {
4295 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4296 continue; // goto next face
4304 double maxRatio = -1., maxDisplacement = -1.;
4305 set<const SMDS_MeshNode*>::iterator nodeToMove;
4306 for ( it = 0; it < theNbIterations; it++ ) {
4307 maxDisplacement = 0.;
4308 nodeToMove = setMovableNodes.begin();
4309 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4310 const SMDS_MeshNode* node = (*nodeToMove);
4311 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4314 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4315 if ( theSmoothMethod == LAPLACIAN )
4316 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4318 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4320 // node displacement
4321 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4322 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4323 if ( aDispl > maxDisplacement )
4324 maxDisplacement = aDispl;
4326 // no node movement => exit
4327 //if ( maxDisplacement < 1.e-16 ) {
4328 if ( maxDisplacement < disttol ) {
4329 MESSAGE("-- no node movement --");
4333 // check elements quality
4335 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4336 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4337 const SMDS_MeshElement* elem = (*elemIt);
4338 if ( !elem || elem->GetType() != SMDSAbs_Face )
4340 SMESH::Controls::TSequenceOfXYZ aPoints;
4341 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4342 double aValue = aQualityFunc.GetValue( aPoints );
4343 if ( aValue > maxRatio )
4347 if ( maxRatio <= theTgtAspectRatio ) {
4348 //MESSAGE("-- quality achieved --");
4351 if (it+1 == theNbIterations) {
4352 //MESSAGE("-- Iteration limit exceeded --");
4354 } // smoothing iterations
4356 // MESSAGE(" Face id: " << *fId <<
4357 // " Nb iterstions: " << it <<
4358 // " Displacement: " << maxDisplacement <<
4359 // " Aspect Ratio " << maxRatio);
4361 // ---------------------------------------
4362 // new nodes positions are computed,
4363 // record movement in DS and set new UV
4364 // ---------------------------------------
4365 nodeToMove = setMovableNodes.begin();
4366 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4367 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4368 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4369 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4370 if ( node_uv != uvMap.end() ) {
4371 gp_XY* uv = node_uv->second;
4373 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4377 // move medium nodes of quadratic elements
4380 vector<const SMDS_MeshNode*> nodes;
4382 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4383 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4385 const SMDS_MeshElement* QF = *elemIt;
4386 if ( QF->IsQuadratic() )
4388 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4389 SMDS_MeshElement::iterator() );
4390 nodes.push_back( nodes[0] );
4392 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4394 if ( !surface.IsNull() )
4396 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4397 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4398 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4399 xyz = surface->Value( uv.X(), uv.Y() );
4402 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4404 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4405 // we have to move a medium node
4406 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4412 } // loop on face ids
4418 //=======================================================================
4419 //function : isReverse
4420 //purpose : Return true if normal of prevNodes is not co-directied with
4421 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4422 // iNotSame is where prevNodes and nextNodes are different.
4423 // If result is true then future volume orientation is OK
4424 //=======================================================================
4426 bool isReverse(const SMDS_MeshElement* face,
4427 const vector<const SMDS_MeshNode*>& prevNodes,
4428 const vector<const SMDS_MeshNode*>& nextNodes,
4432 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4433 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4434 gp_XYZ extrDir( pN - pP ), faceNorm;
4435 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4437 return faceNorm * extrDir < 0.0;
4440 //================================================================================
4442 * \brief Assure that theElemSets[0] holds elements, not nodes
4444 //================================================================================
4446 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4448 if ( !theElemSets[0].empty() &&
4449 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4451 std::swap( theElemSets[0], theElemSets[1] );
4453 else if ( !theElemSets[1].empty() &&
4454 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4456 std::swap( theElemSets[0], theElemSets[1] );
4461 //=======================================================================
4463 * \brief Create elements by sweeping an element
4464 * \param elem - element to sweep
4465 * \param newNodesItVec - nodes generated from each node of the element
4466 * \param newElems - generated elements
4467 * \param nbSteps - number of sweeping steps
4468 * \param srcElements - to append elem for each generated element
4470 //=======================================================================
4472 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4473 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4474 list<const SMDS_MeshElement*>& newElems,
4475 const size_t nbSteps,
4476 SMESH_SequenceOfElemPtr& srcElements)
4478 SMESHDS_Mesh* aMesh = GetMeshDS();
4480 const int nbNodes = elem->NbNodes();
4481 const int nbCorners = elem->NbCornerNodes();
4482 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4483 polyhedron creation !!! */
4484 // Loop on elem nodes:
4485 // find new nodes and detect same nodes indices
4486 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4487 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4488 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4489 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4491 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4492 vector<int> sames(nbNodes);
4493 vector<bool> isSingleNode(nbNodes);
4495 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4496 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4497 const SMDS_MeshNode* node = nnIt->first;
4498 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4499 if ( listNewNodes.empty() )
4502 itNN [ iNode ] = listNewNodes.begin();
4503 prevNod[ iNode ] = node;
4504 nextNod[ iNode ] = listNewNodes.front();
4506 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4507 corner node of linear */
4508 if ( prevNod[ iNode ] != nextNod [ iNode ])
4509 nbDouble += !isSingleNode[iNode];
4511 if( iNode < nbCorners ) { // check corners only
4512 if ( prevNod[ iNode ] == nextNod [ iNode ])
4513 sames[nbSame++] = iNode;
4515 iNotSameNode = iNode;
4519 if ( nbSame == nbNodes || nbSame > 2) {
4520 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4524 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4526 // fix nodes order to have bottom normal external
4527 if ( baseType == SMDSEntity_Polygon )
4529 std::reverse( itNN.begin(), itNN.end() );
4530 std::reverse( prevNod.begin(), prevNod.end() );
4531 std::reverse( midlNod.begin(), midlNod.end() );
4532 std::reverse( nextNod.begin(), nextNod.end() );
4533 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4537 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4538 SMDS_MeshCell::applyInterlace( ind, itNN );
4539 SMDS_MeshCell::applyInterlace( ind, prevNod );
4540 SMDS_MeshCell::applyInterlace( ind, nextNod );
4541 SMDS_MeshCell::applyInterlace( ind, midlNod );
4542 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4545 sames[nbSame] = iNotSameNode;
4546 for ( int j = 0; j <= nbSame; ++j )
4547 for ( size_t i = 0; i < ind.size(); ++i )
4548 if ( ind[i] == sames[j] )
4553 iNotSameNode = sames[nbSame];
4557 else if ( elem->GetType() == SMDSAbs_Edge )
4559 // orient a new face same as adjacent one
4561 const SMDS_MeshElement* e;
4562 TIDSortedElemSet dummy;
4563 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4564 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4565 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4567 // there is an adjacent face, check order of nodes in it
4568 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4571 std::swap( itNN[0], itNN[1] );
4572 std::swap( prevNod[0], prevNod[1] );
4573 std::swap( nextNod[0], nextNod[1] );
4574 #if defined(__APPLE__)
4575 std::swap( isSingleNode[0], isSingleNode[1] );
4577 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4580 sames[0] = 1 - sames[0];
4581 iNotSameNode = 1 - iNotSameNode;
4586 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4588 iSameNode = sames[ nbSame-1 ];
4589 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4590 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4591 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4594 if ( baseType == SMDSEntity_Polygon )
4596 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4597 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4599 else if ( baseType == SMDSEntity_Quad_Polygon )
4601 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4602 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4605 // make new elements
4606 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4609 for ( iNode = 0; iNode < nbNodes; iNode++ )
4611 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4612 nextNod[ iNode ] = *itNN[ iNode ]++;
4615 SMDS_MeshElement* aNewElem = 0;
4616 /*if(!elem->IsPoly())*/ {
4617 switch ( baseType ) {
4619 case SMDSEntity_Node: { // sweep NODE
4620 if ( nbSame == 0 ) {
4621 if ( isSingleNode[0] )
4622 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4624 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4630 case SMDSEntity_Edge: { // sweep EDGE
4631 if ( nbDouble == 0 )
4633 if ( nbSame == 0 ) // ---> quadrangle
4634 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4635 nextNod[ 1 ], nextNod[ 0 ] );
4636 else // ---> triangle
4637 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4638 nextNod[ iNotSameNode ] );
4640 else // ---> polygon
4642 vector<const SMDS_MeshNode*> poly_nodes;
4643 poly_nodes.push_back( prevNod[0] );
4644 poly_nodes.push_back( prevNod[1] );
4645 if ( prevNod[1] != nextNod[1] )
4647 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4648 poly_nodes.push_back( nextNod[1] );
4650 if ( prevNod[0] != nextNod[0] )
4652 poly_nodes.push_back( nextNod[0] );
4653 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4655 switch ( poly_nodes.size() ) {
4657 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4660 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4661 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4664 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4669 case SMDSEntity_Triangle: // TRIANGLE --->
4671 if ( nbDouble > 0 ) break;
4672 if ( nbSame == 0 ) // ---> pentahedron
4673 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4674 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4676 else if ( nbSame == 1 ) // ---> pyramid
4677 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4678 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4679 nextNod[ iSameNode ]);
4681 else // 2 same nodes: ---> tetrahedron
4682 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4683 nextNod[ iNotSameNode ]);
4686 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4690 if ( nbDouble+nbSame == 2 )
4692 if(nbSame==0) { // ---> quadratic quadrangle
4693 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4694 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4696 else { //(nbSame==1) // ---> quadratic triangle
4698 return; // medium node on axis
4700 else if(sames[0]==0)
4701 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4702 prevNod[2], midlNod[1], nextNod[2] );
4704 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4705 prevNod[2], nextNod[2], midlNod[0]);
4708 else if ( nbDouble == 3 )
4710 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4711 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4712 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4719 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4720 if ( nbDouble > 0 ) break;
4722 if ( nbSame == 0 ) // ---> hexahedron
4723 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4724 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4726 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4727 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4728 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4729 nextNod[ iSameNode ]);
4730 newElems.push_back( aNewElem );
4731 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4732 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4733 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4735 else if ( nbSame == 2 ) { // ---> pentahedron
4736 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4737 // iBeforeSame is same too
4738 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4739 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4740 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4742 // iAfterSame is same too
4743 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4744 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4745 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4749 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4750 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4751 if ( nbDouble+nbSame != 3 ) break;
4753 // ---> pentahedron with 15 nodes
4754 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4755 nextNod[0], nextNod[1], nextNod[2],
4756 prevNod[3], prevNod[4], prevNod[5],
4757 nextNod[3], nextNod[4], nextNod[5],
4758 midlNod[0], midlNod[1], midlNod[2]);
4760 else if(nbSame==1) {
4761 // ---> 2d order pyramid of 13 nodes
4762 int apex = iSameNode;
4763 int i0 = ( apex + 1 ) % nbCorners;
4764 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4768 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4769 nextNod[i0], nextNod[i1], prevNod[apex],
4770 prevNod[i01], midlNod[i0],
4771 nextNod[i01], midlNod[i1],
4772 prevNod[i1a], prevNod[i0a],
4773 nextNod[i0a], nextNod[i1a]);
4775 else if(nbSame==2) {
4776 // ---> 2d order tetrahedron of 10 nodes
4777 int n1 = iNotSameNode;
4778 int n2 = ( n1 + 1 ) % nbCorners;
4779 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4783 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4784 prevNod[n12], prevNod[n23], prevNod[n31],
4785 midlNod[n1], nextNod[n12], nextNod[n31]);
4789 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4791 if ( nbDouble != 4 ) break;
4792 // ---> hexahedron with 20 nodes
4793 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4794 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4795 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4796 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4797 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4799 else if(nbSame==1) {
4800 // ---> pyramid + pentahedron - can not be created since it is needed
4801 // additional middle node at the center of face
4802 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4805 else if( nbSame == 2 ) {
4806 if ( nbDouble != 2 ) break;
4807 // ---> 2d order Pentahedron with 15 nodes
4809 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4810 // iBeforeSame is same too
4817 // iAfterSame is same too
4827 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4828 prevNod[n4], prevNod[n5], nextNod[n5],
4829 prevNod[n12], midlNod[n2], nextNod[n12],
4830 prevNod[n45], midlNod[n5], nextNod[n45],
4831 prevNod[n14], prevNod[n25], nextNod[n25]);
4835 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4837 if( nbSame == 0 && nbDouble == 9 ) {
4838 // ---> tri-quadratic hexahedron with 27 nodes
4839 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4840 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4841 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4842 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4843 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4844 prevNod[8], // bottom center
4845 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4846 nextNod[8], // top center
4847 midlNod[8]);// elem center
4855 case SMDSEntity_Polygon: { // sweep POLYGON
4857 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4858 // ---> hexagonal prism
4859 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4860 prevNod[3], prevNod[4], prevNod[5],
4861 nextNod[0], nextNod[1], nextNod[2],
4862 nextNod[3], nextNod[4], nextNod[5]);
4866 case SMDSEntity_Ball:
4871 } // switch ( baseType )
4874 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4876 if ( baseType != SMDSEntity_Polygon )
4878 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4879 SMDS_MeshCell::applyInterlace( ind, prevNod );
4880 SMDS_MeshCell::applyInterlace( ind, nextNod );
4881 SMDS_MeshCell::applyInterlace( ind, midlNod );
4882 SMDS_MeshCell::applyInterlace( ind, itNN );
4883 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4884 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4886 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4887 vector<int> quantities (nbNodes + 2);
4888 polyedre_nodes.clear();
4892 for (int inode = 0; inode < nbNodes; inode++)
4893 polyedre_nodes.push_back( prevNod[inode] );
4894 quantities.push_back( nbNodes );
4897 polyedre_nodes.push_back( nextNod[0] );
4898 for (int inode = nbNodes; inode-1; --inode )
4899 polyedre_nodes.push_back( nextNod[inode-1] );
4900 quantities.push_back( nbNodes );
4908 const int iQuad = elem->IsQuadratic();
4909 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4911 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4912 int inextface = (iface+1+iQuad) % nbNodes;
4913 int imid = (iface+1) % nbNodes;
4914 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4915 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4916 polyedre_nodes.push_back( prevNod[iface] ); // 1
4917 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4919 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4920 polyedre_nodes.push_back( nextNod[iface] ); // 2
4922 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4923 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4925 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4926 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4928 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4929 if ( nbFaceNodes > 2 )
4930 quantities.push_back( nbFaceNodes );
4931 else // degenerated face
4932 polyedre_nodes.resize( prevNbNodes );
4934 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4936 } // try to create a polyherdal prism
4939 newElems.push_back( aNewElem );
4940 myLastCreatedElems.Append(aNewElem);
4941 srcElements.Append( elem );
4944 // set new prev nodes
4945 for ( iNode = 0; iNode < nbNodes; iNode++ )
4946 prevNod[ iNode ] = nextNod[ iNode ];
4951 //=======================================================================
4953 * \brief Create 1D and 2D elements around swept elements
4954 * \param mapNewNodes - source nodes and ones generated from them
4955 * \param newElemsMap - source elements and ones generated from them
4956 * \param elemNewNodesMap - nodes generated from each node of each element
4957 * \param elemSet - all swept elements
4958 * \param nbSteps - number of sweeping steps
4959 * \param srcElements - to append elem for each generated element
4961 //=======================================================================
4963 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4964 TTElemOfElemListMap & newElemsMap,
4965 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4966 TIDSortedElemSet& elemSet,
4968 SMESH_SequenceOfElemPtr& srcElements)
4970 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4971 SMESHDS_Mesh* aMesh = GetMeshDS();
4973 // Find nodes belonging to only one initial element - sweep them into edges.
4975 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4976 for ( ; nList != mapNewNodes.end(); nList++ )
4978 const SMDS_MeshNode* node =
4979 static_cast<const SMDS_MeshNode*>( nList->first );
4980 if ( newElemsMap.count( node ))
4981 continue; // node was extruded into edge
4982 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4983 int nbInitElems = 0;
4984 const SMDS_MeshElement* el = 0;
4985 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4986 while ( eIt->more() && nbInitElems < 2 ) {
4987 const SMDS_MeshElement* e = eIt->next();
4988 SMDSAbs_ElementType type = e->GetType();
4989 if ( type == SMDSAbs_Volume ||
4993 if ( type > highType ) {
5000 if ( nbInitElems == 1 ) {
5001 bool NotCreateEdge = el && el->IsMediumNode(node);
5002 if(!NotCreateEdge) {
5003 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5004 list<const SMDS_MeshElement*> newEdges;
5005 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5010 // Make a ceiling for each element ie an equal element of last new nodes.
5011 // Find free links of faces - make edges and sweep them into faces.
5013 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5015 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5016 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5017 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5019 const SMDS_MeshElement* elem = itElem->first;
5020 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5022 if(itElem->second.size()==0) continue;
5024 const bool isQuadratic = elem->IsQuadratic();
5026 if ( elem->GetType() == SMDSAbs_Edge ) {
5027 // create a ceiling edge
5028 if ( !isQuadratic ) {
5029 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5030 vecNewNodes[ 1 ]->second.back())) {
5031 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5032 vecNewNodes[ 1 ]->second.back()));
5033 srcElements.Append( elem );
5037 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5038 vecNewNodes[ 1 ]->second.back(),
5039 vecNewNodes[ 2 ]->second.back())) {
5040 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5041 vecNewNodes[ 1 ]->second.back(),
5042 vecNewNodes[ 2 ]->second.back()));
5043 srcElements.Append( elem );
5047 if ( elem->GetType() != SMDSAbs_Face )
5050 bool hasFreeLinks = false;
5052 TIDSortedElemSet avoidSet;
5053 avoidSet.insert( elem );
5055 set<const SMDS_MeshNode*> aFaceLastNodes;
5056 int iNode, nbNodes = vecNewNodes.size();
5057 if ( !isQuadratic ) {
5058 // loop on the face nodes
5059 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5060 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5061 // look for free links of the face
5062 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5063 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5064 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5065 // check if a link n1-n2 is free
5066 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5067 hasFreeLinks = true;
5068 // make a new edge and a ceiling for a new edge
5069 const SMDS_MeshElement* edge;
5070 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5071 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5072 srcElements.Append( myLastCreatedElems.Last() );
5074 n1 = vecNewNodes[ iNode ]->second.back();
5075 n2 = vecNewNodes[ iNext ]->second.back();
5076 if ( !aMesh->FindEdge( n1, n2 )) {
5077 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5078 srcElements.Append( edge );
5083 else { // elem is quadratic face
5084 int nbn = nbNodes/2;
5085 for ( iNode = 0; iNode < nbn; iNode++ ) {
5086 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5087 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5088 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5089 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5090 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5091 // check if a link is free
5092 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5093 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5094 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5095 hasFreeLinks = true;
5096 // make an edge and a ceiling for a new edge
5098 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5099 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5100 srcElements.Append( elem );
5102 n1 = vecNewNodes[ iNode ]->second.back();
5103 n2 = vecNewNodes[ iNext ]->second.back();
5104 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5105 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5106 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5107 srcElements.Append( elem );
5111 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5112 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5116 // sweep free links into faces
5118 if ( hasFreeLinks ) {
5119 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5120 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5122 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5123 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5124 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5125 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5126 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5128 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5129 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5130 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5132 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5133 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5134 std::advance( v, volNb );
5135 // find indices of free faces of a volume and their source edges
5136 list< int > freeInd;
5137 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5138 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5139 int iF, nbF = vTool.NbFaces();
5140 for ( iF = 0; iF < nbF; iF ++ ) {
5141 if (vTool.IsFreeFace( iF ) &&
5142 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5143 initNodeSet != faceNodeSet) // except an initial face
5145 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5147 if ( faceNodeSet == initNodeSetNoCenter )
5149 freeInd.push_back( iF );
5150 // find source edge of a free face iF
5151 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5152 vector<const SMDS_MeshNode*>::iterator lastCommom;
5153 commonNodes.resize( nbNodes, 0 );
5154 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5155 initNodeSet.begin(), initNodeSet.end(),
5156 commonNodes.begin());
5157 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5158 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5160 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5162 if ( !srcEdges.back() )
5164 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5165 << iF << " of volume #" << vTool.ID() << endl;
5170 if ( freeInd.empty() )
5173 // create wall faces for all steps;
5174 // if such a face has been already created by sweep of edge,
5175 // assure that its orientation is OK
5176 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5178 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5179 vTool.SetExternalNormal();
5180 const int nextShift = vTool.IsForward() ? +1 : -1;
5181 list< int >::iterator ind = freeInd.begin();
5182 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5183 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5185 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5186 int nbn = vTool.NbFaceNodes( *ind );
5187 const SMDS_MeshElement * f = 0;
5188 if ( nbn == 3 ) ///// triangle
5190 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5192 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5194 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5196 nodes[ 1 + nextShift ] };
5198 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5200 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5204 else if ( nbn == 4 ) ///// quadrangle
5206 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5208 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5210 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5211 nodes[ 2 ], nodes[ 2+nextShift ] };
5213 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5215 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5216 newOrder[ 2 ], newOrder[ 3 ]));
5219 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5221 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5223 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5225 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5227 nodes[2 + 2*nextShift],
5228 nodes[3 - 2*nextShift],
5230 nodes[3 + 2*nextShift]};
5232 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5234 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5242 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5244 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5245 nodes[1], nodes[3], nodes[5], nodes[7] );
5247 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5249 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5250 nodes[4 - 2*nextShift],
5252 nodes[4 + 2*nextShift],
5254 nodes[5 - 2*nextShift],
5256 nodes[5 + 2*nextShift] };
5258 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5260 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5261 newOrder[ 2 ], newOrder[ 3 ],
5262 newOrder[ 4 ], newOrder[ 5 ],
5263 newOrder[ 6 ], newOrder[ 7 ]));
5266 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5268 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5269 SMDSAbs_Face, /*noMedium=*/false);
5271 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5273 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5274 nodes[4 - 2*nextShift],
5276 nodes[4 + 2*nextShift],
5278 nodes[5 - 2*nextShift],
5280 nodes[5 + 2*nextShift],
5283 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5285 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5286 newOrder[ 2 ], newOrder[ 3 ],
5287 newOrder[ 4 ], newOrder[ 5 ],
5288 newOrder[ 6 ], newOrder[ 7 ],
5292 else //////// polygon
5294 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5295 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5297 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5299 if ( !vTool.IsForward() )
5300 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5302 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5304 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5308 while ( srcElements.Length() < myLastCreatedElems.Length() )
5309 srcElements.Append( *srcEdge );
5311 } // loop on free faces
5313 // go to the next volume
5315 while ( iVol++ < nbVolumesByStep ) v++;
5318 } // loop on volumes of one step
5319 } // sweep free links into faces
5321 // Make a ceiling face with a normal external to a volume
5323 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5324 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5325 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5327 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5328 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5329 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5333 lastVol.SetExternalNormal();
5334 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5335 const int nbn = lastVol.NbFaceNodes( iF );
5336 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5337 if ( !hasFreeLinks ||
5338 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5340 const vector<int>& interlace =
5341 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5342 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5344 AddElement( nodeVec, anyFace.Init( elem ));
5346 while ( srcElements.Length() < myLastCreatedElems.Length() )
5347 srcElements.Append( elem );
5350 } // loop on swept elements
5353 //=======================================================================
5354 //function : RotationSweep
5356 //=======================================================================
5358 SMESH_MeshEditor::PGroupIDs
5359 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5360 const gp_Ax1& theAxis,
5361 const double theAngle,
5362 const int theNbSteps,
5363 const double theTol,
5364 const bool theMakeGroups,
5365 const bool theMakeWalls)
5367 myLastCreatedElems.Clear();
5368 myLastCreatedNodes.Clear();
5370 // source elements for each generated one
5371 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5374 aTrsf.SetRotation( theAxis, theAngle );
5376 aTrsf2.SetRotation( theAxis, theAngle/2. );
5378 gp_Lin aLine( theAxis );
5379 double aSqTol = theTol * theTol;
5381 SMESHDS_Mesh* aMesh = GetMeshDS();
5383 TNodeOfNodeListMap mapNewNodes;
5384 TElemOfVecOfNnlmiMap mapElemNewNodes;
5385 TTElemOfElemListMap newElemsMap;
5387 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5388 myMesh->NbFaces(ORDER_QUADRATIC) +
5389 myMesh->NbVolumes(ORDER_QUADRATIC) );
5390 // loop on theElemSets
5391 setElemsFirst( theElemSets );
5392 TIDSortedElemSet::iterator itElem;
5393 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5395 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5396 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5397 const SMDS_MeshElement* elem = *itElem;
5398 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5400 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5401 newNodesItVec.reserve( elem->NbNodes() );
5403 // loop on elem nodes
5404 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5405 while ( itN->more() )
5407 const SMDS_MeshNode* node = cast2Node( itN->next() );
5409 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5411 aXYZ.Coord( coord[0], coord[1], coord[2] );
5412 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5414 // check if a node has been already sweeped
5415 TNodeOfNodeListMapItr nIt =
5416 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5417 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5418 if ( listNewNodes.empty() )
5420 // check if we are to create medium nodes between corner ones
5421 bool needMediumNodes = false;
5422 if ( isQuadraticMesh )
5424 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5425 while (it->more() && !needMediumNodes )
5427 const SMDS_MeshElement* invElem = it->next();
5428 if ( invElem != elem && !theElems.count( invElem )) continue;
5429 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5430 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5431 needMediumNodes = true;
5436 const SMDS_MeshNode * newNode = node;
5437 for ( int i = 0; i < theNbSteps; i++ ) {
5439 if ( needMediumNodes ) // create a medium node
5441 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5442 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5443 myLastCreatedNodes.Append(newNode);
5444 srcNodes.Append( node );
5445 listNewNodes.push_back( newNode );
5446 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5449 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5451 // create a corner node
5452 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5453 myLastCreatedNodes.Append(newNode);
5454 srcNodes.Append( node );
5455 listNewNodes.push_back( newNode );
5458 listNewNodes.push_back( newNode );
5459 // if ( needMediumNodes )
5460 // listNewNodes.push_back( newNode );
5464 newNodesItVec.push_back( nIt );
5466 // make new elements
5467 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5472 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5474 PGroupIDs newGroupIDs;
5475 if ( theMakeGroups )
5476 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5481 //=======================================================================
5482 //function : ExtrusParam
5483 //purpose : standard construction
5484 //=======================================================================
5486 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5487 const int theNbSteps,
5488 const std::list<double>& theScales,
5489 const gp_XYZ* theBasePoint,
5491 const double theTolerance):
5493 myBaseP( Precision::Infinite(), 0, 0 ),
5494 myFlags( theFlags ),
5495 myTolerance( theTolerance ),
5496 myElemsToUse( NULL )
5498 mySteps = new TColStd_HSequenceOfReal;
5499 const double stepSize = theStep.Magnitude();
5500 for (int i=1; i<=theNbSteps; i++ )
5501 mySteps->Append( stepSize );
5503 int nbScales = theScales.size();
5506 if ( IsLinearVariation() && nbScales < theNbSteps )
5508 myScales.reserve( theNbSteps );
5509 std::list<double>::const_iterator scale = theScales.begin();
5510 double prevScale = 1.0;
5511 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5513 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5514 int stDelta = Max( 1, iStep - myScales.size());
5515 double scDelta = ( *scale - prevScale ) / stDelta;
5516 for ( int iStep = 0; iStep < stDelta; ++iStep )
5518 myScales.push_back( prevScale + scDelta );
5519 prevScale = myScales.back();
5526 myScales.assign( theScales.begin(), theScales.end() );
5531 myBaseP = *theBasePoint;
5534 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5535 ( theTolerance > 0 ))
5537 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5541 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5545 //=======================================================================
5546 //function : ExtrusParam
5547 //purpose : steps are given explicitly
5548 //=======================================================================
5550 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5551 Handle(TColStd_HSequenceOfReal) theSteps,
5553 const double theTolerance):
5555 mySteps( theSteps ),
5556 myFlags( theFlags ),
5557 myTolerance( theTolerance ),
5558 myElemsToUse( NULL )
5560 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5561 ( theTolerance > 0 ))
5563 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5567 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5571 //=======================================================================
5572 //function : ExtrusParam
5573 //purpose : for extrusion by normal
5574 //=======================================================================
5576 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5577 const int theNbSteps,
5581 mySteps( new TColStd_HSequenceOfReal ),
5582 myFlags( theFlags ),
5584 myElemsToUse( NULL )
5586 for (int i = 0; i < theNbSteps; i++ )
5587 mySteps->Append( theStepSize );
5591 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5595 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5599 //=======================================================================
5600 //function : ExtrusParam::SetElementsToUse
5601 //purpose : stores elements to use for extrusion by normal, depending on
5602 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5603 // define myBaseP for scaling
5604 //=======================================================================
5606 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5607 const TIDSortedElemSet& nodes )
5609 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5611 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5613 myBaseP.SetCoord( 0.,0.,0. );
5614 TIDSortedElemSet newNodes;
5616 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5617 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5619 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5620 TIDSortedElemSet::const_iterator itElem = elements.begin();
5621 for ( ; itElem != elements.end(); itElem++ )
5623 const SMDS_MeshElement* elem = *itElem;
5624 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5625 while ( itN->more() ) {
5626 const SMDS_MeshElement* node = itN->next();
5627 if ( newNodes.insert( node ).second )
5628 myBaseP += SMESH_TNodeXYZ( node );
5632 myBaseP /= newNodes.size();
5636 //=======================================================================
5637 //function : ExtrusParam::beginStepIter
5638 //purpose : prepare iteration on steps
5639 //=======================================================================
5641 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5643 myWithMediumNodes = withMediumNodes;
5647 //=======================================================================
5648 //function : ExtrusParam::moreSteps
5649 //purpose : are there more steps?
5650 //=======================================================================
5652 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5654 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5656 //=======================================================================
5657 //function : ExtrusParam::nextStep
5658 //purpose : returns the next step
5659 //=======================================================================
5661 double SMESH_MeshEditor::ExtrusParam::nextStep()
5664 if ( !myCurSteps.empty() )
5666 res = myCurSteps.back();
5667 myCurSteps.pop_back();
5669 else if ( myNextStep <= mySteps->Length() )
5671 myCurSteps.push_back( mySteps->Value( myNextStep ));
5673 if ( myWithMediumNodes )
5675 myCurSteps.back() /= 2.;
5676 myCurSteps.push_back( myCurSteps.back() );
5683 //=======================================================================
5684 //function : ExtrusParam::makeNodesByDir
5685 //purpose : create nodes for standard extrusion
5686 //=======================================================================
5688 int SMESH_MeshEditor::ExtrusParam::
5689 makeNodesByDir( SMESHDS_Mesh* mesh,
5690 const SMDS_MeshNode* srcNode,
5691 std::list<const SMDS_MeshNode*> & newNodes,
5692 const bool makeMediumNodes)
5694 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5697 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5699 p += myDir.XYZ() * nextStep();
5700 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5701 newNodes.push_back( newNode );
5704 if ( !myScales.empty() )
5706 if ( makeMediumNodes && myMediumScales.empty() )
5708 myMediumScales.resize( myScales.size() );
5709 double prevFactor = 1.;
5710 for ( size_t i = 0; i < myScales.size(); ++i )
5712 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5713 prevFactor = myScales[i];
5716 typedef std::vector<double>::iterator ScaleIt;
5717 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5719 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5721 gp_XYZ center = myBaseP;
5722 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5724 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5726 center += myDir.XYZ() * nextStep();
5728 iSc += int( makeMediumNodes );
5729 ScaleIt& scale = scales[ iSc % 2 ];
5731 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5732 xyz = ( *scale * ( xyz - center )) + center;
5733 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5741 //=======================================================================
5742 //function : ExtrusParam::makeNodesByDirAndSew
5743 //purpose : create nodes for standard extrusion with sewing
5744 //=======================================================================
5746 int SMESH_MeshEditor::ExtrusParam::
5747 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5748 const SMDS_MeshNode* srcNode,
5749 std::list<const SMDS_MeshNode*> & newNodes,
5750 const bool makeMediumNodes)
5752 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5755 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5757 P1 += myDir.XYZ() * nextStep();
5759 // try to search in sequence of existing nodes
5760 // if myNodes.Length()>0 we 'nave to use given sequence
5761 // else - use all nodes of mesh
5762 const SMDS_MeshNode * node = 0;
5763 if ( myNodes.Length() > 0 ) {
5765 for(i=1; i<=myNodes.Length(); i++) {
5766 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5767 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5769 node = myNodes.Value(i);
5775 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5776 while(itn->more()) {
5777 SMESH_TNodeXYZ P2( itn->next() );
5778 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5787 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5789 newNodes.push_back( node );
5796 //=======================================================================
5797 //function : ExtrusParam::makeNodesByNormal2D
5798 //purpose : create nodes for extrusion using normals of faces
5799 //=======================================================================
5801 int SMESH_MeshEditor::ExtrusParam::
5802 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5803 const SMDS_MeshNode* srcNode,
5804 std::list<const SMDS_MeshNode*> & newNodes,
5805 const bool makeMediumNodes)
5807 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5809 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5811 // get normals to faces sharing srcNode
5812 vector< gp_XYZ > norms, baryCenters;
5813 gp_XYZ norm, avgNorm( 0,0,0 );
5814 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5815 while ( faceIt->more() )
5817 const SMDS_MeshElement* face = faceIt->next();
5818 if ( myElemsToUse && !myElemsToUse->count( face ))
5820 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5822 norms.push_back( norm );
5824 if ( !alongAvgNorm )
5828 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5829 bc += SMESH_TNodeXYZ( nIt->next() );
5830 baryCenters.push_back( bc / nbN );
5835 if ( norms.empty() ) return 0;
5837 double normSize = avgNorm.Modulus();
5838 if ( normSize < std::numeric_limits<double>::min() )
5841 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5844 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5847 avgNorm /= normSize;
5850 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5853 double stepSize = nextStep();
5855 if ( norms.size() > 1 )
5857 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5859 // translate plane of a face
5860 baryCenters[ iF ] += norms[ iF ] * stepSize;
5862 // find point of intersection of the face plane located at baryCenters[ iF ]
5863 // and avgNorm located at pNew
5864 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5865 double dot = ( norms[ iF ] * avgNorm );
5866 if ( dot < std::numeric_limits<double>::min() )
5867 dot = stepSize * 1e-3;
5868 double step = -( norms[ iF ] * pNew + d ) / dot;
5869 pNew += step * avgNorm;
5874 pNew += stepSize * avgNorm;
5878 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5879 newNodes.push_back( newNode );
5884 //=======================================================================
5885 //function : ExtrusParam::makeNodesByNormal1D
5886 //purpose : create nodes for extrusion using normals of edges
5887 //=======================================================================
5889 int SMESH_MeshEditor::ExtrusParam::
5890 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5891 const SMDS_MeshNode* srcNode,
5892 std::list<const SMDS_MeshNode*> & newNodes,
5893 const bool makeMediumNodes)
5895 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5899 //=======================================================================
5900 //function : ExtrusionSweep
5902 //=======================================================================
5904 SMESH_MeshEditor::PGroupIDs
5905 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5906 const gp_Vec& theStep,
5907 const int theNbSteps,
5908 TTElemOfElemListMap& newElemsMap,
5910 const double theTolerance)
5912 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5913 return ExtrusionSweep( theElems, aParams, newElemsMap );
5917 //=======================================================================
5918 //function : ExtrusionSweep
5920 //=======================================================================
5922 SMESH_MeshEditor::PGroupIDs
5923 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5924 ExtrusParam& theParams,
5925 TTElemOfElemListMap& newElemsMap)
5927 myLastCreatedElems.Clear();
5928 myLastCreatedNodes.Clear();
5930 // source elements for each generated one
5931 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5933 setElemsFirst( theElemSets );
5934 const int nbSteps = theParams.NbSteps();
5935 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5937 TNodeOfNodeListMap mapNewNodes;
5938 TElemOfVecOfNnlmiMap mapElemNewNodes;
5940 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5941 myMesh->NbFaces(ORDER_QUADRATIC) +
5942 myMesh->NbVolumes(ORDER_QUADRATIC) );
5944 TIDSortedElemSet::iterator itElem;
5945 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5947 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5948 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5950 // check element type
5951 const SMDS_MeshElement* elem = *itElem;
5952 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5955 const size_t nbNodes = elem->NbNodes();
5956 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5957 newNodesItVec.reserve( nbNodes );
5959 // loop on elem nodes
5960 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5961 while ( itN->more() )
5963 // check if a node has been already sweeped
5964 const SMDS_MeshNode* node = cast2Node( itN->next() );
5965 TNodeOfNodeListMap::iterator nIt =
5966 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5967 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5968 if ( listNewNodes.empty() )
5972 // check if we are to create medium nodes between corner ones
5973 bool needMediumNodes = false;
5974 if ( isQuadraticMesh )
5976 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5977 while (it->more() && !needMediumNodes )
5979 const SMDS_MeshElement* invElem = it->next();
5980 if ( invElem != elem && !theElems.count( invElem )) continue;
5981 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5982 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5983 needMediumNodes = true;
5986 // create nodes for all steps
5987 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5989 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5990 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5992 myLastCreatedNodes.Append( *newNodesIt );
5993 srcNodes.Append( node );
5998 break; // newNodesItVec will be shorter than nbNodes
6001 newNodesItVec.push_back( nIt );
6003 // make new elements
6004 if ( newNodesItVec.size() == nbNodes )
6005 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6009 if ( theParams.ToMakeBoundary() ) {
6010 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6012 PGroupIDs newGroupIDs;
6013 if ( theParams.ToMakeGroups() )
6014 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6019 //=======================================================================
6020 //function : ExtrusionAlongTrack
6022 //=======================================================================
6023 SMESH_MeshEditor::Extrusion_Error
6024 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6025 SMESH_subMesh* theTrack,
6026 const SMDS_MeshNode* theN1,
6027 const bool theHasAngles,
6028 list<double>& theAngles,
6029 const bool theLinearVariation,
6030 const bool theHasRefPoint,
6031 const gp_Pnt& theRefPoint,
6032 const bool theMakeGroups)
6034 myLastCreatedElems.Clear();
6035 myLastCreatedNodes.Clear();
6038 std::list<double> aPrms;
6039 TIDSortedElemSet::iterator itElem;
6042 TopoDS_Edge aTrackEdge;
6043 TopoDS_Vertex aV1, aV2;
6045 SMDS_ElemIteratorPtr aItE;
6046 SMDS_NodeIteratorPtr aItN;
6047 SMDSAbs_ElementType aTypeE;
6049 TNodeOfNodeListMap mapNewNodes;
6052 aNbE = theElements[0].size() + theElements[1].size();
6055 return EXTR_NO_ELEMENTS;
6057 // 1.1 Track Pattern
6060 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6062 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6063 theHasAngles, theAngles, theLinearVariation,
6064 theHasRefPoint, theRefPoint, theMakeGroups );
6066 aItE = pSubMeshDS->GetElements();
6067 while ( aItE->more() ) {
6068 const SMDS_MeshElement* pE = aItE->next();
6069 aTypeE = pE->GetType();
6070 // Pattern must contain links only
6071 if ( aTypeE != SMDSAbs_Edge )
6072 return EXTR_PATH_NOT_EDGE;
6075 list<SMESH_MeshEditor_PathPoint> fullList;
6077 const TopoDS_Shape& aS = theTrack->GetSubShape();
6078 // Sub-shape for the Pattern must be an Edge or Wire
6079 if( aS.ShapeType() == TopAbs_EDGE ) {
6080 aTrackEdge = TopoDS::Edge( aS );
6081 // the Edge must not be degenerated
6082 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6083 return EXTR_BAD_PATH_SHAPE;
6084 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6085 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6086 const SMDS_MeshNode* aN1 = aItN->next();
6087 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6088 const SMDS_MeshNode* aN2 = aItN->next();
6089 // starting node must be aN1 or aN2
6090 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6091 return EXTR_BAD_STARTING_NODE;
6092 aItN = pSubMeshDS->GetNodes();
6093 while ( aItN->more() ) {
6094 const SMDS_MeshNode* pNode = aItN->next();
6095 const SMDS_EdgePosition* pEPos =
6096 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6097 double aT = pEPos->GetUParameter();
6098 aPrms.push_back( aT );
6100 //Extrusion_Error err =
6101 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6102 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6103 list< SMESH_subMesh* > LSM;
6104 TopTools_SequenceOfShape Edges;
6105 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6106 while(itSM->more()) {
6107 SMESH_subMesh* SM = itSM->next();
6109 const TopoDS_Shape& aS = SM->GetSubShape();
6112 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6113 int startNid = theN1->GetID();
6114 TColStd_MapOfInteger UsedNums;
6116 int NbEdges = Edges.Length();
6118 for(; i<=NbEdges; i++) {
6120 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6121 for(; itLSM!=LSM.end(); itLSM++) {
6123 if(UsedNums.Contains(k)) continue;
6124 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6125 SMESH_subMesh* locTrack = *itLSM;
6126 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6127 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6128 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6129 const SMDS_MeshNode* aN1 = aItN->next();
6130 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6131 const SMDS_MeshNode* aN2 = aItN->next();
6132 // starting node must be aN1 or aN2
6133 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6134 // 2. Collect parameters on the track edge
6136 aItN = locMeshDS->GetNodes();
6137 while ( aItN->more() ) {
6138 const SMDS_MeshNode* pNode = aItN->next();
6139 const SMDS_EdgePosition* pEPos =
6140 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6141 double aT = pEPos->GetUParameter();
6142 aPrms.push_back( aT );
6144 list<SMESH_MeshEditor_PathPoint> LPP;
6145 //Extrusion_Error err =
6146 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6147 LLPPs.push_back(LPP);
6149 // update startN for search following egde
6150 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6151 else startNid = aN1->GetID();
6155 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6156 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6157 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6158 for(; itPP!=firstList.end(); itPP++) {
6159 fullList.push_back( *itPP );
6161 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6162 fullList.pop_back();
6164 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6165 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6166 itPP = currList.begin();
6167 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6168 gp_Dir D1 = PP1.Tangent();
6169 gp_Dir D2 = PP2.Tangent();
6170 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6171 (D1.Z()+D2.Z())/2 ) );
6172 PP1.SetTangent(Dnew);
6173 fullList.push_back(PP1);
6175 for(; itPP!=firstList.end(); itPP++) {
6176 fullList.push_back( *itPP );
6178 PP1 = fullList.back();
6179 fullList.pop_back();
6181 // if wire not closed
6182 fullList.push_back(PP1);
6186 return EXTR_BAD_PATH_SHAPE;
6189 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6190 theHasRefPoint, theRefPoint, theMakeGroups);
6194 //=======================================================================
6195 //function : ExtrusionAlongTrack
6197 //=======================================================================
6198 SMESH_MeshEditor::Extrusion_Error
6199 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6200 SMESH_Mesh* theTrack,
6201 const SMDS_MeshNode* theN1,
6202 const bool theHasAngles,
6203 list<double>& theAngles,
6204 const bool theLinearVariation,
6205 const bool theHasRefPoint,
6206 const gp_Pnt& theRefPoint,
6207 const bool theMakeGroups)
6209 myLastCreatedElems.Clear();
6210 myLastCreatedNodes.Clear();
6213 std::list<double> aPrms;
6214 TIDSortedElemSet::iterator itElem;
6217 TopoDS_Edge aTrackEdge;
6218 TopoDS_Vertex aV1, aV2;
6220 SMDS_ElemIteratorPtr aItE;
6221 SMDS_NodeIteratorPtr aItN;
6222 SMDSAbs_ElementType aTypeE;
6224 TNodeOfNodeListMap mapNewNodes;
6227 aNbE = theElements[0].size() + theElements[1].size();
6230 return EXTR_NO_ELEMENTS;
6232 // 1.1 Track Pattern
6235 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6237 aItE = pMeshDS->elementsIterator();
6238 while ( aItE->more() ) {
6239 const SMDS_MeshElement* pE = aItE->next();
6240 aTypeE = pE->GetType();
6241 // Pattern must contain links only
6242 if ( aTypeE != SMDSAbs_Edge )
6243 return EXTR_PATH_NOT_EDGE;
6246 list<SMESH_MeshEditor_PathPoint> fullList;
6248 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6250 if ( !theTrack->HasShapeToMesh() ) {
6251 //Mesh without shape
6252 const SMDS_MeshNode* currentNode = NULL;
6253 const SMDS_MeshNode* prevNode = theN1;
6254 std::vector<const SMDS_MeshNode*> aNodesList;
6255 aNodesList.push_back(theN1);
6256 int nbEdges = 0, conn=0;
6257 const SMDS_MeshElement* prevElem = NULL;
6258 const SMDS_MeshElement* currentElem = NULL;
6259 int totalNbEdges = theTrack->NbEdges();
6260 SMDS_ElemIteratorPtr nIt;
6263 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6264 return EXTR_BAD_STARTING_NODE;
6267 conn = nbEdgeConnectivity(theN1);
6269 return EXTR_PATH_NOT_EDGE;
6271 aItE = theN1->GetInverseElementIterator();
6272 prevElem = aItE->next();
6273 currentElem = prevElem;
6275 if(totalNbEdges == 1 ) {
6276 nIt = currentElem->nodesIterator();
6277 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6278 if(currentNode == prevNode)
6279 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6280 aNodesList.push_back(currentNode);
6282 nIt = currentElem->nodesIterator();
6283 while( nIt->more() ) {
6284 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6285 if(currentNode == prevNode)
6286 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6287 aNodesList.push_back(currentNode);
6289 //case of the closed mesh
6290 if(currentNode == theN1) {
6295 conn = nbEdgeConnectivity(currentNode);
6297 return EXTR_PATH_NOT_EDGE;
6298 }else if( conn == 1 && nbEdges > 0 ) {
6303 prevNode = currentNode;
6304 aItE = currentNode->GetInverseElementIterator();
6305 currentElem = aItE->next();
6306 if( currentElem == prevElem)
6307 currentElem = aItE->next();
6308 nIt = currentElem->nodesIterator();
6309 prevElem = currentElem;
6315 if(nbEdges != totalNbEdges)
6316 return EXTR_PATH_NOT_EDGE;
6318 TopTools_SequenceOfShape Edges;
6319 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6320 int startNid = theN1->GetID();
6321 for ( size_t i = 1; i < aNodesList.size(); i++ )
6323 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6324 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6325 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6326 list<SMESH_MeshEditor_PathPoint> LPP;
6328 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6329 LLPPs.push_back(LPP);
6330 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6331 else startNid = aNodesList[i-1]->GetID();
6334 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6335 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6336 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6337 for(; itPP!=firstList.end(); itPP++) {
6338 fullList.push_back( *itPP );
6341 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6342 SMESH_MeshEditor_PathPoint PP2;
6343 fullList.pop_back();
6345 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6346 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6347 itPP = currList.begin();
6348 PP2 = currList.front();
6349 gp_Dir D1 = PP1.Tangent();
6350 gp_Dir D2 = PP2.Tangent();
6351 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6352 PP1.SetTangent(Dnew);
6353 fullList.push_back(PP1);
6355 for(; itPP!=currList.end(); itPP++) {
6356 fullList.push_back( *itPP );
6358 PP1 = fullList.back();
6359 fullList.pop_back();
6361 fullList.push_back(PP1);
6363 } // Sub-shape for the Pattern must be an Edge or Wire
6364 else if ( aS.ShapeType() == TopAbs_EDGE )
6366 aTrackEdge = TopoDS::Edge( aS );
6367 // the Edge must not be degenerated
6368 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6369 return EXTR_BAD_PATH_SHAPE;
6370 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6371 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6372 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6373 // starting node must be aN1 or aN2
6374 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6375 return EXTR_BAD_STARTING_NODE;
6376 aItN = pMeshDS->nodesIterator();
6377 while ( aItN->more() ) {
6378 const SMDS_MeshNode* pNode = aItN->next();
6379 if( pNode==aN1 || pNode==aN2 ) continue;
6380 const SMDS_EdgePosition* pEPos =
6381 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6382 double aT = pEPos->GetUParameter();
6383 aPrms.push_back( aT );
6385 //Extrusion_Error err =
6386 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6388 else if( aS.ShapeType() == TopAbs_WIRE ) {
6389 list< SMESH_subMesh* > LSM;
6390 TopTools_SequenceOfShape Edges;
6391 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6392 for(; eExp.More(); eExp.Next()) {
6393 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6394 if( SMESH_Algo::isDegenerated(E) ) continue;
6395 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6401 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6402 TopoDS_Vertex aVprev;
6403 TColStd_MapOfInteger UsedNums;
6404 int NbEdges = Edges.Length();
6406 for(; i<=NbEdges; i++) {
6408 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6409 for(; itLSM!=LSM.end(); itLSM++) {
6411 if(UsedNums.Contains(k)) continue;
6412 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6413 SMESH_subMesh* locTrack = *itLSM;
6414 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6415 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6416 bool aN1isOK = false, aN2isOK = false;
6417 if ( aVprev.IsNull() ) {
6418 // if previous vertex is not yet defined, it means that we in the beginning of wire
6419 // and we have to find initial vertex corresponding to starting node theN1
6420 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6421 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6422 // starting node must be aN1 or aN2
6423 aN1isOK = ( aN1 && aN1 == theN1 );
6424 aN2isOK = ( aN2 && aN2 == theN1 );
6427 // we have specified ending vertex of the previous edge on the previous iteration
6428 // and we have just to check that it corresponds to any vertex in current segment
6429 aN1isOK = aVprev.IsSame( aV1 );
6430 aN2isOK = aVprev.IsSame( aV2 );
6432 if ( !aN1isOK && !aN2isOK ) continue;
6433 // 2. Collect parameters on the track edge
6435 aItN = locMeshDS->GetNodes();
6436 while ( aItN->more() ) {
6437 const SMDS_MeshNode* pNode = aItN->next();
6438 const SMDS_EdgePosition* pEPos =
6439 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6440 double aT = pEPos->GetUParameter();
6441 aPrms.push_back( aT );
6443 list<SMESH_MeshEditor_PathPoint> LPP;
6444 //Extrusion_Error err =
6445 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6446 LLPPs.push_back(LPP);
6448 // update startN for search following egde
6449 if ( aN1isOK ) aVprev = aV2;
6454 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6455 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6456 fullList.splice( fullList.end(), firstList );
6458 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6459 fullList.pop_back();
6461 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6462 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6463 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6464 gp_Dir D1 = PP1.Tangent();
6465 gp_Dir D2 = PP2.Tangent();
6466 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6467 PP1.SetTangent(Dnew);
6468 fullList.push_back(PP1);
6469 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6470 PP1 = fullList.back();
6471 fullList.pop_back();
6473 // if wire not closed
6474 fullList.push_back(PP1);
6478 return EXTR_BAD_PATH_SHAPE;
6481 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6482 theHasRefPoint, theRefPoint, theMakeGroups);
6486 //=======================================================================
6487 //function : makeEdgePathPoints
6488 //purpose : auxiliary for ExtrusionAlongTrack
6489 //=======================================================================
6490 SMESH_MeshEditor::Extrusion_Error
6491 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6492 const TopoDS_Edge& aTrackEdge,
6494 list<SMESH_MeshEditor_PathPoint>& LPP)
6496 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6498 aTolVec2=aTolVec*aTolVec;
6500 TopoDS_Vertex aV1, aV2;
6501 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6502 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6503 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6504 // 2. Collect parameters on the track edge
6505 aPrms.push_front( aT1 );
6506 aPrms.push_back( aT2 );
6509 if( FirstIsStart ) {
6520 SMESH_MeshEditor_PathPoint aPP;
6521 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6522 std::list<double>::iterator aItD = aPrms.begin();
6523 for(; aItD != aPrms.end(); ++aItD) {
6527 aC3D->D1( aT, aP3D, aVec );
6528 aL2 = aVec.SquareMagnitude();
6529 if ( aL2 < aTolVec2 )
6530 return EXTR_CANT_GET_TANGENT;
6531 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6533 aPP.SetTangent( aTgt );
6534 aPP.SetParameter( aT );
6541 //=======================================================================
6542 //function : makeExtrElements
6543 //purpose : auxiliary for ExtrusionAlongTrack
6544 //=======================================================================
6545 SMESH_MeshEditor::Extrusion_Error
6546 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6547 list<SMESH_MeshEditor_PathPoint>& fullList,
6548 const bool theHasAngles,
6549 list<double>& theAngles,
6550 const bool theLinearVariation,
6551 const bool theHasRefPoint,
6552 const gp_Pnt& theRefPoint,
6553 const bool theMakeGroups)
6555 const int aNbTP = fullList.size();
6558 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6559 linearAngleVariation(aNbTP-1, theAngles);
6561 // fill vector of path points with angles
6562 vector<SMESH_MeshEditor_PathPoint> aPPs;
6563 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6564 list<double>::iterator itAngles = theAngles.begin();
6565 aPPs.push_back( *itPP++ );
6566 for( ; itPP != fullList.end(); itPP++) {
6567 aPPs.push_back( *itPP );
6568 if ( theHasAngles && itAngles != theAngles.end() )
6569 aPPs.back().SetAngle( *itAngles++ );
6572 TNodeOfNodeListMap mapNewNodes;
6573 TElemOfVecOfNnlmiMap mapElemNewNodes;
6574 TTElemOfElemListMap newElemsMap;
6575 TIDSortedElemSet::iterator itElem;
6576 // source elements for each generated one
6577 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6579 // 3. Center of rotation aV0
6580 gp_Pnt aV0 = theRefPoint;
6581 if ( !theHasRefPoint )
6583 gp_XYZ aGC( 0.,0.,0. );
6584 TIDSortedElemSet newNodes;
6586 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6588 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6589 itElem = theElements.begin();
6590 for ( ; itElem != theElements.end(); itElem++ )
6592 const SMDS_MeshElement* elem = *itElem;
6593 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6594 while ( itN->more() ) {
6595 const SMDS_MeshElement* node = itN->next();
6596 if ( newNodes.insert( node ).second )
6597 aGC += SMESH_TNodeXYZ( node );
6601 aGC /= newNodes.size();
6603 } // if (!theHasRefPoint) {
6605 // 4. Processing the elements
6606 SMESHDS_Mesh* aMesh = GetMeshDS();
6607 list<const SMDS_MeshNode*> emptyList;
6609 setElemsFirst( theElemSets );
6610 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6612 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6613 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6615 const SMDS_MeshElement* elem = *itElem;
6617 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6618 newNodesItVec.reserve( elem->NbNodes() );
6620 // loop on elem nodes
6622 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6623 while ( itN->more() )
6626 // check if a node has been already processed
6627 const SMDS_MeshNode* node = cast2Node( itN->next() );
6628 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6629 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6630 if ( listNewNodes.empty() )
6633 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6634 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6635 gp_Ax1 anAx1, anAxT1T0;
6636 gp_Dir aDT1x, aDT0x, aDT1T0;
6641 aPN0 = SMESH_TNodeXYZ( node );
6643 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6645 aDT0x= aPP0.Tangent();
6647 for ( int j = 1; j < aNbTP; ++j ) {
6648 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6650 aDT1x = aPP1.Tangent();
6651 aAngle1x = aPP1.Angle();
6653 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6655 gp_Vec aV01x( aP0x, aP1x );
6656 aTrsf.SetTranslation( aV01x );
6659 aV1x = aV0x.Transformed( aTrsf );
6660 aPN1 = aPN0.Transformed( aTrsf );
6662 // rotation 1 [ T1,T0 ]
6663 aAngleT1T0=-aDT1x.Angle( aDT0x );
6664 if (fabs(aAngleT1T0) > aTolAng)
6667 anAxT1T0.SetLocation( aV1x );
6668 anAxT1T0.SetDirection( aDT1T0 );
6669 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6671 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6675 if ( theHasAngles ) {
6676 anAx1.SetLocation( aV1x );
6677 anAx1.SetDirection( aDT1x );
6678 aTrsfRot.SetRotation( anAx1, aAngle1x );
6680 aPN1 = aPN1.Transformed( aTrsfRot );
6684 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6686 // create additional node
6687 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6688 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6689 myLastCreatedNodes.Append(newNode);
6690 srcNodes.Append( node );
6691 listNewNodes.push_back( newNode );
6693 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6694 myLastCreatedNodes.Append(newNode);
6695 srcNodes.Append( node );
6696 listNewNodes.push_back( newNode );
6704 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6706 // if current elem is quadratic and current node is not medium
6707 // we have to check - may be it is needed to insert additional nodes
6708 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6709 if ((int) listNewNodes.size() == aNbTP-1 )
6711 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6712 gp_XYZ P(node->X(), node->Y(), node->Z());
6713 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6715 for(i=0; i<aNbTP-1; i++) {
6716 const SMDS_MeshNode* N = *it;
6717 double x = ( N->X() + P.X() )/2.;
6718 double y = ( N->Y() + P.Y() )/2.;
6719 double z = ( N->Z() + P.Z() )/2.;
6720 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6721 srcNodes.Append( node );
6722 myLastCreatedNodes.Append(newN);
6725 P = gp_XYZ(N->X(),N->Y(),N->Z());
6727 listNewNodes.clear();
6728 for(i=0; i<2*(aNbTP-1); i++) {
6729 listNewNodes.push_back(aNodes[i]);
6734 newNodesItVec.push_back( nIt );
6737 // make new elements
6738 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6742 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6744 if ( theMakeGroups )
6745 generateGroups( srcNodes, srcElems, "extruded");
6751 //=======================================================================
6752 //function : linearAngleVariation
6753 //purpose : spread values over nbSteps
6754 //=======================================================================
6756 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6757 list<double>& Angles)
6759 int nbAngles = Angles.size();
6760 if( nbSteps > nbAngles && nbAngles > 0 )
6762 vector<double> theAngles(nbAngles);
6763 theAngles.assign( Angles.begin(), Angles.end() );
6766 double rAn2St = double( nbAngles ) / double( nbSteps );
6767 double angPrev = 0, angle;
6768 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6770 double angCur = rAn2St * ( iSt+1 );
6771 double angCurFloor = floor( angCur );
6772 double angPrevFloor = floor( angPrev );
6773 if ( angPrevFloor == angCurFloor )
6774 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6776 int iP = int( angPrevFloor );
6777 double angPrevCeil = ceil(angPrev);
6778 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6780 int iC = int( angCurFloor );
6781 if ( iC < nbAngles )
6782 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6784 iP = int( angPrevCeil );
6786 angle += theAngles[ iC ];
6788 res.push_back(angle);
6796 //================================================================================
6798 * \brief Move or copy theElements applying theTrsf to their nodes
6799 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6800 * \param theTrsf - transformation to apply
6801 * \param theCopy - if true, create translated copies of theElems
6802 * \param theMakeGroups - if true and theCopy, create translated groups
6803 * \param theTargetMesh - mesh to copy translated elements into
6804 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6806 //================================================================================
6808 SMESH_MeshEditor::PGroupIDs
6809 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6810 const gp_Trsf& theTrsf,
6812 const bool theMakeGroups,
6813 SMESH_Mesh* theTargetMesh)
6815 myLastCreatedElems.Clear();
6816 myLastCreatedNodes.Clear();
6818 bool needReverse = false;
6819 string groupPostfix;
6820 switch ( theTrsf.Form() ) {
6823 groupPostfix = "mirrored";
6826 groupPostfix = "mirrored";
6830 groupPostfix = "mirrored";
6833 groupPostfix = "rotated";
6835 case gp_Translation:
6836 groupPostfix = "translated";
6839 groupPostfix = "scaled";
6841 case gp_CompoundTrsf: // different scale by axis
6842 groupPostfix = "scaled";
6845 needReverse = false;
6846 groupPostfix = "transformed";
6849 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6850 SMESHDS_Mesh* aMesh = GetMeshDS();
6852 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6853 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6854 SMESH_MeshEditor::ElemFeatures elemType;
6856 // map old node to new one
6857 TNodeNodeMap nodeMap;
6859 // elements sharing moved nodes; those of them which have all
6860 // nodes mirrored but are not in theElems are to be reversed
6861 TIDSortedElemSet inverseElemSet;
6863 // source elements for each generated one
6864 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6866 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6867 TIDSortedElemSet orphanNode;
6869 if ( theElems.empty() ) // transform the whole mesh
6872 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6873 while ( eIt->more() ) theElems.insert( eIt->next() );
6875 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6876 while ( nIt->more() )
6878 const SMDS_MeshNode* node = nIt->next();
6879 if ( node->NbInverseElements() == 0)
6880 orphanNode.insert( node );
6884 // loop on elements to transform nodes : first orphan nodes then elems
6885 TIDSortedElemSet::iterator itElem;
6886 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6887 for (int i=0; i<2; i++)
6888 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6890 const SMDS_MeshElement* elem = *itElem;
6894 // loop on elem nodes
6896 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6897 while ( itN->more() )
6899 const SMDS_MeshNode* node = cast2Node( itN->next() );
6900 // check if a node has been already transformed
6901 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6902 nodeMap.insert( make_pair ( node, node ));
6903 if ( !n2n_isnew.second )
6906 node->GetXYZ( coord );
6907 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6908 if ( theTargetMesh ) {
6909 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6910 n2n_isnew.first->second = newNode;
6911 myLastCreatedNodes.Append(newNode);
6912 srcNodes.Append( node );
6914 else if ( theCopy ) {
6915 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6916 n2n_isnew.first->second = newNode;
6917 myLastCreatedNodes.Append(newNode);
6918 srcNodes.Append( node );
6921 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6922 // node position on shape becomes invalid
6923 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6924 ( SMDS_SpacePosition::originSpacePosition() );
6927 // keep inverse elements
6928 if ( !theCopy && !theTargetMesh && needReverse ) {
6929 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6930 while ( invElemIt->more() ) {
6931 const SMDS_MeshElement* iel = invElemIt->next();
6932 inverseElemSet.insert( iel );
6936 } // loop on elems in { &orphanNode, &theElems };
6938 // either create new elements or reverse mirrored ones
6939 if ( !theCopy && !needReverse && !theTargetMesh )
6942 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6944 // Replicate or reverse elements
6946 std::vector<int> iForw;
6947 vector<const SMDS_MeshNode*> nodes;
6948 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6950 const SMDS_MeshElement* elem = *itElem;
6951 if ( !elem ) continue;
6953 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6954 size_t nbNodes = elem->NbNodes();
6955 if ( geomType == SMDSGeom_NONE ) continue; // node
6957 nodes.resize( nbNodes );
6959 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6961 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6965 bool allTransformed = true;
6966 int nbFaces = aPolyedre->NbFaces();
6967 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6969 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6970 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6972 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6973 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6974 if ( nodeMapIt == nodeMap.end() )
6975 allTransformed = false; // not all nodes transformed
6977 nodes.push_back((*nodeMapIt).second);
6979 if ( needReverse && allTransformed )
6980 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6982 if ( !allTransformed )
6983 continue; // not all nodes transformed
6985 else // ----------------------- the rest element types
6987 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6988 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6989 const vector<int>& i = needReverse ? iRev : iForw;
6991 // find transformed nodes
6993 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6994 while ( itN->more() ) {
6995 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6996 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6997 if ( nodeMapIt == nodeMap.end() )
6998 break; // not all nodes transformed
6999 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
7001 if ( iNode != nbNodes )
7002 continue; // not all nodes transformed
7006 // copy in this or a new mesh
7007 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7008 srcElems.Append( elem );
7011 // reverse element as it was reversed by transformation
7013 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7016 } // loop on elements
7018 if ( editor && editor != this )
7019 myLastCreatedElems = editor->myLastCreatedElems;
7021 PGroupIDs newGroupIDs;
7023 if ( ( theMakeGroups && theCopy ) ||
7024 ( theMakeGroups && theTargetMesh ) )
7025 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7030 //=======================================================================
7032 * \brief Create groups of elements made during transformation
7033 * \param nodeGens - nodes making corresponding myLastCreatedNodes
7034 * \param elemGens - elements making corresponding myLastCreatedElems
7035 * \param postfix - to append to names of new groups
7036 * \param targetMesh - mesh to create groups in
7037 * \param topPresent - is there "top" elements that are created by sweeping
7039 //=======================================================================
7041 SMESH_MeshEditor::PGroupIDs
7042 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7043 const SMESH_SequenceOfElemPtr& elemGens,
7044 const std::string& postfix,
7045 SMESH_Mesh* targetMesh,
7046 const bool topPresent)
7048 PGroupIDs newGroupIDs( new list<int> );
7049 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7051 // Sort existing groups by types and collect their names
7053 // containers to store an old group and generated new ones;
7054 // 1st new group is for result elems of different type than a source one;
7055 // 2nd new group is for same type result elems ("top" group at extrusion)
7057 using boost::make_tuple;
7058 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7059 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7060 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7062 set< string > groupNames;
7064 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7065 if ( !groupIt->more() ) return newGroupIDs;
7067 int newGroupID = mesh->GetGroupIds().back()+1;
7068 while ( groupIt->more() )
7070 SMESH_Group * group = groupIt->next();
7071 if ( !group ) continue;
7072 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7073 if ( !groupDS || groupDS->IsEmpty() ) continue;
7074 groupNames.insert ( group->GetName() );
7075 groupDS->SetStoreName( group->GetName() );
7076 const SMDSAbs_ElementType type = groupDS->GetType();
7077 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7078 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7079 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7080 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7083 // Loop on nodes and elements to add them in new groups
7085 vector< const SMDS_MeshElement* > resultElems;
7086 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7088 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7089 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7090 if ( gens.Length() != elems.Length() )
7091 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7093 // loop on created elements
7094 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7096 const SMDS_MeshElement* sourceElem = gens( iElem );
7097 if ( !sourceElem ) {
7098 MESSAGE("generateGroups(): NULL source element");
7101 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7102 if ( groupsOldNew.empty() ) { // no groups of this type at all
7103 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7104 ++iElem; // skip all elements made by sourceElem
7107 // collect all elements made by the iElem-th sourceElem
7108 resultElems.clear();
7109 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7110 if ( resElem != sourceElem )
7111 resultElems.push_back( resElem );
7112 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7113 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7114 if ( resElem != sourceElem )
7115 resultElems.push_back( resElem );
7117 const SMDS_MeshElement* topElem = 0;
7118 if ( isNodes ) // there must be a top element
7120 topElem = resultElems.back();
7121 resultElems.pop_back();
7125 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7126 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7127 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7129 topElem = *resElemIt;
7130 *resElemIt = 0; // erase *resElemIt
7134 // add resultElems to groups originted from ones the sourceElem belongs to
7135 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7136 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7138 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7139 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7141 // fill in a new group
7142 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7143 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7144 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7146 newGroup.Add( *resElemIt );
7148 // fill a "top" group
7151 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7152 newTopGroup.Add( topElem );
7156 } // loop on created elements
7157 }// loop on nodes and elements
7159 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7161 list<int> topGrouIds;
7162 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7164 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7165 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7166 orderedOldNewGroups[i]->get<2>() };
7167 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7169 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7170 if ( newGroupDS->IsEmpty() )
7172 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7177 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7180 const bool isTop = ( topPresent &&
7181 newGroupDS->GetType() == oldGroupDS->GetType() &&
7184 string name = oldGroupDS->GetStoreName();
7185 { // remove trailing whitespaces (issue 22599)
7186 size_t size = name.size();
7187 while ( size > 1 && isspace( name[ size-1 ]))
7189 if ( size != name.size() )
7191 name.resize( size );
7192 oldGroupDS->SetStoreName( name.c_str() );
7195 if ( !targetMesh ) {
7196 string suffix = ( isTop ? "top": postfix.c_str() );
7200 while ( !groupNames.insert( name ).second ) // name exists
7201 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7206 newGroupDS->SetStoreName( name.c_str() );
7208 // make a SMESH_Groups
7209 mesh->AddGroup( newGroupDS );
7211 topGrouIds.push_back( newGroupDS->GetID() );
7213 newGroupIDs->push_back( newGroupDS->GetID() );
7217 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7222 //================================================================================
7224 * * \brief Return list of group of nodes close to each other within theTolerance
7225 * * Search among theNodes or in the whole mesh if theNodes is empty using
7226 * * an Octree algorithm
7227 * \param [in,out] theNodes - the nodes to treat
7228 * \param [in] theTolerance - the tolerance
7229 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7230 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7231 * corner and medium nodes in separate groups
7233 //================================================================================
7235 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7236 const double theTolerance,
7237 TListOfListOfNodes & theGroupsOfNodes,
7238 bool theSeparateCornersAndMedium)
7240 myLastCreatedElems.Clear();
7241 myLastCreatedNodes.Clear();
7243 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7244 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7245 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7246 theSeparateCornersAndMedium = false;
7248 TIDSortedNodeSet& corners = theNodes;
7249 TIDSortedNodeSet medium;
7251 if ( theNodes.empty() ) // get all nodes in the mesh
7253 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7254 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7255 if ( theSeparateCornersAndMedium )
7256 while ( nIt->more() )
7258 const SMDS_MeshNode* n = nIt->next();
7259 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7260 nodeSet->insert( nodeSet->end(), n );
7263 while ( nIt->more() )
7264 theNodes.insert( theNodes.end(), nIt->next() );
7266 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7268 TIDSortedNodeSet::iterator nIt = corners.begin();
7269 while ( nIt != corners.end() )
7270 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7272 medium.insert( medium.end(), *nIt );
7273 corners.erase( nIt++ );
7281 if ( !corners.empty() )
7282 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7283 if ( !medium.empty() )
7284 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7287 //=======================================================================
7288 //function : SimplifyFace
7289 //purpose : split a chain of nodes into several closed chains
7290 //=======================================================================
7292 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7293 vector<const SMDS_MeshNode *>& poly_nodes,
7294 vector<int>& quantities) const
7296 int nbNodes = faceNodes.size();
7297 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7301 size_t prevNbQuant = quantities.size();
7303 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7304 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7305 map< const SMDS_MeshNode*, int >::iterator nInd;
7307 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7308 simpleNodes.push_back( faceNodes[0] );
7309 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7311 if ( faceNodes[ iCur ] != simpleNodes.back() )
7313 int index = simpleNodes.size();
7314 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7315 int prevIndex = nInd->second;
7316 if ( prevIndex < index )
7319 int loopLen = index - prevIndex;
7322 // store the sub-loop
7323 quantities.push_back( loopLen );
7324 for ( int i = prevIndex; i < index; i++ )
7325 poly_nodes.push_back( simpleNodes[ i ]);
7327 simpleNodes.resize( prevIndex+1 );
7331 simpleNodes.push_back( faceNodes[ iCur ]);
7336 if ( simpleNodes.size() > 2 )
7338 quantities.push_back( simpleNodes.size() );
7339 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7342 return quantities.size() - prevNbQuant;
7345 //=======================================================================
7346 //function : MergeNodes
7347 //purpose : In each group, the cdr of nodes are substituted by the first one
7349 //=======================================================================
7351 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7352 const bool theAvoidMakingHoles)
7354 myLastCreatedElems.Clear();
7355 myLastCreatedNodes.Clear();
7357 SMESHDS_Mesh* mesh = GetMeshDS();
7359 TNodeNodeMap nodeNodeMap; // node to replace - new node
7360 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7361 list< int > rmElemIds, rmNodeIds;
7362 vector< ElemFeatures > newElemDefs;
7364 // Fill nodeNodeMap and elems
7366 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7367 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7369 list<const SMDS_MeshNode*>& nodes = *grIt;
7370 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7371 const SMDS_MeshNode* nToKeep = *nIt;
7372 for ( ++nIt; nIt != nodes.end(); nIt++ )
7374 const SMDS_MeshNode* nToRemove = *nIt;
7375 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7376 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7377 while ( invElemIt->more() ) {
7378 const SMDS_MeshElement* elem = invElemIt->next();
7384 // Apply recursive replacements (BUG 0020185)
7385 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7386 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7388 const SMDS_MeshNode* nToKeep = nnIt->second;
7389 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7390 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7391 nToKeep = nnIt_i->second;
7392 nnIt->second = nToKeep;
7395 if ( theAvoidMakingHoles )
7397 // find elements whose topology changes
7399 vector<const SMDS_MeshElement*> pbElems;
7400 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7401 for ( ; eIt != elems.end(); ++eIt )
7403 const SMDS_MeshElement* elem = *eIt;
7404 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7405 while ( itN->more() )
7407 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7408 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7409 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7411 // several nodes of elem stick
7412 pbElems.push_back( elem );
7417 // exclude from merge nodes causing spoiling element
7418 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7420 bool nodesExcluded = false;
7421 for ( size_t i = 0; i < pbElems.size(); ++i )
7423 size_t prevNbMergeNodes = nodeNodeMap.size();
7424 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7425 prevNbMergeNodes < nodeNodeMap.size() )
7426 nodesExcluded = true;
7428 if ( !nodesExcluded )
7433 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7435 const SMDS_MeshNode* nToRemove = nnIt->first;
7436 const SMDS_MeshNode* nToKeep = nnIt->second;
7437 if ( nToRemove != nToKeep )
7439 rmNodeIds.push_back( nToRemove->GetID() );
7440 AddToSameGroups( nToKeep, nToRemove, mesh );
7441 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7442 // w/o creating node in place of merged ones.
7443 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7444 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7445 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7446 sm->SetIsAlwaysComputed( true );
7450 // Change element nodes or remove an element
7452 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7453 for ( ; eIt != elems.end(); eIt++ )
7455 const SMDS_MeshElement* elem = *eIt;
7456 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7458 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7460 rmElemIds.push_back( elem->GetID() );
7462 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7464 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7465 & newElemDefs[i].myNodes[0],
7466 newElemDefs[i].myNodes.size() ))
7470 newElemDefs[i].SetID( elem->GetID() );
7471 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7472 if ( !keepElem ) rmElemIds.pop_back();
7476 newElemDefs[i].SetID( -1 );
7478 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7479 if ( sm && newElem )
7480 sm->AddElement( newElem );
7481 if ( elem != newElem )
7482 ReplaceElemInGroups( elem, newElem, mesh );
7487 // Remove bad elements, then equal nodes (order important)
7488 Remove( rmElemIds, /*isNodes=*/false );
7489 Remove( rmNodeIds, /*isNodes=*/true );
7494 //=======================================================================
7495 //function : applyMerge
7496 //purpose : Compute new connectivity of an element after merging nodes
7497 // \param [in] elems - the element
7498 // \param [out] newElemDefs - definition(s) of result element(s)
7499 // \param [inout] nodeNodeMap - nodes to merge
7500 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7501 // after merging (but not degenerated), removes nodes causing
7502 // the invalidity from \a nodeNodeMap.
7503 // \return bool - true if the element should be removed
7504 //=======================================================================
7506 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7507 vector< ElemFeatures >& newElemDefs,
7508 TNodeNodeMap& nodeNodeMap,
7509 const bool avoidMakingHoles )
7511 bool toRemove = false; // to remove elem
7512 int nbResElems = 1; // nb new elements
7514 newElemDefs.resize(nbResElems);
7515 newElemDefs[0].Init( elem );
7516 newElemDefs[0].myNodes.clear();
7518 set<const SMDS_MeshNode*> nodeSet;
7519 vector< const SMDS_MeshNode*> curNodes;
7520 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7523 const int nbNodes = elem->NbNodes();
7524 SMDSAbs_EntityType entity = elem->GetEntityType();
7526 curNodes.resize( nbNodes );
7527 uniqueNodes.resize( nbNodes );
7528 iRepl.resize( nbNodes );
7529 int iUnique = 0, iCur = 0, nbRepl = 0;
7531 // Get new seq of nodes
7533 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7534 while ( itN->more() )
7536 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7538 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7539 if ( nnIt != nodeNodeMap.end() ) {
7542 curNodes[ iCur ] = n;
7543 bool isUnique = nodeSet.insert( n ).second;
7545 uniqueNodes[ iUnique++ ] = n;
7547 iRepl[ nbRepl++ ] = iCur;
7551 // Analyse element topology after replacement
7553 int nbUniqueNodes = nodeSet.size();
7554 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7559 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7561 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7562 int nbCorners = nbNodes / 2;
7563 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7565 int iNext = ( iCur + 1 ) % nbCorners;
7566 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7568 int iMedium = iCur + nbCorners;
7569 vector< const SMDS_MeshNode* >::iterator i =
7570 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7572 curNodes[ iMedium ]);
7573 if ( i != uniqueNodes.end() )
7576 for ( ; i+1 != uniqueNodes.end(); ++i )
7585 case SMDSEntity_Polygon:
7586 case SMDSEntity_Quad_Polygon: // Polygon
7588 ElemFeatures* elemType = & newElemDefs[0];
7589 const bool isQuad = elemType->myIsQuad;
7591 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7592 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7594 // a polygon can divide into several elements
7595 vector<const SMDS_MeshNode *> polygons_nodes;
7596 vector<int> quantities;
7597 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7598 newElemDefs.resize( nbResElems );
7599 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7601 ElemFeatures* elemType = & newElemDefs[iface];
7602 if ( iface ) elemType->Init( elem );
7604 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7605 int nbNewNodes = quantities[iface];
7606 face_nodes.assign( polygons_nodes.begin() + inode,
7607 polygons_nodes.begin() + inode + nbNewNodes );
7608 inode += nbNewNodes;
7609 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7611 bool isValid = ( nbNewNodes % 2 == 0 );
7612 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7613 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7614 elemType->SetQuad( isValid );
7615 if ( isValid ) // put medium nodes after corners
7616 SMDS_MeshCell::applyInterlaceRev
7617 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7618 nbNewNodes ), face_nodes );
7620 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7622 nbUniqueNodes = newElemDefs[0].myNodes.size();
7626 case SMDSEntity_Polyhedra: // Polyhedral volume
7628 if ( nbUniqueNodes >= 4 )
7630 // each face has to be analyzed in order to check volume validity
7631 if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7633 int nbFaces = aPolyedre->NbFaces();
7635 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7636 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7637 vector<const SMDS_MeshNode *> faceNodes;
7641 for (int iface = 1; iface <= nbFaces; iface++)
7643 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7644 faceNodes.resize( nbFaceNodes );
7645 for (int inode = 1; inode <= nbFaceNodes; inode++)
7647 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7648 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7649 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7650 faceNode = (*nnIt).second;
7651 faceNodes[inode - 1] = faceNode;
7653 SimplifyFace(faceNodes, poly_nodes, quantities);
7656 if ( quantities.size() > 3 )
7658 // TODO: remove coincident faces
7660 nbUniqueNodes = newElemDefs[0].myNodes.size();
7668 // TODO not all the possible cases are solved. Find something more generic?
7669 case SMDSEntity_Edge: //////// EDGE
7670 case SMDSEntity_Triangle: //// TRIANGLE
7671 case SMDSEntity_Quad_Triangle:
7672 case SMDSEntity_Tetra:
7673 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7677 case SMDSEntity_Quad_Edge:
7681 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7683 if ( nbUniqueNodes < 3 )
7685 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7686 toRemove = true; // opposite nodes stick
7691 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7700 if ( nbUniqueNodes == 6 &&
7702 ( nbRepl == 1 || iRepl[1] >= 4 ))
7708 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7717 if ( nbUniqueNodes == 7 &&
7719 ( nbRepl == 1 || iRepl[1] != 8 ))
7725 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7727 if ( nbUniqueNodes == 4 ) {
7728 // ---------------------------------> tetrahedron
7729 if ( curNodes[3] == curNodes[4] &&
7730 curNodes[3] == curNodes[5] ) {
7734 else if ( curNodes[0] == curNodes[1] &&
7735 curNodes[0] == curNodes[2] ) {
7736 // bottom nodes stick: set a top before
7737 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7738 uniqueNodes[ 0 ] = curNodes [ 5 ];
7739 uniqueNodes[ 1 ] = curNodes [ 4 ];
7740 uniqueNodes[ 2 ] = curNodes [ 3 ];
7743 else if (( curNodes[0] == curNodes[3] ) +
7744 ( curNodes[1] == curNodes[4] ) +
7745 ( curNodes[2] == curNodes[5] ) == 2 ) {
7746 // a lateral face turns into a line
7750 else if ( nbUniqueNodes == 5 ) {
7751 // PENTAHEDRON --------------------> pyramid
7752 if ( curNodes[0] == curNodes[3] )
7754 uniqueNodes[ 0 ] = curNodes[ 1 ];
7755 uniqueNodes[ 1 ] = curNodes[ 4 ];
7756 uniqueNodes[ 2 ] = curNodes[ 5 ];
7757 uniqueNodes[ 3 ] = curNodes[ 2 ];
7758 uniqueNodes[ 4 ] = curNodes[ 0 ];
7761 if ( curNodes[1] == curNodes[4] )
7763 uniqueNodes[ 0 ] = curNodes[ 0 ];
7764 uniqueNodes[ 1 ] = curNodes[ 2 ];
7765 uniqueNodes[ 2 ] = curNodes[ 5 ];
7766 uniqueNodes[ 3 ] = curNodes[ 3 ];
7767 uniqueNodes[ 4 ] = curNodes[ 1 ];
7770 if ( curNodes[2] == curNodes[5] )
7772 uniqueNodes[ 0 ] = curNodes[ 0 ];
7773 uniqueNodes[ 1 ] = curNodes[ 3 ];
7774 uniqueNodes[ 2 ] = curNodes[ 4 ];
7775 uniqueNodes[ 3 ] = curNodes[ 1 ];
7776 uniqueNodes[ 4 ] = curNodes[ 2 ];
7782 case SMDSEntity_Hexa:
7784 //////////////////////////////////// HEXAHEDRON
7785 SMDS_VolumeTool hexa (elem);
7786 hexa.SetExternalNormal();
7787 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7788 //////////////////////// HEX ---> tetrahedron
7789 for ( int iFace = 0; iFace < 6; iFace++ ) {
7790 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7791 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7792 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7793 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7794 // one face turns into a point ...
7795 int pickInd = ind[ 0 ];
7796 int iOppFace = hexa.GetOppFaceIndex( iFace );
7797 ind = hexa.GetFaceNodesIndices( iOppFace );
7799 uniqueNodes.clear();
7800 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7801 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7804 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7806 if ( nbStick == 1 ) {
7807 // ... and the opposite one - into a triangle.
7809 uniqueNodes.push_back( curNodes[ pickInd ]);
7816 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7817 //////////////////////// HEX ---> prism
7818 int nbTria = 0, iTria[3];
7819 const int *ind; // indices of face nodes
7820 // look for triangular faces
7821 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7822 ind = hexa.GetFaceNodesIndices( iFace );
7823 TIDSortedNodeSet faceNodes;
7824 for ( iCur = 0; iCur < 4; iCur++ )
7825 faceNodes.insert( curNodes[ind[iCur]] );
7826 if ( faceNodes.size() == 3 )
7827 iTria[ nbTria++ ] = iFace;
7829 // check if triangles are opposite
7830 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7832 // set nodes of the bottom triangle
7833 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7835 for ( iCur = 0; iCur < 4; iCur++ )
7836 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7837 indB.push_back( ind[iCur] );
7838 if ( !hexa.IsForward() )
7839 std::swap( indB[0], indB[2] );
7840 for ( iCur = 0; iCur < 3; iCur++ )
7841 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7842 // set nodes of the top triangle
7843 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7844 for ( iCur = 0; iCur < 3; ++iCur )
7845 for ( int j = 0; j < 4; ++j )
7846 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7848 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7855 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7856 //////////////////// HEXAHEDRON ---> pyramid
7857 for ( int iFace = 0; iFace < 6; iFace++ ) {
7858 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7859 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7860 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7861 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7862 // one face turns into a point ...
7863 int iOppFace = hexa.GetOppFaceIndex( iFace );
7864 ind = hexa.GetFaceNodesIndices( iOppFace );
7865 uniqueNodes.clear();
7866 for ( iCur = 0; iCur < 4; iCur++ ) {
7867 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7870 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7872 if ( uniqueNodes.size() == 4 ) {
7873 // ... and the opposite one is a quadrangle
7875 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7876 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7884 if ( toRemove && nbUniqueNodes > 4 ) {
7885 ////////////////// HEXAHEDRON ---> polyhedron
7886 hexa.SetExternalNormal();
7887 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7888 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7889 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7890 quantities.reserve( 6 ); quantities.clear();
7891 for ( int iFace = 0; iFace < 6; iFace++ )
7893 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7894 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7895 curNodes[ind[1]] == curNodes[ind[3]] )
7898 break; // opposite nodes stick
7901 for ( iCur = 0; iCur < 4; iCur++ )
7903 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7904 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7906 if ( nodeSet.size() < 3 )
7907 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7909 quantities.push_back( nodeSet.size() );
7911 if ( quantities.size() >= 4 )
7914 nbUniqueNodes = poly_nodes.size();
7915 newElemDefs[0].SetPoly(true);
7919 } // case HEXAHEDRON
7924 } // switch ( entity )
7926 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7928 // erase from nodeNodeMap nodes whose merge spoils elem
7929 vector< const SMDS_MeshNode* > noMergeNodes;
7930 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7931 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7932 nodeNodeMap.erase( noMergeNodes[i] );
7935 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7937 uniqueNodes.resize( nbUniqueNodes );
7939 if ( !toRemove && nbResElems == 0 )
7942 newElemDefs.resize( nbResElems );
7948 // ========================================================
7949 // class : SortableElement
7950 // purpose : allow sorting elements basing on their nodes
7951 // ========================================================
7952 class SortableElement : public set <const SMDS_MeshElement*>
7956 SortableElement( const SMDS_MeshElement* theElem )
7959 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7960 while ( nodeIt->more() )
7961 this->insert( nodeIt->next() );
7964 const SMDS_MeshElement* Get() const
7968 mutable const SMDS_MeshElement* myElem;
7971 //=======================================================================
7972 //function : FindEqualElements
7973 //purpose : Return list of group of elements built on the same nodes.
7974 // Search among theElements or in the whole mesh if theElements is empty
7975 //=======================================================================
7977 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7978 TListOfListOfElementsID & theGroupsOfElementsID)
7980 myLastCreatedElems.Clear();
7981 myLastCreatedNodes.Clear();
7983 typedef map< SortableElement, int > TMapOfNodeSet;
7984 typedef list<int> TGroupOfElems;
7986 if ( theElements.empty() )
7987 { // get all elements in the mesh
7988 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7989 while ( eIt->more() )
7990 theElements.insert( theElements.end(), eIt->next() );
7993 vector< TGroupOfElems > arrayOfGroups;
7994 TGroupOfElems groupOfElems;
7995 TMapOfNodeSet mapOfNodeSet;
7997 TIDSortedElemSet::iterator elemIt = theElements.begin();
7998 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
8000 const SMDS_MeshElement* curElem = *elemIt;
8001 SortableElement SE(curElem);
8003 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8004 if ( !pp.second ) { // one more coincident elem
8005 TMapOfNodeSet::iterator& itSE = pp.first;
8006 int ind = (*itSE).second;
8007 arrayOfGroups[ind].push_back( curElem->GetID() );
8010 arrayOfGroups.push_back( groupOfElems );
8011 arrayOfGroups.back().push_back( curElem->GetID() );
8016 groupOfElems.clear();
8017 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8018 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8020 if ( groupIt->size() > 1 ) {
8021 //groupOfElems.sort(); -- theElements is sorted already
8022 theGroupsOfElementsID.push_back( groupOfElems );
8023 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8028 //=======================================================================
8029 //function : MergeElements
8030 //purpose : In each given group, substitute all elements by the first one.
8031 //=======================================================================
8033 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8035 myLastCreatedElems.Clear();
8036 myLastCreatedNodes.Clear();
8038 typedef list<int> TListOfIDs;
8039 TListOfIDs rmElemIds; // IDs of elems to remove
8041 SMESHDS_Mesh* aMesh = GetMeshDS();
8043 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8044 while ( groupsIt != theGroupsOfElementsID.end() ) {
8045 TListOfIDs& aGroupOfElemID = *groupsIt;
8046 aGroupOfElemID.sort();
8047 int elemIDToKeep = aGroupOfElemID.front();
8048 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8049 aGroupOfElemID.pop_front();
8050 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8051 while ( idIt != aGroupOfElemID.end() ) {
8052 int elemIDToRemove = *idIt;
8053 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8054 // add the kept element in groups of removed one (PAL15188)
8055 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8056 rmElemIds.push_back( elemIDToRemove );
8062 Remove( rmElemIds, false );
8065 //=======================================================================
8066 //function : MergeEqualElements
8067 //purpose : Remove all but one of elements built on the same nodes.
8068 //=======================================================================
8070 void SMESH_MeshEditor::MergeEqualElements()
8072 TIDSortedElemSet aMeshElements; /* empty input ==
8073 to merge equal elements in the whole mesh */
8074 TListOfListOfElementsID aGroupsOfElementsID;
8075 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8076 MergeElements(aGroupsOfElementsID);
8079 //=======================================================================
8080 //function : findAdjacentFace
8082 //=======================================================================
8084 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8085 const SMDS_MeshNode* n2,
8086 const SMDS_MeshElement* elem)
8088 TIDSortedElemSet elemSet, avoidSet;
8090 avoidSet.insert ( elem );
8091 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8094 //=======================================================================
8095 //function : findSegment
8096 //purpose : Return a mesh segment by two nodes one of which can be medium
8097 //=======================================================================
8099 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8100 const SMDS_MeshNode* n2)
8102 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8103 while ( it->more() )
8105 const SMDS_MeshElement* seg = it->next();
8106 if ( seg->GetNodeIndex( n2 ) >= 0 )
8112 //=======================================================================
8113 //function : FindFreeBorder
8115 //=======================================================================
8117 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8119 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8120 const SMDS_MeshNode* theSecondNode,
8121 const SMDS_MeshNode* theLastNode,
8122 list< const SMDS_MeshNode* > & theNodes,
8123 list< const SMDS_MeshElement* >& theFaces)
8125 if ( !theFirstNode || !theSecondNode )
8127 // find border face between theFirstNode and theSecondNode
8128 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8132 theFaces.push_back( curElem );
8133 theNodes.push_back( theFirstNode );
8134 theNodes.push_back( theSecondNode );
8136 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8137 TIDSortedElemSet foundElems;
8138 bool needTheLast = ( theLastNode != 0 );
8140 while ( nStart != theLastNode ) {
8141 if ( nStart == theFirstNode )
8142 return !needTheLast;
8144 // find all free border faces sharing form nStart
8146 list< const SMDS_MeshElement* > curElemList;
8147 list< const SMDS_MeshNode* > nStartList;
8148 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8149 while ( invElemIt->more() ) {
8150 const SMDS_MeshElement* e = invElemIt->next();
8151 if ( e == curElem || foundElems.insert( e ).second ) {
8153 int iNode = 0, nbNodes = e->NbNodes();
8154 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8156 if ( e->IsQuadratic() ) {
8157 const SMDS_VtkFace* F =
8158 dynamic_cast<const SMDS_VtkFace*>(e);
8159 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8160 // use special nodes iterator
8161 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8162 while( anIter->more() ) {
8163 nodes[ iNode++ ] = cast2Node(anIter->next());
8167 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8168 while ( nIt->more() )
8169 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8171 nodes[ iNode ] = nodes[ 0 ];
8173 for ( iNode = 0; iNode < nbNodes; iNode++ )
8174 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8175 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8176 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8178 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8179 curElemList.push_back( e );
8183 // analyse the found
8185 int nbNewBorders = curElemList.size();
8186 if ( nbNewBorders == 0 ) {
8187 // no free border furthermore
8188 return !needTheLast;
8190 else if ( nbNewBorders == 1 ) {
8191 // one more element found
8193 nStart = nStartList.front();
8194 curElem = curElemList.front();
8195 theFaces.push_back( curElem );
8196 theNodes.push_back( nStart );
8199 // several continuations found
8200 list< const SMDS_MeshElement* >::iterator curElemIt;
8201 list< const SMDS_MeshNode* >::iterator nStartIt;
8202 // check if one of them reached the last node
8203 if ( needTheLast ) {
8204 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8205 curElemIt!= curElemList.end();
8206 curElemIt++, nStartIt++ )
8207 if ( *nStartIt == theLastNode ) {
8208 theFaces.push_back( *curElemIt );
8209 theNodes.push_back( *nStartIt );
8213 // find the best free border by the continuations
8214 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8215 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8216 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8217 curElemIt!= curElemList.end();
8218 curElemIt++, nStartIt++ )
8220 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8221 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8222 // find one more free border
8223 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8227 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8228 // choice: clear a worse one
8229 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8230 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8231 contNodes[ iWorse ].clear();
8232 contFaces[ iWorse ].clear();
8235 if ( contNodes[0].empty() && contNodes[1].empty() )
8238 // append the best free border
8239 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8240 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8241 theNodes.pop_back(); // remove nIgnore
8242 theNodes.pop_back(); // remove nStart
8243 theFaces.pop_back(); // remove curElem
8244 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8245 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8246 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8247 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8250 } // several continuations found
8251 } // while ( nStart != theLastNode )
8256 //=======================================================================
8257 //function : CheckFreeBorderNodes
8258 //purpose : Return true if the tree nodes are on a free border
8259 //=======================================================================
8261 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8262 const SMDS_MeshNode* theNode2,
8263 const SMDS_MeshNode* theNode3)
8265 list< const SMDS_MeshNode* > nodes;
8266 list< const SMDS_MeshElement* > faces;
8267 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8270 //=======================================================================
8271 //function : SewFreeBorder
8273 //warning : for border-to-side sewing theSideSecondNode is considered as
8274 // the last side node and theSideThirdNode is not used
8275 //=======================================================================
8277 SMESH_MeshEditor::Sew_Error
8278 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8279 const SMDS_MeshNode* theBordSecondNode,
8280 const SMDS_MeshNode* theBordLastNode,
8281 const SMDS_MeshNode* theSideFirstNode,
8282 const SMDS_MeshNode* theSideSecondNode,
8283 const SMDS_MeshNode* theSideThirdNode,
8284 const bool theSideIsFreeBorder,
8285 const bool toCreatePolygons,
8286 const bool toCreatePolyedrs)
8288 myLastCreatedElems.Clear();
8289 myLastCreatedNodes.Clear();
8291 Sew_Error aResult = SEW_OK;
8293 // ====================================
8294 // find side nodes and elements
8295 // ====================================
8297 list< const SMDS_MeshNode* > nSide[ 2 ];
8298 list< const SMDS_MeshElement* > eSide[ 2 ];
8299 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8300 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8304 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8305 nSide[0], eSide[0])) {
8306 MESSAGE(" Free Border 1 not found " );
8307 aResult = SEW_BORDER1_NOT_FOUND;
8309 if (theSideIsFreeBorder) {
8312 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8313 nSide[1], eSide[1])) {
8314 MESSAGE(" Free Border 2 not found " );
8315 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8318 if ( aResult != SEW_OK )
8321 if (!theSideIsFreeBorder) {
8325 // -------------------------------------------------------------------------
8327 // 1. If nodes to merge are not coincident, move nodes of the free border
8328 // from the coord sys defined by the direction from the first to last
8329 // nodes of the border to the correspondent sys of the side 2
8330 // 2. On the side 2, find the links most co-directed with the correspondent
8331 // links of the free border
8332 // -------------------------------------------------------------------------
8334 // 1. Since sewing may break if there are volumes to split on the side 2,
8335 // we won't move nodes but just compute new coordinates for them
8336 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8337 TNodeXYZMap nBordXYZ;
8338 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8339 list< const SMDS_MeshNode* >::iterator nBordIt;
8341 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8342 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8343 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8344 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8345 double tol2 = 1.e-8;
8346 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8347 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8348 // Need node movement.
8350 // find X and Z axes to create trsf
8351 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8353 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8355 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8358 gp_Ax3 toBordAx( Pb1, Zb, X );
8359 gp_Ax3 fromSideAx( Ps1, Zs, X );
8360 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8362 gp_Trsf toBordSys, fromSide2Sys;
8363 toBordSys.SetTransformation( toBordAx );
8364 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8365 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8368 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8369 const SMDS_MeshNode* n = *nBordIt;
8370 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8371 toBordSys.Transforms( xyz );
8372 fromSide2Sys.Transforms( xyz );
8373 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8377 // just insert nodes XYZ in the nBordXYZ map
8378 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8379 const SMDS_MeshNode* n = *nBordIt;
8380 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8384 // 2. On the side 2, find the links most co-directed with the correspondent
8385 // links of the free border
8387 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8388 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8389 sideNodes.push_back( theSideFirstNode );
8391 bool hasVolumes = false;
8392 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8393 set<long> foundSideLinkIDs, checkedLinkIDs;
8394 SMDS_VolumeTool volume;
8395 //const SMDS_MeshNode* faceNodes[ 4 ];
8397 const SMDS_MeshNode* sideNode;
8398 const SMDS_MeshElement* sideElem = 0;
8399 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8400 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8401 nBordIt = bordNodes.begin();
8403 // border node position and border link direction to compare with
8404 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8405 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8406 // choose next side node by link direction or by closeness to
8407 // the current border node:
8408 bool searchByDir = ( *nBordIt != theBordLastNode );
8410 // find the next node on the Side 2
8412 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8414 checkedLinkIDs.clear();
8415 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8417 // loop on inverse elements of current node (prevSideNode) on the Side 2
8418 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8419 while ( invElemIt->more() )
8421 const SMDS_MeshElement* elem = invElemIt->next();
8422 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8423 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8424 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8425 bool isVolume = volume.Set( elem );
8426 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8427 if ( isVolume ) // --volume
8429 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8430 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8431 if(elem->IsQuadratic()) {
8432 const SMDS_VtkFace* F =
8433 dynamic_cast<const SMDS_VtkFace*>(elem);
8434 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8435 // use special nodes iterator
8436 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8437 while( anIter->more() ) {
8438 nodes[ iNode ] = cast2Node(anIter->next());
8439 if ( nodes[ iNode++ ] == prevSideNode )
8440 iPrevNode = iNode - 1;
8444 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8445 while ( nIt->more() ) {
8446 nodes[ iNode ] = cast2Node( nIt->next() );
8447 if ( nodes[ iNode++ ] == prevSideNode )
8448 iPrevNode = iNode - 1;
8451 // there are 2 links to check
8456 // loop on links, to be precise, on the second node of links
8457 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8458 const SMDS_MeshNode* n = nodes[ iNode ];
8460 if ( !volume.IsLinked( n, prevSideNode ))
8464 if ( iNode ) // a node before prevSideNode
8465 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8466 else // a node after prevSideNode
8467 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8469 // check if this link was already used
8470 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8471 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8472 if (!isJustChecked &&
8473 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8475 // test a link geometrically
8476 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8477 bool linkIsBetter = false;
8478 double dot = 0.0, dist = 0.0;
8479 if ( searchByDir ) { // choose most co-directed link
8480 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8481 linkIsBetter = ( dot > maxDot );
8483 else { // choose link with the node closest to bordPos
8484 dist = ( nextXYZ - bordPos ).SquareModulus();
8485 linkIsBetter = ( dist < minDist );
8487 if ( linkIsBetter ) {
8496 } // loop on inverse elements of prevSideNode
8499 MESSAGE(" Can't find path by links of the Side 2 ");
8500 return SEW_BAD_SIDE_NODES;
8502 sideNodes.push_back( sideNode );
8503 sideElems.push_back( sideElem );
8504 foundSideLinkIDs.insert ( linkID );
8505 prevSideNode = sideNode;
8507 if ( *nBordIt == theBordLastNode )
8508 searchByDir = false;
8510 // find the next border link to compare with
8511 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8512 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8513 // move to next border node if sideNode is before forward border node (bordPos)
8514 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8515 prevBordNode = *nBordIt;
8517 bordPos = nBordXYZ[ *nBordIt ];
8518 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8519 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8523 while ( sideNode != theSideSecondNode );
8525 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8526 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8527 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8529 } // end nodes search on the side 2
8531 // ============================
8532 // sew the border to the side 2
8533 // ============================
8535 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8536 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8538 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8539 if ( toMergeConformal && toCreatePolygons )
8541 // do not merge quadrangles if polygons are OK (IPAL0052824)
8542 eIt[0] = eSide[0].begin();
8543 eIt[1] = eSide[1].begin();
8544 bool allQuads[2] = { true, true };
8545 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8546 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8547 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8549 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8552 TListOfListOfNodes nodeGroupsToMerge;
8553 if (( toMergeConformal ) ||
8554 ( theSideIsFreeBorder && !theSideThirdNode )) {
8556 // all nodes are to be merged
8558 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8559 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8560 nIt[0]++, nIt[1]++ )
8562 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8563 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8564 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8569 // insert new nodes into the border and the side to get equal nb of segments
8571 // get normalized parameters of nodes on the borders
8572 vector< double > param[ 2 ];
8573 param[0].resize( maxNbNodes );
8574 param[1].resize( maxNbNodes );
8576 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8577 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8578 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8579 const SMDS_MeshNode* nPrev = *nIt;
8580 double bordLength = 0;
8581 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8582 const SMDS_MeshNode* nCur = *nIt;
8583 gp_XYZ segment (nCur->X() - nPrev->X(),
8584 nCur->Y() - nPrev->Y(),
8585 nCur->Z() - nPrev->Z());
8586 double segmentLen = segment.Modulus();
8587 bordLength += segmentLen;
8588 param[ iBord ][ iNode ] = bordLength;
8591 // normalize within [0,1]
8592 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8593 param[ iBord ][ iNode ] /= bordLength;
8597 // loop on border segments
8598 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8599 int i[ 2 ] = { 0, 0 };
8600 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8601 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8603 TElemOfNodeListMap insertMap;
8604 TElemOfNodeListMap::iterator insertMapIt;
8606 // key: elem to insert nodes into
8607 // value: 2 nodes to insert between + nodes to be inserted
8609 bool next[ 2 ] = { false, false };
8611 // find min adjacent segment length after sewing
8612 double nextParam = 10., prevParam = 0;
8613 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8614 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8615 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8616 if ( i[ iBord ] > 0 )
8617 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8619 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8620 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8621 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8623 // choose to insert or to merge nodes
8624 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8625 if ( Abs( du ) <= minSegLen * 0.2 ) {
8628 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8629 const SMDS_MeshNode* n0 = *nIt[0];
8630 const SMDS_MeshNode* n1 = *nIt[1];
8631 nodeGroupsToMerge.back().push_back( n1 );
8632 nodeGroupsToMerge.back().push_back( n0 );
8633 // position of node of the border changes due to merge
8634 param[ 0 ][ i[0] ] += du;
8635 // move n1 for the sake of elem shape evaluation during insertion.
8636 // n1 will be removed by MergeNodes() anyway
8637 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8638 next[0] = next[1] = true;
8643 int intoBord = ( du < 0 ) ? 0 : 1;
8644 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8645 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8646 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8647 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8648 if ( intoBord == 1 ) {
8649 // move node of the border to be on a link of elem of the side
8650 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8651 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8652 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8653 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8654 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8656 insertMapIt = insertMap.find( elem );
8657 bool notFound = ( insertMapIt == insertMap.end() );
8658 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8660 // insert into another link of the same element:
8661 // 1. perform insertion into the other link of the elem
8662 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8663 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8664 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8665 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8666 // 2. perform insertion into the link of adjacent faces
8667 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8668 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8670 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8671 InsertNodesIntoLink( seg, n12, n22, nodeList );
8673 if (toCreatePolyedrs) {
8674 // perform insertion into the links of adjacent volumes
8675 UpdateVolumes(n12, n22, nodeList);
8677 // 3. find an element appeared on n1 and n2 after the insertion
8678 insertMap.erase( elem );
8679 elem = findAdjacentFace( n1, n2, 0 );
8681 if ( notFound || otherLink ) {
8682 // add element and nodes of the side into the insertMap
8683 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8684 (*insertMapIt).second.push_back( n1 );
8685 (*insertMapIt).second.push_back( n2 );
8687 // add node to be inserted into elem
8688 (*insertMapIt).second.push_back( nIns );
8689 next[ 1 - intoBord ] = true;
8692 // go to the next segment
8693 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8694 if ( next[ iBord ] ) {
8695 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8697 nPrev[ iBord ] = *nIt[ iBord ];
8698 nIt[ iBord ]++; i[ iBord ]++;
8702 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8704 // perform insertion of nodes into elements
8706 for (insertMapIt = insertMap.begin();
8707 insertMapIt != insertMap.end();
8710 const SMDS_MeshElement* elem = (*insertMapIt).first;
8711 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8712 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8713 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8715 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8717 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8718 InsertNodesIntoLink( seg, n1, n2, nodeList );
8721 if ( !theSideIsFreeBorder ) {
8722 // look for and insert nodes into the faces adjacent to elem
8723 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8724 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8727 if (toCreatePolyedrs) {
8728 // perform insertion into the links of adjacent volumes
8729 UpdateVolumes(n1, n2, nodeList);
8732 } // end: insert new nodes
8734 MergeNodes ( nodeGroupsToMerge );
8737 // Remove coincident segments
8740 TIDSortedElemSet segments;
8741 SMESH_SequenceOfElemPtr newFaces;
8742 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8744 if ( !myLastCreatedElems(i) ) continue;
8745 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8746 segments.insert( segments.end(), myLastCreatedElems(i) );
8748 newFaces.Append( myLastCreatedElems(i) );
8750 // get segments adjacent to merged nodes
8751 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8752 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8754 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8755 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8756 while ( segIt->more() )
8757 segments.insert( segIt->next() );
8761 TListOfListOfElementsID equalGroups;
8762 if ( !segments.empty() )
8763 FindEqualElements( segments, equalGroups );
8764 if ( !equalGroups.empty() )
8766 // remove from segments those that will be removed
8767 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8768 for ( ; itGroups != equalGroups.end(); ++itGroups )
8770 list< int >& group = *itGroups;
8771 list< int >::iterator id = group.begin();
8772 for ( ++id; id != group.end(); ++id )
8773 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8774 segments.erase( seg );
8776 // remove equal segments
8777 MergeElements( equalGroups );
8779 // restore myLastCreatedElems
8780 myLastCreatedElems = newFaces;
8781 TIDSortedElemSet::iterator seg = segments.begin();
8782 for ( ; seg != segments.end(); ++seg )
8783 myLastCreatedElems.Append( *seg );
8789 //=======================================================================
8790 //function : InsertNodesIntoLink
8791 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8792 // and theBetweenNode2 and split theElement
8793 //=======================================================================
8795 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8796 const SMDS_MeshNode* theBetweenNode1,
8797 const SMDS_MeshNode* theBetweenNode2,
8798 list<const SMDS_MeshNode*>& theNodesToInsert,
8799 const bool toCreatePoly)
8801 if ( !theElement ) return;
8803 SMESHDS_Mesh *aMesh = GetMeshDS();
8804 vector<const SMDS_MeshElement*> newElems;
8806 if ( theElement->GetType() == SMDSAbs_Edge )
8808 theNodesToInsert.push_front( theBetweenNode1 );
8809 theNodesToInsert.push_back ( theBetweenNode2 );
8810 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8811 const SMDS_MeshNode* n1 = *n;
8812 for ( ++n; n != theNodesToInsert.end(); ++n )
8814 const SMDS_MeshNode* n2 = *n;
8815 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8816 AddToSameGroups( seg, theElement, aMesh );
8818 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8821 theNodesToInsert.pop_front();
8822 theNodesToInsert.pop_back();
8824 if ( theElement->IsQuadratic() ) // add a not split part
8826 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8827 theElement->end_nodes() );
8828 int iOther = 0, nbN = nodes.size();
8829 for ( ; iOther < nbN; ++iOther )
8830 if ( nodes[iOther] != theBetweenNode1 &&
8831 nodes[iOther] != theBetweenNode2 )
8835 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8836 AddToSameGroups( seg, theElement, aMesh );
8838 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8840 else if ( iOther == 2 )
8842 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8843 AddToSameGroups( seg, theElement, aMesh );
8845 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8848 // treat new elements
8849 for ( size_t i = 0; i < newElems.size(); ++i )
8852 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8853 myLastCreatedElems.Append( newElems[i] );
8855 ReplaceElemInGroups( theElement, newElems, aMesh );
8856 aMesh->RemoveElement( theElement );
8859 } // if ( theElement->GetType() == SMDSAbs_Edge )
8861 const SMDS_MeshElement* theFace = theElement;
8862 if ( theFace->GetType() != SMDSAbs_Face ) return;
8864 // find indices of 2 link nodes and of the rest nodes
8865 int iNode = 0, il1, il2, i3, i4;
8866 il1 = il2 = i3 = i4 = -1;
8867 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8869 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8870 while ( nodeIt->more() ) {
8871 const SMDS_MeshNode* n = nodeIt->next();
8872 if ( n == theBetweenNode1 )
8874 else if ( n == theBetweenNode2 )
8880 nodes[ iNode++ ] = n;
8882 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8885 // arrange link nodes to go one after another regarding the face orientation
8886 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8887 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8892 aNodesToInsert.reverse();
8894 // check that not link nodes of a quadrangles are in good order
8895 int nbFaceNodes = theFace->NbNodes();
8896 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8902 if (toCreatePoly || theFace->IsPoly()) {
8905 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8907 // add nodes of face up to first node of link
8910 if ( theFace->IsQuadratic() ) {
8911 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8912 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8913 // use special nodes iterator
8914 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8915 while( anIter->more() && !isFLN ) {
8916 const SMDS_MeshNode* n = cast2Node(anIter->next());
8917 poly_nodes[iNode++] = n;
8918 if (n == nodes[il1]) {
8922 // add nodes to insert
8923 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8924 for (; nIt != aNodesToInsert.end(); nIt++) {
8925 poly_nodes[iNode++] = *nIt;
8927 // add nodes of face starting from last node of link
8928 while ( anIter->more() ) {
8929 poly_nodes[iNode++] = cast2Node(anIter->next());
8933 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8934 while ( nodeIt->more() && !isFLN ) {
8935 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8936 poly_nodes[iNode++] = n;
8937 if (n == nodes[il1]) {
8941 // add nodes to insert
8942 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8943 for (; nIt != aNodesToInsert.end(); nIt++) {
8944 poly_nodes[iNode++] = *nIt;
8946 // add nodes of face starting from last node of link
8947 while ( nodeIt->more() ) {
8948 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8949 poly_nodes[iNode++] = n;
8954 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8957 else if ( !theFace->IsQuadratic() )
8959 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8960 int nbLinkNodes = 2 + aNodesToInsert.size();
8961 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8962 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8963 linkNodes[ 0 ] = nodes[ il1 ];
8964 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8965 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8966 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8967 linkNodes[ iNode++ ] = *nIt;
8969 // decide how to split a quadrangle: compare possible variants
8970 // and choose which of splits to be a quadrangle
8971 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8972 if ( nbFaceNodes == 3 ) {
8973 iBestQuad = nbSplits;
8976 else if ( nbFaceNodes == 4 ) {
8977 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8978 double aBestRate = DBL_MAX;
8979 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8981 double aBadRate = 0;
8982 // evaluate elements quality
8983 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8984 if ( iSplit == iQuad ) {
8985 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8989 aBadRate += getBadRate( &quad, aCrit );
8992 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8994 nodes[ iSplit < iQuad ? i4 : i3 ]);
8995 aBadRate += getBadRate( &tria, aCrit );
8999 if ( aBadRate < aBestRate ) {
9001 aBestRate = aBadRate;
9006 // create new elements
9008 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9010 if ( iSplit == iBestQuad )
9011 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9016 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9018 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9021 const SMDS_MeshNode* newNodes[ 4 ];
9022 newNodes[ 0 ] = linkNodes[ i1 ];
9023 newNodes[ 1 ] = linkNodes[ i2 ];
9024 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9025 newNodes[ 3 ] = nodes[ i4 ];
9026 if (iSplit == iBestQuad)
9027 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9029 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9031 } // end if(!theFace->IsQuadratic())
9033 else { // theFace is quadratic
9034 // we have to split theFace on simple triangles and one simple quadrangle
9036 int nbshift = tmp*2;
9037 // shift nodes in nodes[] by nbshift
9039 for(i=0; i<nbshift; i++) {
9040 const SMDS_MeshNode* n = nodes[0];
9041 for(j=0; j<nbFaceNodes-1; j++) {
9042 nodes[j] = nodes[j+1];
9044 nodes[nbFaceNodes-1] = n;
9046 il1 = il1 - nbshift;
9047 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9048 // n0 n1 n2 n0 n1 n2
9049 // +-----+-----+ +-----+-----+
9058 // create new elements
9060 if ( nbFaceNodes == 6 ) { // quadratic triangle
9061 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9062 if ( theFace->IsMediumNode(nodes[il1]) ) {
9063 // create quadrangle
9064 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9070 // create quadrangle
9071 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9077 else { // nbFaceNodes==8 - quadratic quadrangle
9078 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9079 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9080 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9081 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9082 // create quadrangle
9083 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9089 // create quadrangle
9090 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9096 // create needed triangles using n1,n2,n3 and inserted nodes
9097 int nbn = 2 + aNodesToInsert.size();
9098 vector<const SMDS_MeshNode*> aNodes(nbn);
9099 aNodes[0 ] = nodes[n1];
9100 aNodes[nbn-1] = nodes[n2];
9101 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9102 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9103 aNodes[iNode++] = *nIt;
9105 for ( i = 1; i < nbn; i++ )
9106 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9109 // remove the old face
9110 for ( size_t i = 0; i < newElems.size(); ++i )
9113 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9114 myLastCreatedElems.Append( newElems[i] );
9116 ReplaceElemInGroups( theFace, newElems, aMesh );
9117 aMesh->RemoveElement(theFace);
9119 } // InsertNodesIntoLink()
9121 //=======================================================================
9122 //function : UpdateVolumes
9124 //=======================================================================
9126 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9127 const SMDS_MeshNode* theBetweenNode2,
9128 list<const SMDS_MeshNode*>& theNodesToInsert)
9130 myLastCreatedElems.Clear();
9131 myLastCreatedNodes.Clear();
9133 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9134 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9135 const SMDS_MeshElement* elem = invElemIt->next();
9137 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9138 SMDS_VolumeTool aVolume (elem);
9139 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9142 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9143 int iface, nbFaces = aVolume.NbFaces();
9144 vector<const SMDS_MeshNode *> poly_nodes;
9145 vector<int> quantities (nbFaces);
9147 for (iface = 0; iface < nbFaces; iface++) {
9148 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9149 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9150 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9152 for (int inode = 0; inode < nbFaceNodes; inode++) {
9153 poly_nodes.push_back(faceNodes[inode]);
9155 if (nbInserted == 0) {
9156 if (faceNodes[inode] == theBetweenNode1) {
9157 if (faceNodes[inode + 1] == theBetweenNode2) {
9158 nbInserted = theNodesToInsert.size();
9160 // add nodes to insert
9161 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9162 for (; nIt != theNodesToInsert.end(); nIt++) {
9163 poly_nodes.push_back(*nIt);
9167 else if (faceNodes[inode] == theBetweenNode2) {
9168 if (faceNodes[inode + 1] == theBetweenNode1) {
9169 nbInserted = theNodesToInsert.size();
9171 // add nodes to insert in reversed order
9172 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9174 for (; nIt != theNodesToInsert.begin(); nIt--) {
9175 poly_nodes.push_back(*nIt);
9177 poly_nodes.push_back(*nIt);
9184 quantities[iface] = nbFaceNodes + nbInserted;
9187 // Replace the volume
9188 SMESHDS_Mesh *aMesh = GetMeshDS();
9190 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9192 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9193 myLastCreatedElems.Append( newElem );
9194 ReplaceElemInGroups( elem, newElem, aMesh );
9196 aMesh->RemoveElement( elem );
9202 //================================================================================
9204 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9206 //================================================================================
9208 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9209 vector<const SMDS_MeshNode *> & nodes,
9210 vector<int> & nbNodeInFaces )
9213 nbNodeInFaces.clear();
9214 SMDS_VolumeTool vTool ( elem );
9215 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9217 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9218 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9219 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9224 //=======================================================================
9226 * \brief Convert elements contained in a sub-mesh to quadratic
9227 * \return int - nb of checked elements
9229 //=======================================================================
9231 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9232 SMESH_MesherHelper& theHelper,
9233 const bool theForce3d)
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 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9262 case SMDSEntity_BiQuad_Triangle:
9263 case SMDSEntity_BiQuad_Quadrangle:
9264 case SMDSEntity_TriQuad_Hexa:
9265 alreadyOK = theHelper.GetIsBiQuadratic();
9266 hasCentralNodes = true;
9271 // take into account already present modium nodes
9273 case SMDSAbs_Volume:
9274 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9276 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9278 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9284 // get elem data needed to re-create it
9286 const int id = elem->GetID();
9287 const int nbNodes = elem->NbCornerNodes();
9288 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9289 if ( aGeomType == SMDSEntity_Polyhedra )
9290 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9291 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9292 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9294 // remove a linear element
9295 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9297 // remove central nodes of biquadratic elements (biquad->quad conversion)
9298 if ( hasCentralNodes )
9299 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9300 if ( nodes[i]->NbInverseElements() == 0 )
9301 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9303 const SMDS_MeshElement* NewElem = 0;
9309 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9317 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9320 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9323 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9327 case SMDSAbs_Volume :
9331 case SMDSEntity_Tetra:
9332 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9334 case SMDSEntity_Pyramid:
9335 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9337 case SMDSEntity_Penta:
9338 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9340 case SMDSEntity_Hexa:
9341 case SMDSEntity_Quad_Hexa:
9342 case SMDSEntity_TriQuad_Hexa:
9343 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9344 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9346 case SMDSEntity_Hexagonal_Prism:
9348 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9355 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9356 if( NewElem && NewElem->getshapeId() < 1 )
9357 theSm->AddElement( NewElem );
9361 //=======================================================================
9362 //function : ConvertToQuadratic
9364 //=======================================================================
9366 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9368 SMESHDS_Mesh* meshDS = GetMeshDS();
9370 SMESH_MesherHelper aHelper(*myMesh);
9372 aHelper.SetIsQuadratic( true );
9373 aHelper.SetIsBiQuadratic( theToBiQuad );
9374 aHelper.SetElementsOnShape(true);
9375 aHelper.ToFixNodeParameters( true );
9377 // convert elements assigned to sub-meshes
9378 int nbCheckedElems = 0;
9379 if ( myMesh->HasShapeToMesh() )
9381 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9383 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9384 while ( smIt->more() ) {
9385 SMESH_subMesh* sm = smIt->next();
9386 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9387 aHelper.SetSubShape( sm->GetSubShape() );
9388 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9394 // convert elements NOT assigned to sub-meshes
9395 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9396 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9398 aHelper.SetElementsOnShape(false);
9399 SMESHDS_SubMesh *smDS = 0;
9402 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9403 while( aEdgeItr->more() )
9405 const SMDS_MeshEdge* edge = aEdgeItr->next();
9406 if ( !edge->IsQuadratic() )
9408 int id = edge->GetID();
9409 const SMDS_MeshNode* n1 = edge->GetNode(0);
9410 const SMDS_MeshNode* n2 = edge->GetNode(1);
9412 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9414 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9415 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9419 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9424 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9425 while( aFaceItr->more() )
9427 const SMDS_MeshFace* face = aFaceItr->next();
9428 if ( !face ) continue;
9430 const SMDSAbs_EntityType type = face->GetEntityType();
9434 case SMDSEntity_Quad_Triangle:
9435 case SMDSEntity_Quad_Quadrangle:
9436 alreadyOK = !theToBiQuad;
9437 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9439 case SMDSEntity_BiQuad_Triangle:
9440 case SMDSEntity_BiQuad_Quadrangle:
9441 alreadyOK = theToBiQuad;
9442 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9444 default: alreadyOK = false;
9449 const int id = face->GetID();
9450 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9452 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9454 SMDS_MeshFace * NewFace = 0;
9457 case SMDSEntity_Triangle:
9458 case SMDSEntity_Quad_Triangle:
9459 case SMDSEntity_BiQuad_Triangle:
9460 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9461 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9462 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9465 case SMDSEntity_Quadrangle:
9466 case SMDSEntity_Quad_Quadrangle:
9467 case SMDSEntity_BiQuad_Quadrangle:
9468 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9469 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9470 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9474 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9476 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9480 vector<int> nbNodeInFaces;
9481 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9482 while(aVolumeItr->more())
9484 const SMDS_MeshVolume* volume = aVolumeItr->next();
9485 if ( !volume ) continue;
9487 const SMDSAbs_EntityType type = volume->GetEntityType();
9488 if ( volume->IsQuadratic() )
9493 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9494 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9495 default: alreadyOK = true;
9499 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9503 const int id = volume->GetID();
9504 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9505 if ( type == SMDSEntity_Polyhedra )
9506 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9507 else if ( type == SMDSEntity_Hexagonal_Prism )
9508 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9510 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9512 SMDS_MeshVolume * NewVolume = 0;
9515 case SMDSEntity_Tetra:
9516 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9518 case SMDSEntity_Hexa:
9519 case SMDSEntity_Quad_Hexa:
9520 case SMDSEntity_TriQuad_Hexa:
9521 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9522 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9523 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9524 if ( nodes[i]->NbInverseElements() == 0 )
9525 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9527 case SMDSEntity_Pyramid:
9528 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9529 nodes[3], nodes[4], id, theForce3d);
9531 case SMDSEntity_Penta:
9532 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9533 nodes[3], nodes[4], nodes[5], id, theForce3d);
9535 case SMDSEntity_Hexagonal_Prism:
9537 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9539 ReplaceElemInGroups(volume, NewVolume, meshDS);
9544 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9545 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9546 // aHelper.FixQuadraticElements(myError);
9547 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9551 //================================================================================
9553 * \brief Makes given elements quadratic
9554 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9555 * \param theElements - elements to make quadratic
9557 //================================================================================
9559 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9560 TIDSortedElemSet& theElements,
9561 const bool theToBiQuad)
9563 if ( theElements.empty() ) return;
9565 // we believe that all theElements are of the same type
9566 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9568 // get all nodes shared by theElements
9569 TIDSortedNodeSet allNodes;
9570 TIDSortedElemSet::iterator eIt = theElements.begin();
9571 for ( ; eIt != theElements.end(); ++eIt )
9572 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9574 // complete theElements with elements of lower dim whose all nodes are in allNodes
9576 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9577 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9578 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9579 for ( ; nIt != allNodes.end(); ++nIt )
9581 const SMDS_MeshNode* n = *nIt;
9582 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9583 while ( invIt->more() )
9585 const SMDS_MeshElement* e = invIt->next();
9586 const SMDSAbs_ElementType type = e->GetType();
9587 if ( e->IsQuadratic() )
9589 quadAdjacentElems[ type ].insert( e );
9592 switch ( e->GetEntityType() ) {
9593 case SMDSEntity_Quad_Triangle:
9594 case SMDSEntity_Quad_Quadrangle:
9595 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9596 case SMDSEntity_BiQuad_Triangle:
9597 case SMDSEntity_BiQuad_Quadrangle:
9598 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9599 default: alreadyOK = true;
9604 if ( type >= elemType )
9605 continue; // same type or more complex linear element
9607 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9608 continue; // e is already checked
9612 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9613 while ( nodeIt->more() && allIn )
9614 allIn = allNodes.count( nodeIt->next() );
9616 theElements.insert(e );
9620 SMESH_MesherHelper helper(*myMesh);
9621 helper.SetIsQuadratic( true );
9622 helper.SetIsBiQuadratic( theToBiQuad );
9624 // add links of quadratic adjacent elements to the helper
9626 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9627 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9628 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9630 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9632 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9633 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9634 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9636 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9638 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9639 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9640 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9642 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9645 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9647 SMESHDS_Mesh* meshDS = GetMeshDS();
9648 SMESHDS_SubMesh* smDS = 0;
9649 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9651 const SMDS_MeshElement* elem = *eIt;
9654 int nbCentralNodes = 0;
9655 switch ( elem->GetEntityType() ) {
9656 // linear convertible
9657 case SMDSEntity_Edge:
9658 case SMDSEntity_Triangle:
9659 case SMDSEntity_Quadrangle:
9660 case SMDSEntity_Tetra:
9661 case SMDSEntity_Pyramid:
9662 case SMDSEntity_Hexa:
9663 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9664 // quadratic that can become bi-quadratic
9665 case SMDSEntity_Quad_Triangle:
9666 case SMDSEntity_Quad_Quadrangle:
9667 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9669 case SMDSEntity_BiQuad_Triangle:
9670 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9671 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9673 default: alreadyOK = true;
9675 if ( alreadyOK ) continue;
9677 const SMDSAbs_ElementType type = elem->GetType();
9678 const int id = elem->GetID();
9679 const int nbNodes = elem->NbCornerNodes();
9680 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9682 helper.SetSubShape( elem->getshapeId() );
9684 if ( !smDS || !smDS->Contains( elem ))
9685 smDS = meshDS->MeshElements( elem->getshapeId() );
9686 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9688 SMDS_MeshElement * newElem = 0;
9691 case 4: // cases for most frequently used element types go first (for optimization)
9692 if ( type == SMDSAbs_Volume )
9693 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9695 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9698 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9699 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9702 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9705 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9708 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9709 nodes[4], id, theForce3d);
9712 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9713 nodes[4], nodes[5], id, theForce3d);
9717 ReplaceElemInGroups( elem, newElem, meshDS);
9718 if( newElem && smDS )
9719 smDS->AddElement( newElem );
9721 // remove central nodes
9722 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9723 if ( nodes[i]->NbInverseElements() == 0 )
9724 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9726 } // loop on theElements
9729 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9730 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9731 // helper.FixQuadraticElements( myError );
9732 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9736 //=======================================================================
9738 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9739 * \return int - nb of checked elements
9741 //=======================================================================
9743 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9744 SMDS_ElemIteratorPtr theItr,
9745 const int theShapeID)
9748 SMESHDS_Mesh* meshDS = GetMeshDS();
9749 ElemFeatures elemType;
9750 vector<const SMDS_MeshNode *> nodes;
9752 while( theItr->more() )
9754 const SMDS_MeshElement* elem = theItr->next();
9756 if( elem && elem->IsQuadratic())
9759 int nbCornerNodes = elem->NbCornerNodes();
9760 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9762 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9764 //remove a quadratic element
9765 if ( !theSm || !theSm->Contains( elem ))
9766 theSm = meshDS->MeshElements( elem->getshapeId() );
9767 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9769 // remove medium nodes
9770 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9771 if ( nodes[i]->NbInverseElements() == 0 )
9772 meshDS->RemoveFreeNode( nodes[i], theSm );
9774 // add a linear element
9775 nodes.resize( nbCornerNodes );
9776 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9777 ReplaceElemInGroups(elem, newElem, meshDS);
9778 if( theSm && newElem )
9779 theSm->AddElement( newElem );
9785 //=======================================================================
9786 //function : ConvertFromQuadratic
9788 //=======================================================================
9790 bool SMESH_MeshEditor::ConvertFromQuadratic()
9792 int nbCheckedElems = 0;
9793 if ( myMesh->HasShapeToMesh() )
9795 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9797 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9798 while ( smIt->more() ) {
9799 SMESH_subMesh* sm = smIt->next();
9800 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9801 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9807 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9808 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9810 SMESHDS_SubMesh *aSM = 0;
9811 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9819 //================================================================================
9821 * \brief Return true if all medium nodes of the element are in the node set
9823 //================================================================================
9825 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9827 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9828 if ( !nodeSet.count( elem->GetNode(i) ))
9834 //================================================================================
9836 * \brief Makes given elements linear
9838 //================================================================================
9840 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9842 if ( theElements.empty() ) return;
9844 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9845 set<int> mediumNodeIDs;
9846 TIDSortedElemSet::iterator eIt = theElements.begin();
9847 for ( ; eIt != theElements.end(); ++eIt )
9849 const SMDS_MeshElement* e = *eIt;
9850 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9851 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9854 // replace given elements by linear ones
9855 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9856 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9858 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9859 // except those elements sharing medium nodes of quadratic element whose medium nodes
9860 // are not all in mediumNodeIDs
9862 // get remaining medium nodes
9863 TIDSortedNodeSet mediumNodes;
9864 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9865 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9866 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9867 mediumNodes.insert( mediumNodes.end(), n );
9869 // find more quadratic elements to convert
9870 TIDSortedElemSet moreElemsToConvert;
9871 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9872 for ( ; nIt != mediumNodes.end(); ++nIt )
9874 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9875 while ( invIt->more() )
9877 const SMDS_MeshElement* e = invIt->next();
9878 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9880 // find a more complex element including e and
9881 // whose medium nodes are not in mediumNodes
9882 bool complexFound = false;
9883 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9885 SMDS_ElemIteratorPtr invIt2 =
9886 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9887 while ( invIt2->more() )
9889 const SMDS_MeshElement* eComplex = invIt2->next();
9890 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9892 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9893 if ( nbCommonNodes == e->NbNodes())
9895 complexFound = true;
9896 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9902 if ( !complexFound )
9903 moreElemsToConvert.insert( e );
9907 elemIt = elemSetIterator( moreElemsToConvert );
9908 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9911 //=======================================================================
9912 //function : SewSideElements
9914 //=======================================================================
9916 SMESH_MeshEditor::Sew_Error
9917 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9918 TIDSortedElemSet& theSide2,
9919 const SMDS_MeshNode* theFirstNode1,
9920 const SMDS_MeshNode* theFirstNode2,
9921 const SMDS_MeshNode* theSecondNode1,
9922 const SMDS_MeshNode* theSecondNode2)
9924 myLastCreatedElems.Clear();
9925 myLastCreatedNodes.Clear();
9927 if ( theSide1.size() != theSide2.size() )
9928 return SEW_DIFF_NB_OF_ELEMENTS;
9930 Sew_Error aResult = SEW_OK;
9932 // 1. Build set of faces representing each side
9933 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9934 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9936 // =======================================================================
9937 // 1. Build set of faces representing each side:
9938 // =======================================================================
9939 // a. build set of nodes belonging to faces
9940 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9941 // c. create temporary faces representing side of volumes if correspondent
9942 // face does not exist
9944 SMESHDS_Mesh* aMesh = GetMeshDS();
9945 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9946 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9947 TIDSortedElemSet faceSet1, faceSet2;
9948 set<const SMDS_MeshElement*> volSet1, volSet2;
9949 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9950 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9951 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9952 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9953 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9954 int iSide, iFace, iNode;
9956 list<const SMDS_MeshElement* > tempFaceList;
9957 for ( iSide = 0; iSide < 2; iSide++ ) {
9958 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9959 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9960 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9961 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9962 set<const SMDS_MeshElement*>::iterator vIt;
9963 TIDSortedElemSet::iterator eIt;
9964 set<const SMDS_MeshNode*>::iterator nIt;
9966 // check that given nodes belong to given elements
9967 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9968 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9969 int firstIndex = -1, secondIndex = -1;
9970 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9971 const SMDS_MeshElement* elem = *eIt;
9972 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9973 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9974 if ( firstIndex > -1 && secondIndex > -1 ) break;
9976 if ( firstIndex < 0 || secondIndex < 0 ) {
9977 // we can simply return until temporary faces created
9978 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9981 // -----------------------------------------------------------
9982 // 1a. Collect nodes of existing faces
9983 // and build set of face nodes in order to detect missing
9984 // faces corresponding to sides of volumes
9985 // -----------------------------------------------------------
9987 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9989 // loop on the given element of a side
9990 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9991 //const SMDS_MeshElement* elem = *eIt;
9992 const SMDS_MeshElement* elem = *eIt;
9993 if ( elem->GetType() == SMDSAbs_Face ) {
9994 faceSet->insert( elem );
9995 set <const SMDS_MeshNode*> faceNodeSet;
9996 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9997 while ( nodeIt->more() ) {
9998 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9999 nodeSet->insert( n );
10000 faceNodeSet.insert( n );
10002 setOfFaceNodeSet.insert( faceNodeSet );
10004 else if ( elem->GetType() == SMDSAbs_Volume )
10005 volSet->insert( elem );
10007 // ------------------------------------------------------------------------------
10008 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10009 // ------------------------------------------------------------------------------
10011 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10012 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10013 while ( fIt->more() ) { // loop on faces sharing a node
10014 const SMDS_MeshElement* f = fIt->next();
10015 if ( faceSet->find( f ) == faceSet->end() ) {
10016 // check if all nodes are in nodeSet and
10017 // complete setOfFaceNodeSet if they are
10018 set <const SMDS_MeshNode*> faceNodeSet;
10019 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10020 bool allInSet = true;
10021 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10022 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10023 if ( nodeSet->find( n ) == nodeSet->end() )
10026 faceNodeSet.insert( n );
10029 faceSet->insert( f );
10030 setOfFaceNodeSet.insert( faceNodeSet );
10036 // -------------------------------------------------------------------------
10037 // 1c. Create temporary faces representing sides of volumes if correspondent
10038 // face does not exist
10039 // -------------------------------------------------------------------------
10041 if ( !volSet->empty() ) {
10042 //int nodeSetSize = nodeSet->size();
10044 // loop on given volumes
10045 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10046 SMDS_VolumeTool vol (*vIt);
10047 // loop on volume faces: find free faces
10048 // --------------------------------------
10049 list<const SMDS_MeshElement* > freeFaceList;
10050 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10051 if ( !vol.IsFreeFace( iFace ))
10053 // check if there is already a face with same nodes in a face set
10054 const SMDS_MeshElement* aFreeFace = 0;
10055 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10056 int nbNodes = vol.NbFaceNodes( iFace );
10057 set <const SMDS_MeshNode*> faceNodeSet;
10058 vol.GetFaceNodes( iFace, faceNodeSet );
10059 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10061 // no such a face is given but it still can exist, check it
10062 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10063 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10065 if ( !aFreeFace ) {
10066 // create a temporary face
10067 if ( nbNodes == 3 ) {
10068 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10069 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10071 else if ( nbNodes == 4 ) {
10072 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10073 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10076 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10077 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10078 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10081 tempFaceList.push_back( aFreeFace );
10085 freeFaceList.push_back( aFreeFace );
10087 } // loop on faces of a volume
10089 // choose one of several free faces of a volume
10090 // --------------------------------------------
10091 if ( freeFaceList.size() > 1 ) {
10092 // choose a face having max nb of nodes shared by other elems of a side
10093 int maxNbNodes = -1;
10094 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10095 while ( fIt != freeFaceList.end() ) { // loop on free faces
10096 int nbSharedNodes = 0;
10097 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10098 while ( nodeIt->more() ) { // loop on free face nodes
10099 const SMDS_MeshNode* n =
10100 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10101 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10102 while ( invElemIt->more() ) {
10103 const SMDS_MeshElement* e = invElemIt->next();
10104 nbSharedNodes += faceSet->count( e );
10105 nbSharedNodes += elemSet->count( e );
10108 if ( nbSharedNodes > maxNbNodes ) {
10109 maxNbNodes = nbSharedNodes;
10110 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10112 else if ( nbSharedNodes == maxNbNodes ) {
10116 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10119 if ( freeFaceList.size() > 1 )
10121 // could not choose one face, use another way
10122 // choose a face most close to the bary center of the opposite side
10123 gp_XYZ aBC( 0., 0., 0. );
10124 set <const SMDS_MeshNode*> addedNodes;
10125 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10126 eIt = elemSet2->begin();
10127 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10128 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10129 while ( nodeIt->more() ) { // loop on free face nodes
10130 const SMDS_MeshNode* n =
10131 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10132 if ( addedNodes.insert( n ).second )
10133 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10136 aBC /= addedNodes.size();
10137 double minDist = DBL_MAX;
10138 fIt = freeFaceList.begin();
10139 while ( fIt != freeFaceList.end() ) { // loop on free faces
10141 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10142 while ( nodeIt->more() ) { // loop on free face nodes
10143 const SMDS_MeshNode* n =
10144 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10145 gp_XYZ p( n->X(),n->Y(),n->Z() );
10146 dist += ( aBC - p ).SquareModulus();
10148 if ( dist < minDist ) {
10150 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10153 fIt = freeFaceList.erase( fIt++ );
10156 } // choose one of several free faces of a volume
10158 if ( freeFaceList.size() == 1 ) {
10159 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10160 faceSet->insert( aFreeFace );
10161 // complete a node set with nodes of a found free face
10162 // for ( iNode = 0; iNode < ; iNode++ )
10163 // nodeSet->insert( fNodes[ iNode ] );
10166 } // loop on volumes of a side
10168 // // complete a set of faces if new nodes in a nodeSet appeared
10169 // // ----------------------------------------------------------
10170 // if ( nodeSetSize != nodeSet->size() ) {
10171 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10172 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10173 // while ( fIt->more() ) { // loop on faces sharing a node
10174 // const SMDS_MeshElement* f = fIt->next();
10175 // if ( faceSet->find( f ) == faceSet->end() ) {
10176 // // check if all nodes are in nodeSet and
10177 // // complete setOfFaceNodeSet if they are
10178 // set <const SMDS_MeshNode*> faceNodeSet;
10179 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10180 // bool allInSet = true;
10181 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10182 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10183 // if ( nodeSet->find( n ) == nodeSet->end() )
10184 // allInSet = false;
10186 // faceNodeSet.insert( n );
10188 // if ( allInSet ) {
10189 // faceSet->insert( f );
10190 // setOfFaceNodeSet.insert( faceNodeSet );
10196 } // Create temporary faces, if there are volumes given
10199 if ( faceSet1.size() != faceSet2.size() ) {
10200 // delete temporary faces: they are in reverseElements of actual nodes
10201 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10202 // while ( tmpFaceIt->more() )
10203 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10204 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10205 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10206 // aMesh->RemoveElement(*tmpFaceIt);
10207 MESSAGE("Diff nb of faces");
10208 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10211 // ============================================================
10212 // 2. Find nodes to merge:
10213 // bind a node to remove to a node to put instead
10214 // ============================================================
10216 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10217 if ( theFirstNode1 != theFirstNode2 )
10218 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10219 if ( theSecondNode1 != theSecondNode2 )
10220 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10222 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10223 set< long > linkIdSet; // links to process
10224 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10226 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10227 list< NLink > linkList[2];
10228 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10229 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10230 // loop on links in linkList; find faces by links and append links
10231 // of the found faces to linkList
10232 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10233 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10235 NLink link[] = { *linkIt[0], *linkIt[1] };
10236 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10237 if ( !linkIdSet.count( linkID ) )
10240 // by links, find faces in the face sets,
10241 // and find indices of link nodes in the found faces;
10242 // in a face set, there is only one or no face sharing a link
10243 // ---------------------------------------------------------------
10245 const SMDS_MeshElement* face[] = { 0, 0 };
10246 vector<const SMDS_MeshNode*> fnodes[2];
10247 int iLinkNode[2][2];
10248 TIDSortedElemSet avoidSet;
10249 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10250 const SMDS_MeshNode* n1 = link[iSide].first;
10251 const SMDS_MeshNode* n2 = link[iSide].second;
10252 //cout << "Side " << iSide << " ";
10253 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10254 // find a face by two link nodes
10255 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10256 *faceSetPtr[ iSide ], avoidSet,
10257 &iLinkNode[iSide][0],
10258 &iLinkNode[iSide][1] );
10259 if ( face[ iSide ])
10261 //cout << " F " << face[ iSide]->GetID() <<endl;
10262 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10263 // put face nodes to fnodes
10264 if ( face[ iSide ]->IsQuadratic() )
10266 // use interlaced nodes iterator
10267 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10268 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10269 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10270 while ( nIter->more() )
10271 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10275 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10276 face[ iSide ]->end_nodes() );
10278 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10282 // check similarity of elements of the sides
10283 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10284 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10285 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10286 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10289 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10291 break; // do not return because it's necessary to remove tmp faces
10294 // set nodes to merge
10295 // -------------------
10297 if ( face[0] && face[1] ) {
10298 const int nbNodes = face[0]->NbNodes();
10299 if ( nbNodes != face[1]->NbNodes() ) {
10300 MESSAGE("Diff nb of face nodes");
10301 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10302 break; // do not return because it s necessary to remove tmp faces
10304 bool reverse[] = { false, false }; // order of nodes in the link
10305 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10306 // analyse link orientation in faces
10307 int i1 = iLinkNode[ iSide ][ 0 ];
10308 int i2 = iLinkNode[ iSide ][ 1 ];
10309 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10311 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10312 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10313 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10315 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10316 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10319 // add other links of the faces to linkList
10320 // -----------------------------------------
10322 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10323 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10324 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10325 if ( !iter_isnew.second ) { // already in a set: no need to process
10326 linkIdSet.erase( iter_isnew.first );
10328 else // new in set == encountered for the first time: add
10330 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10331 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10332 linkList[0].push_back ( NLink( n1, n2 ));
10333 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10338 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10341 } // loop on link lists
10343 if ( aResult == SEW_OK &&
10344 ( //linkIt[0] != linkList[0].end() ||
10345 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10346 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10347 " " << (faceSetPtr[1]->empty()));
10348 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10351 // ====================================================================
10352 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10353 // ====================================================================
10355 // delete temporary faces
10356 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10357 // while ( tmpFaceIt->more() )
10358 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10359 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10360 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10361 aMesh->RemoveElement(*tmpFaceIt);
10363 if ( aResult != SEW_OK)
10366 list< int > nodeIDsToRemove;
10367 vector< const SMDS_MeshNode*> nodes;
10368 ElemFeatures elemType;
10370 // loop on nodes replacement map
10371 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10372 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10373 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10375 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10376 nodeIDsToRemove.push_back( nToRemove->GetID() );
10377 // loop on elements sharing nToRemove
10378 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10379 while ( invElemIt->more() ) {
10380 const SMDS_MeshElement* e = invElemIt->next();
10381 // get a new suite of nodes: make replacement
10382 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10383 nodes.resize( nbNodes );
10384 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10385 while ( nIt->more() ) {
10386 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10387 nnIt = nReplaceMap.find( n );
10388 if ( nnIt != nReplaceMap.end() ) {
10390 n = (*nnIt).second;
10394 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10395 // elemIDsToRemove.push_back( e->GetID() );
10399 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10400 aMesh->RemoveElement( e );
10402 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10404 AddToSameGroups( newElem, e, aMesh );
10405 if ( int aShapeId = e->getshapeId() )
10406 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10412 Remove( nodeIDsToRemove, true );
10417 //================================================================================
10419 * \brief Find corresponding nodes in two sets of faces
10420 * \param theSide1 - first face set
10421 * \param theSide2 - second first face
10422 * \param theFirstNode1 - a boundary node of set 1
10423 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10424 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10425 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10426 * \param nReplaceMap - output map of corresponding nodes
10427 * \return bool - is a success or not
10429 //================================================================================
10432 //#define DEBUG_MATCHING_NODES
10435 SMESH_MeshEditor::Sew_Error
10436 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10437 set<const SMDS_MeshElement*>& theSide2,
10438 const SMDS_MeshNode* theFirstNode1,
10439 const SMDS_MeshNode* theFirstNode2,
10440 const SMDS_MeshNode* theSecondNode1,
10441 const SMDS_MeshNode* theSecondNode2,
10442 TNodeNodeMap & nReplaceMap)
10444 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10446 nReplaceMap.clear();
10447 if ( theFirstNode1 != theFirstNode2 )
10448 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10449 if ( theSecondNode1 != theSecondNode2 )
10450 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10452 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10453 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10455 list< NLink > linkList[2];
10456 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10457 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10459 // loop on links in linkList; find faces by links and append links
10460 // of the found faces to linkList
10461 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10462 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10463 NLink link[] = { *linkIt[0], *linkIt[1] };
10464 if ( linkSet.find( link[0] ) == linkSet.end() )
10467 // by links, find faces in the face sets,
10468 // and find indices of link nodes in the found faces;
10469 // in a face set, there is only one or no face sharing a link
10470 // ---------------------------------------------------------------
10472 const SMDS_MeshElement* face[] = { 0, 0 };
10473 list<const SMDS_MeshNode*> notLinkNodes[2];
10474 //bool reverse[] = { false, false }; // order of notLinkNodes
10476 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10478 const SMDS_MeshNode* n1 = link[iSide].first;
10479 const SMDS_MeshNode* n2 = link[iSide].second;
10480 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10481 set< const SMDS_MeshElement* > facesOfNode1;
10482 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10484 // during a loop of the first node, we find all faces around n1,
10485 // during a loop of the second node, we find one face sharing both n1 and n2
10486 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10487 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10488 while ( fIt->more() ) { // loop on faces sharing a node
10489 const SMDS_MeshElement* f = fIt->next();
10490 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10491 ! facesOfNode1.insert( f ).second ) // f encounters twice
10493 if ( face[ iSide ] ) {
10494 MESSAGE( "2 faces per link " );
10495 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10498 faceSet->erase( f );
10500 // get not link nodes
10501 int nbN = f->NbNodes();
10502 if ( f->IsQuadratic() )
10504 nbNodes[ iSide ] = nbN;
10505 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10506 int i1 = f->GetNodeIndex( n1 );
10507 int i2 = f->GetNodeIndex( n2 );
10508 int iEnd = nbN, iBeg = -1, iDelta = 1;
10509 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10511 std::swap( iEnd, iBeg ); iDelta = -1;
10516 if ( i == iEnd ) i = iBeg + iDelta;
10517 if ( i == i1 ) break;
10518 nodes.push_back ( f->GetNode( i ) );
10524 // check similarity of elements of the sides
10525 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10526 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10527 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10528 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10531 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10535 // set nodes to merge
10536 // -------------------
10538 if ( face[0] && face[1] ) {
10539 if ( nbNodes[0] != nbNodes[1] ) {
10540 MESSAGE("Diff nb of face nodes");
10541 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10543 #ifdef DEBUG_MATCHING_NODES
10544 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10545 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10546 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10548 int nbN = nbNodes[0];
10550 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10551 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10552 for ( int i = 0 ; i < nbN - 2; ++i ) {
10553 #ifdef DEBUG_MATCHING_NODES
10554 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10556 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10560 // add other links of the face 1 to linkList
10561 // -----------------------------------------
10563 const SMDS_MeshElement* f0 = face[0];
10564 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10565 for ( int i = 0; i < nbN; i++ )
10567 const SMDS_MeshNode* n2 = f0->GetNode( i );
10568 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10569 linkSet.insert( SMESH_TLink( n1, n2 ));
10570 if ( !iter_isnew.second ) { // already in a set: no need to process
10571 linkSet.erase( iter_isnew.first );
10573 else // new in set == encountered for the first time: add
10575 #ifdef DEBUG_MATCHING_NODES
10576 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10577 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10579 linkList[0].push_back ( NLink( n1, n2 ));
10580 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10585 } // loop on link lists
10590 //================================================================================
10592 * \brief Create elements equal (on same nodes) to given ones
10593 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10594 * elements of the uppest dimension are duplicated.
10596 //================================================================================
10598 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10600 ClearLastCreated();
10601 SMESHDS_Mesh* mesh = GetMeshDS();
10603 // get an element type and an iterator over elements
10605 SMDSAbs_ElementType type = SMDSAbs_All;
10606 SMDS_ElemIteratorPtr elemIt;
10607 vector< const SMDS_MeshElement* > allElems;
10608 if ( theElements.empty() )
10610 if ( mesh->NbNodes() == 0 )
10612 // get most complex type
10613 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10614 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10615 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10617 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10618 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10623 // put all elements in the vector <allElems>
10624 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10625 elemIt = mesh->elementsIterator( type );
10626 while ( elemIt->more() )
10627 allElems.push_back( elemIt->next());
10628 elemIt = elemSetIterator( allElems );
10632 type = (*theElements.begin())->GetType();
10633 elemIt = elemSetIterator( theElements );
10636 // duplicate elements
10638 ElemFeatures elemType;
10640 vector< const SMDS_MeshNode* > nodes;
10641 while ( elemIt->more() )
10643 const SMDS_MeshElement* elem = elemIt->next();
10644 if ( elem->GetType() != type )
10647 elemType.Init( elem, /*basicOnly=*/false );
10648 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10650 AddElement( nodes, elemType );
10654 //================================================================================
10656 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10657 \param theElems - the list of elements (edges or faces) to be replicated
10658 The nodes for duplication could be found from these elements
10659 \param theNodesNot - list of nodes to NOT replicate
10660 \param theAffectedElems - the list of elements (cells and edges) to which the
10661 replicated nodes should be associated to.
10662 \return TRUE if operation has been completed successfully, FALSE otherwise
10664 //================================================================================
10666 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10667 const TIDSortedElemSet& theNodesNot,
10668 const TIDSortedElemSet& theAffectedElems )
10670 myLastCreatedElems.Clear();
10671 myLastCreatedNodes.Clear();
10673 if ( theElems.size() == 0 )
10676 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10681 TNodeNodeMap anOldNodeToNewNode;
10682 // duplicate elements and nodes
10683 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10684 // replce nodes by duplications
10685 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10689 //================================================================================
10691 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10692 \param theMeshDS - mesh instance
10693 \param theElems - the elements replicated or modified (nodes should be changed)
10694 \param theNodesNot - nodes to NOT replicate
10695 \param theNodeNodeMap - relation of old node to new created node
10696 \param theIsDoubleElem - flag os to replicate element or modify
10697 \return TRUE if operation has been completed successfully, FALSE otherwise
10699 //================================================================================
10701 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10702 const TIDSortedElemSet& theElems,
10703 const TIDSortedElemSet& theNodesNot,
10704 TNodeNodeMap& theNodeNodeMap,
10705 const bool theIsDoubleElem )
10707 // iterate through element and duplicate them (by nodes duplication)
10709 std::vector<const SMDS_MeshNode*> newNodes;
10710 ElemFeatures elemType;
10712 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10713 for ( ; elemItr != theElems.end(); ++elemItr )
10715 const SMDS_MeshElement* anElem = *elemItr;
10719 // duplicate nodes to duplicate element
10720 bool isDuplicate = false;
10721 newNodes.resize( anElem->NbNodes() );
10722 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10724 while ( anIter->more() )
10726 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10727 const SMDS_MeshNode* aNewNode = aCurrNode;
10728 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10729 if ( n2n != theNodeNodeMap.end() )
10731 aNewNode = n2n->second;
10733 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10736 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10737 copyPosition( aCurrNode, aNewNode );
10738 theNodeNodeMap[ aCurrNode ] = aNewNode;
10739 myLastCreatedNodes.Append( aNewNode );
10741 isDuplicate |= (aCurrNode != aNewNode);
10742 newNodes[ ind++ ] = aNewNode;
10744 if ( !isDuplicate )
10747 if ( theIsDoubleElem )
10748 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10750 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10757 //================================================================================
10759 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10760 \param theNodes - identifiers of nodes to be doubled
10761 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10762 nodes. If list of element identifiers is empty then nodes are doubled but
10763 they not assigned to elements
10764 \return TRUE if operation has been completed successfully, FALSE otherwise
10766 //================================================================================
10768 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10769 const std::list< int >& theListOfModifiedElems )
10771 myLastCreatedElems.Clear();
10772 myLastCreatedNodes.Clear();
10774 if ( theListOfNodes.size() == 0 )
10777 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10781 // iterate through nodes and duplicate them
10783 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10785 std::list< int >::const_iterator aNodeIter;
10786 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10788 int aCurr = *aNodeIter;
10789 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10795 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10798 copyPosition( aNode, aNewNode );
10799 anOldNodeToNewNode[ aNode ] = aNewNode;
10800 myLastCreatedNodes.Append( aNewNode );
10804 // Create map of new nodes for modified elements
10806 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10808 std::list< int >::const_iterator anElemIter;
10809 for ( anElemIter = theListOfModifiedElems.begin();
10810 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10812 int aCurr = *anElemIter;
10813 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10817 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10819 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10821 while ( anIter->more() )
10823 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10824 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10826 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10827 aNodeArr[ ind++ ] = aNewNode;
10830 aNodeArr[ ind++ ] = aCurrNode;
10832 anElemToNodes[ anElem ] = aNodeArr;
10835 // Change nodes of elements
10837 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10838 anElemToNodesIter = anElemToNodes.begin();
10839 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10841 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10842 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10845 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10854 //================================================================================
10856 \brief Check if element located inside shape
10857 \return TRUE if IN or ON shape, FALSE otherwise
10859 //================================================================================
10861 template<class Classifier>
10862 bool isInside(const SMDS_MeshElement* theElem,
10863 Classifier& theClassifier,
10864 const double theTol)
10866 gp_XYZ centerXYZ (0, 0, 0);
10867 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10868 while (aNodeItr->more())
10869 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10871 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10872 theClassifier.Perform(aPnt, theTol);
10873 TopAbs_State aState = theClassifier.State();
10874 return (aState == TopAbs_IN || aState == TopAbs_ON );
10877 //================================================================================
10879 * \brief Classifier of the 3D point on the TopoDS_Face
10880 * with interaface suitable for isInside()
10882 //================================================================================
10884 struct _FaceClassifier
10886 Extrema_ExtPS _extremum;
10887 BRepAdaptor_Surface _surface;
10888 TopAbs_State _state;
10890 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10892 _extremum.Initialize( _surface,
10893 _surface.FirstUParameter(), _surface.LastUParameter(),
10894 _surface.FirstVParameter(), _surface.LastVParameter(),
10895 _surface.Tolerance(), _surface.Tolerance() );
10897 void Perform(const gp_Pnt& aPnt, double theTol)
10900 _state = TopAbs_OUT;
10901 _extremum.Perform(aPnt);
10902 if ( _extremum.IsDone() )
10903 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10904 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10906 TopAbs_State State() const
10913 //================================================================================
10915 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10916 This method is the first step of DoubleNodeElemGroupsInRegion.
10917 \param theElems - list of groups of elements (edges or faces) to be replicated
10918 \param theNodesNot - list of groups of nodes not to replicated
10919 \param theShape - shape to detect affected elements (element which geometric center
10920 located on or inside shape). If the shape is null, detection is done on faces orientations
10921 (select elements with a gravity center on the side given by faces normals).
10922 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10923 The replicated nodes should be associated to affected elements.
10924 \return groups of affected elements
10925 \sa DoubleNodeElemGroupsInRegion()
10927 //================================================================================
10929 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10930 const TIDSortedElemSet& theNodesNot,
10931 const TopoDS_Shape& theShape,
10932 TIDSortedElemSet& theAffectedElems)
10934 if ( theShape.IsNull() )
10936 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10937 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10938 std::set<const SMDS_MeshElement*> edgesToCheck;
10939 alreadyCheckedNodes.clear();
10940 alreadyCheckedElems.clear();
10941 edgesToCheck.clear();
10943 // --- iterates on elements to be replicated and get elements by back references from their nodes
10945 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10946 for ( ; elemItr != theElems.end(); ++elemItr )
10948 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10949 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10952 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10953 std::set<const SMDS_MeshNode*> nodesElem;
10955 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10956 while ( nodeItr->more() )
10958 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10959 nodesElem.insert(aNode);
10961 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10962 for (; nodit != nodesElem.end(); nodit++)
10964 const SMDS_MeshNode* aNode = *nodit;
10965 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10967 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10969 alreadyCheckedNodes.insert(aNode);
10970 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10971 while ( backElemItr->more() )
10973 const SMDS_MeshElement* curElem = backElemItr->next();
10974 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10976 if (theElems.find(curElem) != theElems.end())
10978 alreadyCheckedElems.insert(curElem);
10979 double x=0, y=0, z=0;
10981 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10982 while ( nodeItr2->more() )
10984 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10985 x += anotherNode->X();
10986 y += anotherNode->Y();
10987 z += anotherNode->Z();
10991 p.SetCoord( x/nb -aNode->X(),
10993 z/nb -aNode->Z() );
10996 theAffectedElems.insert( curElem );
10998 else if (curElem->GetType() == SMDSAbs_Edge)
10999 edgesToCheck.insert(curElem);
11003 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11004 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11005 for( ; eit != edgesToCheck.end(); eit++)
11007 bool onside = true;
11008 const SMDS_MeshElement* anEdge = *eit;
11009 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11010 while ( nodeItr->more() )
11012 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11013 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11021 theAffectedElems.insert(anEdge);
11027 const double aTol = Precision::Confusion();
11028 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11029 auto_ptr<_FaceClassifier> aFaceClassifier;
11030 if ( theShape.ShapeType() == TopAbs_SOLID )
11032 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11033 bsc3d->PerformInfinitePoint(aTol);
11035 else if (theShape.ShapeType() == TopAbs_FACE )
11037 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11040 // iterates on indicated elements and get elements by back references from their nodes
11041 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11042 for ( ; elemItr != theElems.end(); ++elemItr )
11044 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11047 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11048 while ( nodeItr->more() )
11050 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11051 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11053 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11054 while ( backElemItr->more() )
11056 const SMDS_MeshElement* curElem = backElemItr->next();
11057 if ( curElem && theElems.find(curElem) == theElems.end() &&
11059 isInside( curElem, *bsc3d, aTol ) :
11060 isInside( curElem, *aFaceClassifier, aTol )))
11061 theAffectedElems.insert( curElem );
11069 //================================================================================
11071 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11072 \param theElems - group of of elements (edges or faces) to be replicated
11073 \param theNodesNot - group of nodes not to replicate
11074 \param theShape - shape to detect affected elements (element which geometric center
11075 located on or inside shape).
11076 The replicated nodes should be associated to affected elements.
11077 \return TRUE if operation has been completed successfully, FALSE otherwise
11079 //================================================================================
11081 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11082 const TIDSortedElemSet& theNodesNot,
11083 const TopoDS_Shape& theShape )
11085 if ( theShape.IsNull() )
11088 const double aTol = Precision::Confusion();
11089 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11090 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11091 if ( theShape.ShapeType() == TopAbs_SOLID )
11093 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11094 bsc3d->PerformInfinitePoint(aTol);
11096 else if (theShape.ShapeType() == TopAbs_FACE )
11098 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11101 // iterates on indicated elements and get elements by back references from their nodes
11102 TIDSortedElemSet anAffected;
11103 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11104 for ( ; elemItr != theElems.end(); ++elemItr )
11106 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11110 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11111 while ( nodeItr->more() )
11113 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11114 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11116 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11117 while ( backElemItr->more() )
11119 const SMDS_MeshElement* curElem = backElemItr->next();
11120 if ( curElem && theElems.find(curElem) == theElems.end() &&
11122 isInside( curElem, *bsc3d, aTol ) :
11123 isInside( curElem, *aFaceClassifier, aTol )))
11124 anAffected.insert( curElem );
11128 return DoubleNodes( theElems, theNodesNot, anAffected );
11132 * \brief compute an oriented angle between two planes defined by four points.
11133 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11134 * @param p0 base of the rotation axe
11135 * @param p1 extremity of the rotation axe
11136 * @param g1 belongs to the first plane
11137 * @param g2 belongs to the second plane
11139 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11141 gp_Vec vref(p0, p1);
11144 gp_Vec n1 = vref.Crossed(v1);
11145 gp_Vec n2 = vref.Crossed(v2);
11147 return n2.AngleWithRef(n1, vref);
11149 catch ( Standard_Failure ) {
11151 return Max( v1.Magnitude(), v2.Magnitude() );
11155 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11156 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11157 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11158 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11159 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11160 * 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.
11161 * 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.
11162 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11163 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11164 * \param theElems - list of groups of volumes, where a group of volume is a set of
11165 * SMDS_MeshElements sorted by Id.
11166 * \param createJointElems - if TRUE, create the elements
11167 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11168 * the boundary between \a theDomains and the rest mesh
11169 * \return TRUE if operation has been completed successfully, FALSE otherwise
11171 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11172 bool createJointElems,
11173 bool onAllBoundaries)
11175 // MESSAGE("----------------------------------------------");
11176 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11177 // MESSAGE("----------------------------------------------");
11179 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11180 meshDS->BuildDownWardConnectivity(true);
11182 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11184 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11185 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11186 // build the list of nodes shared by 2 or more domains, with their domain indexes
11188 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11189 std::map<int,int>celldom; // cell vtkId --> domain
11190 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11191 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11192 faceDomains.clear();
11194 cellDomains.clear();
11195 nodeDomains.clear();
11196 std::map<int,int> emptyMap;
11197 std::set<int> emptySet;
11200 //MESSAGE(".. Number of domains :"<<theElems.size());
11202 TIDSortedElemSet theRestDomElems;
11203 const int iRestDom = -1;
11204 const int idom0 = onAllBoundaries ? iRestDom : 0;
11205 const int nbDomains = theElems.size();
11207 // Check if the domains do not share an element
11208 for (int idom = 0; idom < nbDomains-1; idom++)
11210 // MESSAGE("... Check of domain #" << idom);
11211 const TIDSortedElemSet& domain = theElems[idom];
11212 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11213 for (; elemItr != domain.end(); ++elemItr)
11215 const SMDS_MeshElement* anElem = *elemItr;
11216 int idombisdeb = idom + 1 ;
11217 // check if the element belongs to a domain further in the list
11218 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11220 const TIDSortedElemSet& domainbis = theElems[idombis];
11221 if ( domainbis.count( anElem ))
11223 MESSAGE(".... Domain #" << idom);
11224 MESSAGE(".... Domain #" << idombis);
11225 throw SALOME_Exception("The domains are not disjoint.");
11232 for (int idom = 0; idom < nbDomains; idom++)
11235 // --- build a map (face to duplicate --> volume to modify)
11236 // with all the faces shared by 2 domains (group of elements)
11237 // and corresponding volume of this domain, for each shared face.
11238 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11240 //MESSAGE("... Neighbors of domain #" << idom);
11241 const TIDSortedElemSet& domain = theElems[idom];
11242 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11243 for (; elemItr != domain.end(); ++elemItr)
11245 const SMDS_MeshElement* anElem = *elemItr;
11248 int vtkId = anElem->getVtkId();
11249 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11250 int neighborsVtkIds[NBMAXNEIGHBORS];
11251 int downIds[NBMAXNEIGHBORS];
11252 unsigned char downTypes[NBMAXNEIGHBORS];
11253 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11254 for (int n = 0; n < nbNeighbors; n++)
11256 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11257 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11258 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11261 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11263 // MESSAGE("Domain " << idombis);
11264 const TIDSortedElemSet& domainbis = theElems[idombis];
11265 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11267 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11269 DownIdType face(downIds[n], downTypes[n]);
11270 if (!faceDomains[face].count(idom))
11272 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11273 celldom[vtkId] = idom;
11274 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11278 theRestDomElems.insert( elem );
11279 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11280 celldom[neighborsVtkIds[n]] = iRestDom;
11288 //MESSAGE("Number of shared faces " << faceDomains.size());
11289 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11291 // --- explore the shared faces domain by domain,
11292 // explore the nodes of the face and see if they belong to a cell in the domain,
11293 // which has only a node or an edge on the border (not a shared face)
11295 for (int idomain = idom0; idomain < nbDomains; idomain++)
11297 //MESSAGE("Domain " << idomain);
11298 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11299 itface = faceDomains.begin();
11300 for (; itface != faceDomains.end(); ++itface)
11302 const std::map<int, int>& domvol = itface->second;
11303 if (!domvol.count(idomain))
11305 DownIdType face = itface->first;
11306 //MESSAGE(" --- face " << face.cellId);
11307 std::set<int> oldNodes;
11309 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11310 std::set<int>::iterator itn = oldNodes.begin();
11311 for (; itn != oldNodes.end(); ++itn)
11314 //MESSAGE(" node " << oldId);
11315 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11316 for (int i=0; i<l.ncells; i++)
11318 int vtkId = l.cells[i];
11319 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11320 if (!domain.count(anElem))
11322 int vtkType = grid->GetCellType(vtkId);
11323 int downId = grid->CellIdToDownId(vtkId);
11326 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11327 continue; // not OK at this stage of the algorithm:
11328 //no cells created after BuildDownWardConnectivity
11330 DownIdType aCell(downId, vtkType);
11331 cellDomains[aCell][idomain] = vtkId;
11332 celldom[vtkId] = idomain;
11333 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11339 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11340 // for each shared face, get the nodes
11341 // for each node, for each domain of the face, create a clone of the node
11343 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11344 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11345 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11347 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11348 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11349 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11351 //MESSAGE(".. Duplication of the nodes");
11352 for (int idomain = idom0; idomain < nbDomains; idomain++)
11354 itface = faceDomains.begin();
11355 for (; itface != faceDomains.end(); ++itface)
11357 const std::map<int, int>& domvol = itface->second;
11358 if (!domvol.count(idomain))
11360 DownIdType face = itface->first;
11361 //MESSAGE(" --- face " << face.cellId);
11362 std::set<int> oldNodes;
11364 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11365 std::set<int>::iterator itn = oldNodes.begin();
11366 for (; itn != oldNodes.end(); ++itn)
11369 if (nodeDomains[oldId].empty())
11371 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11372 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11374 std::map<int, int>::const_iterator itdom = domvol.begin();
11375 for (; itdom != domvol.end(); ++itdom)
11377 int idom = itdom->first;
11378 //MESSAGE(" domain " << idom);
11379 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11381 if (nodeDomains[oldId].size() >= 2) // a multiple node
11383 vector<int> orderedDoms;
11384 //MESSAGE("multiple node " << oldId);
11385 if (mutipleNodes.count(oldId))
11386 orderedDoms = mutipleNodes[oldId];
11389 map<int,int>::iterator it = nodeDomains[oldId].begin();
11390 for (; it != nodeDomains[oldId].end(); ++it)
11391 orderedDoms.push_back(it->first);
11393 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11394 //stringstream txt;
11395 //for (int i=0; i<orderedDoms.size(); i++)
11396 // txt << orderedDoms[i] << " ";
11397 //MESSAGE("orderedDoms " << txt.str());
11398 mutipleNodes[oldId] = orderedDoms;
11400 double *coords = grid->GetPoint(oldId);
11401 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11402 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11403 int newId = newNode->getVtkId();
11404 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11405 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11412 //MESSAGE(".. Creation of elements");
11413 for (int idomain = idom0; idomain < nbDomains; idomain++)
11415 itface = faceDomains.begin();
11416 for (; itface != faceDomains.end(); ++itface)
11418 std::map<int, int> domvol = itface->second;
11419 if (!domvol.count(idomain))
11421 DownIdType face = itface->first;
11422 //MESSAGE(" --- face " << face.cellId);
11423 std::set<int> oldNodes;
11425 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11426 int nbMultipleNodes = 0;
11427 std::set<int>::iterator itn = oldNodes.begin();
11428 for (; itn != oldNodes.end(); ++itn)
11431 if (mutipleNodes.count(oldId))
11434 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11436 //MESSAGE("multiple Nodes detected on a shared face");
11437 int downId = itface->first.cellId;
11438 unsigned char cellType = itface->first.cellType;
11439 // --- shared edge or shared face ?
11440 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11443 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11444 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11445 if (mutipleNodes.count(nodes[i]))
11446 if (!mutipleNodesToFace.count(nodes[i]))
11447 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11449 else // shared face (between two volumes)
11451 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11452 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11453 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11454 for (int ie =0; ie < nbEdges; ie++)
11457 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11458 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11460 vector<int> vn0 = mutipleNodes[nodes[0]];
11461 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11463 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11464 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11465 if ( vn0[i0] == vn1[i1] )
11466 doms.push_back( vn0[ i0 ]);
11467 if ( doms.size() > 2 )
11469 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11470 double *coords = grid->GetPoint(nodes[0]);
11471 gp_Pnt p0(coords[0], coords[1], coords[2]);
11472 coords = grid->GetPoint(nodes[nbNodes - 1]);
11473 gp_Pnt p1(coords[0], coords[1], coords[2]);
11475 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11476 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11477 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11478 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11479 for ( size_t id = 0; id < doms.size(); id++ )
11481 int idom = doms[id];
11482 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11483 for ( int ivol = 0; ivol < nbvol; ivol++ )
11485 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11486 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11487 if (domain.count(elem))
11489 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11490 domvol[idom] = svol;
11491 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11493 vtkIdType npts = 0;
11494 vtkIdType* pts = 0;
11495 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11496 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11499 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11500 angleDom[idom] = 0;
11504 gp_Pnt g(values[0], values[1], values[2]);
11505 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11506 //MESSAGE(" angle=" << angleDom[idom]);
11512 map<double, int> sortedDom; // sort domains by angle
11513 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11514 sortedDom[ia->second] = ia->first;
11515 vector<int> vnodes;
11517 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11519 vdom.push_back(ib->second);
11520 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11522 for (int ino = 0; ino < nbNodes; ino++)
11523 vnodes.push_back(nodes[ino]);
11524 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11533 // --- iterate on shared faces (volumes to modify, face to extrude)
11534 // get node id's of the face (id SMDS = id VTK)
11535 // create flat element with old and new nodes if requested
11537 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11538 // (domain1 X domain2) = domain1 + MAXINT*domain2
11540 std::map<int, std::map<long,int> > nodeQuadDomains;
11541 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11543 //MESSAGE(".. Creation of elements: simple junction");
11544 if (createJointElems)
11547 string joints2DName = "joints2D";
11548 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11549 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11550 string joints3DName = "joints3D";
11551 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11552 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11554 itface = faceDomains.begin();
11555 for (; itface != faceDomains.end(); ++itface)
11557 DownIdType face = itface->first;
11558 std::set<int> oldNodes;
11559 std::set<int>::iterator itn;
11561 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11563 std::map<int, int> domvol = itface->second;
11564 std::map<int, int>::iterator itdom = domvol.begin();
11565 int dom1 = itdom->first;
11566 int vtkVolId = itdom->second;
11568 int dom2 = itdom->first;
11569 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11571 stringstream grpname;
11574 grpname << dom1 << "_" << dom2;
11576 grpname << dom2 << "_" << dom1;
11577 string namegrp = grpname.str();
11578 if (!mapOfJunctionGroups.count(namegrp))
11579 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11580 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11582 sgrp->Add(vol->GetID());
11583 if (vol->GetType() == SMDSAbs_Volume)
11584 joints3DGrp->Add(vol->GetID());
11585 else if (vol->GetType() == SMDSAbs_Face)
11586 joints2DGrp->Add(vol->GetID());
11590 // --- create volumes on multiple domain intersection if requested
11591 // iterate on mutipleNodesToFace
11592 // iterate on edgesMultiDomains
11594 //MESSAGE(".. Creation of elements: multiple junction");
11595 if (createJointElems)
11597 // --- iterate on mutipleNodesToFace
11599 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11600 for (; itn != mutipleNodesToFace.end(); ++itn)
11602 int node = itn->first;
11603 vector<int> orderDom = itn->second;
11604 vector<vtkIdType> orderedNodes;
11605 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11606 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11607 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11609 stringstream grpname;
11611 grpname << 0 << "_" << 0;
11613 string namegrp = grpname.str();
11614 if (!mapOfJunctionGroups.count(namegrp))
11615 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11616 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11618 sgrp->Add(face->GetID());
11621 // --- iterate on edgesMultiDomains
11623 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11624 for (; ite != edgesMultiDomains.end(); ++ite)
11626 vector<int> nodes = ite->first;
11627 vector<int> orderDom = ite->second;
11628 vector<vtkIdType> orderedNodes;
11629 if (nodes.size() == 2)
11631 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11632 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11633 if ( orderDom.size() == 3 )
11634 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11635 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11637 for (int idom = orderDom.size()-1; idom >=0; idom--)
11638 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11639 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11642 string namegrp = "jointsMultiples";
11643 if (!mapOfJunctionGroups.count(namegrp))
11644 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11645 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11647 sgrp->Add(vol->GetID());
11651 //INFOS("Quadratic multiple joints not implemented");
11652 // TODO quadratic nodes
11657 // --- list the explicit faces and edges of the mesh that need to be modified,
11658 // i.e. faces and edges built with one or more duplicated nodes.
11659 // associate these faces or edges to their corresponding domain.
11660 // only the first domain found is kept when a face or edge is shared
11662 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11663 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11664 faceOrEdgeDom.clear();
11667 //MESSAGE(".. Modification of elements");
11668 for (int idomain = idom0; idomain < nbDomains; idomain++)
11670 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11671 for (; itnod != nodeDomains.end(); ++itnod)
11673 int oldId = itnod->first;
11674 //MESSAGE(" node " << oldId);
11675 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11676 for (int i = 0; i < l.ncells; i++)
11678 int vtkId = l.cells[i];
11679 int vtkType = grid->GetCellType(vtkId);
11680 int downId = grid->CellIdToDownId(vtkId);
11682 continue; // new cells: not to be modified
11683 DownIdType aCell(downId, vtkType);
11684 int volParents[1000];
11685 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11686 for (int j = 0; j < nbvol; j++)
11687 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11688 if (!feDom.count(vtkId))
11690 feDom[vtkId] = idomain;
11691 faceOrEdgeDom[aCell] = emptyMap;
11692 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11693 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11694 // << " type " << vtkType << " downId " << downId);
11700 // --- iterate on shared faces (volumes to modify, face to extrude)
11701 // get node id's of the face
11702 // replace old nodes by new nodes in volumes, and update inverse connectivity
11704 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11705 for (int m=0; m<3; m++)
11707 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11708 itface = (*amap).begin();
11709 for (; itface != (*amap).end(); ++itface)
11711 DownIdType face = itface->first;
11712 std::set<int> oldNodes;
11713 std::set<int>::iterator itn;
11715 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11716 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11717 std::map<int, int> localClonedNodeIds;
11719 std::map<int, int> domvol = itface->second;
11720 std::map<int, int>::iterator itdom = domvol.begin();
11721 for (; itdom != domvol.end(); ++itdom)
11723 int idom = itdom->first;
11724 int vtkVolId = itdom->second;
11725 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11726 localClonedNodeIds.clear();
11727 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11730 if (nodeDomains[oldId].count(idom))
11732 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11733 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11736 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11741 // Remove empty groups (issue 0022812)
11742 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11743 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11745 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11746 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11749 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11750 grid->DeleteLinks();
11758 * \brief Double nodes on some external faces and create flat elements.
11759 * Flat elements are mainly used by some types of mechanic calculations.
11761 * Each group of the list must be constituted of faces.
11762 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11763 * @param theElems - list of groups of faces, where a group of faces is a set of
11764 * SMDS_MeshElements sorted by Id.
11765 * @return TRUE if operation has been completed successfully, FALSE otherwise
11767 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11769 // MESSAGE("-------------------------------------------------");
11770 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11771 // MESSAGE("-------------------------------------------------");
11773 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11775 // --- For each group of faces
11776 // duplicate the nodes, create a flat element based on the face
11777 // replace the nodes of the faces by their clones
11779 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11780 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11781 clonedNodes.clear();
11782 intermediateNodes.clear();
11783 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11784 mapOfJunctionGroups.clear();
11786 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11788 const TIDSortedElemSet& domain = theElems[idom];
11789 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11790 for ( ; elemItr != domain.end(); ++elemItr )
11792 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11793 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11796 // MESSAGE("aFace=" << aFace->GetID());
11797 bool isQuad = aFace->IsQuadratic();
11798 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11800 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11802 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11803 while (nodeIt->more())
11805 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11806 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11808 ln2.push_back(node);
11810 ln0.push_back(node);
11812 const SMDS_MeshNode* clone = 0;
11813 if (!clonedNodes.count(node))
11815 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11816 copyPosition( node, clone );
11817 clonedNodes[node] = clone;
11820 clone = clonedNodes[node];
11823 ln3.push_back(clone);
11825 ln1.push_back(clone);
11827 const SMDS_MeshNode* inter = 0;
11828 if (isQuad && (!isMedium))
11830 if (!intermediateNodes.count(node))
11832 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11833 copyPosition( node, inter );
11834 intermediateNodes[node] = inter;
11837 inter = intermediateNodes[node];
11838 ln4.push_back(inter);
11842 // --- extrude the face
11844 vector<const SMDS_MeshNode*> ln;
11845 SMDS_MeshVolume* vol = 0;
11846 vtkIdType aType = aFace->GetVtkType();
11850 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11851 // MESSAGE("vol prism " << vol->GetID());
11852 ln.push_back(ln1[0]);
11853 ln.push_back(ln1[1]);
11854 ln.push_back(ln1[2]);
11857 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11858 // MESSAGE("vol hexa " << vol->GetID());
11859 ln.push_back(ln1[0]);
11860 ln.push_back(ln1[1]);
11861 ln.push_back(ln1[2]);
11862 ln.push_back(ln1[3]);
11864 case VTK_QUADRATIC_TRIANGLE:
11865 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11866 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11867 // MESSAGE("vol quad prism " << vol->GetID());
11868 ln.push_back(ln1[0]);
11869 ln.push_back(ln1[1]);
11870 ln.push_back(ln1[2]);
11871 ln.push_back(ln3[0]);
11872 ln.push_back(ln3[1]);
11873 ln.push_back(ln3[2]);
11875 case VTK_QUADRATIC_QUAD:
11876 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11877 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11878 // ln4[0], ln4[1], ln4[2], ln4[3]);
11879 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11880 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11881 ln4[0], ln4[1], ln4[2], ln4[3]);
11882 // MESSAGE("vol quad hexa " << vol->GetID());
11883 ln.push_back(ln1[0]);
11884 ln.push_back(ln1[1]);
11885 ln.push_back(ln1[2]);
11886 ln.push_back(ln1[3]);
11887 ln.push_back(ln3[0]);
11888 ln.push_back(ln3[1]);
11889 ln.push_back(ln3[2]);
11890 ln.push_back(ln3[3]);
11900 stringstream grpname;
11904 string namegrp = grpname.str();
11905 if (!mapOfJunctionGroups.count(namegrp))
11906 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11907 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11909 sgrp->Add(vol->GetID());
11912 // --- modify the face
11914 aFace->ChangeNodes(&ln[0], ln.size());
11921 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11922 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11923 * groups of faces to remove inside the object, (idem edges).
11924 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11926 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11927 const TopoDS_Shape& theShape,
11928 SMESH_NodeSearcher* theNodeSearcher,
11929 const char* groupName,
11930 std::vector<double>& nodesCoords,
11931 std::vector<std::vector<int> >& listOfListOfNodes)
11933 // MESSAGE("--------------------------------");
11934 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11935 // MESSAGE("--------------------------------");
11937 // --- zone of volumes to remove is given :
11938 // 1 either by a geom shape (one or more vertices) and a radius,
11939 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11940 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11941 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11942 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11943 // defined by it's name.
11945 SMESHDS_GroupBase* groupDS = 0;
11946 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11947 while ( groupIt->more() )
11950 SMESH_Group * group = groupIt->next();
11951 if ( !group ) continue;
11952 groupDS = group->GetGroupDS();
11953 if ( !groupDS || groupDS->IsEmpty() ) continue;
11954 std::string grpName = group->GetName();
11955 //MESSAGE("grpName=" << grpName);
11956 if (grpName == groupName)
11962 bool isNodeGroup = false;
11963 bool isNodeCoords = false;
11966 if (groupDS->GetType() != SMDSAbs_Node)
11968 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11971 if (nodesCoords.size() > 0)
11972 isNodeCoords = true; // a list o nodes given by their coordinates
11973 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11975 // --- define groups to build
11977 int idg; // --- group of SMDS volumes
11978 string grpvName = groupName;
11979 grpvName += "_vol";
11980 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11983 MESSAGE("group not created " << grpvName);
11986 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11988 int idgs; // --- group of SMDS faces on the skin
11989 string grpsName = groupName;
11990 grpsName += "_skin";
11991 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11994 MESSAGE("group not created " << grpsName);
11997 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11999 int idgi; // --- group of SMDS faces internal (several shapes)
12000 string grpiName = groupName;
12001 grpiName += "_internalFaces";
12002 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12005 MESSAGE("group not created " << grpiName);
12008 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12010 int idgei; // --- group of SMDS faces internal (several shapes)
12011 string grpeiName = groupName;
12012 grpeiName += "_internalEdges";
12013 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12016 MESSAGE("group not created " << grpeiName);
12019 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12021 // --- build downward connectivity
12023 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12024 meshDS->BuildDownWardConnectivity(true);
12025 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12027 // --- set of volumes detected inside
12029 std::set<int> setOfInsideVol;
12030 std::set<int> setOfVolToCheck;
12032 std::vector<gp_Pnt> gpnts;
12035 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12037 //MESSAGE("group of nodes provided");
12038 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12039 while ( elemIt->more() )
12041 const SMDS_MeshElement* elem = elemIt->next();
12044 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12047 SMDS_MeshElement* vol = 0;
12048 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12049 while (volItr->more())
12051 vol = (SMDS_MeshElement*)volItr->next();
12052 setOfInsideVol.insert(vol->getVtkId());
12053 sgrp->Add(vol->GetID());
12057 else if (isNodeCoords)
12059 //MESSAGE("list of nodes coordinates provided");
12062 while ( i < nodesCoords.size()-2 )
12064 double x = nodesCoords[i++];
12065 double y = nodesCoords[i++];
12066 double z = nodesCoords[i++];
12067 gp_Pnt p = gp_Pnt(x, y ,z);
12068 gpnts.push_back(p);
12069 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12073 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12075 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12076 TopTools_IndexedMapOfShape vertexMap;
12077 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12078 gp_Pnt p = gp_Pnt(0,0,0);
12079 if (vertexMap.Extent() < 1)
12082 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12084 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12085 p = BRep_Tool::Pnt(vertex);
12086 gpnts.push_back(p);
12087 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12091 if (gpnts.size() > 0)
12093 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12094 //MESSAGE("startNode->nodeId " << nodeId);
12096 double radius2 = radius*radius;
12097 //MESSAGE("radius2 " << radius2);
12099 // --- volumes on start node
12101 setOfVolToCheck.clear();
12102 SMDS_MeshElement* startVol = 0;
12103 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12104 while (volItr->more())
12106 startVol = (SMDS_MeshElement*)volItr->next();
12107 setOfVolToCheck.insert(startVol->getVtkId());
12109 if (setOfVolToCheck.empty())
12111 MESSAGE("No volumes found");
12115 // --- starting with central volumes then their neighbors, check if they are inside
12116 // or outside the domain, until no more new neighbor volume is inside.
12117 // Fill the group of inside volumes
12119 std::map<int, double> mapOfNodeDistance2;
12120 mapOfNodeDistance2.clear();
12121 std::set<int> setOfOutsideVol;
12122 while (!setOfVolToCheck.empty())
12124 std::set<int>::iterator it = setOfVolToCheck.begin();
12126 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12127 bool volInside = false;
12128 vtkIdType npts = 0;
12129 vtkIdType* pts = 0;
12130 grid->GetCellPoints(vtkId, npts, pts);
12131 for (int i=0; i<npts; i++)
12133 double distance2 = 0;
12134 if (mapOfNodeDistance2.count(pts[i]))
12136 distance2 = mapOfNodeDistance2[pts[i]];
12137 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12141 double *coords = grid->GetPoint(pts[i]);
12142 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12144 for ( size_t j = 0; j < gpnts.size(); j++ )
12146 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12147 if (d2 < distance2)
12150 if (distance2 < radius2)
12154 mapOfNodeDistance2[pts[i]] = distance2;
12155 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12157 if (distance2 < radius2)
12159 volInside = true; // one or more nodes inside the domain
12160 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12166 setOfInsideVol.insert(vtkId);
12167 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12168 int neighborsVtkIds[NBMAXNEIGHBORS];
12169 int downIds[NBMAXNEIGHBORS];
12170 unsigned char downTypes[NBMAXNEIGHBORS];
12171 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12172 for (int n = 0; n < nbNeighbors; n++)
12173 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12174 setOfVolToCheck.insert(neighborsVtkIds[n]);
12178 setOfOutsideVol.insert(vtkId);
12179 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12181 setOfVolToCheck.erase(vtkId);
12185 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12186 // If yes, add the volume to the inside set
12188 bool addedInside = true;
12189 std::set<int> setOfVolToReCheck;
12190 while (addedInside)
12192 //MESSAGE(" --------------------------- re check");
12193 addedInside = false;
12194 std::set<int>::iterator itv = setOfInsideVol.begin();
12195 for (; itv != setOfInsideVol.end(); ++itv)
12198 int neighborsVtkIds[NBMAXNEIGHBORS];
12199 int downIds[NBMAXNEIGHBORS];
12200 unsigned char downTypes[NBMAXNEIGHBORS];
12201 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12202 for (int n = 0; n < nbNeighbors; n++)
12203 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12204 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12206 setOfVolToCheck = setOfVolToReCheck;
12207 setOfVolToReCheck.clear();
12208 while (!setOfVolToCheck.empty())
12210 std::set<int>::iterator it = setOfVolToCheck.begin();
12212 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12214 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12215 int countInside = 0;
12216 int neighborsVtkIds[NBMAXNEIGHBORS];
12217 int downIds[NBMAXNEIGHBORS];
12218 unsigned char downTypes[NBMAXNEIGHBORS];
12219 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12220 for (int n = 0; n < nbNeighbors; n++)
12221 if (setOfInsideVol.count(neighborsVtkIds[n]))
12223 //MESSAGE("countInside " << countInside);
12224 if (countInside > 1)
12226 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12227 setOfInsideVol.insert(vtkId);
12228 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12229 addedInside = true;
12232 setOfVolToReCheck.insert(vtkId);
12234 setOfVolToCheck.erase(vtkId);
12238 // --- map of Downward faces at the boundary, inside the global volume
12239 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12240 // fill group of SMDS faces inside the volume (when several volume shapes)
12241 // fill group of SMDS faces on the skin of the global volume (if skin)
12243 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12244 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12245 std::set<int>::iterator it = setOfInsideVol.begin();
12246 for (; it != setOfInsideVol.end(); ++it)
12249 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12250 int neighborsVtkIds[NBMAXNEIGHBORS];
12251 int downIds[NBMAXNEIGHBORS];
12252 unsigned char downTypes[NBMAXNEIGHBORS];
12253 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12254 for (int n = 0; n < nbNeighbors; n++)
12256 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12257 if (neighborDim == 3)
12259 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12261 DownIdType face(downIds[n], downTypes[n]);
12262 boundaryFaces[face] = vtkId;
12264 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12265 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12266 if (vtkFaceId >= 0)
12268 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12269 // find also the smds edges on this face
12270 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12271 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12272 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12273 for (int i = 0; i < nbEdges; i++)
12275 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12276 if (vtkEdgeId >= 0)
12277 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12281 else if (neighborDim == 2) // skin of the volume
12283 DownIdType face(downIds[n], downTypes[n]);
12284 skinFaces[face] = vtkId;
12285 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12286 if (vtkFaceId >= 0)
12287 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12292 // --- identify the edges constituting the wire of each subshape on the skin
12293 // define polylines with the nodes of edges, equivalent to wires
12294 // project polylines on subshapes, and partition, to get geom faces
12296 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12297 std::set<int> emptySet;
12299 std::set<int> shapeIds;
12301 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12302 while (itelem->more())
12304 const SMDS_MeshElement *elem = itelem->next();
12305 int shapeId = elem->getshapeId();
12306 int vtkId = elem->getVtkId();
12307 if (!shapeIdToVtkIdSet.count(shapeId))
12309 shapeIdToVtkIdSet[shapeId] = emptySet;
12310 shapeIds.insert(shapeId);
12312 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12315 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12316 std::set<DownIdType, DownIdCompare> emptyEdges;
12317 emptyEdges.clear();
12319 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12320 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12322 int shapeId = itShape->first;
12323 //MESSAGE(" --- Shape ID --- "<< shapeId);
12324 shapeIdToEdges[shapeId] = emptyEdges;
12326 std::vector<int> nodesEdges;
12328 std::set<int>::iterator its = itShape->second.begin();
12329 for (; its != itShape->second.end(); ++its)
12332 //MESSAGE(" " << vtkId);
12333 int neighborsVtkIds[NBMAXNEIGHBORS];
12334 int downIds[NBMAXNEIGHBORS];
12335 unsigned char downTypes[NBMAXNEIGHBORS];
12336 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12337 for (int n = 0; n < nbNeighbors; n++)
12339 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12341 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12342 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12343 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12345 DownIdType edge(downIds[n], downTypes[n]);
12346 if (!shapeIdToEdges[shapeId].count(edge))
12348 shapeIdToEdges[shapeId].insert(edge);
12350 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12351 nodesEdges.push_back(vtkNodeId[0]);
12352 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12353 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12359 std::list<int> order;
12361 if (nodesEdges.size() > 0)
12363 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12364 nodesEdges[0] = -1;
12365 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12366 nodesEdges[1] = -1; // do not reuse this edge
12370 int nodeTofind = order.back(); // try first to push back
12372 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12373 if (nodesEdges[i] == nodeTofind)
12375 if ( i == (int) nodesEdges.size() )
12376 found = false; // no follower found on back
12379 if (i%2) // odd ==> use the previous one
12380 if (nodesEdges[i-1] < 0)
12384 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12385 nodesEdges[i-1] = -1;
12387 else // even ==> use the next one
12388 if (nodesEdges[i+1] < 0)
12392 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12393 nodesEdges[i+1] = -1;
12398 // try to push front
12400 nodeTofind = order.front(); // try to push front
12401 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12402 if ( nodesEdges[i] == nodeTofind )
12404 if ( i == (int)nodesEdges.size() )
12406 found = false; // no predecessor found on front
12409 if (i%2) // odd ==> use the previous one
12410 if (nodesEdges[i-1] < 0)
12414 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12415 nodesEdges[i-1] = -1;
12417 else // even ==> use the next one
12418 if (nodesEdges[i+1] < 0)
12422 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12423 nodesEdges[i+1] = -1;
12429 std::vector<int> nodes;
12430 nodes.push_back(shapeId);
12431 std::list<int>::iterator itl = order.begin();
12432 for (; itl != order.end(); itl++)
12434 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12435 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12437 listOfListOfNodes.push_back(nodes);
12440 // partition geom faces with blocFissure
12441 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12442 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12448 //================================================================================
12450 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12451 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12452 * \return TRUE if operation has been completed successfully, FALSE otherwise
12454 //================================================================================
12456 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12458 // iterates on volume elements and detect all free faces on them
12459 SMESHDS_Mesh* aMesh = GetMeshDS();
12463 ElemFeatures faceType( SMDSAbs_Face );
12464 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12465 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12468 const SMDS_MeshVolume* volume = vIt->next();
12469 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12470 vTool.SetExternalNormal();
12471 const int iQuad = volume->IsQuadratic();
12472 faceType.SetQuad( iQuad );
12473 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12475 if (!vTool.IsFreeFace(iface))
12478 vector<const SMDS_MeshNode *> nodes;
12479 int nbFaceNodes = vTool.NbFaceNodes(iface);
12480 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12482 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12483 nodes.push_back(faceNodes[inode]);
12485 if (iQuad) // add medium nodes
12487 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12488 nodes.push_back(faceNodes[inode]);
12489 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12490 nodes.push_back(faceNodes[8]);
12492 // add new face based on volume nodes
12493 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12495 nbExisted++; // face already exsist
12499 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12504 return ( nbFree == ( nbExisted + nbCreated ));
12509 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12511 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12513 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12516 //================================================================================
12518 * \brief Creates missing boundary elements
12519 * \param elements - elements whose boundary is to be checked
12520 * \param dimension - defines type of boundary elements to create
12521 * \param group - a group to store created boundary elements in
12522 * \param targetMesh - a mesh to store created boundary elements in
12523 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12524 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12525 * boundary elements will be copied into the targetMesh
12526 * \param toAddExistingBondary - if true, not only new but also pre-existing
12527 * boundary elements will be added into the new group
12528 * \param aroundElements - if true, elements will be created on boundary of given
12529 * elements else, on boundary of the whole mesh.
12530 * \return nb of added boundary elements
12532 //================================================================================
12534 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12535 Bnd_Dimension dimension,
12536 SMESH_Group* group/*=0*/,
12537 SMESH_Mesh* targetMesh/*=0*/,
12538 bool toCopyElements/*=false*/,
12539 bool toCopyExistingBoundary/*=false*/,
12540 bool toAddExistingBondary/*= false*/,
12541 bool aroundElements/*= false*/)
12543 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12544 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12545 // hope that all elements are of the same type, do not check them all
12546 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12547 throw SALOME_Exception(LOCALIZED("wrong element type"));
12550 toCopyElements = toCopyExistingBoundary = false;
12552 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12553 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12554 int nbAddedBnd = 0;
12556 // editor adding present bnd elements and optionally holding elements to add to the group
12557 SMESH_MeshEditor* presentEditor;
12558 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12559 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12561 SMESH_MesherHelper helper( *myMesh );
12562 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12563 SMDS_VolumeTool vTool;
12564 TIDSortedElemSet avoidSet;
12565 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12568 typedef vector<const SMDS_MeshNode*> TConnectivity;
12569 TConnectivity tgtNodes;
12570 ElemFeatures elemKind( missType ), elemToCopy;
12572 vector<const SMDS_MeshElement*> presentBndElems;
12573 vector<TConnectivity> missingBndElems;
12574 vector<int> freeFacets;
12575 TConnectivity nodes, elemNodes;
12577 SMDS_ElemIteratorPtr eIt;
12578 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12579 else eIt = elemSetIterator( elements );
12581 while (eIt->more())
12583 const SMDS_MeshElement* elem = eIt->next();
12584 const int iQuad = elem->IsQuadratic();
12585 elemKind.SetQuad( iQuad );
12587 // ------------------------------------------------------------------------------------
12588 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12589 // ------------------------------------------------------------------------------------
12590 presentBndElems.clear();
12591 missingBndElems.clear();
12592 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12593 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12595 const SMDS_MeshElement* otherVol = 0;
12596 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12598 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12599 ( !aroundElements || elements.count( otherVol )))
12601 freeFacets.push_back( iface );
12603 if ( missType == SMDSAbs_Face )
12604 vTool.SetExternalNormal();
12605 for ( size_t i = 0; i < freeFacets.size(); ++i )
12607 int iface = freeFacets[i];
12608 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12609 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12610 if ( missType == SMDSAbs_Edge ) // boundary edges
12612 nodes.resize( 2+iQuad );
12613 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12615 for ( size_t j = 0; j < nodes.size(); ++j )
12616 nodes[ j ] = nn[ i+j ];
12617 if ( const SMDS_MeshElement* edge =
12618 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12619 presentBndElems.push_back( edge );
12621 missingBndElems.push_back( nodes );
12624 else // boundary face
12627 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12628 nodes.push_back( nn[inode] ); // add corner nodes
12630 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12631 nodes.push_back( nn[inode] ); // add medium nodes
12632 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12634 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12636 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12637 SMDSAbs_Face, /*noMedium=*/false ))
12638 presentBndElems.push_back( f );
12640 missingBndElems.push_back( nodes );
12642 if ( targetMesh != myMesh )
12644 // add 1D elements on face boundary to be added to a new mesh
12645 const SMDS_MeshElement* edge;
12646 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12649 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12651 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12652 if ( edge && avoidSet.insert( edge ).second )
12653 presentBndElems.push_back( edge );
12659 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12661 avoidSet.clear(), avoidSet.insert( elem );
12662 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12663 SMDS_MeshElement::iterator() );
12664 elemNodes.push_back( elemNodes[0] );
12665 nodes.resize( 2 + iQuad );
12666 const int nbLinks = elem->NbCornerNodes();
12667 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12669 nodes[0] = elemNodes[iN];
12670 nodes[1] = elemNodes[iN+1+iQuad];
12671 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12672 continue; // not free link
12674 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12675 if ( const SMDS_MeshElement* edge =
12676 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12677 presentBndElems.push_back( edge );
12679 missingBndElems.push_back( nodes );
12683 // ---------------------------------
12684 // 2. Add missing boundary elements
12685 // ---------------------------------
12686 if ( targetMesh != myMesh )
12687 // instead of making a map of nodes in this mesh and targetMesh,
12688 // we create nodes with same IDs.
12689 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12691 TConnectivity& srcNodes = missingBndElems[i];
12692 tgtNodes.resize( srcNodes.size() );
12693 for ( inode = 0; inode < srcNodes.size(); ++inode )
12694 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12695 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12697 /*noMedium=*/false))
12699 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12703 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12705 TConnectivity& nodes = missingBndElems[ i ];
12706 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12708 /*noMedium=*/false))
12710 SMDS_MeshElement* newElem =
12711 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12712 nbAddedBnd += bool( newElem );
12714 // try to set a new element to a shape
12715 if ( myMesh->HasShapeToMesh() )
12718 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12719 const size_t nbN = nodes.size() / (iQuad+1 );
12720 for ( inode = 0; inode < nbN && ok; ++inode )
12722 pair<int, TopAbs_ShapeEnum> i_stype =
12723 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12724 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12725 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12727 if ( ok && mediumShapes.size() > 1 )
12729 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12730 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12731 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12733 if (( ok = ( stype_i->first != stype_i_0.first )))
12734 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12735 aMesh->IndexToShape( stype_i_0.second ));
12738 if ( ok && mediumShapes.begin()->first == missShapeType )
12739 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12743 // ----------------------------------
12744 // 3. Copy present boundary elements
12745 // ----------------------------------
12746 if ( toCopyExistingBoundary )
12747 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12749 const SMDS_MeshElement* e = presentBndElems[i];
12750 tgtNodes.resize( e->NbNodes() );
12751 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12752 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12753 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12755 else // store present elements to add them to a group
12756 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12758 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12761 } // loop on given elements
12763 // ---------------------------------------------
12764 // 4. Fill group with boundary elements
12765 // ---------------------------------------------
12768 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12769 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12770 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12772 tgtEditor.myLastCreatedElems.Clear();
12773 tgtEditor2.myLastCreatedElems.Clear();
12775 // -----------------------
12776 // 5. Copy given elements
12777 // -----------------------
12778 if ( toCopyElements && targetMesh != myMesh )
12780 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12781 else eIt = elemSetIterator( elements );
12782 while (eIt->more())
12784 const SMDS_MeshElement* elem = eIt->next();
12785 tgtNodes.resize( elem->NbNodes() );
12786 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12787 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12788 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12790 tgtEditor.myLastCreatedElems.Clear();
12796 //================================================================================
12798 * \brief Copy node position and set \a to node on the same geometry
12800 //================================================================================
12802 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12803 const SMDS_MeshNode* to )
12805 if ( !from || !to ) return;
12807 SMDS_PositionPtr pos = from->GetPosition();
12808 if ( !pos || from->getshapeId() < 1 ) return;
12810 switch ( pos->GetTypeOfPosition() )
12812 case SMDS_TOP_3DSPACE: break;
12814 case SMDS_TOP_FACE:
12816 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12817 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12818 fPos->GetUParameter(), fPos->GetVParameter() );
12821 case SMDS_TOP_EDGE:
12823 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12824 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12825 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12828 case SMDS_TOP_VERTEX:
12830 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12833 case SMDS_TOP_UNSPEC:
12838 namespace // utils for MakePolyLine
12840 //================================================================================
12842 * \brief Sequence of found points and a current point data
12846 std::vector< gp_XYZ > myPoints;
12849 int mySrcPntInd; //!< start point index
12850 const SMDS_MeshElement* myFace;
12851 SMESH_NodeXYZ myNode1;
12852 SMESH_NodeXYZ myNode2;
12857 TIDSortedElemSet myElemSet, myAvoidSet;
12859 Path(): myLength(0.0), myFace(0) {}
12861 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
12862 const SMDS_MeshElement* face,
12863 const gp_XYZ& plnNorm,
12864 const gp_XYZ& plnOrig );
12866 void AddPoint( const gp_XYZ& p );
12868 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
12870 bool ReachSamePoint( const Path& other );
12872 static void Remove( std::vector< Path > & paths, size_t& i );
12875 //================================================================================
12877 * \brief Return true if this Path meats another
12879 //================================================================================
12881 bool Path::ReachSamePoint( const Path& other )
12883 return ( mySrcPntInd != other.mySrcPntInd &&
12884 myFace == other.myFace );
12887 //================================================================================
12889 * \brief Remove a path from a vector
12891 //================================================================================
12893 void Path::Remove( std::vector< Path > & paths, size_t& i )
12895 if ( paths.size() > 1 )
12897 size_t j = paths.size() - 1; // last item to be removed
12900 paths[ i ].myPoints.swap( paths[ j ].myPoints );
12901 paths[ i ].myLength = paths[ j ].myLength;
12902 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
12903 paths[ i ].myFace = paths[ j ].myFace;
12904 paths[ i ].myNode1 = paths[ j ].myNode1;
12905 paths[ i ].myNode2 = paths[ j ].myNode2;
12906 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
12907 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
12908 paths[ i ].myDot1 = paths[ j ].myDot1;
12909 paths[ i ].myDot2 = paths[ j ].myDot2;
12917 //================================================================================
12919 * \brief Store a point that is at a node of a face if the face is intersected by plane.
12920 * Return false if the node is a sole intersection point of the face and the plane
12922 //================================================================================
12924 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
12925 const SMDS_MeshElement* face,
12926 const gp_XYZ& plnNorm,
12927 const gp_XYZ& plnOrig )
12929 if ( face == myFace )
12931 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
12932 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
12933 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
12934 myNode1.Set( face->GetNode( ind3 ));
12935 myNode2.Set( face->GetNode( myNodeInd2 ));
12937 myDot1 = plnNorm * ( myNode1 - plnOrig );
12938 myDot2 = plnNorm * ( myNode2 - plnOrig );
12940 bool ok = ( myDot1 * myDot2 < 0 );
12941 if ( !ok && myDot1 * myDot2 == 0 )
12943 ok = ( myDot1 != myDot2 );
12944 if ( ok && myFace )
12945 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
12951 AddPoint( cornerNode );
12956 //================================================================================
12958 * \brief Store a point and update myLength
12960 //================================================================================
12962 void Path::AddPoint( const gp_XYZ& p )
12964 if ( !myPoints.empty() )
12965 myLength += ( p - myPoints.back() ).Modulus();
12968 myPoints.push_back( p );
12971 //================================================================================
12973 * \brief Try to find the next point
12974 * \param [in] plnNorm - cutting plane normal
12975 * \param [in] plnOrig - cutting plane origin
12977 //================================================================================
12979 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
12981 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
12982 if ( myNodeInd2 == nodeInd3 )
12983 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
12985 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
12986 double dot3 = plnNorm * ( node3 - plnOrig );
12988 if ( dot3 * myDot1 < 0. )
12991 myNodeInd2 = nodeInd3;
12994 else if ( dot3 * myDot2 < 0. )
12997 myNodeInd1 = nodeInd3;
13000 else if ( dot3 == 0. )
13002 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13003 while ( fIt->more() )
13004 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13008 else if ( myDot2 == 0. )
13010 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13011 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13012 while ( fIt->more() )
13013 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13018 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13019 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13021 myAvoidSet.clear();
13022 myAvoidSet.insert( myFace );
13023 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13024 myElemSet, myAvoidSet,
13025 &myNodeInd1, &myNodeInd2 );
13029 //================================================================================
13031 * \brief Compute a path between two points of PolySegment
13033 struct PolyPathCompute
13035 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13036 std::vector< Path >& myPaths; //!< path of each of segments to compute
13037 SMESH_Mesh* myMesh;
13038 mutable std::vector< std::string > myErrors;
13040 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13041 std::vector< Path >& thePaths,
13042 SMESH_Mesh* theMesh):
13043 mySegments( theSegments ),
13044 myPaths( thePaths ),
13046 myErrors( theSegments.size() )
13049 #undef SMESH_CAUGHT
13050 #define SMESH_CAUGHT myErrors[i] =
13051 void operator() ( const int i ) const
13054 const_cast< PolyPathCompute* >( this )->Compute( i );
13055 SMESH_CATCH( SMESH::returnError );
13057 #undef SMESH_CAUGHT
13058 //================================================================================
13060 * \brief Compute a path of a given segment
13062 //================================================================================
13064 void Compute( const int iSeg )
13066 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13068 // get a cutting plane
13070 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13071 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13072 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13073 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13075 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13076 gp_XYZ plnOrig = p2;
13078 // find paths connecting the 2 end points of polySeg
13080 std::vector< Path > paths; paths.reserve(10);
13082 // initialize paths
13084 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13087 path.mySrcPntInd = iP;
13088 size_t nbPaths = paths.size();
13090 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13092 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13093 polySeg.myNode2[ iP ],
13097 &path.myNodeInd2 )))
13099 path.myNode1.Set( polySeg.myNode1[ iP ]);
13100 path.myNode2.Set( polySeg.myNode2[ iP ]);
13101 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13102 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13103 path.myPoints.clear();
13104 path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13105 path.myAvoidSet.insert( path.myFace );
13106 paths.push_back( path );
13108 if ( nbPaths == paths.size() )
13109 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13110 << " in a PolySegment " << iSeg );
13112 else // an end point is at node
13114 std::set<const SMDS_MeshNode* > nodes;
13115 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13116 while ( fIt->more() )
13118 path.myPoints.clear();
13119 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13121 if (( path.myDot1 * path.myDot2 != 0 ) ||
13122 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13123 paths.push_back( path );
13128 // look for a one-segment path
13129 for ( size_t i = 0; i < nbPaths; ++i )
13130 for ( size_t j = nbPaths; j < paths.size(); ++j )
13131 if ( paths[i].myFace == paths[j].myFace )
13133 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13134 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13141 myPaths[ iSeg ].myLength = 1e100;
13143 while ( paths.size() >= 2 )
13145 for ( size_t i = 0; i < paths.size(); ++i )
13147 Path& path = paths[ i ];
13148 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13149 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13151 Path::Remove( paths, i );
13155 // join paths that reach same point
13156 for ( size_t j = 0; j < paths.size(); ++j )
13158 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13160 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13161 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13162 if ( fullLength < myPaths[ iSeg ].myLength )
13164 myPaths[ iSeg ].myLength = fullLength;
13165 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13166 allPoints.swap( paths[i].myPoints );
13167 allPoints.insert( allPoints.end(),
13168 paths[j].myPoints.rbegin(),
13169 paths[j].myPoints.rend() );
13171 Path::Remove( paths, i );
13172 Path::Remove( paths, j );
13176 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13177 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13180 if ( myPaths[ iSeg ].myPoints.empty() )
13181 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13183 } // PolyPathCompute::Compute()
13185 }; // struct PolyPathCompute
13189 //=======================================================================
13190 //function : MakePolyLine
13191 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13192 // the initial mesh
13193 //=======================================================================
13195 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13196 SMESHDS_Group* theGroup,
13197 SMESH_ElementSearcher* theSearcher)
13199 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13201 SMESH_ElementSearcher* searcher = theSearcher;
13202 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13205 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13206 delSearcher._obj = searcher;
13209 // get cutting planes
13211 std::vector< bool > isVectorOK( theSegments.size(), true );
13212 const double planarCoef = 0.333; // plane height in planar case
13214 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13216 PolySegment& polySeg = theSegments[ iSeg ];
13218 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13219 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13220 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13221 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13223 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13225 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13226 if ( !isVectorOK[ iSeg ])
13228 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13229 const SMDS_MeshElement* face;
13230 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13231 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13234 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13236 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13237 polySeg.myVector * faceNorm < Precision::Confusion() )
13239 polySeg.myVector = faceNorm;
13240 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13245 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13249 // assure that inverse elements are constructed, avoid their concurrent building in threads
13250 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13254 PolyPathCompute algo( theSegments, segPaths, myMesh );
13255 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13257 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13258 if ( !algo.myErrors[ iSeg ].empty() )
13259 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13261 // create an 1D mesh
13263 const SMDS_MeshNode *n, *nPrev = 0;
13264 SMESHDS_Mesh* mesh = GetMeshDS();
13266 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13268 const Path& path = segPaths[iSeg];
13269 if ( path.myPoints.size() < 2 )
13272 double tol = path.myLength / path.myPoints.size() / 1000.;
13273 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13275 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13276 myLastCreatedNodes.Append( nPrev );
13278 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13280 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13281 myLastCreatedNodes.Append( n );
13283 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13284 myLastCreatedElems.Append( elem );
13286 theGroup->Add( elem );
13293 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13294 if ( isVectorOK[ iSeg ])
13296 // find the most distance point of a path
13297 double maxDist = 0;
13298 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13300 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13301 if ( dist > maxDist )
13304 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13307 if ( maxDist < Precision::Confusion() ) // planar case
13308 theSegments[iSeg].myMidProjPoint =
13309 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13311 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );