1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100 #include <OSD_Parallel.hxx>
102 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
104 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
107 using namespace SMESH::Controls;
111 template < class ELEM_SET >
112 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
114 typedef SMDS_SetIterator
115 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
116 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
120 //=======================================================================
121 //function : SMESH_MeshEditor
123 //=======================================================================
125 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
126 :myMesh( theMesh ) // theMesh may be NULL
130 //================================================================================
132 * \brief Return mesh DS
134 //================================================================================
136 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
138 return myMesh->GetMeshDS();
142 //================================================================================
144 * \brief Clears myLastCreatedNodes and myLastCreatedElems
146 //================================================================================
148 void SMESH_MeshEditor::ClearLastCreated()
150 myLastCreatedNodes.Clear();
151 myLastCreatedElems.Clear();
154 //================================================================================
156 * \brief Initializes members by an existing element
157 * \param [in] elem - the source element
158 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
160 //================================================================================
162 SMESH_MeshEditor::ElemFeatures&
163 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
167 myType = elem->GetType();
168 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
170 myIsPoly = elem->IsPoly();
173 myIsQuad = elem->IsQuadratic();
174 if ( myType == SMDSAbs_Volume && !basicOnly )
176 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
177 myPolyhedQuantities.swap( quant );
181 else if ( myType == SMDSAbs_Ball && !basicOnly )
183 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
189 //=======================================================================
193 //=======================================================================
196 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
197 const ElemFeatures& features)
199 SMDS_MeshElement* e = 0;
200 int nbnode = node.size();
201 SMESHDS_Mesh* mesh = GetMeshDS();
202 const int ID = features.myID;
204 switch ( features.myType ) {
206 if ( !features.myIsPoly ) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
209 else e = mesh->AddFace (node[0], node[1], node[2] );
211 else if (nbnode == 4) {
212 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
213 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
215 else if (nbnode == 6) {
216 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217 node[4], node[5], ID);
218 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
221 else if (nbnode == 7) {
222 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], ID);
224 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6] );
227 else if (nbnode == 8) {
228 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
229 node[4], node[5], node[6], node[7], ID);
230 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
231 node[4], node[5], node[6], node[7] );
233 else if (nbnode == 9) {
234 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
235 node[4], node[5], node[6], node[7], node[8], ID);
236 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
237 node[4], node[5], node[6], node[7], node[8] );
240 else if ( !features.myIsQuad )
242 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
243 else e = mesh->AddPolygonalFace (node );
245 else if ( nbnode % 2 == 0 ) // just a protection
247 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
248 else e = mesh->AddQuadPolygonalFace (node );
253 if ( !features.myIsPoly ) {
255 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
256 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
258 else if (nbnode == 5) {
259 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
264 else if (nbnode == 6) {
265 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266 node[4], node[5], ID);
267 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 else if (nbnode == 8) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7], ID);
273 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
274 node[4], node[5], node[6], node[7] );
276 else if (nbnode == 10) {
277 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
279 node[8], node[9], ID);
280 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
281 node[4], node[5], node[6], node[7],
284 else if (nbnode == 12) {
285 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
286 node[4], node[5], node[6], node[7],
287 node[8], node[9], node[10], node[11], ID);
288 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
289 node[4], node[5], node[6], node[7],
290 node[8], node[9], node[10], node[11] );
292 else if (nbnode == 13) {
293 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
297 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
298 node[4], node[5], node[6], node[7],
299 node[8], node[9], node[10],node[11],
302 else if (nbnode == 15) {
303 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
304 node[4], node[5], node[6], node[7],
305 node[8], node[9], node[10],node[11],
306 node[12],node[13],node[14],ID);
307 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
308 node[4], node[5], node[6], node[7],
309 node[8], node[9], node[10],node[11],
310 node[12],node[13],node[14] );
312 else if (nbnode == 20) {
313 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314 node[4], node[5], node[6], node[7],
315 node[8], node[9], node[10],node[11],
316 node[12],node[13],node[14],node[15],
317 node[16],node[17],node[18],node[19],ID);
318 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
319 node[4], node[5], node[6], node[7],
320 node[8], node[9], node[10],node[11],
321 node[12],node[13],node[14],node[15],
322 node[16],node[17],node[18],node[19] );
324 else if (nbnode == 27) {
325 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
326 node[4], node[5], node[6], node[7],
327 node[8], node[9], node[10],node[11],
328 node[12],node[13],node[14],node[15],
329 node[16],node[17],node[18],node[19],
330 node[20],node[21],node[22],node[23],
331 node[24],node[25],node[26], ID);
332 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
333 node[4], node[5], node[6], node[7],
334 node[8], node[9], node[10],node[11],
335 node[12],node[13],node[14],node[15],
336 node[16],node[17],node[18],node[19],
337 node[20],node[21],node[22],node[23],
338 node[24],node[25],node[26] );
341 else if ( !features.myIsQuad )
343 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
344 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
348 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
349 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
355 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
356 else e = mesh->AddEdge (node[0], node[1] );
358 else if ( nbnode == 3 ) {
359 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
360 else e = mesh->AddEdge (node[0], node[1], node[2] );
364 case SMDSAbs_0DElement:
366 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
367 else e = mesh->Add0DElement (node[0] );
372 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
373 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
377 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
378 else e = mesh->AddBall (node[0], features.myBallDiameter );
383 if ( e ) myLastCreatedElems.Append( e );
387 //=======================================================================
391 //=======================================================================
393 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
394 const ElemFeatures& features)
396 vector<const SMDS_MeshNode*> nodes;
397 nodes.reserve( nodeIDs.size() );
398 vector<int>::const_iterator id = nodeIDs.begin();
399 while ( id != nodeIDs.end() ) {
400 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
401 nodes.push_back( node );
405 return AddElement( nodes, features );
408 //=======================================================================
410 //purpose : Remove a node or an element.
411 // Modify a compute state of sub-meshes which become empty
412 //=======================================================================
414 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
417 myLastCreatedElems.Clear();
418 myLastCreatedNodes.Clear();
420 SMESHDS_Mesh* aMesh = GetMeshDS();
421 set< SMESH_subMesh *> smmap;
424 list<int>::const_iterator it = theIDs.begin();
425 for ( ; it != theIDs.end(); it++ ) {
426 const SMDS_MeshElement * elem;
428 elem = aMesh->FindNode( *it );
430 elem = aMesh->FindElement( *it );
434 // Notify VERTEX sub-meshes about modification
436 const SMDS_MeshNode* node = cast2Node( elem );
437 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
438 if ( int aShapeID = node->getshapeId() )
439 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
442 // Find sub-meshes to notify about modification
443 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
444 // while ( nodeIt->more() ) {
445 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
446 // const SMDS_PositionPtr& aPosition = node->GetPosition();
447 // if ( aPosition.get() ) {
448 // if ( int aShapeID = aPosition->GetShapeId() ) {
449 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
450 // smmap.insert( sm );
457 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
459 aMesh->RemoveElement( elem );
463 // Notify sub-meshes about modification
464 if ( !smmap.empty() ) {
465 set< SMESH_subMesh *>::iterator smIt;
466 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
467 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
470 // // Check if the whole mesh becomes empty
471 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
472 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
477 //================================================================================
479 * \brief Create 0D elements on all nodes of the given object.
480 * \param elements - Elements on whose nodes to create 0D elements; if empty,
481 * the all mesh is treated
482 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
483 * \param duplicateElements - to add one more 0D element to a node or not
485 //================================================================================
487 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
488 TIDSortedElemSet& all0DElems,
489 const bool duplicateElements )
491 SMDS_ElemIteratorPtr elemIt;
492 if ( elements.empty() )
494 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
498 elemIt = elemSetIterator( elements );
501 while ( elemIt->more() )
503 const SMDS_MeshElement* e = elemIt->next();
504 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
505 while ( nodeIt->more() )
507 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
508 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
509 if ( duplicateElements || !it0D->more() )
511 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
512 all0DElems.insert( myLastCreatedElems.Last() );
514 while ( it0D->more() )
515 all0DElems.insert( it0D->next() );
520 //=======================================================================
521 //function : FindShape
522 //purpose : Return an index of the shape theElem is on
523 // or zero if a shape not found
524 //=======================================================================
526 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
528 myLastCreatedElems.Clear();
529 myLastCreatedNodes.Clear();
531 SMESHDS_Mesh * aMesh = GetMeshDS();
532 if ( aMesh->ShapeToMesh().IsNull() )
535 int aShapeID = theElem->getshapeId();
539 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
540 if ( sm->Contains( theElem ))
543 if ( theElem->GetType() == SMDSAbs_Node ) {
544 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
547 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
550 TopoDS_Shape aShape; // the shape a node of theElem is on
551 if ( theElem->GetType() != SMDSAbs_Node )
553 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
554 while ( nodeIt->more() ) {
555 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
556 if ((aShapeID = node->getshapeId()) > 0) {
557 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
558 if ( sm->Contains( theElem ))
560 if ( aShape.IsNull() )
561 aShape = aMesh->IndexToShape( aShapeID );
567 // None of nodes is on a proper shape,
568 // find the shape among ancestors of aShape on which a node is
569 if ( !aShape.IsNull() ) {
570 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
571 for ( ; ancIt.More(); ancIt.Next() ) {
572 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
573 if ( sm && sm->Contains( theElem ))
574 return aMesh->ShapeToIndex( ancIt.Value() );
579 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
580 while ( const SMESHDS_SubMesh* sm = smIt->next() )
581 if ( sm->Contains( theElem ))
588 //=======================================================================
589 //function : IsMedium
591 //=======================================================================
593 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
594 const SMDSAbs_ElementType typeToCheck)
596 bool isMedium = false;
597 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
598 while (it->more() && !isMedium ) {
599 const SMDS_MeshElement* elem = it->next();
600 isMedium = elem->IsMediumNode(node);
605 //=======================================================================
606 //function : shiftNodesQuadTria
607 //purpose : Shift nodes in the array corresponded to quadratic triangle
608 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
609 //=======================================================================
611 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
613 const SMDS_MeshNode* nd1 = aNodes[0];
614 aNodes[0] = aNodes[1];
615 aNodes[1] = aNodes[2];
617 const SMDS_MeshNode* nd2 = aNodes[3];
618 aNodes[3] = aNodes[4];
619 aNodes[4] = aNodes[5];
623 //=======================================================================
624 //function : nbEdgeConnectivity
625 //purpose : return number of the edges connected with the theNode.
626 // if theEdges has connections with the other type of the
627 // elements, return -1
628 //=======================================================================
630 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
632 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
634 // while(elemIt->more()) {
639 return theNode->NbInverseElements();
642 //=======================================================================
643 //function : getNodesFromTwoTria
645 //=======================================================================
647 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
648 const SMDS_MeshElement * theTria2,
649 vector< const SMDS_MeshNode*>& N1,
650 vector< const SMDS_MeshNode*>& N2)
652 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
653 if ( N1.size() < 6 ) return false;
654 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
655 if ( N2.size() < 6 ) return false;
657 int sames[3] = {-1,-1,-1};
669 if(nbsames!=2) return false;
671 shiftNodesQuadTria(N1);
673 shiftNodesQuadTria(N1);
676 i = sames[0] + sames[1] + sames[2];
678 shiftNodesQuadTria(N2);
680 // now we receive following N1 and N2 (using numeration as in the image below)
681 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
682 // i.e. first nodes from both arrays form a new diagonal
686 //=======================================================================
687 //function : InverseDiag
688 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
689 // but having other common link.
690 // Return False if args are improper
691 //=======================================================================
693 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
694 const SMDS_MeshElement * theTria2 )
696 myLastCreatedElems.Clear();
697 myLastCreatedNodes.Clear();
699 if (!theTria1 || !theTria2)
702 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
703 if (!F1) return false;
704 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
705 if (!F2) return false;
706 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
707 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
709 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
710 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
714 // put nodes in array and find out indices of the same ones
715 const SMDS_MeshNode* aNodes [6];
716 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
718 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
719 while ( it->more() ) {
720 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
722 if ( i > 2 ) // theTria2
723 // find same node of theTria1
724 for ( int j = 0; j < 3; j++ )
725 if ( aNodes[ i ] == aNodes[ j ]) {
734 return false; // theTria1 is not a triangle
735 it = theTria2->nodesIterator();
737 if ( i == 6 && it->more() )
738 return false; // theTria2 is not a triangle
741 // find indices of 1,2 and of A,B in theTria1
742 int iA = -1, iB = 0, i1 = 0, i2 = 0;
743 for ( i = 0; i < 6; i++ ) {
744 if ( sameInd [ i ] == -1 ) {
749 if ( iA >= 0) iB = i;
753 // nodes 1 and 2 should not be the same
754 if ( aNodes[ i1 ] == aNodes[ i2 ] )
758 aNodes[ iA ] = aNodes[ i2 ];
760 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
762 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
763 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
767 } // end if(F1 && F2)
769 // check case of quadratic faces
770 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
771 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
773 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
774 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
778 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
779 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
787 vector< const SMDS_MeshNode* > N1;
788 vector< const SMDS_MeshNode* > N2;
789 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
791 // now we receive following N1 and N2 (using numeration as above image)
792 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
793 // i.e. first nodes from both arrays determ new diagonal
795 vector< const SMDS_MeshNode*> N1new( N1.size() );
796 vector< const SMDS_MeshNode*> N2new( N2.size() );
797 N1new.back() = N1.back(); // central node of biquadratic
798 N2new.back() = N2.back();
799 N1new[0] = N1[0]; N2new[0] = N1[0];
800 N1new[1] = N2[0]; N2new[1] = N1[1];
801 N1new[2] = N2[1]; N2new[2] = N2[0];
802 N1new[3] = N1[4]; N2new[3] = N1[3];
803 N1new[4] = N2[3]; N2new[4] = N2[5];
804 N1new[5] = N1[5]; N2new[5] = N1[4];
805 // change nodes in faces
806 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
807 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
809 // move the central node of biquadratic triangle
810 SMESH_MesherHelper helper( *GetMesh() );
811 for ( int is2nd = 0; is2nd < 2; ++is2nd )
813 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
814 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
815 if ( nodes.size() < 7 )
817 helper.SetSubShape( tria->getshapeId() );
818 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
822 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
823 SMESH_TNodeXYZ( nodes[4] ) +
824 SMESH_TNodeXYZ( nodes[5] )) / 3.;
829 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
830 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
831 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
833 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
834 xyz = S->Value( uv.X(), uv.Y() );
835 xyz.Transform( loc );
836 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
837 nodes[6]->getshapeId() > 0 )
838 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
840 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
845 //=======================================================================
846 //function : findTriangles
847 //purpose : find triangles sharing theNode1-theNode2 link
848 //=======================================================================
850 static bool findTriangles(const SMDS_MeshNode * theNode1,
851 const SMDS_MeshNode * theNode2,
852 const SMDS_MeshElement*& theTria1,
853 const SMDS_MeshElement*& theTria2)
855 if ( !theNode1 || !theNode2 ) return false;
857 theTria1 = theTria2 = 0;
859 set< const SMDS_MeshElement* > emap;
860 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
862 const SMDS_MeshElement* elem = it->next();
863 if ( elem->NbCornerNodes() == 3 )
866 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
868 const SMDS_MeshElement* elem = it->next();
869 if ( emap.count( elem )) {
877 // theTria1 must be element with minimum ID
878 if ( theTria2->GetID() < theTria1->GetID() )
879 std::swap( theTria2, theTria1 );
887 //=======================================================================
888 //function : InverseDiag
889 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
890 // with ones built on the same 4 nodes but having other common link.
891 // Return false if proper faces not found
892 //=======================================================================
894 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
895 const SMDS_MeshNode * theNode2)
897 myLastCreatedElems.Clear();
898 myLastCreatedNodes.Clear();
900 const SMDS_MeshElement *tr1, *tr2;
901 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
904 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
905 if (!F1) return false;
906 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
907 if (!F2) return false;
908 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
909 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
911 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
912 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
916 // put nodes in array
917 // and find indices of 1,2 and of A in tr1 and of B in tr2
918 int i, iA1 = 0, i1 = 0;
919 const SMDS_MeshNode* aNodes1 [3];
920 SMDS_ElemIteratorPtr it;
921 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
922 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
923 if ( aNodes1[ i ] == theNode1 )
924 iA1 = i; // node A in tr1
925 else if ( aNodes1[ i ] != theNode2 )
929 const SMDS_MeshNode* aNodes2 [3];
930 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
931 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
932 if ( aNodes2[ i ] == theNode2 )
933 iB2 = i; // node B in tr2
934 else if ( aNodes2[ i ] != theNode1 )
938 // nodes 1 and 2 should not be the same
939 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
943 aNodes1[ iA1 ] = aNodes2[ i2 ];
945 aNodes2[ iB2 ] = aNodes1[ i1 ];
947 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
948 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
953 // check case of quadratic faces
954 return InverseDiag(tr1,tr2);
957 //=======================================================================
958 //function : getQuadrangleNodes
959 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
960 // fusion of triangles tr1 and tr2 having shared link on
961 // theNode1 and theNode2
962 //=======================================================================
964 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
965 const SMDS_MeshNode * theNode1,
966 const SMDS_MeshNode * theNode2,
967 const SMDS_MeshElement * tr1,
968 const SMDS_MeshElement * tr2 )
970 if( tr1->NbNodes() != tr2->NbNodes() )
972 // find the 4-th node to insert into tr1
973 const SMDS_MeshNode* n4 = 0;
974 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
976 while ( !n4 && i<3 ) {
977 const SMDS_MeshNode * n = cast2Node( it->next() );
979 bool isDiag = ( n == theNode1 || n == theNode2 );
983 // Make an array of nodes to be in a quadrangle
984 int iNode = 0, iFirstDiag = -1;
985 it = tr1->nodesIterator();
988 const SMDS_MeshNode * n = cast2Node( it->next() );
990 bool isDiag = ( n == theNode1 || n == theNode2 );
992 if ( iFirstDiag < 0 )
994 else if ( iNode - iFirstDiag == 1 )
995 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
997 else if ( n == n4 ) {
998 return false; // tr1 and tr2 should not have all the same nodes
1000 theQuadNodes[ iNode++ ] = n;
1002 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1003 theQuadNodes[ iNode ] = n4;
1008 //=======================================================================
1009 //function : DeleteDiag
1010 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1011 // with a quadrangle built on the same 4 nodes.
1012 // Return false if proper faces not found
1013 //=======================================================================
1015 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1016 const SMDS_MeshNode * theNode2)
1018 myLastCreatedElems.Clear();
1019 myLastCreatedNodes.Clear();
1021 const SMDS_MeshElement *tr1, *tr2;
1022 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1025 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1026 if (!F1) return false;
1027 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1028 if (!F2) return false;
1029 SMESHDS_Mesh * aMesh = GetMeshDS();
1031 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1032 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1034 const SMDS_MeshNode* aNodes [ 4 ];
1035 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1038 const SMDS_MeshElement* newElem = 0;
1039 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1040 myLastCreatedElems.Append(newElem);
1041 AddToSameGroups( newElem, tr1, aMesh );
1042 int aShapeId = tr1->getshapeId();
1045 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1047 aMesh->RemoveElement( tr1 );
1048 aMesh->RemoveElement( tr2 );
1053 // check case of quadratic faces
1054 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1056 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1060 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1061 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1069 vector< const SMDS_MeshNode* > N1;
1070 vector< const SMDS_MeshNode* > N2;
1071 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1073 // now we receive following N1 and N2 (using numeration as above image)
1074 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1075 // i.e. first nodes from both arrays determ new diagonal
1077 const SMDS_MeshNode* aNodes[8];
1087 const SMDS_MeshElement* newElem = 0;
1088 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1089 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1090 myLastCreatedElems.Append(newElem);
1091 AddToSameGroups( newElem, tr1, aMesh );
1092 int aShapeId = tr1->getshapeId();
1095 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1097 aMesh->RemoveElement( tr1 );
1098 aMesh->RemoveElement( tr2 );
1100 // remove middle node (9)
1101 GetMeshDS()->RemoveNode( N1[4] );
1106 //=======================================================================
1107 //function : Reorient
1108 //purpose : Reverse theElement orientation
1109 //=======================================================================
1111 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1113 myLastCreatedElems.Clear();
1114 myLastCreatedNodes.Clear();
1118 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1119 if ( !it || !it->more() )
1122 const SMDSAbs_ElementType type = theElem->GetType();
1123 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1126 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1127 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1129 const SMDS_VtkVolume* aPolyedre =
1130 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1132 MESSAGE("Warning: bad volumic element");
1135 const int nbFaces = aPolyedre->NbFaces();
1136 vector<const SMDS_MeshNode *> poly_nodes;
1137 vector<int> quantities (nbFaces);
1139 // reverse each face of the polyedre
1140 for (int iface = 1; iface <= nbFaces; iface++) {
1141 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1142 quantities[iface - 1] = nbFaceNodes;
1144 for (inode = nbFaceNodes; inode >= 1; inode--) {
1145 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1146 poly_nodes.push_back(curNode);
1149 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1151 else // other elements
1153 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1154 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1155 if ( interlace.empty() )
1157 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1161 SMDS_MeshCell::applyInterlace( interlace, nodes );
1163 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1168 //================================================================================
1170 * \brief Reorient faces.
1171 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1172 * \param theDirection - desired direction of normal of \a theFace
1173 * \param theFace - one of \a theFaces that should be oriented according to
1174 * \a theDirection and whose orientation defines orientation of other faces
1175 * \return number of reoriented faces.
1177 //================================================================================
1179 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1180 const gp_Dir& theDirection,
1181 const SMDS_MeshElement * theFace)
1184 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1186 if ( theFaces.empty() )
1188 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1189 while ( fIt->more() )
1190 theFaces.insert( theFaces.end(), fIt->next() );
1193 // orient theFace according to theDirection
1195 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1196 if ( normal * theDirection.XYZ() < 0 )
1197 nbReori += Reorient( theFace );
1199 // Orient other faces
1201 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1202 TIDSortedElemSet avoidSet;
1203 set< SMESH_TLink > checkedLinks;
1204 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1206 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1207 theFaces.erase( theFace );
1208 startFaces.insert( theFace );
1210 int nodeInd1, nodeInd2;
1211 const SMDS_MeshElement* otherFace;
1212 vector< const SMDS_MeshElement* > facesNearLink;
1213 vector< std::pair< int, int > > nodeIndsOfFace;
1215 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1216 while ( !startFaces.empty() )
1218 startFace = startFaces.begin();
1219 theFace = *startFace;
1220 startFaces.erase( startFace );
1221 if ( !visitedFaces.insert( theFace ).second )
1225 avoidSet.insert(theFace);
1227 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1229 const int nbNodes = theFace->NbCornerNodes();
1230 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1232 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1233 linkIt_isNew = checkedLinks.insert( link );
1234 if ( !linkIt_isNew.second )
1236 // link has already been checked and won't be encountered more
1237 // if the group (theFaces) is manifold
1238 //checkedLinks.erase( linkIt_isNew.first );
1242 facesNearLink.clear();
1243 nodeIndsOfFace.clear();
1244 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1246 &nodeInd1, &nodeInd2 )))
1247 if ( otherFace != theFace)
1249 facesNearLink.push_back( otherFace );
1250 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1251 avoidSet.insert( otherFace );
1253 if ( facesNearLink.size() > 1 )
1255 // NON-MANIFOLD mesh shell !
1256 // select a face most co-directed with theFace,
1257 // other faces won't be visited this time
1259 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1260 double proj, maxProj = -1;
1261 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1262 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1263 if (( proj = Abs( NF * NOF )) > maxProj ) {
1265 otherFace = facesNearLink[i];
1266 nodeInd1 = nodeIndsOfFace[i].first;
1267 nodeInd2 = nodeIndsOfFace[i].second;
1270 // not to visit rejected faces
1271 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1272 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1273 visitedFaces.insert( facesNearLink[i] );
1275 else if ( facesNearLink.size() == 1 )
1277 otherFace = facesNearLink[0];
1278 nodeInd1 = nodeIndsOfFace.back().first;
1279 nodeInd2 = nodeIndsOfFace.back().second;
1281 if ( otherFace && otherFace != theFace)
1283 // link must be reverse in otherFace if orientation to otherFace
1284 // is same as that of theFace
1285 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1287 nbReori += Reorient( otherFace );
1289 startFaces.insert( otherFace );
1292 std::swap( link.first, link.second ); // reverse the link
1298 //================================================================================
1300 * \brief Reorient faces basing on orientation of adjacent volumes.
1301 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1302 * \param theVolumes - reference volumes.
1303 * \param theOutsideNormal - to orient faces to have their normal
1304 * pointing either \a outside or \a inside the adjacent volumes.
1305 * \return number of reoriented faces.
1307 //================================================================================
1309 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1310 TIDSortedElemSet & theVolumes,
1311 const bool theOutsideNormal)
1315 SMDS_ElemIteratorPtr faceIt;
1316 if ( theFaces.empty() )
1317 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1319 faceIt = elemSetIterator( theFaces );
1321 vector< const SMDS_MeshNode* > faceNodes;
1322 TIDSortedElemSet checkedVolumes;
1323 set< const SMDS_MeshNode* > faceNodesSet;
1324 SMDS_VolumeTool volumeTool;
1326 while ( faceIt->more() ) // loop on given faces
1328 const SMDS_MeshElement* face = faceIt->next();
1329 if ( face->GetType() != SMDSAbs_Face )
1332 const size_t nbCornersNodes = face->NbCornerNodes();
1333 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1335 checkedVolumes.clear();
1336 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1337 while ( vIt->more() )
1339 const SMDS_MeshElement* volume = vIt->next();
1341 if ( !checkedVolumes.insert( volume ).second )
1343 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1346 // is volume adjacent?
1347 bool allNodesCommon = true;
1348 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1349 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1350 if ( !allNodesCommon )
1353 // get nodes of a corresponding volume facet
1354 faceNodesSet.clear();
1355 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1356 volumeTool.Set( volume );
1357 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1358 if ( facetID < 0 ) continue;
1359 volumeTool.SetExternalNormal();
1360 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1362 // compare order of faceNodes and facetNodes
1363 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1365 for ( int i = 0; i < 2; ++i )
1367 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1368 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1369 if ( faceNodes[ iN ] == n )
1375 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1376 if ( isOutside != theOutsideNormal )
1377 nbReori += Reorient( face );
1379 } // loop on given faces
1384 //=======================================================================
1385 //function : getBadRate
1387 //=======================================================================
1389 static double getBadRate (const SMDS_MeshElement* theElem,
1390 SMESH::Controls::NumericalFunctorPtr& theCrit)
1392 SMESH::Controls::TSequenceOfXYZ P;
1393 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1395 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1396 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1399 //=======================================================================
1400 //function : QuadToTri
1401 //purpose : Cut quadrangles into triangles.
1402 // theCrit is used to select a diagonal to cut
1403 //=======================================================================
1405 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1406 SMESH::Controls::NumericalFunctorPtr theCrit)
1408 myLastCreatedElems.Clear();
1409 myLastCreatedNodes.Clear();
1411 if ( !theCrit.get() )
1414 SMESHDS_Mesh * aMesh = GetMeshDS();
1416 Handle(Geom_Surface) surface;
1417 SMESH_MesherHelper helper( *GetMesh() );
1419 TIDSortedElemSet::iterator itElem;
1420 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1422 const SMDS_MeshElement* elem = *itElem;
1423 if ( !elem || elem->GetType() != SMDSAbs_Face )
1425 if ( elem->NbCornerNodes() != 4 )
1428 // retrieve element nodes
1429 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1431 // compare two sets of possible triangles
1432 double aBadRate1, aBadRate2; // to what extent a set is bad
1433 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1434 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1435 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1437 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1438 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1439 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1441 const int aShapeId = FindShape( elem );
1442 const SMDS_MeshElement* newElem1 = 0;
1443 const SMDS_MeshElement* newElem2 = 0;
1445 if ( !elem->IsQuadratic() ) // split liner quadrangle
1447 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1448 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1449 if ( aBadRate1 <= aBadRate2 ) {
1450 // tr1 + tr2 is better
1451 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1452 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1455 // tr3 + tr4 is better
1456 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1457 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1460 else // split quadratic quadrangle
1462 helper.SetIsQuadratic( true );
1463 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1465 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1466 if ( aNodes.size() == 9 )
1468 helper.SetIsBiQuadratic( true );
1469 if ( aBadRate1 <= aBadRate2 )
1470 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1472 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1474 // create a new element
1475 if ( aBadRate1 <= aBadRate2 ) {
1476 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1477 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1480 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1481 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1485 // care of a new element
1487 myLastCreatedElems.Append(newElem1);
1488 myLastCreatedElems.Append(newElem2);
1489 AddToSameGroups( newElem1, elem, aMesh );
1490 AddToSameGroups( newElem2, elem, aMesh );
1492 // put a new triangle on the same shape
1494 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1495 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1497 aMesh->RemoveElement( elem );
1502 //=======================================================================
1504 * \brief Split each of given quadrangles into 4 triangles.
1505 * \param theElems - The faces to be split. If empty all faces are split.
1507 //=======================================================================
1509 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1511 myLastCreatedElems.Clear();
1512 myLastCreatedNodes.Clear();
1514 SMESH_MesherHelper helper( *GetMesh() );
1515 helper.SetElementsOnShape( true );
1517 SMDS_ElemIteratorPtr faceIt;
1518 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1519 else faceIt = elemSetIterator( theElems );
1522 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1524 vector< const SMDS_MeshNode* > nodes;
1525 SMESHDS_SubMesh* subMeshDS = 0;
1527 Handle(Geom_Surface) surface;
1528 TopLoc_Location loc;
1530 while ( faceIt->more() )
1532 const SMDS_MeshElement* quad = faceIt->next();
1533 if ( !quad || quad->NbCornerNodes() != 4 )
1536 // get a surface the quad is on
1538 if ( quad->getshapeId() < 1 )
1541 helper.SetSubShape( 0 );
1544 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1546 helper.SetSubShape( quad->getshapeId() );
1547 if ( !helper.GetSubShape().IsNull() &&
1548 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1550 F = TopoDS::Face( helper.GetSubShape() );
1551 surface = BRep_Tool::Surface( F, loc );
1552 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1556 helper.SetSubShape( 0 );
1561 // create a central node
1563 const SMDS_MeshNode* nCentral;
1564 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1566 if ( nodes.size() == 9 )
1568 nCentral = nodes.back();
1575 for ( ; iN < nodes.size(); ++iN )
1576 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1578 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1579 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1581 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1582 xyz[0], xyz[1], xyz[2], xyz[3],
1583 xyz[4], xyz[5], xyz[6], xyz[7] );
1587 for ( ; iN < nodes.size(); ++iN )
1588 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1590 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1591 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1593 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1594 uv[0], uv[1], uv[2], uv[3],
1595 uv[4], uv[5], uv[6], uv[7] );
1597 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1601 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1602 uv[8].X(), uv[8].Y() );
1603 myLastCreatedNodes.Append( nCentral );
1606 // create 4 triangles
1608 helper.SetIsQuadratic ( nodes.size() > 4 );
1609 helper.SetIsBiQuadratic( nodes.size() == 9 );
1610 if ( helper.GetIsQuadratic() )
1611 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1613 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1615 for ( int i = 0; i < 4; ++i )
1617 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1620 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1621 myLastCreatedElems.Append( tria );
1626 //=======================================================================
1627 //function : BestSplit
1628 //purpose : Find better diagonal for cutting.
1629 //=======================================================================
1631 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1632 SMESH::Controls::NumericalFunctorPtr theCrit)
1634 myLastCreatedElems.Clear();
1635 myLastCreatedNodes.Clear();
1640 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1643 if( theQuad->NbNodes()==4 ||
1644 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1646 // retrieve element nodes
1647 const SMDS_MeshNode* aNodes [4];
1648 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1650 //while (itN->more())
1652 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1654 // compare two sets of possible triangles
1655 double aBadRate1, aBadRate2; // to what extent a set is bad
1656 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1657 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1658 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1660 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1661 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1662 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1663 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1664 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1665 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1666 return 1; // diagonal 1-3
1668 return 2; // diagonal 2-4
1675 // Methods of splitting volumes into tetra
1677 const int theHexTo5_1[5*4+1] =
1679 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1681 const int theHexTo5_2[5*4+1] =
1683 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1685 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1687 const int theHexTo6_1[6*4+1] =
1689 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
1691 const int theHexTo6_2[6*4+1] =
1693 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
1695 const int theHexTo6_3[6*4+1] =
1697 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
1699 const int theHexTo6_4[6*4+1] =
1701 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
1703 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1705 const int thePyraTo2_1[2*4+1] =
1707 0, 1, 2, 4, 0, 2, 3, 4, -1
1709 const int thePyraTo2_2[2*4+1] =
1711 1, 2, 3, 4, 1, 3, 0, 4, -1
1713 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1715 const int thePentaTo3_1[3*4+1] =
1717 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1719 const int thePentaTo3_2[3*4+1] =
1721 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1723 const int thePentaTo3_3[3*4+1] =
1725 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1727 const int thePentaTo3_4[3*4+1] =
1729 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1731 const int thePentaTo3_5[3*4+1] =
1733 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1735 const int thePentaTo3_6[3*4+1] =
1737 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1739 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1740 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1742 // Methods of splitting hexahedron into prisms
1744 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1746 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
1748 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1750 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
1752 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1754 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
1757 const int theHexTo2Prisms_BT_1[6*2+1] =
1759 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1761 const int theHexTo2Prisms_BT_2[6*2+1] =
1763 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1765 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1767 const int theHexTo2Prisms_LR_1[6*2+1] =
1769 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1771 const int theHexTo2Prisms_LR_2[6*2+1] =
1773 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1775 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1777 const int theHexTo2Prisms_FB_1[6*2+1] =
1779 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1781 const int theHexTo2Prisms_FB_2[6*2+1] =
1783 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1785 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1788 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1791 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1792 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1793 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1794 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1800 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1801 bool _baryNode; //!< additional node is to be created at cell barycenter
1802 bool _ownConn; //!< to delete _connectivity in destructor
1803 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1805 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1806 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1807 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1808 bool hasFacet( const TTriangleFacet& facet ) const
1810 if ( _nbCorners == 4 )
1812 const int* tetConn = _connectivity;
1813 for ( ; tetConn[0] >= 0; tetConn += 4 )
1814 if (( facet.contains( tetConn[0] ) +
1815 facet.contains( tetConn[1] ) +
1816 facet.contains( tetConn[2] ) +
1817 facet.contains( tetConn[3] )) == 3 )
1820 else // prism, _nbCorners == 6
1822 const int* prismConn = _connectivity;
1823 for ( ; prismConn[0] >= 0; prismConn += 6 )
1825 if (( facet.contains( prismConn[0] ) &&
1826 facet.contains( prismConn[1] ) &&
1827 facet.contains( prismConn[2] ))
1829 ( facet.contains( prismConn[3] ) &&
1830 facet.contains( prismConn[4] ) &&
1831 facet.contains( prismConn[5] )))
1839 //=======================================================================
1841 * \brief return TSplitMethod for the given element to split into tetrahedra
1843 //=======================================================================
1845 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1847 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1849 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1850 // an edge and a face barycenter; tertaherdons are based on triangles and
1851 // a volume barycenter
1852 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1854 // Find out how adjacent volumes are split
1856 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1857 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1858 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1860 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1861 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1862 if ( nbNodes < 4 ) continue;
1864 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1865 const int* nInd = vol.GetFaceNodesIndices( iF );
1868 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1869 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1870 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1871 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1875 int iCom = 0; // common node of triangle faces to split into
1876 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1878 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1879 nInd[ iQ * ( (iCom+1)%nbNodes )],
1880 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1881 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1882 nInd[ iQ * ( (iCom+2)%nbNodes )],
1883 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1884 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1886 triaSplits.push_back( t012 );
1887 triaSplits.push_back( t023 );
1892 if ( !triaSplits.empty() )
1893 hasAdjacentSplits = true;
1896 // Among variants of split method select one compliant with adjacent volumes
1898 TSplitMethod method;
1899 if ( !vol.Element()->IsPoly() && !is24TetMode )
1901 int nbVariants = 2, nbTet = 0;
1902 const int** connVariants = 0;
1903 switch ( vol.Element()->GetEntityType() )
1905 case SMDSEntity_Hexa:
1906 case SMDSEntity_Quad_Hexa:
1907 case SMDSEntity_TriQuad_Hexa:
1908 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1909 connVariants = theHexTo5, nbTet = 5;
1911 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1913 case SMDSEntity_Pyramid:
1914 case SMDSEntity_Quad_Pyramid:
1915 connVariants = thePyraTo2; nbTet = 2;
1917 case SMDSEntity_Penta:
1918 case SMDSEntity_Quad_Penta:
1919 case SMDSEntity_BiQuad_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 std::swap( isSingleNode[0], isSingleNode[1] );
4576 sames[0] = 1 - sames[0];
4577 iNotSameNode = 1 - iNotSameNode;
4582 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4584 iSameNode = sames[ nbSame-1 ];
4585 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4586 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4587 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4590 if ( baseType == SMDSEntity_Polygon )
4592 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4593 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4595 else if ( baseType == SMDSEntity_Quad_Polygon )
4597 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4598 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4601 // make new elements
4602 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4605 for ( iNode = 0; iNode < nbNodes; iNode++ )
4607 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4608 nextNod[ iNode ] = *itNN[ iNode ]++;
4611 SMDS_MeshElement* aNewElem = 0;
4612 /*if(!elem->IsPoly())*/ {
4613 switch ( baseType ) {
4615 case SMDSEntity_Node: { // sweep NODE
4616 if ( nbSame == 0 ) {
4617 if ( isSingleNode[0] )
4618 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4620 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4626 case SMDSEntity_Edge: { // sweep EDGE
4627 if ( nbDouble == 0 )
4629 if ( nbSame == 0 ) // ---> quadrangle
4630 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4631 nextNod[ 1 ], nextNod[ 0 ] );
4632 else // ---> triangle
4633 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4634 nextNod[ iNotSameNode ] );
4636 else // ---> polygon
4638 vector<const SMDS_MeshNode*> poly_nodes;
4639 poly_nodes.push_back( prevNod[0] );
4640 poly_nodes.push_back( prevNod[1] );
4641 if ( prevNod[1] != nextNod[1] )
4643 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4644 poly_nodes.push_back( nextNod[1] );
4646 if ( prevNod[0] != nextNod[0] )
4648 poly_nodes.push_back( nextNod[0] );
4649 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4651 switch ( poly_nodes.size() ) {
4653 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4656 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4657 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4660 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4665 case SMDSEntity_Triangle: // TRIANGLE --->
4667 if ( nbDouble > 0 ) break;
4668 if ( nbSame == 0 ) // ---> pentahedron
4669 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4670 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4672 else if ( nbSame == 1 ) // ---> pyramid
4673 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4674 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4675 nextNod[ iSameNode ]);
4677 else // 2 same nodes: ---> tetrahedron
4678 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4679 nextNod[ iNotSameNode ]);
4682 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4686 if ( nbDouble+nbSame == 2 )
4688 if(nbSame==0) { // ---> quadratic quadrangle
4689 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4690 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4692 else { //(nbSame==1) // ---> quadratic triangle
4694 return; // medium node on axis
4696 else if(sames[0]==0)
4697 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4698 prevNod[2], midlNod[1], nextNod[2] );
4700 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4701 prevNod[2], nextNod[2], midlNod[0]);
4704 else if ( nbDouble == 3 )
4706 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4707 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4708 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4715 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4716 if ( nbDouble > 0 ) break;
4718 if ( nbSame == 0 ) // ---> hexahedron
4719 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4720 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4722 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4723 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4724 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4725 nextNod[ iSameNode ]);
4726 newElems.push_back( aNewElem );
4727 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4728 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4729 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4731 else if ( nbSame == 2 ) { // ---> pentahedron
4732 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4733 // iBeforeSame is same too
4734 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4735 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4736 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4738 // iAfterSame is same too
4739 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4740 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4741 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4745 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4746 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4747 if ( nbDouble+nbSame != 3 ) break;
4749 // ---> pentahedron with 15 nodes
4750 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4751 nextNod[0], nextNod[1], nextNod[2],
4752 prevNod[3], prevNod[4], prevNod[5],
4753 nextNod[3], nextNod[4], nextNod[5],
4754 midlNod[0], midlNod[1], midlNod[2]);
4756 else if(nbSame==1) {
4757 // ---> 2d order pyramid of 13 nodes
4758 int apex = iSameNode;
4759 int i0 = ( apex + 1 ) % nbCorners;
4760 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4764 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4765 nextNod[i0], nextNod[i1], prevNod[apex],
4766 prevNod[i01], midlNod[i0],
4767 nextNod[i01], midlNod[i1],
4768 prevNod[i1a], prevNod[i0a],
4769 nextNod[i0a], nextNod[i1a]);
4771 else if(nbSame==2) {
4772 // ---> 2d order tetrahedron of 10 nodes
4773 int n1 = iNotSameNode;
4774 int n2 = ( n1 + 1 ) % nbCorners;
4775 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4779 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4780 prevNod[n12], prevNod[n23], prevNod[n31],
4781 midlNod[n1], nextNod[n12], nextNod[n31]);
4785 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4787 if ( nbDouble != 4 ) break;
4788 // ---> hexahedron with 20 nodes
4789 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4790 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4791 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4792 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4793 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4795 else if(nbSame==1) {
4796 // ---> pyramid + pentahedron - can not be created since it is needed
4797 // additional middle node at the center of face
4798 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4801 else if( nbSame == 2 ) {
4802 if ( nbDouble != 2 ) break;
4803 // ---> 2d order Pentahedron with 15 nodes
4805 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4806 // iBeforeSame is same too
4813 // iAfterSame is same too
4823 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4824 prevNod[n4], prevNod[n5], nextNod[n5],
4825 prevNod[n12], midlNod[n2], nextNod[n12],
4826 prevNod[n45], midlNod[n5], nextNod[n45],
4827 prevNod[n14], prevNod[n25], nextNod[n25]);
4831 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4833 if( nbSame == 0 && nbDouble == 9 ) {
4834 // ---> tri-quadratic hexahedron with 27 nodes
4835 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4836 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4837 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4838 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4839 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4840 prevNod[8], // bottom center
4841 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4842 nextNod[8], // top center
4843 midlNod[8]);// elem center
4851 case SMDSEntity_Polygon: { // sweep POLYGON
4853 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4854 // ---> hexagonal prism
4855 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4856 prevNod[3], prevNod[4], prevNod[5],
4857 nextNod[0], nextNod[1], nextNod[2],
4858 nextNod[3], nextNod[4], nextNod[5]);
4862 case SMDSEntity_Ball:
4867 } // switch ( baseType )
4870 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4872 if ( baseType != SMDSEntity_Polygon )
4874 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4875 SMDS_MeshCell::applyInterlace( ind, prevNod );
4876 SMDS_MeshCell::applyInterlace( ind, nextNod );
4877 SMDS_MeshCell::applyInterlace( ind, midlNod );
4878 SMDS_MeshCell::applyInterlace( ind, itNN );
4879 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4880 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4882 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4883 vector<int> quantities (nbNodes + 2);
4884 polyedre_nodes.clear();
4888 for (int inode = 0; inode < nbNodes; inode++)
4889 polyedre_nodes.push_back( prevNod[inode] );
4890 quantities.push_back( nbNodes );
4893 polyedre_nodes.push_back( nextNod[0] );
4894 for (int inode = nbNodes; inode-1; --inode )
4895 polyedre_nodes.push_back( nextNod[inode-1] );
4896 quantities.push_back( nbNodes );
4904 const int iQuad = elem->IsQuadratic();
4905 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4907 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4908 int inextface = (iface+1+iQuad) % nbNodes;
4909 int imid = (iface+1) % nbNodes;
4910 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4911 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4912 polyedre_nodes.push_back( prevNod[iface] ); // 1
4913 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4915 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4916 polyedre_nodes.push_back( nextNod[iface] ); // 2
4918 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4919 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4921 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4922 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4924 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4925 if ( nbFaceNodes > 2 )
4926 quantities.push_back( nbFaceNodes );
4927 else // degenerated face
4928 polyedre_nodes.resize( prevNbNodes );
4930 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4932 } // try to create a polyherdal prism
4935 newElems.push_back( aNewElem );
4936 myLastCreatedElems.Append(aNewElem);
4937 srcElements.Append( elem );
4940 // set new prev nodes
4941 for ( iNode = 0; iNode < nbNodes; iNode++ )
4942 prevNod[ iNode ] = nextNod[ iNode ];
4947 //=======================================================================
4949 * \brief Create 1D and 2D elements around swept elements
4950 * \param mapNewNodes - source nodes and ones generated from them
4951 * \param newElemsMap - source elements and ones generated from them
4952 * \param elemNewNodesMap - nodes generated from each node of each element
4953 * \param elemSet - all swept elements
4954 * \param nbSteps - number of sweeping steps
4955 * \param srcElements - to append elem for each generated element
4957 //=======================================================================
4959 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4960 TTElemOfElemListMap & newElemsMap,
4961 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4962 TIDSortedElemSet& elemSet,
4964 SMESH_SequenceOfElemPtr& srcElements)
4966 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4967 SMESHDS_Mesh* aMesh = GetMeshDS();
4969 // Find nodes belonging to only one initial element - sweep them into edges.
4971 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4972 for ( ; nList != mapNewNodes.end(); nList++ )
4974 const SMDS_MeshNode* node =
4975 static_cast<const SMDS_MeshNode*>( nList->first );
4976 if ( newElemsMap.count( node ))
4977 continue; // node was extruded into edge
4978 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4979 int nbInitElems = 0;
4980 const SMDS_MeshElement* el = 0;
4981 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4982 while ( eIt->more() && nbInitElems < 2 ) {
4983 const SMDS_MeshElement* e = eIt->next();
4984 SMDSAbs_ElementType type = e->GetType();
4985 if ( type == SMDSAbs_Volume ||
4989 if ( type > highType ) {
4996 if ( nbInitElems == 1 ) {
4997 bool NotCreateEdge = el && el->IsMediumNode(node);
4998 if(!NotCreateEdge) {
4999 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5000 list<const SMDS_MeshElement*> newEdges;
5001 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5006 // Make a ceiling for each element ie an equal element of last new nodes.
5007 // Find free links of faces - make edges and sweep them into faces.
5009 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5011 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5012 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5013 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5015 const SMDS_MeshElement* elem = itElem->first;
5016 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5018 if(itElem->second.size()==0) continue;
5020 const bool isQuadratic = elem->IsQuadratic();
5022 if ( elem->GetType() == SMDSAbs_Edge ) {
5023 // create a ceiling edge
5024 if ( !isQuadratic ) {
5025 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5026 vecNewNodes[ 1 ]->second.back())) {
5027 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5028 vecNewNodes[ 1 ]->second.back()));
5029 srcElements.Append( elem );
5033 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5034 vecNewNodes[ 1 ]->second.back(),
5035 vecNewNodes[ 2 ]->second.back())) {
5036 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5037 vecNewNodes[ 1 ]->second.back(),
5038 vecNewNodes[ 2 ]->second.back()));
5039 srcElements.Append( elem );
5043 if ( elem->GetType() != SMDSAbs_Face )
5046 bool hasFreeLinks = false;
5048 TIDSortedElemSet avoidSet;
5049 avoidSet.insert( elem );
5051 set<const SMDS_MeshNode*> aFaceLastNodes;
5052 int iNode, nbNodes = vecNewNodes.size();
5053 if ( !isQuadratic ) {
5054 // loop on the face nodes
5055 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5056 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5057 // look for free links of the face
5058 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5059 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5060 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5061 // check if a link n1-n2 is free
5062 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5063 hasFreeLinks = true;
5064 // make a new edge and a ceiling for a new edge
5065 const SMDS_MeshElement* edge;
5066 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5067 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5068 srcElements.Append( myLastCreatedElems.Last() );
5070 n1 = vecNewNodes[ iNode ]->second.back();
5071 n2 = vecNewNodes[ iNext ]->second.back();
5072 if ( !aMesh->FindEdge( n1, n2 )) {
5073 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5074 srcElements.Append( edge );
5079 else { // elem is quadratic face
5080 int nbn = nbNodes/2;
5081 for ( iNode = 0; iNode < nbn; iNode++ ) {
5082 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5083 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5084 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5085 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5086 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5087 // check if a link is free
5088 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5089 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5090 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5091 hasFreeLinks = true;
5092 // make an edge and a ceiling for a new edge
5094 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5095 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5096 srcElements.Append( elem );
5098 n1 = vecNewNodes[ iNode ]->second.back();
5099 n2 = vecNewNodes[ iNext ]->second.back();
5100 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5101 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5102 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5103 srcElements.Append( elem );
5107 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5108 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5112 // sweep free links into faces
5114 if ( hasFreeLinks ) {
5115 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5116 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5118 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5119 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5120 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5121 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5122 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5124 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5125 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5126 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5128 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5129 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5130 std::advance( v, volNb );
5131 // find indices of free faces of a volume and their source edges
5132 list< int > freeInd;
5133 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5134 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5135 int iF, nbF = vTool.NbFaces();
5136 for ( iF = 0; iF < nbF; iF ++ ) {
5137 if (vTool.IsFreeFace( iF ) &&
5138 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5139 initNodeSet != faceNodeSet) // except an initial face
5141 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5143 if ( faceNodeSet == initNodeSetNoCenter )
5145 freeInd.push_back( iF );
5146 // find source edge of a free face iF
5147 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5148 vector<const SMDS_MeshNode*>::iterator lastCommom;
5149 commonNodes.resize( nbNodes, 0 );
5150 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5151 initNodeSet.begin(), initNodeSet.end(),
5152 commonNodes.begin());
5153 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5154 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5156 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5158 if ( !srcEdges.back() )
5160 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5161 << iF << " of volume #" << vTool.ID() << endl;
5166 if ( freeInd.empty() )
5169 // create wall faces for all steps;
5170 // if such a face has been already created by sweep of edge,
5171 // assure that its orientation is OK
5172 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5174 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5175 vTool.SetExternalNormal();
5176 const int nextShift = vTool.IsForward() ? +1 : -1;
5177 list< int >::iterator ind = freeInd.begin();
5178 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5179 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5181 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5182 int nbn = vTool.NbFaceNodes( *ind );
5183 const SMDS_MeshElement * f = 0;
5184 if ( nbn == 3 ) ///// triangle
5186 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5188 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5190 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5192 nodes[ 1 + nextShift ] };
5194 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5196 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5200 else if ( nbn == 4 ) ///// quadrangle
5202 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5204 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5206 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5207 nodes[ 2 ], nodes[ 2+nextShift ] };
5209 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5211 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5212 newOrder[ 2 ], newOrder[ 3 ]));
5215 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5217 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5219 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5221 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5223 nodes[2 + 2*nextShift],
5224 nodes[3 - 2*nextShift],
5226 nodes[3 + 2*nextShift]};
5228 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5230 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5238 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5240 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5241 nodes[1], nodes[3], nodes[5], nodes[7] );
5243 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5245 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5246 nodes[4 - 2*nextShift],
5248 nodes[4 + 2*nextShift],
5250 nodes[5 - 2*nextShift],
5252 nodes[5 + 2*nextShift] };
5254 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5256 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5257 newOrder[ 2 ], newOrder[ 3 ],
5258 newOrder[ 4 ], newOrder[ 5 ],
5259 newOrder[ 6 ], newOrder[ 7 ]));
5262 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5264 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5265 SMDSAbs_Face, /*noMedium=*/false);
5267 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5269 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5270 nodes[4 - 2*nextShift],
5272 nodes[4 + 2*nextShift],
5274 nodes[5 - 2*nextShift],
5276 nodes[5 + 2*nextShift],
5279 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5281 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5282 newOrder[ 2 ], newOrder[ 3 ],
5283 newOrder[ 4 ], newOrder[ 5 ],
5284 newOrder[ 6 ], newOrder[ 7 ],
5288 else //////// polygon
5290 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5291 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5293 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5295 if ( !vTool.IsForward() )
5296 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5298 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5300 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5304 while ( srcElements.Length() < myLastCreatedElems.Length() )
5305 srcElements.Append( *srcEdge );
5307 } // loop on free faces
5309 // go to the next volume
5311 while ( iVol++ < nbVolumesByStep ) v++;
5314 } // loop on volumes of one step
5315 } // sweep free links into faces
5317 // Make a ceiling face with a normal external to a volume
5319 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5320 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5321 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5323 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5324 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5325 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5329 lastVol.SetExternalNormal();
5330 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5331 const int nbn = lastVol.NbFaceNodes( iF );
5332 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5333 if ( !hasFreeLinks ||
5334 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5336 const vector<int>& interlace =
5337 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5338 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5340 AddElement( nodeVec, anyFace.Init( elem ));
5342 while ( srcElements.Length() < myLastCreatedElems.Length() )
5343 srcElements.Append( elem );
5346 } // loop on swept elements
5349 //=======================================================================
5350 //function : RotationSweep
5352 //=======================================================================
5354 SMESH_MeshEditor::PGroupIDs
5355 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5356 const gp_Ax1& theAxis,
5357 const double theAngle,
5358 const int theNbSteps,
5359 const double theTol,
5360 const bool theMakeGroups,
5361 const bool theMakeWalls)
5363 myLastCreatedElems.Clear();
5364 myLastCreatedNodes.Clear();
5366 // source elements for each generated one
5367 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5370 aTrsf.SetRotation( theAxis, theAngle );
5372 aTrsf2.SetRotation( theAxis, theAngle/2. );
5374 gp_Lin aLine( theAxis );
5375 double aSqTol = theTol * theTol;
5377 SMESHDS_Mesh* aMesh = GetMeshDS();
5379 TNodeOfNodeListMap mapNewNodes;
5380 TElemOfVecOfNnlmiMap mapElemNewNodes;
5381 TTElemOfElemListMap newElemsMap;
5383 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5384 myMesh->NbFaces(ORDER_QUADRATIC) +
5385 myMesh->NbVolumes(ORDER_QUADRATIC) );
5386 // loop on theElemSets
5387 setElemsFirst( theElemSets );
5388 TIDSortedElemSet::iterator itElem;
5389 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5391 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5392 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5393 const SMDS_MeshElement* elem = *itElem;
5394 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5396 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5397 newNodesItVec.reserve( elem->NbNodes() );
5399 // loop on elem nodes
5400 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5401 while ( itN->more() )
5403 const SMDS_MeshNode* node = cast2Node( itN->next() );
5405 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5407 aXYZ.Coord( coord[0], coord[1], coord[2] );
5408 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5410 // check if a node has been already sweeped
5411 TNodeOfNodeListMapItr nIt =
5412 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5413 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5414 if ( listNewNodes.empty() )
5416 // check if we are to create medium nodes between corner ones
5417 bool needMediumNodes = false;
5418 if ( isQuadraticMesh )
5420 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5421 while (it->more() && !needMediumNodes )
5423 const SMDS_MeshElement* invElem = it->next();
5424 if ( invElem != elem && !theElems.count( invElem )) continue;
5425 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5426 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5427 needMediumNodes = true;
5432 const SMDS_MeshNode * newNode = node;
5433 for ( int i = 0; i < theNbSteps; i++ ) {
5435 if ( needMediumNodes ) // create a medium node
5437 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5438 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5439 myLastCreatedNodes.Append(newNode);
5440 srcNodes.Append( node );
5441 listNewNodes.push_back( newNode );
5442 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5445 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5447 // create a corner node
5448 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5449 myLastCreatedNodes.Append(newNode);
5450 srcNodes.Append( node );
5451 listNewNodes.push_back( newNode );
5454 listNewNodes.push_back( newNode );
5455 // if ( needMediumNodes )
5456 // listNewNodes.push_back( newNode );
5460 newNodesItVec.push_back( nIt );
5462 // make new elements
5463 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5468 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5470 PGroupIDs newGroupIDs;
5471 if ( theMakeGroups )
5472 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5477 //=======================================================================
5478 //function : ExtrusParam
5479 //purpose : standard construction
5480 //=======================================================================
5482 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5483 const int theNbSteps,
5484 const std::list<double>& theScales,
5485 const gp_XYZ* theBasePoint,
5487 const double theTolerance):
5489 myBaseP( Precision::Infinite(), 0, 0 ),
5490 myFlags( theFlags ),
5491 myTolerance( theTolerance ),
5492 myElemsToUse( NULL )
5494 mySteps = new TColStd_HSequenceOfReal;
5495 const double stepSize = theStep.Magnitude();
5496 for (int i=1; i<=theNbSteps; i++ )
5497 mySteps->Append( stepSize );
5499 int nbScales = theScales.size();
5502 if ( IsLinearVariation() && nbScales < theNbSteps )
5504 myScales.reserve( theNbSteps );
5505 std::list<double>::const_iterator scale = theScales.begin();
5506 double prevScale = 1.0;
5507 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5509 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5510 int stDelta = Max( 1, iStep - myScales.size());
5511 double scDelta = ( *scale - prevScale ) / stDelta;
5512 for ( int iStep = 0; iStep < stDelta; ++iStep )
5514 myScales.push_back( prevScale + scDelta );
5515 prevScale = myScales.back();
5522 myScales.assign( theScales.begin(), theScales.end() );
5527 myBaseP = *theBasePoint;
5530 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5531 ( theTolerance > 0 ))
5533 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5537 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5541 //=======================================================================
5542 //function : ExtrusParam
5543 //purpose : steps are given explicitly
5544 //=======================================================================
5546 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5547 Handle(TColStd_HSequenceOfReal) theSteps,
5549 const double theTolerance):
5551 mySteps( theSteps ),
5552 myFlags( theFlags ),
5553 myTolerance( theTolerance ),
5554 myElemsToUse( NULL )
5556 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5557 ( theTolerance > 0 ))
5559 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5563 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5567 //=======================================================================
5568 //function : ExtrusParam
5569 //purpose : for extrusion by normal
5570 //=======================================================================
5572 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5573 const int theNbSteps,
5577 mySteps( new TColStd_HSequenceOfReal ),
5578 myFlags( theFlags ),
5580 myElemsToUse( NULL )
5582 for (int i = 0; i < theNbSteps; i++ )
5583 mySteps->Append( theStepSize );
5587 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5591 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5595 //=======================================================================
5596 //function : ExtrusParam::SetElementsToUse
5597 //purpose : stores elements to use for extrusion by normal, depending on
5598 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5599 // define myBaseP for scaling
5600 //=======================================================================
5602 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5603 const TIDSortedElemSet& nodes )
5605 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5607 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5609 myBaseP.SetCoord( 0.,0.,0. );
5610 TIDSortedElemSet newNodes;
5612 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5613 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5615 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5616 TIDSortedElemSet::const_iterator itElem = elements.begin();
5617 for ( ; itElem != elements.end(); itElem++ )
5619 const SMDS_MeshElement* elem = *itElem;
5620 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5621 while ( itN->more() ) {
5622 const SMDS_MeshElement* node = itN->next();
5623 if ( newNodes.insert( node ).second )
5624 myBaseP += SMESH_TNodeXYZ( node );
5628 myBaseP /= newNodes.size();
5632 //=======================================================================
5633 //function : ExtrusParam::beginStepIter
5634 //purpose : prepare iteration on steps
5635 //=======================================================================
5637 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5639 myWithMediumNodes = withMediumNodes;
5643 //=======================================================================
5644 //function : ExtrusParam::moreSteps
5645 //purpose : are there more steps?
5646 //=======================================================================
5648 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5650 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5652 //=======================================================================
5653 //function : ExtrusParam::nextStep
5654 //purpose : returns the next step
5655 //=======================================================================
5657 double SMESH_MeshEditor::ExtrusParam::nextStep()
5660 if ( !myCurSteps.empty() )
5662 res = myCurSteps.back();
5663 myCurSteps.pop_back();
5665 else if ( myNextStep <= mySteps->Length() )
5667 myCurSteps.push_back( mySteps->Value( myNextStep ));
5669 if ( myWithMediumNodes )
5671 myCurSteps.back() /= 2.;
5672 myCurSteps.push_back( myCurSteps.back() );
5679 //=======================================================================
5680 //function : ExtrusParam::makeNodesByDir
5681 //purpose : create nodes for standard extrusion
5682 //=======================================================================
5684 int SMESH_MeshEditor::ExtrusParam::
5685 makeNodesByDir( SMESHDS_Mesh* mesh,
5686 const SMDS_MeshNode* srcNode,
5687 std::list<const SMDS_MeshNode*> & newNodes,
5688 const bool makeMediumNodes)
5690 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5693 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5695 p += myDir.XYZ() * nextStep();
5696 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5697 newNodes.push_back( newNode );
5700 if ( !myScales.empty() )
5702 if ( makeMediumNodes && myMediumScales.empty() )
5704 myMediumScales.resize( myScales.size() );
5705 double prevFactor = 1.;
5706 for ( size_t i = 0; i < myScales.size(); ++i )
5708 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5709 prevFactor = myScales[i];
5712 typedef std::vector<double>::iterator ScaleIt;
5713 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5715 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5717 gp_XYZ center = myBaseP;
5718 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5720 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5722 center += myDir.XYZ() * nextStep();
5724 iSc += int( makeMediumNodes );
5725 ScaleIt& scale = scales[ iSc % 2 ];
5727 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5728 xyz = ( *scale * ( xyz - center )) + center;
5729 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5737 //=======================================================================
5738 //function : ExtrusParam::makeNodesByDirAndSew
5739 //purpose : create nodes for standard extrusion with sewing
5740 //=======================================================================
5742 int SMESH_MeshEditor::ExtrusParam::
5743 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5744 const SMDS_MeshNode* srcNode,
5745 std::list<const SMDS_MeshNode*> & newNodes,
5746 const bool makeMediumNodes)
5748 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5751 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5753 P1 += myDir.XYZ() * nextStep();
5755 // try to search in sequence of existing nodes
5756 // if myNodes.Length()>0 we 'nave to use given sequence
5757 // else - use all nodes of mesh
5758 const SMDS_MeshNode * node = 0;
5759 if ( myNodes.Length() > 0 ) {
5761 for(i=1; i<=myNodes.Length(); i++) {
5762 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5763 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5765 node = myNodes.Value(i);
5771 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5772 while(itn->more()) {
5773 SMESH_TNodeXYZ P2( itn->next() );
5774 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5783 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5785 newNodes.push_back( node );
5792 //=======================================================================
5793 //function : ExtrusParam::makeNodesByNormal2D
5794 //purpose : create nodes for extrusion using normals of faces
5795 //=======================================================================
5797 int SMESH_MeshEditor::ExtrusParam::
5798 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5799 const SMDS_MeshNode* srcNode,
5800 std::list<const SMDS_MeshNode*> & newNodes,
5801 const bool makeMediumNodes)
5803 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5805 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5807 // get normals to faces sharing srcNode
5808 vector< gp_XYZ > norms, baryCenters;
5809 gp_XYZ norm, avgNorm( 0,0,0 );
5810 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5811 while ( faceIt->more() )
5813 const SMDS_MeshElement* face = faceIt->next();
5814 if ( myElemsToUse && !myElemsToUse->count( face ))
5816 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5818 norms.push_back( norm );
5820 if ( !alongAvgNorm )
5824 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5825 bc += SMESH_TNodeXYZ( nIt->next() );
5826 baryCenters.push_back( bc / nbN );
5831 if ( norms.empty() ) return 0;
5833 double normSize = avgNorm.Modulus();
5834 if ( normSize < std::numeric_limits<double>::min() )
5837 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5840 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5843 avgNorm /= normSize;
5846 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5849 double stepSize = nextStep();
5851 if ( norms.size() > 1 )
5853 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5855 // translate plane of a face
5856 baryCenters[ iF ] += norms[ iF ] * stepSize;
5858 // find point of intersection of the face plane located at baryCenters[ iF ]
5859 // and avgNorm located at pNew
5860 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5861 double dot = ( norms[ iF ] * avgNorm );
5862 if ( dot < std::numeric_limits<double>::min() )
5863 dot = stepSize * 1e-3;
5864 double step = -( norms[ iF ] * pNew + d ) / dot;
5865 pNew += step * avgNorm;
5870 pNew += stepSize * avgNorm;
5874 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5875 newNodes.push_back( newNode );
5880 //=======================================================================
5881 //function : ExtrusParam::makeNodesByNormal1D
5882 //purpose : create nodes for extrusion using normals of edges
5883 //=======================================================================
5885 int SMESH_MeshEditor::ExtrusParam::
5886 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5887 const SMDS_MeshNode* srcNode,
5888 std::list<const SMDS_MeshNode*> & newNodes,
5889 const bool makeMediumNodes)
5891 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5895 //=======================================================================
5896 //function : ExtrusionSweep
5898 //=======================================================================
5900 SMESH_MeshEditor::PGroupIDs
5901 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5902 const gp_Vec& theStep,
5903 const int theNbSteps,
5904 TTElemOfElemListMap& newElemsMap,
5906 const double theTolerance)
5908 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5909 return ExtrusionSweep( theElems, aParams, newElemsMap );
5913 //=======================================================================
5914 //function : ExtrusionSweep
5916 //=======================================================================
5918 SMESH_MeshEditor::PGroupIDs
5919 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5920 ExtrusParam& theParams,
5921 TTElemOfElemListMap& newElemsMap)
5923 myLastCreatedElems.Clear();
5924 myLastCreatedNodes.Clear();
5926 // source elements for each generated one
5927 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5929 setElemsFirst( theElemSets );
5930 const int nbSteps = theParams.NbSteps();
5931 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5933 TNodeOfNodeListMap mapNewNodes;
5934 TElemOfVecOfNnlmiMap mapElemNewNodes;
5936 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5937 myMesh->NbFaces(ORDER_QUADRATIC) +
5938 myMesh->NbVolumes(ORDER_QUADRATIC) );
5940 TIDSortedElemSet::iterator itElem;
5941 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5943 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5944 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5946 // check element type
5947 const SMDS_MeshElement* elem = *itElem;
5948 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5951 const size_t nbNodes = elem->NbNodes();
5952 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5953 newNodesItVec.reserve( nbNodes );
5955 // loop on elem nodes
5956 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5957 while ( itN->more() )
5959 // check if a node has been already sweeped
5960 const SMDS_MeshNode* node = cast2Node( itN->next() );
5961 TNodeOfNodeListMap::iterator nIt =
5962 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5963 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5964 if ( listNewNodes.empty() )
5968 // check if we are to create medium nodes between corner ones
5969 bool needMediumNodes = false;
5970 if ( isQuadraticMesh )
5972 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5973 while (it->more() && !needMediumNodes )
5975 const SMDS_MeshElement* invElem = it->next();
5976 if ( invElem != elem && !theElems.count( invElem )) continue;
5977 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5978 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5979 needMediumNodes = true;
5982 // create nodes for all steps
5983 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5985 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5986 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5988 myLastCreatedNodes.Append( *newNodesIt );
5989 srcNodes.Append( node );
5994 break; // newNodesItVec will be shorter than nbNodes
5997 newNodesItVec.push_back( nIt );
5999 // make new elements
6000 if ( newNodesItVec.size() == nbNodes )
6001 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6005 if ( theParams.ToMakeBoundary() ) {
6006 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6008 PGroupIDs newGroupIDs;
6009 if ( theParams.ToMakeGroups() )
6010 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6015 //=======================================================================
6016 //function : ExtrusionAlongTrack
6018 //=======================================================================
6019 SMESH_MeshEditor::Extrusion_Error
6020 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6021 SMESH_subMesh* theTrack,
6022 const SMDS_MeshNode* theN1,
6023 const bool theHasAngles,
6024 list<double>& theAngles,
6025 const bool theLinearVariation,
6026 const bool theHasRefPoint,
6027 const gp_Pnt& theRefPoint,
6028 const bool theMakeGroups)
6030 myLastCreatedElems.Clear();
6031 myLastCreatedNodes.Clear();
6034 std::list<double> aPrms;
6035 TIDSortedElemSet::iterator itElem;
6038 TopoDS_Edge aTrackEdge;
6039 TopoDS_Vertex aV1, aV2;
6041 SMDS_ElemIteratorPtr aItE;
6042 SMDS_NodeIteratorPtr aItN;
6043 SMDSAbs_ElementType aTypeE;
6045 TNodeOfNodeListMap mapNewNodes;
6048 aNbE = theElements[0].size() + theElements[1].size();
6051 return EXTR_NO_ELEMENTS;
6053 // 1.1 Track Pattern
6056 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6058 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6059 theHasAngles, theAngles, theLinearVariation,
6060 theHasRefPoint, theRefPoint, theMakeGroups );
6062 aItE = pSubMeshDS->GetElements();
6063 while ( aItE->more() ) {
6064 const SMDS_MeshElement* pE = aItE->next();
6065 aTypeE = pE->GetType();
6066 // Pattern must contain links only
6067 if ( aTypeE != SMDSAbs_Edge )
6068 return EXTR_PATH_NOT_EDGE;
6071 list<SMESH_MeshEditor_PathPoint> fullList;
6073 const TopoDS_Shape& aS = theTrack->GetSubShape();
6074 // Sub-shape for the Pattern must be an Edge or Wire
6075 if( aS.ShapeType() == TopAbs_EDGE ) {
6076 aTrackEdge = TopoDS::Edge( aS );
6077 // the Edge must not be degenerated
6078 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6079 return EXTR_BAD_PATH_SHAPE;
6080 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6081 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6082 const SMDS_MeshNode* aN1 = aItN->next();
6083 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6084 const SMDS_MeshNode* aN2 = aItN->next();
6085 // starting node must be aN1 or aN2
6086 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6087 return EXTR_BAD_STARTING_NODE;
6088 aItN = pSubMeshDS->GetNodes();
6089 while ( aItN->more() ) {
6090 const SMDS_MeshNode* pNode = aItN->next();
6091 const SMDS_EdgePosition* pEPos =
6092 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6093 double aT = pEPos->GetUParameter();
6094 aPrms.push_back( aT );
6096 //Extrusion_Error err =
6097 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6098 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6099 list< SMESH_subMesh* > LSM;
6100 TopTools_SequenceOfShape Edges;
6101 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6102 while(itSM->more()) {
6103 SMESH_subMesh* SM = itSM->next();
6105 const TopoDS_Shape& aS = SM->GetSubShape();
6108 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6109 int startNid = theN1->GetID();
6110 TColStd_MapOfInteger UsedNums;
6112 int NbEdges = Edges.Length();
6114 for(; i<=NbEdges; i++) {
6116 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6117 for(; itLSM!=LSM.end(); itLSM++) {
6119 if(UsedNums.Contains(k)) continue;
6120 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6121 SMESH_subMesh* locTrack = *itLSM;
6122 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6123 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6124 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6125 const SMDS_MeshNode* aN1 = aItN->next();
6126 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6127 const SMDS_MeshNode* aN2 = aItN->next();
6128 // starting node must be aN1 or aN2
6129 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6130 // 2. Collect parameters on the track edge
6132 aItN = locMeshDS->GetNodes();
6133 while ( aItN->more() ) {
6134 const SMDS_MeshNode* pNode = aItN->next();
6135 const SMDS_EdgePosition* pEPos =
6136 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6137 double aT = pEPos->GetUParameter();
6138 aPrms.push_back( aT );
6140 list<SMESH_MeshEditor_PathPoint> LPP;
6141 //Extrusion_Error err =
6142 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6143 LLPPs.push_back(LPP);
6145 // update startN for search following edge
6146 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6147 else startNid = aN1->GetID();
6151 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6152 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6153 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6154 for(; itPP!=firstList.end(); itPP++) {
6155 fullList.push_back( *itPP );
6157 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158 fullList.pop_back();
6160 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6162 itPP = currList.begin();
6163 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6164 gp_Dir D1 = PP1.Tangent();
6165 gp_Dir D2 = PP2.Tangent();
6166 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6167 (D1.Z()+D2.Z())/2 ) );
6168 PP1.SetTangent(Dnew);
6169 fullList.push_back(PP1);
6171 for(; itPP!=firstList.end(); itPP++) {
6172 fullList.push_back( *itPP );
6174 PP1 = fullList.back();
6175 fullList.pop_back();
6177 // if wire not closed
6178 fullList.push_back(PP1);
6182 return EXTR_BAD_PATH_SHAPE;
6185 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6186 theHasRefPoint, theRefPoint, theMakeGroups);
6190 //=======================================================================
6191 //function : ExtrusionAlongTrack
6193 //=======================================================================
6194 SMESH_MeshEditor::Extrusion_Error
6195 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6196 SMESH_Mesh* theTrack,
6197 const SMDS_MeshNode* theN1,
6198 const bool theHasAngles,
6199 list<double>& theAngles,
6200 const bool theLinearVariation,
6201 const bool theHasRefPoint,
6202 const gp_Pnt& theRefPoint,
6203 const bool theMakeGroups)
6205 myLastCreatedElems.Clear();
6206 myLastCreatedNodes.Clear();
6209 std::list<double> aPrms;
6210 TIDSortedElemSet::iterator itElem;
6213 TopoDS_Edge aTrackEdge;
6214 TopoDS_Vertex aV1, aV2;
6216 SMDS_ElemIteratorPtr aItE;
6217 SMDS_NodeIteratorPtr aItN;
6218 SMDSAbs_ElementType aTypeE;
6220 TNodeOfNodeListMap mapNewNodes;
6223 aNbE = theElements[0].size() + theElements[1].size();
6226 return EXTR_NO_ELEMENTS;
6228 // 1.1 Track Pattern
6231 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6233 aItE = pMeshDS->elementsIterator();
6234 while ( aItE->more() ) {
6235 const SMDS_MeshElement* pE = aItE->next();
6236 aTypeE = pE->GetType();
6237 // Pattern must contain links only
6238 if ( aTypeE != SMDSAbs_Edge )
6239 return EXTR_PATH_NOT_EDGE;
6242 list<SMESH_MeshEditor_PathPoint> fullList;
6244 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6246 if ( !theTrack->HasShapeToMesh() ) {
6247 //Mesh without shape
6248 const SMDS_MeshNode* currentNode = NULL;
6249 const SMDS_MeshNode* prevNode = theN1;
6250 std::vector<const SMDS_MeshNode*> aNodesList;
6251 aNodesList.push_back(theN1);
6252 int nbEdges = 0, conn=0;
6253 const SMDS_MeshElement* prevElem = NULL;
6254 const SMDS_MeshElement* currentElem = NULL;
6255 int totalNbEdges = theTrack->NbEdges();
6256 SMDS_ElemIteratorPtr nIt;
6259 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6260 return EXTR_BAD_STARTING_NODE;
6263 conn = nbEdgeConnectivity(theN1);
6265 return EXTR_PATH_NOT_EDGE;
6267 aItE = theN1->GetInverseElementIterator();
6268 prevElem = aItE->next();
6269 currentElem = prevElem;
6271 if(totalNbEdges == 1 ) {
6272 nIt = currentElem->nodesIterator();
6273 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6274 if(currentNode == prevNode)
6275 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6276 aNodesList.push_back(currentNode);
6278 nIt = currentElem->nodesIterator();
6279 while( nIt->more() ) {
6280 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6281 if(currentNode == prevNode)
6282 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6283 aNodesList.push_back(currentNode);
6285 //case of the closed mesh
6286 if(currentNode == theN1) {
6291 conn = nbEdgeConnectivity(currentNode);
6293 return EXTR_PATH_NOT_EDGE;
6294 }else if( conn == 1 && nbEdges > 0 ) {
6299 prevNode = currentNode;
6300 aItE = currentNode->GetInverseElementIterator();
6301 currentElem = aItE->next();
6302 if( currentElem == prevElem)
6303 currentElem = aItE->next();
6304 nIt = currentElem->nodesIterator();
6305 prevElem = currentElem;
6311 if(nbEdges != totalNbEdges)
6312 return EXTR_PATH_NOT_EDGE;
6314 TopTools_SequenceOfShape Edges;
6315 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6316 int startNid = theN1->GetID();
6317 for ( size_t i = 1; i < aNodesList.size(); i++ )
6319 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6320 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6321 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6322 list<SMESH_MeshEditor_PathPoint> LPP;
6324 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6325 LLPPs.push_back(LPP);
6326 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6327 else startNid = aNodesList[i-1]->GetID();
6330 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6331 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6332 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6333 for(; itPP!=firstList.end(); itPP++) {
6334 fullList.push_back( *itPP );
6337 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6338 SMESH_MeshEditor_PathPoint PP2;
6339 fullList.pop_back();
6341 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6342 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6343 itPP = currList.begin();
6344 PP2 = currList.front();
6345 gp_Dir D1 = PP1.Tangent();
6346 gp_Dir D2 = PP2.Tangent();
6347 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6348 PP1.SetTangent(Dnew);
6349 fullList.push_back(PP1);
6351 for(; itPP!=currList.end(); itPP++) {
6352 fullList.push_back( *itPP );
6354 PP1 = fullList.back();
6355 fullList.pop_back();
6357 fullList.push_back(PP1);
6359 } // Sub-shape for the Pattern must be an Edge or Wire
6360 else if ( aS.ShapeType() == TopAbs_EDGE )
6362 aTrackEdge = TopoDS::Edge( aS );
6363 // the Edge must not be degenerated
6364 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6365 return EXTR_BAD_PATH_SHAPE;
6366 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6367 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6368 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6369 // starting node must be aN1 or aN2
6370 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6371 return EXTR_BAD_STARTING_NODE;
6372 aItN = pMeshDS->nodesIterator();
6373 while ( aItN->more() ) {
6374 const SMDS_MeshNode* pNode = aItN->next();
6375 if( pNode==aN1 || pNode==aN2 ) continue;
6376 const SMDS_EdgePosition* pEPos =
6377 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6378 double aT = pEPos->GetUParameter();
6379 aPrms.push_back( aT );
6381 //Extrusion_Error err =
6382 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6384 else if( aS.ShapeType() == TopAbs_WIRE ) {
6385 list< SMESH_subMesh* > LSM;
6386 TopTools_SequenceOfShape Edges;
6387 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6388 for(; eExp.More(); eExp.Next()) {
6389 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6390 if( SMESH_Algo::isDegenerated(E) ) continue;
6391 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6397 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6398 TopoDS_Vertex aVprev;
6399 TColStd_MapOfInteger UsedNums;
6400 int NbEdges = Edges.Length();
6402 for(; i<=NbEdges; i++) {
6404 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6405 for(; itLSM!=LSM.end(); itLSM++) {
6407 if(UsedNums.Contains(k)) continue;
6408 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6409 SMESH_subMesh* locTrack = *itLSM;
6410 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6411 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6412 bool aN1isOK = false, aN2isOK = false;
6413 if ( aVprev.IsNull() ) {
6414 // if previous vertex is not yet defined, it means that we in the beginning of wire
6415 // and we have to find initial vertex corresponding to starting node theN1
6416 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6417 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6418 // starting node must be aN1 or aN2
6419 aN1isOK = ( aN1 && aN1 == theN1 );
6420 aN2isOK = ( aN2 && aN2 == theN1 );
6423 // we have specified ending vertex of the previous edge on the previous iteration
6424 // and we have just to check that it corresponds to any vertex in current segment
6425 aN1isOK = aVprev.IsSame( aV1 );
6426 aN2isOK = aVprev.IsSame( aV2 );
6428 if ( !aN1isOK && !aN2isOK ) continue;
6429 // 2. Collect parameters on the track edge
6431 aItN = locMeshDS->GetNodes();
6432 while ( aItN->more() ) {
6433 const SMDS_MeshNode* pNode = aItN->next();
6434 const SMDS_EdgePosition* pEPos =
6435 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6436 double aT = pEPos->GetUParameter();
6437 aPrms.push_back( aT );
6439 list<SMESH_MeshEditor_PathPoint> LPP;
6440 //Extrusion_Error err =
6441 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6442 LLPPs.push_back(LPP);
6444 // update startN for search following edge
6445 if ( aN1isOK ) aVprev = aV2;
6450 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6451 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6452 fullList.splice( fullList.end(), firstList );
6454 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6455 fullList.pop_back();
6457 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6458 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6459 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6460 gp_Dir D1 = PP1.Tangent();
6461 gp_Dir D2 = PP2.Tangent();
6462 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6463 PP1.SetTangent(Dnew);
6464 fullList.push_back(PP1);
6465 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6466 PP1 = fullList.back();
6467 fullList.pop_back();
6469 // if wire not closed
6470 fullList.push_back(PP1);
6474 return EXTR_BAD_PATH_SHAPE;
6477 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6478 theHasRefPoint, theRefPoint, theMakeGroups);
6482 //=======================================================================
6483 //function : makeEdgePathPoints
6484 //purpose : auxiliary for ExtrusionAlongTrack
6485 //=======================================================================
6486 SMESH_MeshEditor::Extrusion_Error
6487 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6488 const TopoDS_Edge& aTrackEdge,
6490 list<SMESH_MeshEditor_PathPoint>& LPP)
6492 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6494 aTolVec2=aTolVec*aTolVec;
6496 TopoDS_Vertex aV1, aV2;
6497 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6498 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6499 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6500 // 2. Collect parameters on the track edge
6501 aPrms.push_front( aT1 );
6502 aPrms.push_back( aT2 );
6505 if( FirstIsStart ) {
6516 SMESH_MeshEditor_PathPoint aPP;
6517 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6518 std::list<double>::iterator aItD = aPrms.begin();
6519 for(; aItD != aPrms.end(); ++aItD) {
6523 aC3D->D1( aT, aP3D, aVec );
6524 aL2 = aVec.SquareMagnitude();
6525 if ( aL2 < aTolVec2 )
6526 return EXTR_CANT_GET_TANGENT;
6527 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6529 aPP.SetTangent( aTgt );
6530 aPP.SetParameter( aT );
6537 //=======================================================================
6538 //function : makeExtrElements
6539 //purpose : auxiliary for ExtrusionAlongTrack
6540 //=======================================================================
6541 SMESH_MeshEditor::Extrusion_Error
6542 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6543 list<SMESH_MeshEditor_PathPoint>& fullList,
6544 const bool theHasAngles,
6545 list<double>& theAngles,
6546 const bool theLinearVariation,
6547 const bool theHasRefPoint,
6548 const gp_Pnt& theRefPoint,
6549 const bool theMakeGroups)
6551 const int aNbTP = fullList.size();
6554 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6555 linearAngleVariation(aNbTP-1, theAngles);
6557 // fill vector of path points with angles
6558 vector<SMESH_MeshEditor_PathPoint> aPPs;
6559 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6560 list<double>::iterator itAngles = theAngles.begin();
6561 aPPs.push_back( *itPP++ );
6562 for( ; itPP != fullList.end(); itPP++) {
6563 aPPs.push_back( *itPP );
6564 if ( theHasAngles && itAngles != theAngles.end() )
6565 aPPs.back().SetAngle( *itAngles++ );
6568 TNodeOfNodeListMap mapNewNodes;
6569 TElemOfVecOfNnlmiMap mapElemNewNodes;
6570 TTElemOfElemListMap newElemsMap;
6571 TIDSortedElemSet::iterator itElem;
6572 // source elements for each generated one
6573 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6575 // 3. Center of rotation aV0
6576 gp_Pnt aV0 = theRefPoint;
6577 if ( !theHasRefPoint )
6579 gp_XYZ aGC( 0.,0.,0. );
6580 TIDSortedElemSet newNodes;
6582 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6584 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6585 itElem = theElements.begin();
6586 for ( ; itElem != theElements.end(); itElem++ )
6588 const SMDS_MeshElement* elem = *itElem;
6589 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6590 while ( itN->more() ) {
6591 const SMDS_MeshElement* node = itN->next();
6592 if ( newNodes.insert( node ).second )
6593 aGC += SMESH_TNodeXYZ( node );
6597 aGC /= newNodes.size();
6599 } // if (!theHasRefPoint) {
6601 // 4. Processing the elements
6602 SMESHDS_Mesh* aMesh = GetMeshDS();
6603 list<const SMDS_MeshNode*> emptyList;
6605 setElemsFirst( theElemSets );
6606 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6608 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6609 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6611 const SMDS_MeshElement* elem = *itElem;
6613 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6614 newNodesItVec.reserve( elem->NbNodes() );
6616 // loop on elem nodes
6618 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6619 while ( itN->more() )
6622 // check if a node has been already processed
6623 const SMDS_MeshNode* node = cast2Node( itN->next() );
6624 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6625 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6626 if ( listNewNodes.empty() )
6629 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6630 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6631 gp_Ax1 anAx1, anAxT1T0;
6632 gp_Dir aDT1x, aDT0x, aDT1T0;
6637 aPN0 = SMESH_TNodeXYZ( node );
6639 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6641 aDT0x= aPP0.Tangent();
6643 for ( int j = 1; j < aNbTP; ++j ) {
6644 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6646 aDT1x = aPP1.Tangent();
6647 aAngle1x = aPP1.Angle();
6649 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6651 gp_Vec aV01x( aP0x, aP1x );
6652 aTrsf.SetTranslation( aV01x );
6655 aV1x = aV0x.Transformed( aTrsf );
6656 aPN1 = aPN0.Transformed( aTrsf );
6658 // rotation 1 [ T1,T0 ]
6659 aAngleT1T0=-aDT1x.Angle( aDT0x );
6660 if (fabs(aAngleT1T0) > aTolAng)
6663 anAxT1T0.SetLocation( aV1x );
6664 anAxT1T0.SetDirection( aDT1T0 );
6665 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6667 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6671 if ( theHasAngles ) {
6672 anAx1.SetLocation( aV1x );
6673 anAx1.SetDirection( aDT1x );
6674 aTrsfRot.SetRotation( anAx1, aAngle1x );
6676 aPN1 = aPN1.Transformed( aTrsfRot );
6680 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6682 // create additional node
6683 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6684 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6685 myLastCreatedNodes.Append(newNode);
6686 srcNodes.Append( node );
6687 listNewNodes.push_back( newNode );
6689 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6690 myLastCreatedNodes.Append(newNode);
6691 srcNodes.Append( node );
6692 listNewNodes.push_back( newNode );
6700 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6702 // if current elem is quadratic and current node is not medium
6703 // we have to check - may be it is needed to insert additional nodes
6704 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6705 if ((int) listNewNodes.size() == aNbTP-1 )
6707 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6708 gp_XYZ P(node->X(), node->Y(), node->Z());
6709 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6711 for(i=0; i<aNbTP-1; i++) {
6712 const SMDS_MeshNode* N = *it;
6713 double x = ( N->X() + P.X() )/2.;
6714 double y = ( N->Y() + P.Y() )/2.;
6715 double z = ( N->Z() + P.Z() )/2.;
6716 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6717 srcNodes.Append( node );
6718 myLastCreatedNodes.Append(newN);
6721 P = gp_XYZ(N->X(),N->Y(),N->Z());
6723 listNewNodes.clear();
6724 for(i=0; i<2*(aNbTP-1); i++) {
6725 listNewNodes.push_back(aNodes[i]);
6730 newNodesItVec.push_back( nIt );
6733 // make new elements
6734 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6738 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6740 if ( theMakeGroups )
6741 generateGroups( srcNodes, srcElems, "extruded");
6747 //=======================================================================
6748 //function : linearAngleVariation
6749 //purpose : spread values over nbSteps
6750 //=======================================================================
6752 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6753 list<double>& Angles)
6755 int nbAngles = Angles.size();
6756 if( nbSteps > nbAngles && nbAngles > 0 )
6758 vector<double> theAngles(nbAngles);
6759 theAngles.assign( Angles.begin(), Angles.end() );
6762 double rAn2St = double( nbAngles ) / double( nbSteps );
6763 double angPrev = 0, angle;
6764 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6766 double angCur = rAn2St * ( iSt+1 );
6767 double angCurFloor = floor( angCur );
6768 double angPrevFloor = floor( angPrev );
6769 if ( angPrevFloor == angCurFloor )
6770 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6772 int iP = int( angPrevFloor );
6773 double angPrevCeil = ceil(angPrev);
6774 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6776 int iC = int( angCurFloor );
6777 if ( iC < nbAngles )
6778 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6780 iP = int( angPrevCeil );
6782 angle += theAngles[ iC ];
6784 res.push_back(angle);
6792 //================================================================================
6794 * \brief Move or copy theElements applying theTrsf to their nodes
6795 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6796 * \param theTrsf - transformation to apply
6797 * \param theCopy - if true, create translated copies of theElems
6798 * \param theMakeGroups - if true and theCopy, create translated groups
6799 * \param theTargetMesh - mesh to copy translated elements into
6800 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6802 //================================================================================
6804 SMESH_MeshEditor::PGroupIDs
6805 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6806 const gp_Trsf& theTrsf,
6808 const bool theMakeGroups,
6809 SMESH_Mesh* theTargetMesh)
6811 myLastCreatedElems.Clear();
6812 myLastCreatedNodes.Clear();
6814 bool needReverse = false;
6815 string groupPostfix;
6816 switch ( theTrsf.Form() ) {
6819 groupPostfix = "mirrored";
6822 groupPostfix = "mirrored";
6826 groupPostfix = "mirrored";
6829 groupPostfix = "rotated";
6831 case gp_Translation:
6832 groupPostfix = "translated";
6835 groupPostfix = "scaled";
6837 case gp_CompoundTrsf: // different scale by axis
6838 groupPostfix = "scaled";
6841 needReverse = false;
6842 groupPostfix = "transformed";
6845 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6846 SMESHDS_Mesh* aMesh = GetMeshDS();
6848 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6849 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6850 SMESH_MeshEditor::ElemFeatures elemType;
6852 // map old node to new one
6853 TNodeNodeMap nodeMap;
6855 // elements sharing moved nodes; those of them which have all
6856 // nodes mirrored but are not in theElems are to be reversed
6857 TIDSortedElemSet inverseElemSet;
6859 // source elements for each generated one
6860 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6862 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6863 TIDSortedElemSet orphanNode;
6865 if ( theElems.empty() ) // transform the whole mesh
6868 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6869 while ( eIt->more() ) theElems.insert( eIt->next() );
6871 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6872 while ( nIt->more() )
6874 const SMDS_MeshNode* node = nIt->next();
6875 if ( node->NbInverseElements() == 0)
6876 orphanNode.insert( node );
6880 // loop on elements to transform nodes : first orphan nodes then elems
6881 TIDSortedElemSet::iterator itElem;
6882 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6883 for (int i=0; i<2; i++)
6884 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6886 const SMDS_MeshElement* elem = *itElem;
6890 // loop on elem nodes
6892 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6893 while ( itN->more() )
6895 const SMDS_MeshNode* node = cast2Node( itN->next() );
6896 // check if a node has been already transformed
6897 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6898 nodeMap.insert( make_pair ( node, node ));
6899 if ( !n2n_isnew.second )
6902 node->GetXYZ( coord );
6903 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6904 if ( theTargetMesh ) {
6905 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6906 n2n_isnew.first->second = newNode;
6907 myLastCreatedNodes.Append(newNode);
6908 srcNodes.Append( node );
6910 else if ( theCopy ) {
6911 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6912 n2n_isnew.first->second = newNode;
6913 myLastCreatedNodes.Append(newNode);
6914 srcNodes.Append( node );
6917 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6918 // node position on shape becomes invalid
6919 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6920 ( SMDS_SpacePosition::originSpacePosition() );
6923 // keep inverse elements
6924 if ( !theCopy && !theTargetMesh && needReverse ) {
6925 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6926 while ( invElemIt->more() ) {
6927 const SMDS_MeshElement* iel = invElemIt->next();
6928 inverseElemSet.insert( iel );
6932 } // loop on elems in { &orphanNode, &theElems };
6934 // either create new elements or reverse mirrored ones
6935 if ( !theCopy && !needReverse && !theTargetMesh )
6938 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6940 // Replicate or reverse elements
6942 std::vector<int> iForw;
6943 vector<const SMDS_MeshNode*> nodes;
6944 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6946 const SMDS_MeshElement* elem = *itElem;
6947 if ( !elem ) continue;
6949 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6950 size_t nbNodes = elem->NbNodes();
6951 if ( geomType == SMDSGeom_NONE ) continue; // node
6953 nodes.resize( nbNodes );
6955 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6957 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6961 bool allTransformed = true;
6962 int nbFaces = aPolyedre->NbFaces();
6963 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6965 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6966 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6968 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6969 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6970 if ( nodeMapIt == nodeMap.end() )
6971 allTransformed = false; // not all nodes transformed
6973 nodes.push_back((*nodeMapIt).second);
6975 if ( needReverse && allTransformed )
6976 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6978 if ( !allTransformed )
6979 continue; // not all nodes transformed
6981 else // ----------------------- the rest element types
6983 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6984 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6985 const vector<int>& i = needReverse ? iRev : iForw;
6987 // find transformed nodes
6989 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6990 while ( itN->more() ) {
6991 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6992 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6993 if ( nodeMapIt == nodeMap.end() )
6994 break; // not all nodes transformed
6995 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6997 if ( iNode != nbNodes )
6998 continue; // not all nodes transformed
7002 // copy in this or a new mesh
7003 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7004 srcElems.Append( elem );
7007 // reverse element as it was reversed by transformation
7009 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7012 } // loop on elements
7014 if ( editor && editor != this )
7015 myLastCreatedElems = editor->myLastCreatedElems;
7017 PGroupIDs newGroupIDs;
7019 if ( ( theMakeGroups && theCopy ) ||
7020 ( theMakeGroups && theTargetMesh ) )
7021 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7026 //=======================================================================
7028 * \brief Create groups of elements made during transformation
7029 * \param nodeGens - nodes making corresponding myLastCreatedNodes
7030 * \param elemGens - elements making corresponding myLastCreatedElems
7031 * \param postfix - to append to names of new groups
7032 * \param targetMesh - mesh to create groups in
7033 * \param topPresent - is there "top" elements that are created by sweeping
7035 //=======================================================================
7037 SMESH_MeshEditor::PGroupIDs
7038 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7039 const SMESH_SequenceOfElemPtr& elemGens,
7040 const std::string& postfix,
7041 SMESH_Mesh* targetMesh,
7042 const bool topPresent)
7044 PGroupIDs newGroupIDs( new list<int> );
7045 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7047 // Sort existing groups by types and collect their names
7049 // containers to store an old group and generated new ones;
7050 // 1st new group is for result elems of different type than a source one;
7051 // 2nd new group is for same type result elems ("top" group at extrusion)
7053 using boost::make_tuple;
7054 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7055 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7056 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7058 set< string > groupNames;
7060 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7061 if ( !groupIt->more() ) return newGroupIDs;
7063 int newGroupID = mesh->GetGroupIds().back()+1;
7064 while ( groupIt->more() )
7066 SMESH_Group * group = groupIt->next();
7067 if ( !group ) continue;
7068 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7069 if ( !groupDS || groupDS->IsEmpty() ) continue;
7070 groupNames.insert ( group->GetName() );
7071 groupDS->SetStoreName( group->GetName() );
7072 const SMDSAbs_ElementType type = groupDS->GetType();
7073 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7074 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7075 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7076 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7079 // Loop on nodes and elements to add them in new groups
7081 vector< const SMDS_MeshElement* > resultElems;
7082 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7084 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7085 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7086 if ( gens.Length() != elems.Length() )
7087 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7089 // loop on created elements
7090 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7092 const SMDS_MeshElement* sourceElem = gens( iElem );
7093 if ( !sourceElem ) {
7094 MESSAGE("generateGroups(): NULL source element");
7097 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7098 if ( groupsOldNew.empty() ) { // no groups of this type at all
7099 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7100 ++iElem; // skip all elements made by sourceElem
7103 // collect all elements made by the iElem-th sourceElem
7104 resultElems.clear();
7105 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7106 if ( resElem != sourceElem )
7107 resultElems.push_back( resElem );
7108 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7109 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7110 if ( resElem != sourceElem )
7111 resultElems.push_back( resElem );
7113 const SMDS_MeshElement* topElem = 0;
7114 if ( isNodes ) // there must be a top element
7116 topElem = resultElems.back();
7117 resultElems.pop_back();
7121 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7122 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7123 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7125 topElem = *resElemIt;
7126 *resElemIt = 0; // erase *resElemIt
7130 // add resultElems to groups originted from ones the sourceElem belongs to
7131 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7132 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7134 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7135 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7137 // fill in a new group
7138 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7139 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7140 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7142 newGroup.Add( *resElemIt );
7144 // fill a "top" group
7147 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7148 newTopGroup.Add( topElem );
7152 } // loop on created elements
7153 }// loop on nodes and elements
7155 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7157 list<int> topGrouIds;
7158 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7160 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7161 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7162 orderedOldNewGroups[i]->get<2>() };
7163 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7165 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7166 if ( newGroupDS->IsEmpty() )
7168 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7173 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7176 const bool isTop = ( topPresent &&
7177 newGroupDS->GetType() == oldGroupDS->GetType() &&
7180 string name = oldGroupDS->GetStoreName();
7181 { // remove trailing whitespaces (issue 22599)
7182 size_t size = name.size();
7183 while ( size > 1 && isspace( name[ size-1 ]))
7185 if ( size != name.size() )
7187 name.resize( size );
7188 oldGroupDS->SetStoreName( name.c_str() );
7191 if ( !targetMesh ) {
7192 string suffix = ( isTop ? "top": postfix.c_str() );
7196 while ( !groupNames.insert( name ).second ) // name exists
7197 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7202 newGroupDS->SetStoreName( name.c_str() );
7204 // make a SMESH_Groups
7205 mesh->AddGroup( newGroupDS );
7207 topGrouIds.push_back( newGroupDS->GetID() );
7209 newGroupIDs->push_back( newGroupDS->GetID() );
7213 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7218 //================================================================================
7220 * * \brief Return list of group of nodes close to each other within theTolerance
7221 * * Search among theNodes or in the whole mesh if theNodes is empty using
7222 * * an Octree algorithm
7223 * \param [in,out] theNodes - the nodes to treat
7224 * \param [in] theTolerance - the tolerance
7225 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7226 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7227 * corner and medium nodes in separate groups
7229 //================================================================================
7231 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7232 const double theTolerance,
7233 TListOfListOfNodes & theGroupsOfNodes,
7234 bool theSeparateCornersAndMedium)
7236 myLastCreatedElems.Clear();
7237 myLastCreatedNodes.Clear();
7239 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7240 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7241 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7242 theSeparateCornersAndMedium = false;
7244 TIDSortedNodeSet& corners = theNodes;
7245 TIDSortedNodeSet medium;
7247 if ( theNodes.empty() ) // get all nodes in the mesh
7249 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7250 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7251 if ( theSeparateCornersAndMedium )
7252 while ( nIt->more() )
7254 const SMDS_MeshNode* n = nIt->next();
7255 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7256 nodeSet->insert( nodeSet->end(), n );
7259 while ( nIt->more() )
7260 theNodes.insert( theNodes.end(), nIt->next() );
7262 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7264 TIDSortedNodeSet::iterator nIt = corners.begin();
7265 while ( nIt != corners.end() )
7266 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7268 medium.insert( medium.end(), *nIt );
7269 corners.erase( nIt++ );
7277 if ( !corners.empty() )
7278 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7279 if ( !medium.empty() )
7280 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7283 //=======================================================================
7284 //function : SimplifyFace
7285 //purpose : split a chain of nodes into several closed chains
7286 //=======================================================================
7288 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7289 vector<const SMDS_MeshNode *>& poly_nodes,
7290 vector<int>& quantities) const
7292 int nbNodes = faceNodes.size();
7293 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7297 size_t prevNbQuant = quantities.size();
7299 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7300 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7301 map< const SMDS_MeshNode*, int >::iterator nInd;
7303 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7304 simpleNodes.push_back( faceNodes[0] );
7305 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7307 if ( faceNodes[ iCur ] != simpleNodes.back() )
7309 int index = simpleNodes.size();
7310 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7311 int prevIndex = nInd->second;
7312 if ( prevIndex < index )
7315 int loopLen = index - prevIndex;
7318 // store the sub-loop
7319 quantities.push_back( loopLen );
7320 for ( int i = prevIndex; i < index; i++ )
7321 poly_nodes.push_back( simpleNodes[ i ]);
7323 simpleNodes.resize( prevIndex+1 );
7327 simpleNodes.push_back( faceNodes[ iCur ]);
7332 if ( simpleNodes.size() > 2 )
7334 quantities.push_back( simpleNodes.size() );
7335 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7338 return quantities.size() - prevNbQuant;
7341 //=======================================================================
7342 //function : MergeNodes
7343 //purpose : In each group, the cdr of nodes are substituted by the first one
7345 //=======================================================================
7347 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7348 const bool theAvoidMakingHoles)
7350 myLastCreatedElems.Clear();
7351 myLastCreatedNodes.Clear();
7353 SMESHDS_Mesh* mesh = GetMeshDS();
7355 TNodeNodeMap nodeNodeMap; // node to replace - new node
7356 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7357 list< int > rmElemIds, rmNodeIds;
7358 vector< ElemFeatures > newElemDefs;
7360 // Fill nodeNodeMap and elems
7362 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7363 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7365 list<const SMDS_MeshNode*>& nodes = *grIt;
7366 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7367 const SMDS_MeshNode* nToKeep = *nIt;
7368 for ( ++nIt; nIt != nodes.end(); nIt++ )
7370 const SMDS_MeshNode* nToRemove = *nIt;
7371 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7372 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7373 while ( invElemIt->more() ) {
7374 const SMDS_MeshElement* elem = invElemIt->next();
7380 // Apply recursive replacements (BUG 0020185)
7381 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7382 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7384 const SMDS_MeshNode* nToKeep = nnIt->second;
7385 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7386 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7387 nToKeep = nnIt_i->second;
7388 nnIt->second = nToKeep;
7391 if ( theAvoidMakingHoles )
7393 // find elements whose topology changes
7395 vector<const SMDS_MeshElement*> pbElems;
7396 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7397 for ( ; eIt != elems.end(); ++eIt )
7399 const SMDS_MeshElement* elem = *eIt;
7400 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7401 while ( itN->more() )
7403 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7404 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7405 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7407 // several nodes of elem stick
7408 pbElems.push_back( elem );
7413 // exclude from merge nodes causing spoiling element
7414 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7416 bool nodesExcluded = false;
7417 for ( size_t i = 0; i < pbElems.size(); ++i )
7419 size_t prevNbMergeNodes = nodeNodeMap.size();
7420 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7421 prevNbMergeNodes < nodeNodeMap.size() )
7422 nodesExcluded = true;
7424 if ( !nodesExcluded )
7429 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7431 const SMDS_MeshNode* nToRemove = nnIt->first;
7432 const SMDS_MeshNode* nToKeep = nnIt->second;
7433 if ( nToRemove != nToKeep )
7435 rmNodeIds.push_back( nToRemove->GetID() );
7436 AddToSameGroups( nToKeep, nToRemove, mesh );
7437 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7438 // w/o creating node in place of merged ones.
7439 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7440 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7441 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7442 sm->SetIsAlwaysComputed( true );
7446 // Change element nodes or remove an element
7448 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7449 for ( ; eIt != elems.end(); eIt++ )
7451 const SMDS_MeshElement* elem = *eIt;
7452 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7454 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7456 rmElemIds.push_back( elem->GetID() );
7458 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7460 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7461 & newElemDefs[i].myNodes[0],
7462 newElemDefs[i].myNodes.size() ))
7466 newElemDefs[i].SetID( elem->GetID() );
7467 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7468 if ( !keepElem ) rmElemIds.pop_back();
7472 newElemDefs[i].SetID( -1 );
7474 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7475 if ( sm && newElem )
7476 sm->AddElement( newElem );
7477 if ( elem != newElem )
7478 ReplaceElemInGroups( elem, newElem, mesh );
7483 // Remove bad elements, then equal nodes (order important)
7484 Remove( rmElemIds, /*isNodes=*/false );
7485 Remove( rmNodeIds, /*isNodes=*/true );
7490 //=======================================================================
7491 //function : applyMerge
7492 //purpose : Compute new connectivity of an element after merging nodes
7493 // \param [in] elems - the element
7494 // \param [out] newElemDefs - definition(s) of result element(s)
7495 // \param [inout] nodeNodeMap - nodes to merge
7496 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7497 // after merging (but not degenerated), removes nodes causing
7498 // the invalidity from \a nodeNodeMap.
7499 // \return bool - true if the element should be removed
7500 //=======================================================================
7502 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7503 vector< ElemFeatures >& newElemDefs,
7504 TNodeNodeMap& nodeNodeMap,
7505 const bool avoidMakingHoles )
7507 bool toRemove = false; // to remove elem
7508 int nbResElems = 1; // nb new elements
7510 newElemDefs.resize(nbResElems);
7511 newElemDefs[0].Init( elem );
7512 newElemDefs[0].myNodes.clear();
7514 set<const SMDS_MeshNode*> nodeSet;
7515 vector< const SMDS_MeshNode*> curNodes;
7516 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7519 const int nbNodes = elem->NbNodes();
7520 SMDSAbs_EntityType entity = elem->GetEntityType();
7522 curNodes.resize( nbNodes );
7523 uniqueNodes.resize( nbNodes );
7524 iRepl.resize( nbNodes );
7525 int iUnique = 0, iCur = 0, nbRepl = 0;
7527 // Get new seq of nodes
7529 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7530 while ( itN->more() )
7532 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7534 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7535 if ( nnIt != nodeNodeMap.end() ) {
7538 curNodes[ iCur ] = n;
7539 bool isUnique = nodeSet.insert( n ).second;
7541 uniqueNodes[ iUnique++ ] = n;
7543 iRepl[ nbRepl++ ] = iCur;
7547 // Analyse element topology after replacement
7549 int nbUniqueNodes = nodeSet.size();
7550 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7555 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7557 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7558 int nbCorners = nbNodes / 2;
7559 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7561 int iNext = ( iCur + 1 ) % nbCorners;
7562 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7564 int iMedium = iCur + nbCorners;
7565 vector< const SMDS_MeshNode* >::iterator i =
7566 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7568 curNodes[ iMedium ]);
7569 if ( i != uniqueNodes.end() )
7572 for ( ; i+1 != uniqueNodes.end(); ++i )
7581 case SMDSEntity_Polygon:
7582 case SMDSEntity_Quad_Polygon: // Polygon
7584 ElemFeatures* elemType = & newElemDefs[0];
7585 const bool isQuad = elemType->myIsQuad;
7587 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7588 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7590 // a polygon can divide into several elements
7591 vector<const SMDS_MeshNode *> polygons_nodes;
7592 vector<int> quantities;
7593 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7594 newElemDefs.resize( nbResElems );
7595 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7597 ElemFeatures* elemType = & newElemDefs[iface];
7598 if ( iface ) elemType->Init( elem );
7600 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7601 int nbNewNodes = quantities[iface];
7602 face_nodes.assign( polygons_nodes.begin() + inode,
7603 polygons_nodes.begin() + inode + nbNewNodes );
7604 inode += nbNewNodes;
7605 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7607 bool isValid = ( nbNewNodes % 2 == 0 );
7608 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7609 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7610 elemType->SetQuad( isValid );
7611 if ( isValid ) // put medium nodes after corners
7612 SMDS_MeshCell::applyInterlaceRev
7613 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7614 nbNewNodes ), face_nodes );
7616 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7618 nbUniqueNodes = newElemDefs[0].myNodes.size();
7622 case SMDSEntity_Polyhedra: // Polyhedral volume
7624 if ( nbUniqueNodes >= 4 )
7626 // each face has to be analyzed in order to check volume validity
7627 if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7629 int nbFaces = aPolyedre->NbFaces();
7631 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7632 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7633 vector<const SMDS_MeshNode *> faceNodes;
7637 for (int iface = 1; iface <= nbFaces; iface++)
7639 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7640 faceNodes.resize( nbFaceNodes );
7641 for (int inode = 1; inode <= nbFaceNodes; inode++)
7643 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7644 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7645 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7646 faceNode = (*nnIt).second;
7647 faceNodes[inode - 1] = faceNode;
7649 SimplifyFace(faceNodes, poly_nodes, quantities);
7652 if ( quantities.size() > 3 )
7654 // TODO: remove coincident faces
7656 nbUniqueNodes = newElemDefs[0].myNodes.size();
7664 // TODO not all the possible cases are solved. Find something more generic?
7665 case SMDSEntity_Edge: //////// EDGE
7666 case SMDSEntity_Triangle: //// TRIANGLE
7667 case SMDSEntity_Quad_Triangle:
7668 case SMDSEntity_Tetra:
7669 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7673 case SMDSEntity_Quad_Edge:
7677 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7679 if ( nbUniqueNodes < 3 )
7681 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7682 toRemove = true; // opposite nodes stick
7687 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7696 if ( nbUniqueNodes == 6 &&
7698 ( nbRepl == 1 || iRepl[1] >= 4 ))
7704 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7713 if ( nbUniqueNodes == 7 &&
7715 ( nbRepl == 1 || iRepl[1] != 8 ))
7721 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7723 if ( nbUniqueNodes == 4 ) {
7724 // ---------------------------------> tetrahedron
7725 if ( curNodes[3] == curNodes[4] &&
7726 curNodes[3] == curNodes[5] ) {
7730 else if ( curNodes[0] == curNodes[1] &&
7731 curNodes[0] == curNodes[2] ) {
7732 // bottom nodes stick: set a top before
7733 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7734 uniqueNodes[ 0 ] = curNodes [ 5 ];
7735 uniqueNodes[ 1 ] = curNodes [ 4 ];
7736 uniqueNodes[ 2 ] = curNodes [ 3 ];
7739 else if (( curNodes[0] == curNodes[3] ) +
7740 ( curNodes[1] == curNodes[4] ) +
7741 ( curNodes[2] == curNodes[5] ) == 2 ) {
7742 // a lateral face turns into a line
7746 else if ( nbUniqueNodes == 5 ) {
7747 // PENTAHEDRON --------------------> pyramid
7748 if ( curNodes[0] == curNodes[3] )
7750 uniqueNodes[ 0 ] = curNodes[ 1 ];
7751 uniqueNodes[ 1 ] = curNodes[ 4 ];
7752 uniqueNodes[ 2 ] = curNodes[ 5 ];
7753 uniqueNodes[ 3 ] = curNodes[ 2 ];
7754 uniqueNodes[ 4 ] = curNodes[ 0 ];
7757 if ( curNodes[1] == curNodes[4] )
7759 uniqueNodes[ 0 ] = curNodes[ 0 ];
7760 uniqueNodes[ 1 ] = curNodes[ 2 ];
7761 uniqueNodes[ 2 ] = curNodes[ 5 ];
7762 uniqueNodes[ 3 ] = curNodes[ 3 ];
7763 uniqueNodes[ 4 ] = curNodes[ 1 ];
7766 if ( curNodes[2] == curNodes[5] )
7768 uniqueNodes[ 0 ] = curNodes[ 0 ];
7769 uniqueNodes[ 1 ] = curNodes[ 3 ];
7770 uniqueNodes[ 2 ] = curNodes[ 4 ];
7771 uniqueNodes[ 3 ] = curNodes[ 1 ];
7772 uniqueNodes[ 4 ] = curNodes[ 2 ];
7778 case SMDSEntity_Hexa:
7780 //////////////////////////////////// HEXAHEDRON
7781 SMDS_VolumeTool hexa (elem);
7782 hexa.SetExternalNormal();
7783 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7784 //////////////////////// HEX ---> tetrahedron
7785 for ( int iFace = 0; iFace < 6; iFace++ ) {
7786 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7787 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7788 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7789 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7790 // one face turns into a point ...
7791 int pickInd = ind[ 0 ];
7792 int iOppFace = hexa.GetOppFaceIndex( iFace );
7793 ind = hexa.GetFaceNodesIndices( iOppFace );
7795 uniqueNodes.clear();
7796 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7797 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7800 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7802 if ( nbStick == 1 ) {
7803 // ... and the opposite one - into a triangle.
7805 uniqueNodes.push_back( curNodes[ pickInd ]);
7812 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7813 //////////////////////// HEX ---> prism
7814 int nbTria = 0, iTria[3];
7815 const int *ind; // indices of face nodes
7816 // look for triangular faces
7817 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7818 ind = hexa.GetFaceNodesIndices( iFace );
7819 TIDSortedNodeSet faceNodes;
7820 for ( iCur = 0; iCur < 4; iCur++ )
7821 faceNodes.insert( curNodes[ind[iCur]] );
7822 if ( faceNodes.size() == 3 )
7823 iTria[ nbTria++ ] = iFace;
7825 // check if triangles are opposite
7826 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7828 // set nodes of the bottom triangle
7829 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7831 for ( iCur = 0; iCur < 4; iCur++ )
7832 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7833 indB.push_back( ind[iCur] );
7834 if ( !hexa.IsForward() )
7835 std::swap( indB[0], indB[2] );
7836 for ( iCur = 0; iCur < 3; iCur++ )
7837 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7838 // set nodes of the top triangle
7839 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7840 for ( iCur = 0; iCur < 3; ++iCur )
7841 for ( int j = 0; j < 4; ++j )
7842 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7844 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7851 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7852 //////////////////// HEXAHEDRON ---> pyramid
7853 for ( int iFace = 0; iFace < 6; iFace++ ) {
7854 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7855 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7856 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7857 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7858 // one face turns into a point ...
7859 int iOppFace = hexa.GetOppFaceIndex( iFace );
7860 ind = hexa.GetFaceNodesIndices( iOppFace );
7861 uniqueNodes.clear();
7862 for ( iCur = 0; iCur < 4; iCur++ ) {
7863 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7866 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7868 if ( uniqueNodes.size() == 4 ) {
7869 // ... and the opposite one is a quadrangle
7871 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7872 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7880 if ( toRemove && nbUniqueNodes > 4 ) {
7881 ////////////////// HEXAHEDRON ---> polyhedron
7882 hexa.SetExternalNormal();
7883 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7884 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7885 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7886 quantities.reserve( 6 ); quantities.clear();
7887 for ( int iFace = 0; iFace < 6; iFace++ )
7889 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7890 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7891 curNodes[ind[1]] == curNodes[ind[3]] )
7894 break; // opposite nodes stick
7897 for ( iCur = 0; iCur < 4; iCur++ )
7899 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7900 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7902 if ( nodeSet.size() < 3 )
7903 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7905 quantities.push_back( nodeSet.size() );
7907 if ( quantities.size() >= 4 )
7910 nbUniqueNodes = poly_nodes.size();
7911 newElemDefs[0].SetPoly(true);
7915 } // case HEXAHEDRON
7920 } // switch ( entity )
7922 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7924 // erase from nodeNodeMap nodes whose merge spoils elem
7925 vector< const SMDS_MeshNode* > noMergeNodes;
7926 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7927 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7928 nodeNodeMap.erase( noMergeNodes[i] );
7931 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7933 uniqueNodes.resize( nbUniqueNodes );
7935 if ( !toRemove && nbResElems == 0 )
7938 newElemDefs.resize( nbResElems );
7944 // ========================================================
7945 // class : SortableElement
7946 // purpose : allow sorting elements basing on their nodes
7947 // ========================================================
7948 class SortableElement : public set <const SMDS_MeshElement*>
7952 SortableElement( const SMDS_MeshElement* theElem )
7955 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7956 while ( nodeIt->more() )
7957 this->insert( nodeIt->next() );
7960 const SMDS_MeshElement* Get() const
7964 mutable const SMDS_MeshElement* myElem;
7967 //=======================================================================
7968 //function : FindEqualElements
7969 //purpose : Return list of group of elements built on the same nodes.
7970 // Search among theElements or in the whole mesh if theElements is empty
7971 //=======================================================================
7973 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7974 TListOfListOfElementsID & theGroupsOfElementsID)
7976 myLastCreatedElems.Clear();
7977 myLastCreatedNodes.Clear();
7979 typedef map< SortableElement, int > TMapOfNodeSet;
7980 typedef list<int> TGroupOfElems;
7982 if ( theElements.empty() )
7983 { // get all elements in the mesh
7984 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7985 while ( eIt->more() )
7986 theElements.insert( theElements.end(), eIt->next() );
7989 vector< TGroupOfElems > arrayOfGroups;
7990 TGroupOfElems groupOfElems;
7991 TMapOfNodeSet mapOfNodeSet;
7993 TIDSortedElemSet::iterator elemIt = theElements.begin();
7994 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7996 const SMDS_MeshElement* curElem = *elemIt;
7997 SortableElement SE(curElem);
7999 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8000 if ( !pp.second ) { // one more coincident elem
8001 TMapOfNodeSet::iterator& itSE = pp.first;
8002 int ind = (*itSE).second;
8003 arrayOfGroups[ind].push_back( curElem->GetID() );
8006 arrayOfGroups.push_back( groupOfElems );
8007 arrayOfGroups.back().push_back( curElem->GetID() );
8012 groupOfElems.clear();
8013 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8014 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8016 if ( groupIt->size() > 1 ) {
8017 //groupOfElems.sort(); -- theElements is sorted already
8018 theGroupsOfElementsID.push_back( groupOfElems );
8019 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8024 //=======================================================================
8025 //function : MergeElements
8026 //purpose : In each given group, substitute all elements by the first one.
8027 //=======================================================================
8029 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8031 myLastCreatedElems.Clear();
8032 myLastCreatedNodes.Clear();
8034 typedef list<int> TListOfIDs;
8035 TListOfIDs rmElemIds; // IDs of elems to remove
8037 SMESHDS_Mesh* aMesh = GetMeshDS();
8039 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8040 while ( groupsIt != theGroupsOfElementsID.end() ) {
8041 TListOfIDs& aGroupOfElemID = *groupsIt;
8042 aGroupOfElemID.sort();
8043 int elemIDToKeep = aGroupOfElemID.front();
8044 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8045 aGroupOfElemID.pop_front();
8046 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8047 while ( idIt != aGroupOfElemID.end() ) {
8048 int elemIDToRemove = *idIt;
8049 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8050 // add the kept element in groups of removed one (PAL15188)
8051 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8052 rmElemIds.push_back( elemIDToRemove );
8058 Remove( rmElemIds, false );
8061 //=======================================================================
8062 //function : MergeEqualElements
8063 //purpose : Remove all but one of elements built on the same nodes.
8064 //=======================================================================
8066 void SMESH_MeshEditor::MergeEqualElements()
8068 TIDSortedElemSet aMeshElements; /* empty input ==
8069 to merge equal elements in the whole mesh */
8070 TListOfListOfElementsID aGroupsOfElementsID;
8071 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8072 MergeElements(aGroupsOfElementsID);
8075 //=======================================================================
8076 //function : findAdjacentFace
8078 //=======================================================================
8080 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8081 const SMDS_MeshNode* n2,
8082 const SMDS_MeshElement* elem)
8084 TIDSortedElemSet elemSet, avoidSet;
8086 avoidSet.insert ( elem );
8087 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8090 //=======================================================================
8091 //function : findSegment
8092 //purpose : Return a mesh segment by two nodes one of which can be medium
8093 //=======================================================================
8095 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8096 const SMDS_MeshNode* n2)
8098 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8099 while ( it->more() )
8101 const SMDS_MeshElement* seg = it->next();
8102 if ( seg->GetNodeIndex( n2 ) >= 0 )
8108 //=======================================================================
8109 //function : FindFreeBorder
8111 //=======================================================================
8113 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8115 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8116 const SMDS_MeshNode* theSecondNode,
8117 const SMDS_MeshNode* theLastNode,
8118 list< const SMDS_MeshNode* > & theNodes,
8119 list< const SMDS_MeshElement* >& theFaces)
8121 if ( !theFirstNode || !theSecondNode )
8123 // find border face between theFirstNode and theSecondNode
8124 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8128 theFaces.push_back( curElem );
8129 theNodes.push_back( theFirstNode );
8130 theNodes.push_back( theSecondNode );
8132 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8133 TIDSortedElemSet foundElems;
8134 bool needTheLast = ( theLastNode != 0 );
8136 while ( nStart != theLastNode ) {
8137 if ( nStart == theFirstNode )
8138 return !needTheLast;
8140 // find all free border faces sharing form nStart
8142 list< const SMDS_MeshElement* > curElemList;
8143 list< const SMDS_MeshNode* > nStartList;
8144 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8145 while ( invElemIt->more() ) {
8146 const SMDS_MeshElement* e = invElemIt->next();
8147 if ( e == curElem || foundElems.insert( e ).second ) {
8149 int iNode = 0, nbNodes = e->NbNodes();
8150 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8152 if ( e->IsQuadratic() ) {
8153 const SMDS_VtkFace* F =
8154 dynamic_cast<const SMDS_VtkFace*>(e);
8155 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8156 // use special nodes iterator
8157 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8158 while( anIter->more() ) {
8159 nodes[ iNode++ ] = cast2Node(anIter->next());
8163 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8164 while ( nIt->more() )
8165 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8167 nodes[ iNode ] = nodes[ 0 ];
8169 for ( iNode = 0; iNode < nbNodes; iNode++ )
8170 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8171 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8172 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8174 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8175 curElemList.push_back( e );
8179 // analyse the found
8181 int nbNewBorders = curElemList.size();
8182 if ( nbNewBorders == 0 ) {
8183 // no free border furthermore
8184 return !needTheLast;
8186 else if ( nbNewBorders == 1 ) {
8187 // one more element found
8189 nStart = nStartList.front();
8190 curElem = curElemList.front();
8191 theFaces.push_back( curElem );
8192 theNodes.push_back( nStart );
8195 // several continuations found
8196 list< const SMDS_MeshElement* >::iterator curElemIt;
8197 list< const SMDS_MeshNode* >::iterator nStartIt;
8198 // check if one of them reached the last node
8199 if ( needTheLast ) {
8200 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8201 curElemIt!= curElemList.end();
8202 curElemIt++, nStartIt++ )
8203 if ( *nStartIt == theLastNode ) {
8204 theFaces.push_back( *curElemIt );
8205 theNodes.push_back( *nStartIt );
8209 // find the best free border by the continuations
8210 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8211 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8212 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8213 curElemIt!= curElemList.end();
8214 curElemIt++, nStartIt++ )
8216 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8217 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8218 // find one more free border
8219 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8223 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8224 // choice: clear a worse one
8225 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8226 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8227 contNodes[ iWorse ].clear();
8228 contFaces[ iWorse ].clear();
8231 if ( contNodes[0].empty() && contNodes[1].empty() )
8234 // append the best free border
8235 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8236 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8237 theNodes.pop_back(); // remove nIgnore
8238 theNodes.pop_back(); // remove nStart
8239 theFaces.pop_back(); // remove curElem
8240 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8241 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8242 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8243 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8246 } // several continuations found
8247 } // while ( nStart != theLastNode )
8252 //=======================================================================
8253 //function : CheckFreeBorderNodes
8254 //purpose : Return true if the tree nodes are on a free border
8255 //=======================================================================
8257 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8258 const SMDS_MeshNode* theNode2,
8259 const SMDS_MeshNode* theNode3)
8261 list< const SMDS_MeshNode* > nodes;
8262 list< const SMDS_MeshElement* > faces;
8263 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8266 //=======================================================================
8267 //function : SewFreeBorder
8269 //warning : for border-to-side sewing theSideSecondNode is considered as
8270 // the last side node and theSideThirdNode is not used
8271 //=======================================================================
8273 SMESH_MeshEditor::Sew_Error
8274 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8275 const SMDS_MeshNode* theBordSecondNode,
8276 const SMDS_MeshNode* theBordLastNode,
8277 const SMDS_MeshNode* theSideFirstNode,
8278 const SMDS_MeshNode* theSideSecondNode,
8279 const SMDS_MeshNode* theSideThirdNode,
8280 const bool theSideIsFreeBorder,
8281 const bool toCreatePolygons,
8282 const bool toCreatePolyedrs)
8284 myLastCreatedElems.Clear();
8285 myLastCreatedNodes.Clear();
8287 Sew_Error aResult = SEW_OK;
8289 // ====================================
8290 // find side nodes and elements
8291 // ====================================
8293 list< const SMDS_MeshNode* > nSide[ 2 ];
8294 list< const SMDS_MeshElement* > eSide[ 2 ];
8295 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8296 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8300 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8301 nSide[0], eSide[0])) {
8302 MESSAGE(" Free Border 1 not found " );
8303 aResult = SEW_BORDER1_NOT_FOUND;
8305 if (theSideIsFreeBorder) {
8308 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8309 nSide[1], eSide[1])) {
8310 MESSAGE(" Free Border 2 not found " );
8311 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8314 if ( aResult != SEW_OK )
8317 if (!theSideIsFreeBorder) {
8321 // -------------------------------------------------------------------------
8323 // 1. If nodes to merge are not coincident, move nodes of the free border
8324 // from the coord sys defined by the direction from the first to last
8325 // nodes of the border to the correspondent sys of the side 2
8326 // 2. On the side 2, find the links most co-directed with the correspondent
8327 // links of the free border
8328 // -------------------------------------------------------------------------
8330 // 1. Since sewing may break if there are volumes to split on the side 2,
8331 // we won't move nodes but just compute new coordinates for them
8332 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8333 TNodeXYZMap nBordXYZ;
8334 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8335 list< const SMDS_MeshNode* >::iterator nBordIt;
8337 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8338 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8339 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8340 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8341 double tol2 = 1.e-8;
8342 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8343 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8344 // Need node movement.
8346 // find X and Z axes to create trsf
8347 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8349 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8351 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8354 gp_Ax3 toBordAx( Pb1, Zb, X );
8355 gp_Ax3 fromSideAx( Ps1, Zs, X );
8356 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8358 gp_Trsf toBordSys, fromSide2Sys;
8359 toBordSys.SetTransformation( toBordAx );
8360 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8361 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8364 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8365 const SMDS_MeshNode* n = *nBordIt;
8366 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8367 toBordSys.Transforms( xyz );
8368 fromSide2Sys.Transforms( xyz );
8369 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8373 // just insert nodes XYZ in the nBordXYZ map
8374 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8375 const SMDS_MeshNode* n = *nBordIt;
8376 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8380 // 2. On the side 2, find the links most co-directed with the correspondent
8381 // links of the free border
8383 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8384 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8385 sideNodes.push_back( theSideFirstNode );
8387 bool hasVolumes = false;
8388 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8389 set<long> foundSideLinkIDs, checkedLinkIDs;
8390 SMDS_VolumeTool volume;
8391 //const SMDS_MeshNode* faceNodes[ 4 ];
8393 const SMDS_MeshNode* sideNode;
8394 const SMDS_MeshElement* sideElem = 0;
8395 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8396 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8397 nBordIt = bordNodes.begin();
8399 // border node position and border link direction to compare with
8400 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8401 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8402 // choose next side node by link direction or by closeness to
8403 // the current border node:
8404 bool searchByDir = ( *nBordIt != theBordLastNode );
8406 // find the next node on the Side 2
8408 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8410 checkedLinkIDs.clear();
8411 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8413 // loop on inverse elements of current node (prevSideNode) on the Side 2
8414 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8415 while ( invElemIt->more() )
8417 const SMDS_MeshElement* elem = invElemIt->next();
8418 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8419 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8420 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8421 bool isVolume = volume.Set( elem );
8422 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8423 if ( isVolume ) // --volume
8425 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8426 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8427 if(elem->IsQuadratic()) {
8428 const SMDS_VtkFace* F =
8429 dynamic_cast<const SMDS_VtkFace*>(elem);
8430 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8431 // use special nodes iterator
8432 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8433 while( anIter->more() ) {
8434 nodes[ iNode ] = cast2Node(anIter->next());
8435 if ( nodes[ iNode++ ] == prevSideNode )
8436 iPrevNode = iNode - 1;
8440 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8441 while ( nIt->more() ) {
8442 nodes[ iNode ] = cast2Node( nIt->next() );
8443 if ( nodes[ iNode++ ] == prevSideNode )
8444 iPrevNode = iNode - 1;
8447 // there are 2 links to check
8452 // loop on links, to be precise, on the second node of links
8453 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8454 const SMDS_MeshNode* n = nodes[ iNode ];
8456 if ( !volume.IsLinked( n, prevSideNode ))
8460 if ( iNode ) // a node before prevSideNode
8461 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8462 else // a node after prevSideNode
8463 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8465 // check if this link was already used
8466 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8467 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8468 if (!isJustChecked &&
8469 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8471 // test a link geometrically
8472 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8473 bool linkIsBetter = false;
8474 double dot = 0.0, dist = 0.0;
8475 if ( searchByDir ) { // choose most co-directed link
8476 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8477 linkIsBetter = ( dot > maxDot );
8479 else { // choose link with the node closest to bordPos
8480 dist = ( nextXYZ - bordPos ).SquareModulus();
8481 linkIsBetter = ( dist < minDist );
8483 if ( linkIsBetter ) {
8492 } // loop on inverse elements of prevSideNode
8495 MESSAGE(" Can't find path by links of the Side 2 ");
8496 return SEW_BAD_SIDE_NODES;
8498 sideNodes.push_back( sideNode );
8499 sideElems.push_back( sideElem );
8500 foundSideLinkIDs.insert ( linkID );
8501 prevSideNode = sideNode;
8503 if ( *nBordIt == theBordLastNode )
8504 searchByDir = false;
8506 // find the next border link to compare with
8507 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8508 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8509 // move to next border node if sideNode is before forward border node (bordPos)
8510 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8511 prevBordNode = *nBordIt;
8513 bordPos = nBordXYZ[ *nBordIt ];
8514 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8515 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8519 while ( sideNode != theSideSecondNode );
8521 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8522 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8523 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8525 } // end nodes search on the side 2
8527 // ============================
8528 // sew the border to the side 2
8529 // ============================
8531 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8532 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8534 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8535 if ( toMergeConformal && toCreatePolygons )
8537 // do not merge quadrangles if polygons are OK (IPAL0052824)
8538 eIt[0] = eSide[0].begin();
8539 eIt[1] = eSide[1].begin();
8540 bool allQuads[2] = { true, true };
8541 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8542 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8543 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8545 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8548 TListOfListOfNodes nodeGroupsToMerge;
8549 if (( toMergeConformal ) ||
8550 ( theSideIsFreeBorder && !theSideThirdNode )) {
8552 // all nodes are to be merged
8554 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8555 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8556 nIt[0]++, nIt[1]++ )
8558 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8559 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8560 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8565 // insert new nodes into the border and the side to get equal nb of segments
8567 // get normalized parameters of nodes on the borders
8568 vector< double > param[ 2 ];
8569 param[0].resize( maxNbNodes );
8570 param[1].resize( maxNbNodes );
8572 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8573 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8574 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8575 const SMDS_MeshNode* nPrev = *nIt;
8576 double bordLength = 0;
8577 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8578 const SMDS_MeshNode* nCur = *nIt;
8579 gp_XYZ segment (nCur->X() - nPrev->X(),
8580 nCur->Y() - nPrev->Y(),
8581 nCur->Z() - nPrev->Z());
8582 double segmentLen = segment.Modulus();
8583 bordLength += segmentLen;
8584 param[ iBord ][ iNode ] = bordLength;
8587 // normalize within [0,1]
8588 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8589 param[ iBord ][ iNode ] /= bordLength;
8593 // loop on border segments
8594 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8595 int i[ 2 ] = { 0, 0 };
8596 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8597 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8599 TElemOfNodeListMap insertMap;
8600 TElemOfNodeListMap::iterator insertMapIt;
8602 // key: elem to insert nodes into
8603 // value: 2 nodes to insert between + nodes to be inserted
8605 bool next[ 2 ] = { false, false };
8607 // find min adjacent segment length after sewing
8608 double nextParam = 10., prevParam = 0;
8609 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8610 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8611 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8612 if ( i[ iBord ] > 0 )
8613 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8615 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8616 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8617 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8619 // choose to insert or to merge nodes
8620 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8621 if ( Abs( du ) <= minSegLen * 0.2 ) {
8624 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8625 const SMDS_MeshNode* n0 = *nIt[0];
8626 const SMDS_MeshNode* n1 = *nIt[1];
8627 nodeGroupsToMerge.back().push_back( n1 );
8628 nodeGroupsToMerge.back().push_back( n0 );
8629 // position of node of the border changes due to merge
8630 param[ 0 ][ i[0] ] += du;
8631 // move n1 for the sake of elem shape evaluation during insertion.
8632 // n1 will be removed by MergeNodes() anyway
8633 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8634 next[0] = next[1] = true;
8639 int intoBord = ( du < 0 ) ? 0 : 1;
8640 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8641 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8642 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8643 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8644 if ( intoBord == 1 ) {
8645 // move node of the border to be on a link of elem of the side
8646 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8647 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8648 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8649 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8650 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8652 insertMapIt = insertMap.find( elem );
8653 bool notFound = ( insertMapIt == insertMap.end() );
8654 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8656 // insert into another link of the same element:
8657 // 1. perform insertion into the other link of the elem
8658 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8659 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8660 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8661 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8662 // 2. perform insertion into the link of adjacent faces
8663 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8664 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8666 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8667 InsertNodesIntoLink( seg, n12, n22, nodeList );
8669 if (toCreatePolyedrs) {
8670 // perform insertion into the links of adjacent volumes
8671 UpdateVolumes(n12, n22, nodeList);
8673 // 3. find an element appeared on n1 and n2 after the insertion
8674 insertMap.erase( elem );
8675 elem = findAdjacentFace( n1, n2, 0 );
8677 if ( notFound || otherLink ) {
8678 // add element and nodes of the side into the insertMap
8679 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8680 (*insertMapIt).second.push_back( n1 );
8681 (*insertMapIt).second.push_back( n2 );
8683 // add node to be inserted into elem
8684 (*insertMapIt).second.push_back( nIns );
8685 next[ 1 - intoBord ] = true;
8688 // go to the next segment
8689 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8690 if ( next[ iBord ] ) {
8691 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8693 nPrev[ iBord ] = *nIt[ iBord ];
8694 nIt[ iBord ]++; i[ iBord ]++;
8698 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8700 // perform insertion of nodes into elements
8702 for (insertMapIt = insertMap.begin();
8703 insertMapIt != insertMap.end();
8706 const SMDS_MeshElement* elem = (*insertMapIt).first;
8707 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8708 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8709 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8711 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8713 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8714 InsertNodesIntoLink( seg, n1, n2, nodeList );
8717 if ( !theSideIsFreeBorder ) {
8718 // look for and insert nodes into the faces adjacent to elem
8719 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8720 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8723 if (toCreatePolyedrs) {
8724 // perform insertion into the links of adjacent volumes
8725 UpdateVolumes(n1, n2, nodeList);
8728 } // end: insert new nodes
8730 MergeNodes ( nodeGroupsToMerge );
8733 // Remove coincident segments
8736 TIDSortedElemSet segments;
8737 SMESH_SequenceOfElemPtr newFaces;
8738 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8740 if ( !myLastCreatedElems(i) ) continue;
8741 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8742 segments.insert( segments.end(), myLastCreatedElems(i) );
8744 newFaces.Append( myLastCreatedElems(i) );
8746 // get segments adjacent to merged nodes
8747 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8748 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8750 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8751 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8752 while ( segIt->more() )
8753 segments.insert( segIt->next() );
8757 TListOfListOfElementsID equalGroups;
8758 if ( !segments.empty() )
8759 FindEqualElements( segments, equalGroups );
8760 if ( !equalGroups.empty() )
8762 // remove from segments those that will be removed
8763 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8764 for ( ; itGroups != equalGroups.end(); ++itGroups )
8766 list< int >& group = *itGroups;
8767 list< int >::iterator id = group.begin();
8768 for ( ++id; id != group.end(); ++id )
8769 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8770 segments.erase( seg );
8772 // remove equal segments
8773 MergeElements( equalGroups );
8775 // restore myLastCreatedElems
8776 myLastCreatedElems = newFaces;
8777 TIDSortedElemSet::iterator seg = segments.begin();
8778 for ( ; seg != segments.end(); ++seg )
8779 myLastCreatedElems.Append( *seg );
8785 //=======================================================================
8786 //function : InsertNodesIntoLink
8787 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8788 // and theBetweenNode2 and split theElement
8789 //=======================================================================
8791 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8792 const SMDS_MeshNode* theBetweenNode1,
8793 const SMDS_MeshNode* theBetweenNode2,
8794 list<const SMDS_MeshNode*>& theNodesToInsert,
8795 const bool toCreatePoly)
8797 if ( !theElement ) return;
8799 SMESHDS_Mesh *aMesh = GetMeshDS();
8800 vector<const SMDS_MeshElement*> newElems;
8802 if ( theElement->GetType() == SMDSAbs_Edge )
8804 theNodesToInsert.push_front( theBetweenNode1 );
8805 theNodesToInsert.push_back ( theBetweenNode2 );
8806 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8807 const SMDS_MeshNode* n1 = *n;
8808 for ( ++n; n != theNodesToInsert.end(); ++n )
8810 const SMDS_MeshNode* n2 = *n;
8811 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8812 AddToSameGroups( seg, theElement, aMesh );
8814 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8817 theNodesToInsert.pop_front();
8818 theNodesToInsert.pop_back();
8820 if ( theElement->IsQuadratic() ) // add a not split part
8822 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8823 theElement->end_nodes() );
8824 int iOther = 0, nbN = nodes.size();
8825 for ( ; iOther < nbN; ++iOther )
8826 if ( nodes[iOther] != theBetweenNode1 &&
8827 nodes[iOther] != theBetweenNode2 )
8831 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8832 AddToSameGroups( seg, theElement, aMesh );
8834 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8836 else if ( iOther == 2 )
8838 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8839 AddToSameGroups( seg, theElement, aMesh );
8841 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8844 // treat new elements
8845 for ( size_t i = 0; i < newElems.size(); ++i )
8848 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8849 myLastCreatedElems.Append( newElems[i] );
8851 ReplaceElemInGroups( theElement, newElems, aMesh );
8852 aMesh->RemoveElement( theElement );
8855 } // if ( theElement->GetType() == SMDSAbs_Edge )
8857 const SMDS_MeshElement* theFace = theElement;
8858 if ( theFace->GetType() != SMDSAbs_Face ) return;
8860 // find indices of 2 link nodes and of the rest nodes
8861 int iNode = 0, il1, il2, i3, i4;
8862 il1 = il2 = i3 = i4 = -1;
8863 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8865 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8866 while ( nodeIt->more() ) {
8867 const SMDS_MeshNode* n = nodeIt->next();
8868 if ( n == theBetweenNode1 )
8870 else if ( n == theBetweenNode2 )
8876 nodes[ iNode++ ] = n;
8878 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8881 // arrange link nodes to go one after another regarding the face orientation
8882 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8883 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8888 aNodesToInsert.reverse();
8890 // check that not link nodes of a quadrangles are in good order
8891 int nbFaceNodes = theFace->NbNodes();
8892 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8898 if (toCreatePoly || theFace->IsPoly()) {
8901 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8903 // add nodes of face up to first node of link
8906 if ( theFace->IsQuadratic() ) {
8907 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8908 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8909 // use special nodes iterator
8910 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8911 while( anIter->more() && !isFLN ) {
8912 const SMDS_MeshNode* n = cast2Node(anIter->next());
8913 poly_nodes[iNode++] = n;
8914 if (n == nodes[il1]) {
8918 // add nodes to insert
8919 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8920 for (; nIt != aNodesToInsert.end(); nIt++) {
8921 poly_nodes[iNode++] = *nIt;
8923 // add nodes of face starting from last node of link
8924 while ( anIter->more() ) {
8925 poly_nodes[iNode++] = cast2Node(anIter->next());
8929 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8930 while ( nodeIt->more() && !isFLN ) {
8931 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8932 poly_nodes[iNode++] = n;
8933 if (n == nodes[il1]) {
8937 // add nodes to insert
8938 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8939 for (; nIt != aNodesToInsert.end(); nIt++) {
8940 poly_nodes[iNode++] = *nIt;
8942 // add nodes of face starting from last node of link
8943 while ( nodeIt->more() ) {
8944 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8945 poly_nodes[iNode++] = n;
8950 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8953 else if ( !theFace->IsQuadratic() )
8955 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8956 int nbLinkNodes = 2 + aNodesToInsert.size();
8957 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8958 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8959 linkNodes[ 0 ] = nodes[ il1 ];
8960 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8961 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8962 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8963 linkNodes[ iNode++ ] = *nIt;
8965 // decide how to split a quadrangle: compare possible variants
8966 // and choose which of splits to be a quadrangle
8967 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8968 if ( nbFaceNodes == 3 ) {
8969 iBestQuad = nbSplits;
8972 else if ( nbFaceNodes == 4 ) {
8973 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8974 double aBestRate = DBL_MAX;
8975 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8977 double aBadRate = 0;
8978 // evaluate elements quality
8979 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8980 if ( iSplit == iQuad ) {
8981 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8985 aBadRate += getBadRate( &quad, aCrit );
8988 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8990 nodes[ iSplit < iQuad ? i4 : i3 ]);
8991 aBadRate += getBadRate( &tria, aCrit );
8995 if ( aBadRate < aBestRate ) {
8997 aBestRate = aBadRate;
9002 // create new elements
9004 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9006 if ( iSplit == iBestQuad )
9007 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9012 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9014 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9017 const SMDS_MeshNode* newNodes[ 4 ];
9018 newNodes[ 0 ] = linkNodes[ i1 ];
9019 newNodes[ 1 ] = linkNodes[ i2 ];
9020 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9021 newNodes[ 3 ] = nodes[ i4 ];
9022 if (iSplit == iBestQuad)
9023 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9025 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9027 } // end if(!theFace->IsQuadratic())
9029 else { // theFace is quadratic
9030 // we have to split theFace on simple triangles and one simple quadrangle
9032 int nbshift = tmp*2;
9033 // shift nodes in nodes[] by nbshift
9035 for(i=0; i<nbshift; i++) {
9036 const SMDS_MeshNode* n = nodes[0];
9037 for(j=0; j<nbFaceNodes-1; j++) {
9038 nodes[j] = nodes[j+1];
9040 nodes[nbFaceNodes-1] = n;
9042 il1 = il1 - nbshift;
9043 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9044 // n0 n1 n2 n0 n1 n2
9045 // +-----+-----+ +-----+-----+
9054 // create new elements
9056 if ( nbFaceNodes == 6 ) { // quadratic triangle
9057 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9058 if ( theFace->IsMediumNode(nodes[il1]) ) {
9059 // create quadrangle
9060 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9066 // create quadrangle
9067 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9073 else { // nbFaceNodes==8 - quadratic quadrangle
9074 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9075 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9076 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9077 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9078 // create quadrangle
9079 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9085 // create quadrangle
9086 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9092 // create needed triangles using n1,n2,n3 and inserted nodes
9093 int nbn = 2 + aNodesToInsert.size();
9094 vector<const SMDS_MeshNode*> aNodes(nbn);
9095 aNodes[0 ] = nodes[n1];
9096 aNodes[nbn-1] = nodes[n2];
9097 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9098 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9099 aNodes[iNode++] = *nIt;
9101 for ( i = 1; i < nbn; i++ )
9102 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9105 // remove the old face
9106 for ( size_t i = 0; i < newElems.size(); ++i )
9109 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9110 myLastCreatedElems.Append( newElems[i] );
9112 ReplaceElemInGroups( theFace, newElems, aMesh );
9113 aMesh->RemoveElement(theFace);
9115 } // InsertNodesIntoLink()
9117 //=======================================================================
9118 //function : UpdateVolumes
9120 //=======================================================================
9122 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9123 const SMDS_MeshNode* theBetweenNode2,
9124 list<const SMDS_MeshNode*>& theNodesToInsert)
9126 myLastCreatedElems.Clear();
9127 myLastCreatedNodes.Clear();
9129 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9130 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9131 const SMDS_MeshElement* elem = invElemIt->next();
9133 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9134 SMDS_VolumeTool aVolume (elem);
9135 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9138 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9139 int iface, nbFaces = aVolume.NbFaces();
9140 vector<const SMDS_MeshNode *> poly_nodes;
9141 vector<int> quantities (nbFaces);
9143 for (iface = 0; iface < nbFaces; iface++) {
9144 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9145 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9146 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9148 for (int inode = 0; inode < nbFaceNodes; inode++) {
9149 poly_nodes.push_back(faceNodes[inode]);
9151 if (nbInserted == 0) {
9152 if (faceNodes[inode] == theBetweenNode1) {
9153 if (faceNodes[inode + 1] == theBetweenNode2) {
9154 nbInserted = theNodesToInsert.size();
9156 // add nodes to insert
9157 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9158 for (; nIt != theNodesToInsert.end(); nIt++) {
9159 poly_nodes.push_back(*nIt);
9163 else if (faceNodes[inode] == theBetweenNode2) {
9164 if (faceNodes[inode + 1] == theBetweenNode1) {
9165 nbInserted = theNodesToInsert.size();
9167 // add nodes to insert in reversed order
9168 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9170 for (; nIt != theNodesToInsert.begin(); nIt--) {
9171 poly_nodes.push_back(*nIt);
9173 poly_nodes.push_back(*nIt);
9180 quantities[iface] = nbFaceNodes + nbInserted;
9183 // Replace the volume
9184 SMESHDS_Mesh *aMesh = GetMeshDS();
9186 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9188 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9189 myLastCreatedElems.Append( newElem );
9190 ReplaceElemInGroups( elem, newElem, aMesh );
9192 aMesh->RemoveElement( elem );
9198 //================================================================================
9200 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9202 //================================================================================
9204 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9205 vector<const SMDS_MeshNode *> & nodes,
9206 vector<int> & nbNodeInFaces )
9209 nbNodeInFaces.clear();
9210 SMDS_VolumeTool vTool ( elem );
9211 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9213 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9214 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9215 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9220 //=======================================================================
9222 * \brief Convert elements contained in a sub-mesh to quadratic
9223 * \return int - nb of checked elements
9225 //=======================================================================
9227 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9228 SMESH_MesherHelper& theHelper,
9229 const bool theForce3d)
9231 //MESSAGE("convertElemToQuadratic");
9233 if( !theSm ) return nbElem;
9235 vector<int> nbNodeInFaces;
9236 vector<const SMDS_MeshNode *> nodes;
9237 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9238 while(ElemItr->more())
9241 const SMDS_MeshElement* elem = ElemItr->next();
9242 if( !elem ) continue;
9244 // analyse a necessity of conversion
9245 const SMDSAbs_ElementType aType = elem->GetType();
9246 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9248 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9249 bool hasCentralNodes = false;
9250 if ( elem->IsQuadratic() )
9253 switch ( aGeomType ) {
9254 case SMDSEntity_Quad_Triangle:
9255 case SMDSEntity_Quad_Quadrangle:
9256 case SMDSEntity_Quad_Hexa:
9257 case SMDSEntity_Quad_Penta:
9258 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9260 case SMDSEntity_BiQuad_Triangle:
9261 case SMDSEntity_BiQuad_Quadrangle:
9262 case SMDSEntity_TriQuad_Hexa:
9263 case SMDSEntity_BiQuad_Penta:
9264 alreadyOK = theHelper.GetIsBiQuadratic();
9265 hasCentralNodes = true;
9270 // take into account already present medium nodes
9272 case SMDSAbs_Volume:
9273 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9275 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9277 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9283 // get elem data needed to re-create it
9285 const int id = elem->GetID();
9286 const int nbNodes = elem->NbCornerNodes();
9287 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9288 if ( aGeomType == SMDSEntity_Polyhedra )
9289 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9290 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9291 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9293 // remove a linear element
9294 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9296 // remove central nodes of biquadratic elements (biquad->quad conversion)
9297 if ( hasCentralNodes )
9298 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9299 if ( nodes[i]->NbInverseElements() == 0 )
9300 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9302 const SMDS_MeshElement* NewElem = 0;
9308 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9316 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9319 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9322 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9326 case SMDSAbs_Volume :
9330 case SMDSEntity_Tetra:
9331 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9333 case SMDSEntity_Pyramid:
9334 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9336 case SMDSEntity_Penta:
9337 case SMDSEntity_Quad_Penta:
9338 case SMDSEntity_BiQuad_Penta:
9339 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9341 case SMDSEntity_Hexa:
9342 case SMDSEntity_Quad_Hexa:
9343 case SMDSEntity_TriQuad_Hexa:
9344 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9345 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9347 case SMDSEntity_Hexagonal_Prism:
9349 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9356 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9357 if( NewElem && NewElem->getshapeId() < 1 )
9358 theSm->AddElement( NewElem );
9362 //=======================================================================
9363 //function : ConvertToQuadratic
9365 //=======================================================================
9367 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9369 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9370 SMESHDS_Mesh* meshDS = GetMeshDS();
9372 SMESH_MesherHelper aHelper(*myMesh);
9374 aHelper.SetIsQuadratic( true );
9375 aHelper.SetIsBiQuadratic( theToBiQuad );
9376 aHelper.SetElementsOnShape(true);
9377 aHelper.ToFixNodeParameters( true );
9379 // convert elements assigned to sub-meshes
9380 int nbCheckedElems = 0;
9381 if ( myMesh->HasShapeToMesh() )
9383 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9385 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9386 while ( smIt->more() ) {
9387 SMESH_subMesh* sm = smIt->next();
9388 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9389 aHelper.SetSubShape( sm->GetSubShape() );
9390 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9396 // convert elements NOT assigned to sub-meshes
9397 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9398 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9400 aHelper.SetElementsOnShape(false);
9401 SMESHDS_SubMesh *smDS = 0;
9404 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9405 while( aEdgeItr->more() )
9407 const SMDS_MeshEdge* edge = aEdgeItr->next();
9408 if ( !edge->IsQuadratic() )
9410 int id = edge->GetID();
9411 const SMDS_MeshNode* n1 = edge->GetNode(0);
9412 const SMDS_MeshNode* n2 = edge->GetNode(1);
9414 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9416 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9417 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9421 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9426 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9427 while( aFaceItr->more() )
9429 const SMDS_MeshFace* face = aFaceItr->next();
9430 if ( !face ) continue;
9432 const SMDSAbs_EntityType type = face->GetEntityType();
9436 case SMDSEntity_Quad_Triangle:
9437 case SMDSEntity_Quad_Quadrangle:
9438 alreadyOK = !theToBiQuad;
9439 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9441 case SMDSEntity_BiQuad_Triangle:
9442 case SMDSEntity_BiQuad_Quadrangle:
9443 alreadyOK = theToBiQuad;
9444 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9446 default: alreadyOK = false;
9451 const int id = face->GetID();
9452 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9454 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9456 SMDS_MeshFace * NewFace = 0;
9459 case SMDSEntity_Triangle:
9460 case SMDSEntity_Quad_Triangle:
9461 case SMDSEntity_BiQuad_Triangle:
9462 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9463 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9464 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9467 case SMDSEntity_Quadrangle:
9468 case SMDSEntity_Quad_Quadrangle:
9469 case SMDSEntity_BiQuad_Quadrangle:
9470 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9471 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9472 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9476 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9478 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9482 vector<int> nbNodeInFaces;
9483 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9484 while(aVolumeItr->more())
9486 const SMDS_MeshVolume* volume = aVolumeItr->next();
9487 if ( !volume ) continue;
9489 const SMDSAbs_EntityType type = volume->GetEntityType();
9490 if ( volume->IsQuadratic() )
9495 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9496 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9497 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9498 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9499 default: alreadyOK = true;
9503 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9507 const int id = volume->GetID();
9508 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9509 if ( type == SMDSEntity_Polyhedra )
9510 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9511 else if ( type == SMDSEntity_Hexagonal_Prism )
9512 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9514 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9516 SMDS_MeshVolume * NewVolume = 0;
9519 case SMDSEntity_Tetra:
9520 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9522 case SMDSEntity_Hexa:
9523 case SMDSEntity_Quad_Hexa:
9524 case SMDSEntity_TriQuad_Hexa:
9525 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9526 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9527 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9528 if ( nodes[i]->NbInverseElements() == 0 )
9529 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9531 case SMDSEntity_Pyramid:
9532 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9533 nodes[3], nodes[4], id, theForce3d);
9535 case SMDSEntity_Penta:
9536 case SMDSEntity_Quad_Penta:
9537 case SMDSEntity_BiQuad_Penta:
9538 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9539 nodes[3], nodes[4], nodes[5], id, theForce3d);
9540 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9541 if ( nodes[i]->NbInverseElements() == 0 )
9542 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9544 case SMDSEntity_Hexagonal_Prism:
9546 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9548 ReplaceElemInGroups(volume, NewVolume, meshDS);
9553 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9554 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9555 // aHelper.FixQuadraticElements(myError);
9556 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9560 //================================================================================
9562 * \brief Makes given elements quadratic
9563 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9564 * \param theElements - elements to make quadratic
9566 //================================================================================
9568 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9569 TIDSortedElemSet& theElements,
9570 const bool theToBiQuad)
9572 if ( theElements.empty() ) return;
9574 // we believe that all theElements are of the same type
9575 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9577 // get all nodes shared by theElements
9578 TIDSortedNodeSet allNodes;
9579 TIDSortedElemSet::iterator eIt = theElements.begin();
9580 for ( ; eIt != theElements.end(); ++eIt )
9581 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9583 // complete theElements with elements of lower dim whose all nodes are in allNodes
9585 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9586 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9587 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9588 for ( ; nIt != allNodes.end(); ++nIt )
9590 const SMDS_MeshNode* n = *nIt;
9591 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9592 while ( invIt->more() )
9594 const SMDS_MeshElement* e = invIt->next();
9595 const SMDSAbs_ElementType type = e->GetType();
9596 if ( e->IsQuadratic() )
9598 quadAdjacentElems[ type ].insert( e );
9601 switch ( e->GetEntityType() ) {
9602 case SMDSEntity_Quad_Triangle:
9603 case SMDSEntity_Quad_Quadrangle:
9604 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9605 case SMDSEntity_BiQuad_Triangle:
9606 case SMDSEntity_BiQuad_Quadrangle:
9607 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9608 default: alreadyOK = true;
9613 if ( type >= elemType )
9614 continue; // same type or more complex linear element
9616 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9617 continue; // e is already checked
9621 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9622 while ( nodeIt->more() && allIn )
9623 allIn = allNodes.count( nodeIt->next() );
9625 theElements.insert(e );
9629 SMESH_MesherHelper helper(*myMesh);
9630 helper.SetIsQuadratic( true );
9631 helper.SetIsBiQuadratic( theToBiQuad );
9633 // add links of quadratic adjacent elements to the helper
9635 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9636 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9637 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9639 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9641 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9642 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9643 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9645 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9647 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9648 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9649 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9651 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9654 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9656 SMESHDS_Mesh* meshDS = GetMeshDS();
9657 SMESHDS_SubMesh* smDS = 0;
9658 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9660 const SMDS_MeshElement* elem = *eIt;
9663 int nbCentralNodes = 0;
9664 switch ( elem->GetEntityType() ) {
9665 // linear convertible
9666 case SMDSEntity_Edge:
9667 case SMDSEntity_Triangle:
9668 case SMDSEntity_Quadrangle:
9669 case SMDSEntity_Tetra:
9670 case SMDSEntity_Pyramid:
9671 case SMDSEntity_Hexa:
9672 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9673 // quadratic that can become bi-quadratic
9674 case SMDSEntity_Quad_Triangle:
9675 case SMDSEntity_Quad_Quadrangle:
9676 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9678 case SMDSEntity_BiQuad_Triangle:
9679 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9680 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9682 default: alreadyOK = true;
9684 if ( alreadyOK ) continue;
9686 const SMDSAbs_ElementType type = elem->GetType();
9687 const int id = elem->GetID();
9688 const int nbNodes = elem->NbCornerNodes();
9689 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9691 helper.SetSubShape( elem->getshapeId() );
9693 if ( !smDS || !smDS->Contains( elem ))
9694 smDS = meshDS->MeshElements( elem->getshapeId() );
9695 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9697 SMDS_MeshElement * newElem = 0;
9700 case 4: // cases for most frequently used element types go first (for optimization)
9701 if ( type == SMDSAbs_Volume )
9702 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9704 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9707 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9708 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9711 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9714 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9717 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9718 nodes[4], id, theForce3d);
9721 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9722 nodes[4], nodes[5], id, theForce3d);
9726 ReplaceElemInGroups( elem, newElem, meshDS);
9727 if( newElem && smDS )
9728 smDS->AddElement( newElem );
9730 // remove central nodes
9731 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9732 if ( nodes[i]->NbInverseElements() == 0 )
9733 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9735 } // loop on theElements
9738 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9739 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9740 // helper.FixQuadraticElements( myError );
9741 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9745 //=======================================================================
9747 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9748 * \return int - nb of checked elements
9750 //=======================================================================
9752 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9753 SMDS_ElemIteratorPtr theItr,
9754 const int theShapeID)
9757 SMESHDS_Mesh* meshDS = GetMeshDS();
9758 ElemFeatures elemType;
9759 vector<const SMDS_MeshNode *> nodes;
9761 while( theItr->more() )
9763 const SMDS_MeshElement* elem = theItr->next();
9765 if( elem && elem->IsQuadratic())
9768 int nbCornerNodes = elem->NbCornerNodes();
9769 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9771 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9773 //remove a quadratic element
9774 if ( !theSm || !theSm->Contains( elem ))
9775 theSm = meshDS->MeshElements( elem->getshapeId() );
9776 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9778 // remove medium nodes
9779 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9780 if ( nodes[i]->NbInverseElements() == 0 )
9781 meshDS->RemoveFreeNode( nodes[i], theSm );
9783 // add a linear element
9784 nodes.resize( nbCornerNodes );
9785 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9786 ReplaceElemInGroups(elem, newElem, meshDS);
9787 if( theSm && newElem )
9788 theSm->AddElement( newElem );
9794 //=======================================================================
9795 //function : ConvertFromQuadratic
9797 //=======================================================================
9799 bool SMESH_MeshEditor::ConvertFromQuadratic()
9801 int nbCheckedElems = 0;
9802 if ( myMesh->HasShapeToMesh() )
9804 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9806 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9807 while ( smIt->more() ) {
9808 SMESH_subMesh* sm = smIt->next();
9809 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9810 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9816 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9817 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9819 SMESHDS_SubMesh *aSM = 0;
9820 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9828 //================================================================================
9830 * \brief Return true if all medium nodes of the element are in the node set
9832 //================================================================================
9834 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9836 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9837 if ( !nodeSet.count( elem->GetNode(i) ))
9843 //================================================================================
9845 * \brief Makes given elements linear
9847 //================================================================================
9849 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9851 if ( theElements.empty() ) return;
9853 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9854 set<int> mediumNodeIDs;
9855 TIDSortedElemSet::iterator eIt = theElements.begin();
9856 for ( ; eIt != theElements.end(); ++eIt )
9858 const SMDS_MeshElement* e = *eIt;
9859 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9860 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9863 // replace given elements by linear ones
9864 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9865 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9867 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9868 // except those elements sharing medium nodes of quadratic element whose medium nodes
9869 // are not all in mediumNodeIDs
9871 // get remaining medium nodes
9872 TIDSortedNodeSet mediumNodes;
9873 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9874 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9875 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9876 mediumNodes.insert( mediumNodes.end(), n );
9878 // find more quadratic elements to convert
9879 TIDSortedElemSet moreElemsToConvert;
9880 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9881 for ( ; nIt != mediumNodes.end(); ++nIt )
9883 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9884 while ( invIt->more() )
9886 const SMDS_MeshElement* e = invIt->next();
9887 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9889 // find a more complex element including e and
9890 // whose medium nodes are not in mediumNodes
9891 bool complexFound = false;
9892 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9894 SMDS_ElemIteratorPtr invIt2 =
9895 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9896 while ( invIt2->more() )
9898 const SMDS_MeshElement* eComplex = invIt2->next();
9899 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9901 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9902 if ( nbCommonNodes == e->NbNodes())
9904 complexFound = true;
9905 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9911 if ( !complexFound )
9912 moreElemsToConvert.insert( e );
9916 elemIt = elemSetIterator( moreElemsToConvert );
9917 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9920 //=======================================================================
9921 //function : SewSideElements
9923 //=======================================================================
9925 SMESH_MeshEditor::Sew_Error
9926 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9927 TIDSortedElemSet& theSide2,
9928 const SMDS_MeshNode* theFirstNode1,
9929 const SMDS_MeshNode* theFirstNode2,
9930 const SMDS_MeshNode* theSecondNode1,
9931 const SMDS_MeshNode* theSecondNode2)
9933 myLastCreatedElems.Clear();
9934 myLastCreatedNodes.Clear();
9936 if ( theSide1.size() != theSide2.size() )
9937 return SEW_DIFF_NB_OF_ELEMENTS;
9939 Sew_Error aResult = SEW_OK;
9941 // 1. Build set of faces representing each side
9942 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9943 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9945 // =======================================================================
9946 // 1. Build set of faces representing each side:
9947 // =======================================================================
9948 // a. build set of nodes belonging to faces
9949 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9950 // c. create temporary faces representing side of volumes if correspondent
9951 // face does not exist
9953 SMESHDS_Mesh* aMesh = GetMeshDS();
9954 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9955 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9956 TIDSortedElemSet faceSet1, faceSet2;
9957 set<const SMDS_MeshElement*> volSet1, volSet2;
9958 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9959 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9960 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9961 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9962 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9963 int iSide, iFace, iNode;
9965 list<const SMDS_MeshElement* > tempFaceList;
9966 for ( iSide = 0; iSide < 2; iSide++ ) {
9967 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9968 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9969 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9970 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9971 set<const SMDS_MeshElement*>::iterator vIt;
9972 TIDSortedElemSet::iterator eIt;
9973 set<const SMDS_MeshNode*>::iterator nIt;
9975 // check that given nodes belong to given elements
9976 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9977 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9978 int firstIndex = -1, secondIndex = -1;
9979 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9980 const SMDS_MeshElement* elem = *eIt;
9981 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9982 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9983 if ( firstIndex > -1 && secondIndex > -1 ) break;
9985 if ( firstIndex < 0 || secondIndex < 0 ) {
9986 // we can simply return until temporary faces created
9987 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9990 // -----------------------------------------------------------
9991 // 1a. Collect nodes of existing faces
9992 // and build set of face nodes in order to detect missing
9993 // faces corresponding to sides of volumes
9994 // -----------------------------------------------------------
9996 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9998 // loop on the given element of a side
9999 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10000 //const SMDS_MeshElement* elem = *eIt;
10001 const SMDS_MeshElement* elem = *eIt;
10002 if ( elem->GetType() == SMDSAbs_Face ) {
10003 faceSet->insert( elem );
10004 set <const SMDS_MeshNode*> faceNodeSet;
10005 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10006 while ( nodeIt->more() ) {
10007 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10008 nodeSet->insert( n );
10009 faceNodeSet.insert( n );
10011 setOfFaceNodeSet.insert( faceNodeSet );
10013 else if ( elem->GetType() == SMDSAbs_Volume )
10014 volSet->insert( elem );
10016 // ------------------------------------------------------------------------------
10017 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10018 // ------------------------------------------------------------------------------
10020 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10021 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10022 while ( fIt->more() ) { // loop on faces sharing a node
10023 const SMDS_MeshElement* f = fIt->next();
10024 if ( faceSet->find( f ) == faceSet->end() ) {
10025 // check if all nodes are in nodeSet and
10026 // complete setOfFaceNodeSet if they are
10027 set <const SMDS_MeshNode*> faceNodeSet;
10028 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10029 bool allInSet = true;
10030 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10031 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10032 if ( nodeSet->find( n ) == nodeSet->end() )
10035 faceNodeSet.insert( n );
10038 faceSet->insert( f );
10039 setOfFaceNodeSet.insert( faceNodeSet );
10045 // -------------------------------------------------------------------------
10046 // 1c. Create temporary faces representing sides of volumes if correspondent
10047 // face does not exist
10048 // -------------------------------------------------------------------------
10050 if ( !volSet->empty() ) {
10051 //int nodeSetSize = nodeSet->size();
10053 // loop on given volumes
10054 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10055 SMDS_VolumeTool vol (*vIt);
10056 // loop on volume faces: find free faces
10057 // --------------------------------------
10058 list<const SMDS_MeshElement* > freeFaceList;
10059 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10060 if ( !vol.IsFreeFace( iFace ))
10062 // check if there is already a face with same nodes in a face set
10063 const SMDS_MeshElement* aFreeFace = 0;
10064 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10065 int nbNodes = vol.NbFaceNodes( iFace );
10066 set <const SMDS_MeshNode*> faceNodeSet;
10067 vol.GetFaceNodes( iFace, faceNodeSet );
10068 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10070 // no such a face is given but it still can exist, check it
10071 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10072 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10074 if ( !aFreeFace ) {
10075 // create a temporary face
10076 if ( nbNodes == 3 ) {
10077 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10078 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10080 else if ( nbNodes == 4 ) {
10081 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10082 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10085 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10086 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10087 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10090 tempFaceList.push_back( aFreeFace );
10094 freeFaceList.push_back( aFreeFace );
10096 } // loop on faces of a volume
10098 // choose one of several free faces of a volume
10099 // --------------------------------------------
10100 if ( freeFaceList.size() > 1 ) {
10101 // choose a face having max nb of nodes shared by other elems of a side
10102 int maxNbNodes = -1;
10103 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10104 while ( fIt != freeFaceList.end() ) { // loop on free faces
10105 int nbSharedNodes = 0;
10106 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10107 while ( nodeIt->more() ) { // loop on free face nodes
10108 const SMDS_MeshNode* n =
10109 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10110 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10111 while ( invElemIt->more() ) {
10112 const SMDS_MeshElement* e = invElemIt->next();
10113 nbSharedNodes += faceSet->count( e );
10114 nbSharedNodes += elemSet->count( e );
10117 if ( nbSharedNodes > maxNbNodes ) {
10118 maxNbNodes = nbSharedNodes;
10119 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10121 else if ( nbSharedNodes == maxNbNodes ) {
10125 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10128 if ( freeFaceList.size() > 1 )
10130 // could not choose one face, use another way
10131 // choose a face most close to the bary center of the opposite side
10132 gp_XYZ aBC( 0., 0., 0. );
10133 set <const SMDS_MeshNode*> addedNodes;
10134 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10135 eIt = elemSet2->begin();
10136 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10137 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10138 while ( nodeIt->more() ) { // loop on free face nodes
10139 const SMDS_MeshNode* n =
10140 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10141 if ( addedNodes.insert( n ).second )
10142 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10145 aBC /= addedNodes.size();
10146 double minDist = DBL_MAX;
10147 fIt = freeFaceList.begin();
10148 while ( fIt != freeFaceList.end() ) { // loop on free faces
10150 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10151 while ( nodeIt->more() ) { // loop on free face nodes
10152 const SMDS_MeshNode* n =
10153 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10154 gp_XYZ p( n->X(),n->Y(),n->Z() );
10155 dist += ( aBC - p ).SquareModulus();
10157 if ( dist < minDist ) {
10159 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10162 fIt = freeFaceList.erase( fIt++ );
10165 } // choose one of several free faces of a volume
10167 if ( freeFaceList.size() == 1 ) {
10168 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10169 faceSet->insert( aFreeFace );
10170 // complete a node set with nodes of a found free face
10171 // for ( iNode = 0; iNode < ; iNode++ )
10172 // nodeSet->insert( fNodes[ iNode ] );
10175 } // loop on volumes of a side
10177 // // complete a set of faces if new nodes in a nodeSet appeared
10178 // // ----------------------------------------------------------
10179 // if ( nodeSetSize != nodeSet->size() ) {
10180 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10181 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10182 // while ( fIt->more() ) { // loop on faces sharing a node
10183 // const SMDS_MeshElement* f = fIt->next();
10184 // if ( faceSet->find( f ) == faceSet->end() ) {
10185 // // check if all nodes are in nodeSet and
10186 // // complete setOfFaceNodeSet if they are
10187 // set <const SMDS_MeshNode*> faceNodeSet;
10188 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10189 // bool allInSet = true;
10190 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10191 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10192 // if ( nodeSet->find( n ) == nodeSet->end() )
10193 // allInSet = false;
10195 // faceNodeSet.insert( n );
10197 // if ( allInSet ) {
10198 // faceSet->insert( f );
10199 // setOfFaceNodeSet.insert( faceNodeSet );
10205 } // Create temporary faces, if there are volumes given
10208 if ( faceSet1.size() != faceSet2.size() ) {
10209 // delete temporary faces: they are in reverseElements of actual nodes
10210 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10211 // while ( tmpFaceIt->more() )
10212 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10213 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10214 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10215 // aMesh->RemoveElement(*tmpFaceIt);
10216 MESSAGE("Diff nb of faces");
10217 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10220 // ============================================================
10221 // 2. Find nodes to merge:
10222 // bind a node to remove to a node to put instead
10223 // ============================================================
10225 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10226 if ( theFirstNode1 != theFirstNode2 )
10227 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10228 if ( theSecondNode1 != theSecondNode2 )
10229 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10231 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10232 set< long > linkIdSet; // links to process
10233 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10235 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10236 list< NLink > linkList[2];
10237 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10238 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10239 // loop on links in linkList; find faces by links and append links
10240 // of the found faces to linkList
10241 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10242 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10244 NLink link[] = { *linkIt[0], *linkIt[1] };
10245 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10246 if ( !linkIdSet.count( linkID ) )
10249 // by links, find faces in the face sets,
10250 // and find indices of link nodes in the found faces;
10251 // in a face set, there is only one or no face sharing a link
10252 // ---------------------------------------------------------------
10254 const SMDS_MeshElement* face[] = { 0, 0 };
10255 vector<const SMDS_MeshNode*> fnodes[2];
10256 int iLinkNode[2][2];
10257 TIDSortedElemSet avoidSet;
10258 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10259 const SMDS_MeshNode* n1 = link[iSide].first;
10260 const SMDS_MeshNode* n2 = link[iSide].second;
10261 //cout << "Side " << iSide << " ";
10262 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10263 // find a face by two link nodes
10264 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10265 *faceSetPtr[ iSide ], avoidSet,
10266 &iLinkNode[iSide][0],
10267 &iLinkNode[iSide][1] );
10268 if ( face[ iSide ])
10270 //cout << " F " << face[ iSide]->GetID() <<endl;
10271 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10272 // put face nodes to fnodes
10273 if ( face[ iSide ]->IsQuadratic() )
10275 // use interlaced nodes iterator
10276 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10277 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10278 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10279 while ( nIter->more() )
10280 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10284 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10285 face[ iSide ]->end_nodes() );
10287 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10291 // check similarity of elements of the sides
10292 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10293 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10294 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10295 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10298 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10300 break; // do not return because it's necessary to remove tmp faces
10303 // set nodes to merge
10304 // -------------------
10306 if ( face[0] && face[1] ) {
10307 const int nbNodes = face[0]->NbNodes();
10308 if ( nbNodes != face[1]->NbNodes() ) {
10309 MESSAGE("Diff nb of face nodes");
10310 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10311 break; // do not return because it s necessary to remove tmp faces
10313 bool reverse[] = { false, false }; // order of nodes in the link
10314 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10315 // analyse link orientation in faces
10316 int i1 = iLinkNode[ iSide ][ 0 ];
10317 int i2 = iLinkNode[ iSide ][ 1 ];
10318 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10320 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10321 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10322 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10324 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10325 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10328 // add other links of the faces to linkList
10329 // -----------------------------------------
10331 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10332 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10333 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10334 if ( !iter_isnew.second ) { // already in a set: no need to process
10335 linkIdSet.erase( iter_isnew.first );
10337 else // new in set == encountered for the first time: add
10339 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10340 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10341 linkList[0].push_back ( NLink( n1, n2 ));
10342 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10347 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10350 } // loop on link lists
10352 if ( aResult == SEW_OK &&
10353 ( //linkIt[0] != linkList[0].end() ||
10354 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10355 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10356 " " << (faceSetPtr[1]->empty()));
10357 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10360 // ====================================================================
10361 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10362 // ====================================================================
10364 // delete temporary faces
10365 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10366 // while ( tmpFaceIt->more() )
10367 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10368 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10369 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10370 aMesh->RemoveElement(*tmpFaceIt);
10372 if ( aResult != SEW_OK)
10375 list< int > nodeIDsToRemove;
10376 vector< const SMDS_MeshNode*> nodes;
10377 ElemFeatures elemType;
10379 // loop on nodes replacement map
10380 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10381 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10382 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10384 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10385 nodeIDsToRemove.push_back( nToRemove->GetID() );
10386 // loop on elements sharing nToRemove
10387 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10388 while ( invElemIt->more() ) {
10389 const SMDS_MeshElement* e = invElemIt->next();
10390 // get a new suite of nodes: make replacement
10391 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10392 nodes.resize( nbNodes );
10393 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10394 while ( nIt->more() ) {
10395 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10396 nnIt = nReplaceMap.find( n );
10397 if ( nnIt != nReplaceMap.end() ) {
10399 n = (*nnIt).second;
10403 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10404 // elemIDsToRemove.push_back( e->GetID() );
10408 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10409 aMesh->RemoveElement( e );
10411 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10413 AddToSameGroups( newElem, e, aMesh );
10414 if ( int aShapeId = e->getshapeId() )
10415 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10421 Remove( nodeIDsToRemove, true );
10426 //================================================================================
10428 * \brief Find corresponding nodes in two sets of faces
10429 * \param theSide1 - first face set
10430 * \param theSide2 - second first face
10431 * \param theFirstNode1 - a boundary node of set 1
10432 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10433 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10434 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10435 * \param nReplaceMap - output map of corresponding nodes
10436 * \return bool - is a success or not
10438 //================================================================================
10441 //#define DEBUG_MATCHING_NODES
10444 SMESH_MeshEditor::Sew_Error
10445 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10446 set<const SMDS_MeshElement*>& theSide2,
10447 const SMDS_MeshNode* theFirstNode1,
10448 const SMDS_MeshNode* theFirstNode2,
10449 const SMDS_MeshNode* theSecondNode1,
10450 const SMDS_MeshNode* theSecondNode2,
10451 TNodeNodeMap & nReplaceMap)
10453 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10455 nReplaceMap.clear();
10456 if ( theFirstNode1 != theFirstNode2 )
10457 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10458 if ( theSecondNode1 != theSecondNode2 )
10459 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10461 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10462 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10464 list< NLink > linkList[2];
10465 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10466 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10468 // loop on links in linkList; find faces by links and append links
10469 // of the found faces to linkList
10470 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10471 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10472 NLink link[] = { *linkIt[0], *linkIt[1] };
10473 if ( linkSet.find( link[0] ) == linkSet.end() )
10476 // by links, find faces in the face sets,
10477 // and find indices of link nodes in the found faces;
10478 // in a face set, there is only one or no face sharing a link
10479 // ---------------------------------------------------------------
10481 const SMDS_MeshElement* face[] = { 0, 0 };
10482 list<const SMDS_MeshNode*> notLinkNodes[2];
10483 //bool reverse[] = { false, false }; // order of notLinkNodes
10485 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10487 const SMDS_MeshNode* n1 = link[iSide].first;
10488 const SMDS_MeshNode* n2 = link[iSide].second;
10489 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10490 set< const SMDS_MeshElement* > facesOfNode1;
10491 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10493 // during a loop of the first node, we find all faces around n1,
10494 // during a loop of the second node, we find one face sharing both n1 and n2
10495 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10496 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10497 while ( fIt->more() ) { // loop on faces sharing a node
10498 const SMDS_MeshElement* f = fIt->next();
10499 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10500 ! facesOfNode1.insert( f ).second ) // f encounters twice
10502 if ( face[ iSide ] ) {
10503 MESSAGE( "2 faces per link " );
10504 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10507 faceSet->erase( f );
10509 // get not link nodes
10510 int nbN = f->NbNodes();
10511 if ( f->IsQuadratic() )
10513 nbNodes[ iSide ] = nbN;
10514 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10515 int i1 = f->GetNodeIndex( n1 );
10516 int i2 = f->GetNodeIndex( n2 );
10517 int iEnd = nbN, iBeg = -1, iDelta = 1;
10518 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10520 std::swap( iEnd, iBeg ); iDelta = -1;
10525 if ( i == iEnd ) i = iBeg + iDelta;
10526 if ( i == i1 ) break;
10527 nodes.push_back ( f->GetNode( i ) );
10533 // check similarity of elements of the sides
10534 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10535 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10536 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10537 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10540 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10544 // set nodes to merge
10545 // -------------------
10547 if ( face[0] && face[1] ) {
10548 if ( nbNodes[0] != nbNodes[1] ) {
10549 MESSAGE("Diff nb of face nodes");
10550 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10552 #ifdef DEBUG_MATCHING_NODES
10553 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10554 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10555 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10557 int nbN = nbNodes[0];
10559 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10560 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10561 for ( int i = 0 ; i < nbN - 2; ++i ) {
10562 #ifdef DEBUG_MATCHING_NODES
10563 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10565 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10569 // add other links of the face 1 to linkList
10570 // -----------------------------------------
10572 const SMDS_MeshElement* f0 = face[0];
10573 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10574 for ( int i = 0; i < nbN; i++ )
10576 const SMDS_MeshNode* n2 = f0->GetNode( i );
10577 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10578 linkSet.insert( SMESH_TLink( n1, n2 ));
10579 if ( !iter_isnew.second ) { // already in a set: no need to process
10580 linkSet.erase( iter_isnew.first );
10582 else // new in set == encountered for the first time: add
10584 #ifdef DEBUG_MATCHING_NODES
10585 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10586 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10588 linkList[0].push_back ( NLink( n1, n2 ));
10589 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10594 } // loop on link lists
10599 namespace // automatically find theAffectedElems for DoubleNodes()
10601 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10603 //--------------------------------------------------------------------------------
10604 // Nodes shared by adjacent FissureBorder's.
10605 // 1 node if FissureBorder separates faces
10606 // 2 nodes if FissureBorder separates volumes
10609 const SMDS_MeshNode* _nodes[2];
10612 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10616 _nbNodes = bool( n1 ) + bool( n2 );
10617 if ( _nbNodes == 2 && n1 > n2 )
10618 std::swap( _nodes[0], _nodes[1] );
10620 bool operator<( const SubBorder& other ) const
10622 for ( int i = 0; i < _nbNodes; ++i )
10624 if ( _nodes[i] < other._nodes[i] ) return true;
10625 if ( _nodes[i] > other._nodes[i] ) return false;
10631 //--------------------------------------------------------------------------------
10632 // Map a SubBorder to all FissureBorder it bounds
10633 struct FissureBorder;
10634 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10635 typedef TBorderLinks::iterator TMappedSub;
10637 //--------------------------------------------------------------------------------
10639 * \brief Element border (volume facet or face edge) at a fissure
10641 struct FissureBorder
10643 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10644 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10646 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10647 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10649 FissureBorder( FissureBorder && from ) // move constructor
10651 std::swap( _nodes, from._nodes );
10652 std::swap( _sortedNodes, from._sortedNodes );
10653 _elems[0] = from._elems[0];
10654 _elems[1] = from._elems[1];
10657 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10658 std::vector< const SMDS_MeshElement* > & adjElems)
10659 : _nodes( elemToDuplicate->NbCornerNodes() )
10661 for ( size_t i = 0; i < _nodes.size(); ++i )
10662 _nodes[i] = elemToDuplicate->GetNode( i );
10664 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10665 findAdjacent( type, adjElems );
10668 FissureBorder( const SMDS_MeshNode** nodes,
10669 const size_t nbNodes,
10670 const SMDSAbs_ElementType adjElemsType,
10671 std::vector< const SMDS_MeshElement* > & adjElems)
10672 : _nodes( nodes, nodes + nbNodes )
10674 findAdjacent( adjElemsType, adjElems );
10677 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10678 std::vector< const SMDS_MeshElement* > & adjElems)
10680 _elems[0] = _elems[1] = 0;
10682 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10683 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10684 _elems[i] = adjElems[i];
10687 bool operator<( const FissureBorder& other ) const
10689 return GetSortedNodes() < other.GetSortedNodes();
10692 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10694 if ( _sortedNodes.empty() && !_nodes.empty() )
10696 FissureBorder* me = const_cast<FissureBorder*>( this );
10697 me->_sortedNodes = me->_nodes;
10698 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10700 return _sortedNodes;
10703 size_t NbSub() const
10705 return _nodes.size();
10708 SubBorder Sub(size_t i) const
10710 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10713 void AddSelfTo( TBorderLinks& borderLinks )
10715 _mappedSubs.resize( NbSub() );
10716 for ( size_t i = 0; i < NbSub(); ++i )
10718 TBorderLinks::iterator s2b =
10719 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10720 s2b->second.push_back( this );
10721 _mappedSubs[ i ] = s2b;
10730 const SMDS_MeshElement* GetMarkedElem() const
10732 if ( _nodes.empty() ) return 0; // cleared
10733 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10734 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10738 gp_XYZ GetNorm() const // normal to the border
10741 if ( _nodes.size() == 2 )
10743 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10744 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10746 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10749 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10750 norm = bordDir ^ avgNorm;
10754 SMESH_NodeXYZ p0( _nodes[0] );
10755 SMESH_NodeXYZ p1( _nodes[1] );
10756 SMESH_NodeXYZ p2( _nodes[2] );
10757 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10759 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10765 void ChooseSide() // mark an _elem located at positive side of fissure
10767 _elems[0]->setIsMarked( true );
10768 gp_XYZ norm = GetNorm();
10769 double maxX = norm.Coord(1);
10770 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10771 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10774 _elems[0]->setIsMarked( false );
10775 _elems[1]->setIsMarked( true );
10779 }; // struct FissureBorder
10781 //--------------------------------------------------------------------------------
10783 * \brief Classifier of elements at fissure edge
10785 class FissureNormal
10787 std::vector< gp_XYZ > _normals;
10791 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10794 _normals.reserve(2);
10795 _normals.push_back( bord.GetNorm() );
10796 if ( _normals.size() == 2 )
10797 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10800 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10803 switch ( _normals.size() ) {
10806 isIn = !isOut( n, _normals[0], elem );
10811 bool in1 = !isOut( n, _normals[0], elem );
10812 bool in2 = !isOut( n, _normals[1], elem );
10813 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10820 //================================================================================
10822 * \brief Classify an element by a plane passing through a node
10824 //================================================================================
10826 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10828 SMESH_NodeXYZ p = n;
10830 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10832 SMESH_NodeXYZ pi = elem->GetNode( i );
10833 sumDot += norm * ( pi - p );
10835 return sumDot < -1e-100;
10838 //================================================================================
10840 * \brief Find FissureBorder's by nodes to duplicate
10842 //================================================================================
10844 void findFissureBorders( const TIDSortedElemSet& theNodes,
10845 std::vector< FissureBorder > & theFissureBorders )
10847 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10848 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10850 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10851 if ( n->NbInverseElements( elemType ) == 0 )
10853 elemType = SMDSAbs_Face;
10854 if ( n->NbInverseElements( elemType ) == 0 )
10857 // unmark elements touching the fissure
10858 for ( ; nIt != theNodes.end(); ++nIt )
10859 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10861 // loop on elements touching the fissure to get their borders belonging to the fissure
10862 std::set< FissureBorder > fissureBorders;
10863 std::vector< const SMDS_MeshElement* > adjElems;
10864 std::vector< const SMDS_MeshNode* > nodes;
10865 SMDS_VolumeTool volTool;
10866 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10868 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10869 while ( invIt->more() )
10871 const SMDS_MeshElement* eInv = invIt->next();
10872 if ( eInv->isMarked() ) continue;
10873 eInv->setIsMarked( true );
10875 if ( elemType == SMDSAbs_Volume )
10877 volTool.Set( eInv );
10878 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10879 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10881 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10882 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10884 bool allOnFissure = true;
10885 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10886 if (( allOnFissure = theNodes.count( nn[ iN ])))
10887 nodes.push_back( nn[ iN ]);
10888 if ( allOnFissure )
10889 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10890 elemType, adjElems )));
10893 else // elemType == SMDSAbs_Face
10895 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10896 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10897 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10899 nn[1] = eInv->GetNode( iN );
10900 onFissure1 = theNodes.count( nn[1] );
10901 if ( onFissure0 && onFissure1 )
10902 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10904 onFissure0 = onFissure1;
10910 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10911 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10912 for ( ; bord != fissureBorders.end(); ++bord )
10914 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10917 } // findFissureBorders()
10919 //================================================================================
10921 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10922 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10923 * \param [in] theNodesNot - nodes not to duplicate
10924 * \param [out] theAffectedElems - the found elements
10926 //================================================================================
10928 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10929 TIDSortedElemSet& theAffectedElems)
10931 if ( theElemsOrNodes.empty() ) return;
10933 // find FissureBorder's
10935 std::vector< FissureBorder > fissure;
10936 std::vector< const SMDS_MeshElement* > elemsByFacet;
10938 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10939 if ( (*elIt)->GetType() == SMDSAbs_Node )
10941 findFissureBorders( theElemsOrNodes, fissure );
10945 fissure.reserve( theElemsOrNodes.size() );
10946 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10947 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10949 if ( fissure.empty() )
10952 // fill borderLinks
10954 TBorderLinks borderLinks;
10956 for ( size_t i = 0; i < fissure.size(); ++i )
10958 fissure[i].AddSelfTo( borderLinks );
10961 // get theAffectedElems
10963 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10964 for ( size_t i = 0; i < fissure.size(); ++i )
10965 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10967 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10968 false, /*markElem=*/true );
10971 std::vector<const SMDS_MeshNode *> facetNodes;
10972 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10973 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10975 // choose a side of fissure
10976 fissure[0].ChooseSide();
10977 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10979 size_t nbCheckedBorders = 0;
10980 while ( nbCheckedBorders < fissure.size() )
10982 // find a FissureBorder to treat
10983 FissureBorder* bord = 0;
10984 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10985 if ( fissure[i].GetMarkedElem() )
10986 bord = & fissure[i];
10987 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10988 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10990 bord = & fissure[i];
10991 bord->ChooseSide();
10992 theAffectedElems.insert( bord->GetMarkedElem() );
10994 if ( !bord ) return;
10995 ++nbCheckedBorders;
10997 // treat FissureBorder's linked to bord
10998 fissureNodes.clear();
10999 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
11000 for ( size_t i = 0; i < bord->NbSub(); ++i )
11002 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
11003 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
11004 std::vector< FissureBorder* >& linkedBorders = l2b->second;
11005 const SubBorder& sb = l2b->first;
11006 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
11008 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
11010 for ( int j = 0; j < sb._nbNodes; ++j )
11011 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
11015 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
11016 // until an elem adjacent to a neighbour FissureBorder is found
11017 facetNodes.clear();
11018 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
11019 facetNodes.resize( sb._nbNodes + 1 );
11023 // check if bordElem is adjacent to a neighbour FissureBorder
11024 for ( size_t j = 0; j < linkedBorders.size(); ++j )
11026 FissureBorder* bord2 = linkedBorders[j];
11027 if ( bord2 == bord ) continue;
11028 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
11031 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
11036 // find the next bordElem
11037 const SMDS_MeshElement* nextBordElem = 0;
11038 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
11040 const SMDS_MeshNode* n = bordElem->GetNode( iN );
11041 if ( fissureNodes.count( n )) continue;
11043 facetNodes[ sb._nbNodes ] = n;
11044 elemsByFacet.clear();
11045 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
11047 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
11048 if ( elemsByFacet[ iE ] != bordElem &&
11049 !elemsByFacet[ iE ]->isMarked() )
11051 theAffectedElems.insert( elemsByFacet[ iE ]);
11052 elemsByFacet[ iE ]->setIsMarked( true );
11053 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
11054 nextBordElem = elemsByFacet[ iE ];
11058 bordElem = nextBordElem;
11060 } // while ( bordElem )
11062 linkedBorders.clear(); // not to treat this link any more
11064 } // loop on SubBorder's of a FissureBorder
11068 } // loop on FissureBorder's
11071 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11073 // mark nodes of theAffectedElems
11074 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11076 // unmark nodes of the fissure
11077 elIt = theElemsOrNodes.begin();
11078 if ( (*elIt)->GetType() == SMDSAbs_Node )
11079 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11081 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11083 std::vector< gp_XYZ > normVec;
11085 // loop on nodes of the fissure, add elements having marked nodes
11086 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11088 const SMDS_MeshElement* e = (*elIt);
11089 if ( e->GetType() != SMDSAbs_Node )
11090 e->setIsMarked( true ); // avoid adding a fissure element
11092 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11094 const SMDS_MeshNode* n = e->GetNode( iN );
11095 if ( fissEdgeNodes2Norm.count( n ))
11098 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11099 while ( invIt->more() )
11101 const SMDS_MeshElement* eInv = invIt->next();
11102 if ( eInv->isMarked() ) continue;
11103 eInv->setIsMarked( true );
11105 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11106 while( nIt->more() )
11107 if ( nIt->next()->isMarked())
11109 theAffectedElems.insert( eInv );
11110 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11111 n->setIsMarked( false );
11118 // add elements on the fissure edge
11119 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11120 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11122 const SMDS_MeshNode* edgeNode = n2N->first;
11123 const FissureNormal & normals = n2N->second;
11125 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11126 while ( invIt->more() )
11128 const SMDS_MeshElement* eInv = invIt->next();
11129 if ( eInv->isMarked() ) continue;
11130 eInv->setIsMarked( true );
11132 // classify eInv using normals
11133 bool toAdd = normals.IsIn( edgeNode, eInv );
11134 if ( toAdd ) // check if all nodes lie on the fissure edge
11136 bool notOnEdge = false;
11137 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
11138 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11143 theAffectedElems.insert( eInv );
11149 } // findAffectedElems()
11152 //================================================================================
11154 * \brief Create elements equal (on same nodes) to given ones
11155 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
11156 * elements of the uppest dimension are duplicated.
11158 //================================================================================
11160 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11162 ClearLastCreated();
11163 SMESHDS_Mesh* mesh = GetMeshDS();
11165 // get an element type and an iterator over elements
11167 SMDSAbs_ElementType type = SMDSAbs_All;
11168 SMDS_ElemIteratorPtr elemIt;
11169 if ( theElements.empty() )
11171 if ( mesh->NbNodes() == 0 )
11173 // get most complex type
11174 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11175 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11176 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11178 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11179 if ( mesh->GetMeshInfo().NbElements( types[i] ))
11184 elemIt = mesh->elementsIterator( type );
11188 type = (*theElements.begin())->GetType();
11189 elemIt = elemSetIterator( theElements );
11192 // duplicate elements
11194 ElemFeatures elemType;
11196 vector< const SMDS_MeshNode* > nodes;
11197 while ( elemIt->more() )
11199 const SMDS_MeshElement* elem = elemIt->next();
11200 if ( elem->GetType() != type )
11203 elemType.Init( elem, /*basicOnly=*/false );
11204 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11206 AddElement( nodes, elemType );
11210 //================================================================================
11212 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11213 \param theElems - the list of elements (edges or faces) to be replicated
11214 The nodes for duplication could be found from these elements
11215 \param theNodesNot - list of nodes to NOT replicate
11216 \param theAffectedElems - the list of elements (cells and edges) to which the
11217 replicated nodes should be associated to.
11218 \return TRUE if operation has been completed successfully, FALSE otherwise
11220 //================================================================================
11222 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11223 const TIDSortedElemSet& theNodesNot,
11224 const TIDSortedElemSet& theAffectedElems )
11226 ClearLastCreated();
11228 if ( theElems.size() == 0 )
11231 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11236 TNodeNodeMap anOldNodeToNewNode;
11237 // duplicate elements and nodes
11238 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11239 // replce nodes by duplications
11240 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11244 //================================================================================
11246 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11247 \param theMeshDS - mesh instance
11248 \param theElems - the elements replicated or modified (nodes should be changed)
11249 \param theNodesNot - nodes to NOT replicate
11250 \param theNodeNodeMap - relation of old node to new created node
11251 \param theIsDoubleElem - flag os to replicate element or modify
11252 \return TRUE if operation has been completed successfully, FALSE otherwise
11254 //================================================================================
11256 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11257 const TIDSortedElemSet& theElems,
11258 const TIDSortedElemSet& theNodesNot,
11259 TNodeNodeMap& theNodeNodeMap,
11260 const bool theIsDoubleElem )
11262 // iterate through element and duplicate them (by nodes duplication)
11264 std::vector<const SMDS_MeshNode*> newNodes;
11265 ElemFeatures elemType;
11267 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11268 for ( ; elemItr != theElems.end(); ++elemItr )
11270 const SMDS_MeshElement* anElem = *elemItr;
11274 // duplicate nodes to duplicate element
11275 bool isDuplicate = false;
11276 newNodes.resize( anElem->NbNodes() );
11277 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11279 while ( anIter->more() )
11281 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11282 const SMDS_MeshNode* aNewNode = aCurrNode;
11283 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11284 if ( n2n != theNodeNodeMap.end() )
11286 aNewNode = n2n->second;
11288 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11291 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11292 copyPosition( aCurrNode, aNewNode );
11293 theNodeNodeMap[ aCurrNode ] = aNewNode;
11294 myLastCreatedNodes.Append( aNewNode );
11296 isDuplicate |= (aCurrNode != aNewNode);
11297 newNodes[ ind++ ] = aNewNode;
11299 if ( !isDuplicate )
11302 if ( theIsDoubleElem )
11303 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11305 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11312 //================================================================================
11314 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11315 \param theNodes - identifiers of nodes to be doubled
11316 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11317 nodes. If list of element identifiers is empty then nodes are doubled but
11318 they not assigned to elements
11319 \return TRUE if operation has been completed successfully, FALSE otherwise
11321 //================================================================================
11323 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11324 const std::list< int >& theListOfModifiedElems )
11326 ClearLastCreated();
11328 if ( theListOfNodes.size() == 0 )
11331 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11335 // iterate through nodes and duplicate them
11337 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11339 std::list< int >::const_iterator aNodeIter;
11340 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11342 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11348 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11351 copyPosition( aNode, aNewNode );
11352 anOldNodeToNewNode[ aNode ] = aNewNode;
11353 myLastCreatedNodes.Append( aNewNode );
11357 // Change nodes of elements
11359 std::vector<const SMDS_MeshNode*> aNodeArr;
11361 std::list< int >::const_iterator anElemIter;
11362 for ( anElemIter = theListOfModifiedElems.begin();
11363 anElemIter != theListOfModifiedElems.end();
11366 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11370 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11371 for( size_t i = 0; i < aNodeArr.size(); ++i )
11373 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11374 anOldNodeToNewNode.find( aNodeArr[ i ]);
11375 if ( n2n != anOldNodeToNewNode.end() )
11376 aNodeArr[ i ] = n2n->second;
11378 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11386 //================================================================================
11388 \brief Check if element located inside shape
11389 \return TRUE if IN or ON shape, FALSE otherwise
11391 //================================================================================
11393 template<class Classifier>
11394 bool isInside(const SMDS_MeshElement* theElem,
11395 Classifier& theClassifier,
11396 const double theTol)
11398 gp_XYZ centerXYZ (0, 0, 0);
11399 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11400 while ( aNodeItr->more() )
11401 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11403 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11404 theClassifier.Perform(aPnt, theTol);
11405 TopAbs_State aState = theClassifier.State();
11406 return (aState == TopAbs_IN || aState == TopAbs_ON );
11409 //================================================================================
11411 * \brief Classifier of the 3D point on the TopoDS_Face
11412 * with interaface suitable for isInside()
11414 //================================================================================
11416 struct _FaceClassifier
11418 Extrema_ExtPS _extremum;
11419 BRepAdaptor_Surface _surface;
11420 TopAbs_State _state;
11422 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11424 _extremum.Initialize( _surface,
11425 _surface.FirstUParameter(), _surface.LastUParameter(),
11426 _surface.FirstVParameter(), _surface.LastVParameter(),
11427 _surface.Tolerance(), _surface.Tolerance() );
11429 void Perform(const gp_Pnt& aPnt, double theTol)
11432 _state = TopAbs_OUT;
11433 _extremum.Perform(aPnt);
11434 if ( _extremum.IsDone() )
11435 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11436 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11438 TopAbs_State State() const
11445 //================================================================================
11447 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11448 This method is the first step of DoubleNodeElemGroupsInRegion.
11449 \param theElems - list of groups of elements (edges or faces) to be replicated
11450 \param theNodesNot - list of groups of nodes not to replicated
11451 \param theShape - shape to detect affected elements (element which geometric center
11452 located on or inside shape). If the shape is null, detection is done on faces orientations
11453 (select elements with a gravity center on the side given by faces normals).
11454 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11455 The replicated nodes should be associated to affected elements.
11457 \sa DoubleNodeElemGroupsInRegion()
11459 //================================================================================
11461 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11462 const TIDSortedElemSet& theNodesNot,
11463 const TopoDS_Shape& theShape,
11464 TIDSortedElemSet& theAffectedElems)
11466 if ( theShape.IsNull() )
11468 findAffectedElems( theElems, theAffectedElems );
11472 const double aTol = Precision::Confusion();
11473 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11474 auto_ptr<_FaceClassifier> aFaceClassifier;
11475 if ( theShape.ShapeType() == TopAbs_SOLID )
11477 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11478 bsc3d->PerformInfinitePoint(aTol);
11480 else if (theShape.ShapeType() == TopAbs_FACE )
11482 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11485 // iterates on indicated elements and get elements by back references from their nodes
11486 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11487 for ( ; elemItr != theElems.end(); ++elemItr )
11489 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11490 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11491 while ( nodeItr->more() )
11493 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11494 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11496 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11497 while ( backElemItr->more() )
11499 const SMDS_MeshElement* curElem = backElemItr->next();
11500 if ( curElem && theElems.find(curElem) == theElems.end() &&
11502 isInside( curElem, *bsc3d, aTol ) :
11503 isInside( curElem, *aFaceClassifier, aTol )))
11504 theAffectedElems.insert( curElem );
11512 //================================================================================
11514 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11515 \param theElems - group of of elements (edges or faces) to be replicated
11516 \param theNodesNot - group of nodes not to replicate
11517 \param theShape - shape to detect affected elements (element which geometric center
11518 located on or inside shape).
11519 The replicated nodes should be associated to affected elements.
11520 \return TRUE if operation has been completed successfully, FALSE otherwise
11522 //================================================================================
11524 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11525 const TIDSortedElemSet& theNodesNot,
11526 const TopoDS_Shape& theShape )
11528 if ( theShape.IsNull() )
11531 const double aTol = Precision::Confusion();
11532 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11533 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11534 if ( theShape.ShapeType() == TopAbs_SOLID )
11536 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11537 bsc3d->PerformInfinitePoint(aTol);
11539 else if (theShape.ShapeType() == TopAbs_FACE )
11541 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11544 // iterates on indicated elements and get elements by back references from their nodes
11545 TIDSortedElemSet anAffected;
11546 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11547 for ( ; elemItr != theElems.end(); ++elemItr )
11549 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11553 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11554 while ( nodeItr->more() )
11556 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11557 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11559 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11560 while ( backElemItr->more() )
11562 const SMDS_MeshElement* curElem = backElemItr->next();
11563 if ( curElem && theElems.find(curElem) == theElems.end() &&
11565 isInside( curElem, *bsc3d, aTol ) :
11566 isInside( curElem, *aFaceClassifier, aTol )))
11567 anAffected.insert( curElem );
11571 return DoubleNodes( theElems, theNodesNot, anAffected );
11575 * \brief compute an oriented angle between two planes defined by four points.
11576 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11577 * @param p0 base of the rotation axe
11578 * @param p1 extremity of the rotation axe
11579 * @param g1 belongs to the first plane
11580 * @param g2 belongs to the second plane
11582 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11584 gp_Vec vref(p0, p1);
11587 gp_Vec n1 = vref.Crossed(v1);
11588 gp_Vec n2 = vref.Crossed(v2);
11590 return n2.AngleWithRef(n1, vref);
11592 catch ( Standard_Failure ) {
11594 return Max( v1.Magnitude(), v2.Magnitude() );
11598 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11599 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11600 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11601 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11602 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11603 * 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.
11604 * 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.
11605 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11606 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11607 * \param theElems - list of groups of volumes, where a group of volume is a set of
11608 * SMDS_MeshElements sorted by Id.
11609 * \param createJointElems - if TRUE, create the elements
11610 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11611 * the boundary between \a theDomains and the rest mesh
11612 * \return TRUE if operation has been completed successfully, FALSE otherwise
11614 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11615 bool createJointElems,
11616 bool onAllBoundaries)
11618 // MESSAGE("----------------------------------------------");
11619 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11620 // MESSAGE("----------------------------------------------");
11622 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11623 meshDS->BuildDownWardConnectivity(true);
11625 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11627 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11628 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11629 // build the list of nodes shared by 2 or more domains, with their domain indexes
11631 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11632 std::map<int,int>celldom; // cell vtkId --> domain
11633 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11634 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11635 faceDomains.clear();
11637 cellDomains.clear();
11638 nodeDomains.clear();
11639 std::map<int,int> emptyMap;
11640 std::set<int> emptySet;
11643 //MESSAGE(".. Number of domains :"<<theElems.size());
11645 TIDSortedElemSet theRestDomElems;
11646 const int iRestDom = -1;
11647 const int idom0 = onAllBoundaries ? iRestDom : 0;
11648 const int nbDomains = theElems.size();
11650 // Check if the domains do not share an element
11651 for (int idom = 0; idom < nbDomains-1; idom++)
11653 // MESSAGE("... Check of domain #" << idom);
11654 const TIDSortedElemSet& domain = theElems[idom];
11655 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11656 for (; elemItr != domain.end(); ++elemItr)
11658 const SMDS_MeshElement* anElem = *elemItr;
11659 int idombisdeb = idom + 1 ;
11660 // check if the element belongs to a domain further in the list
11661 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11663 const TIDSortedElemSet& domainbis = theElems[idombis];
11664 if ( domainbis.count( anElem ))
11666 MESSAGE(".... Domain #" << idom);
11667 MESSAGE(".... Domain #" << idombis);
11668 throw SALOME_Exception("The domains are not disjoint.");
11675 for (int idom = 0; idom < nbDomains; idom++)
11678 // --- build a map (face to duplicate --> volume to modify)
11679 // with all the faces shared by 2 domains (group of elements)
11680 // and corresponding volume of this domain, for each shared face.
11681 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11683 //MESSAGE("... Neighbors of domain #" << idom);
11684 const TIDSortedElemSet& domain = theElems[idom];
11685 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11686 for (; elemItr != domain.end(); ++elemItr)
11688 const SMDS_MeshElement* anElem = *elemItr;
11691 int vtkId = anElem->getVtkId();
11692 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11693 int neighborsVtkIds[NBMAXNEIGHBORS];
11694 int downIds[NBMAXNEIGHBORS];
11695 unsigned char downTypes[NBMAXNEIGHBORS];
11696 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11697 for (int n = 0; n < nbNeighbors; n++)
11699 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11700 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11701 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11704 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11706 // MESSAGE("Domain " << idombis);
11707 const TIDSortedElemSet& domainbis = theElems[idombis];
11708 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11710 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11712 DownIdType face(downIds[n], downTypes[n]);
11713 if (!faceDomains[face].count(idom))
11715 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11716 celldom[vtkId] = idom;
11717 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11721 theRestDomElems.insert( elem );
11722 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11723 celldom[neighborsVtkIds[n]] = iRestDom;
11731 //MESSAGE("Number of shared faces " << faceDomains.size());
11732 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11734 // --- explore the shared faces domain by domain,
11735 // explore the nodes of the face and see if they belong to a cell in the domain,
11736 // which has only a node or an edge on the border (not a shared face)
11738 for (int idomain = idom0; idomain < nbDomains; idomain++)
11740 //MESSAGE("Domain " << idomain);
11741 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11742 itface = faceDomains.begin();
11743 for (; itface != faceDomains.end(); ++itface)
11745 const std::map<int, int>& domvol = itface->second;
11746 if (!domvol.count(idomain))
11748 DownIdType face = itface->first;
11749 //MESSAGE(" --- face " << face.cellId);
11750 std::set<int> oldNodes;
11752 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11753 std::set<int>::iterator itn = oldNodes.begin();
11754 for (; itn != oldNodes.end(); ++itn)
11757 //MESSAGE(" node " << oldId);
11758 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11759 for (int i=0; i<l.ncells; i++)
11761 int vtkId = l.cells[i];
11762 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11763 if (!domain.count(anElem))
11765 int vtkType = grid->GetCellType(vtkId);
11766 int downId = grid->CellIdToDownId(vtkId);
11769 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11770 continue; // not OK at this stage of the algorithm:
11771 //no cells created after BuildDownWardConnectivity
11773 DownIdType aCell(downId, vtkType);
11774 cellDomains[aCell][idomain] = vtkId;
11775 celldom[vtkId] = idomain;
11776 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11782 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11783 // for each shared face, get the nodes
11784 // for each node, for each domain of the face, create a clone of the node
11786 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11787 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11788 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11790 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11791 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11792 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11794 //MESSAGE(".. Duplication of the nodes");
11795 for (int idomain = idom0; idomain < nbDomains; idomain++)
11797 itface = faceDomains.begin();
11798 for (; itface != faceDomains.end(); ++itface)
11800 const std::map<int, int>& domvol = itface->second;
11801 if (!domvol.count(idomain))
11803 DownIdType face = itface->first;
11804 //MESSAGE(" --- face " << face.cellId);
11805 std::set<int> oldNodes;
11807 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11808 std::set<int>::iterator itn = oldNodes.begin();
11809 for (; itn != oldNodes.end(); ++itn)
11812 if (nodeDomains[oldId].empty())
11814 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11815 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11817 std::map<int, int>::const_iterator itdom = domvol.begin();
11818 for (; itdom != domvol.end(); ++itdom)
11820 int idom = itdom->first;
11821 //MESSAGE(" domain " << idom);
11822 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11824 if (nodeDomains[oldId].size() >= 2) // a multiple node
11826 vector<int> orderedDoms;
11827 //MESSAGE("multiple node " << oldId);
11828 if (mutipleNodes.count(oldId))
11829 orderedDoms = mutipleNodes[oldId];
11832 map<int,int>::iterator it = nodeDomains[oldId].begin();
11833 for (; it != nodeDomains[oldId].end(); ++it)
11834 orderedDoms.push_back(it->first);
11836 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11837 //stringstream txt;
11838 //for (int i=0; i<orderedDoms.size(); i++)
11839 // txt << orderedDoms[i] << " ";
11840 //MESSAGE("orderedDoms " << txt.str());
11841 mutipleNodes[oldId] = orderedDoms;
11843 double *coords = grid->GetPoint(oldId);
11844 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11845 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11846 int newId = newNode->getVtkId();
11847 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11848 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11855 //MESSAGE(".. Creation of elements");
11856 for (int idomain = idom0; idomain < nbDomains; idomain++)
11858 itface = faceDomains.begin();
11859 for (; itface != faceDomains.end(); ++itface)
11861 std::map<int, int> domvol = itface->second;
11862 if (!domvol.count(idomain))
11864 DownIdType face = itface->first;
11865 //MESSAGE(" --- face " << face.cellId);
11866 std::set<int> oldNodes;
11868 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11869 int nbMultipleNodes = 0;
11870 std::set<int>::iterator itn = oldNodes.begin();
11871 for (; itn != oldNodes.end(); ++itn)
11874 if (mutipleNodes.count(oldId))
11877 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11879 //MESSAGE("multiple Nodes detected on a shared face");
11880 int downId = itface->first.cellId;
11881 unsigned char cellType = itface->first.cellType;
11882 // --- shared edge or shared face ?
11883 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11886 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11887 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11888 if (mutipleNodes.count(nodes[i]))
11889 if (!mutipleNodesToFace.count(nodes[i]))
11890 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11892 else // shared face (between two volumes)
11894 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11895 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11896 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11897 for (int ie =0; ie < nbEdges; ie++)
11900 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11901 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11903 vector<int> vn0 = mutipleNodes[nodes[0]];
11904 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11906 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11907 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11908 if ( vn0[i0] == vn1[i1] )
11909 doms.push_back( vn0[ i0 ]);
11910 if ( doms.size() > 2 )
11912 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11913 double *coords = grid->GetPoint(nodes[0]);
11914 gp_Pnt p0(coords[0], coords[1], coords[2]);
11915 coords = grid->GetPoint(nodes[nbNodes - 1]);
11916 gp_Pnt p1(coords[0], coords[1], coords[2]);
11918 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11919 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11920 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11921 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11922 for ( size_t id = 0; id < doms.size(); id++ )
11924 int idom = doms[id];
11925 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11926 for ( int ivol = 0; ivol < nbvol; ivol++ )
11928 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11929 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11930 if (domain.count(elem))
11932 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11933 domvol[idom] = svol;
11934 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11936 vtkIdType npts = 0;
11937 vtkIdType* pts = 0;
11938 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11939 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11942 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11943 angleDom[idom] = 0;
11947 gp_Pnt g(values[0], values[1], values[2]);
11948 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11949 //MESSAGE(" angle=" << angleDom[idom]);
11955 map<double, int> sortedDom; // sort domains by angle
11956 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11957 sortedDom[ia->second] = ia->first;
11958 vector<int> vnodes;
11960 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11962 vdom.push_back(ib->second);
11963 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11965 for (int ino = 0; ino < nbNodes; ino++)
11966 vnodes.push_back(nodes[ino]);
11967 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11976 // --- iterate on shared faces (volumes to modify, face to extrude)
11977 // get node id's of the face (id SMDS = id VTK)
11978 // create flat element with old and new nodes if requested
11980 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11981 // (domain1 X domain2) = domain1 + MAXINT*domain2
11983 std::map<int, std::map<long,int> > nodeQuadDomains;
11984 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11986 //MESSAGE(".. Creation of elements: simple junction");
11987 if (createJointElems)
11990 string joints2DName = "joints2D";
11991 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11992 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11993 string joints3DName = "joints3D";
11994 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11995 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11997 itface = faceDomains.begin();
11998 for (; itface != faceDomains.end(); ++itface)
12000 DownIdType face = itface->first;
12001 std::set<int> oldNodes;
12002 std::set<int>::iterator itn;
12004 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12006 std::map<int, int> domvol = itface->second;
12007 std::map<int, int>::iterator itdom = domvol.begin();
12008 int dom1 = itdom->first;
12009 int vtkVolId = itdom->second;
12011 int dom2 = itdom->first;
12012 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
12014 stringstream grpname;
12017 grpname << dom1 << "_" << dom2;
12019 grpname << dom2 << "_" << dom1;
12020 string namegrp = grpname.str();
12021 if (!mapOfJunctionGroups.count(namegrp))
12022 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
12023 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12025 sgrp->Add(vol->GetID());
12026 if (vol->GetType() == SMDSAbs_Volume)
12027 joints3DGrp->Add(vol->GetID());
12028 else if (vol->GetType() == SMDSAbs_Face)
12029 joints2DGrp->Add(vol->GetID());
12033 // --- create volumes on multiple domain intersection if requested
12034 // iterate on mutipleNodesToFace
12035 // iterate on edgesMultiDomains
12037 //MESSAGE(".. Creation of elements: multiple junction");
12038 if (createJointElems)
12040 // --- iterate on mutipleNodesToFace
12042 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
12043 for (; itn != mutipleNodesToFace.end(); ++itn)
12045 int node = itn->first;
12046 vector<int> orderDom = itn->second;
12047 vector<vtkIdType> orderedNodes;
12048 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12049 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
12050 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
12052 stringstream grpname;
12054 grpname << 0 << "_" << 0;
12056 string namegrp = grpname.str();
12057 if (!mapOfJunctionGroups.count(namegrp))
12058 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
12059 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12061 sgrp->Add(face->GetID());
12064 // --- iterate on edgesMultiDomains
12066 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
12067 for (; ite != edgesMultiDomains.end(); ++ite)
12069 vector<int> nodes = ite->first;
12070 vector<int> orderDom = ite->second;
12071 vector<vtkIdType> orderedNodes;
12072 if (nodes.size() == 2)
12074 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
12075 for ( size_t ino = 0; ino < nodes.size(); ino++ )
12076 if ( orderDom.size() == 3 )
12077 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12078 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12080 for (int idom = orderDom.size()-1; idom >=0; idom--)
12081 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12082 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
12085 string namegrp = "jointsMultiples";
12086 if (!mapOfJunctionGroups.count(namegrp))
12087 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12088 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12090 sgrp->Add(vol->GetID());
12094 //INFOS("Quadratic multiple joints not implemented");
12095 // TODO quadratic nodes
12100 // --- list the explicit faces and edges of the mesh that need to be modified,
12101 // i.e. faces and edges built with one or more duplicated nodes.
12102 // associate these faces or edges to their corresponding domain.
12103 // only the first domain found is kept when a face or edge is shared
12105 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
12106 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
12107 faceOrEdgeDom.clear();
12110 //MESSAGE(".. Modification of elements");
12111 for (int idomain = idom0; idomain < nbDomains; idomain++)
12113 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
12114 for (; itnod != nodeDomains.end(); ++itnod)
12116 int oldId = itnod->first;
12117 //MESSAGE(" node " << oldId);
12118 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
12119 for (int i = 0; i < l.ncells; i++)
12121 int vtkId = l.cells[i];
12122 int vtkType = grid->GetCellType(vtkId);
12123 int downId = grid->CellIdToDownId(vtkId);
12125 continue; // new cells: not to be modified
12126 DownIdType aCell(downId, vtkType);
12127 int volParents[1000];
12128 int nbvol = grid->GetParentVolumes(volParents, vtkId);
12129 for (int j = 0; j < nbvol; j++)
12130 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
12131 if (!feDom.count(vtkId))
12133 feDom[vtkId] = idomain;
12134 faceOrEdgeDom[aCell] = emptyMap;
12135 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
12136 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
12137 // << " type " << vtkType << " downId " << downId);
12143 // --- iterate on shared faces (volumes to modify, face to extrude)
12144 // get node id's of the face
12145 // replace old nodes by new nodes in volumes, and update inverse connectivity
12147 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
12148 for (int m=0; m<3; m++)
12150 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
12151 itface = (*amap).begin();
12152 for (; itface != (*amap).end(); ++itface)
12154 DownIdType face = itface->first;
12155 std::set<int> oldNodes;
12156 std::set<int>::iterator itn;
12158 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12159 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12160 std::map<int, int> localClonedNodeIds;
12162 std::map<int, int> domvol = itface->second;
12163 std::map<int, int>::iterator itdom = domvol.begin();
12164 for (; itdom != domvol.end(); ++itdom)
12166 int idom = itdom->first;
12167 int vtkVolId = itdom->second;
12168 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
12169 localClonedNodeIds.clear();
12170 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12173 if (nodeDomains[oldId].count(idom))
12175 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12176 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
12179 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12184 // Remove empty groups (issue 0022812)
12185 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12186 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12188 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12189 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12192 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12193 grid->DeleteLinks();
12201 * \brief Double nodes on some external faces and create flat elements.
12202 * Flat elements are mainly used by some types of mechanic calculations.
12204 * Each group of the list must be constituted of faces.
12205 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12206 * @param theElems - list of groups of faces, where a group of faces is a set of
12207 * SMDS_MeshElements sorted by Id.
12208 * @return TRUE if operation has been completed successfully, FALSE otherwise
12210 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12212 // MESSAGE("-------------------------------------------------");
12213 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12214 // MESSAGE("-------------------------------------------------");
12216 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12218 // --- For each group of faces
12219 // duplicate the nodes, create a flat element based on the face
12220 // replace the nodes of the faces by their clones
12222 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12223 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12224 clonedNodes.clear();
12225 intermediateNodes.clear();
12226 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12227 mapOfJunctionGroups.clear();
12229 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12231 const TIDSortedElemSet& domain = theElems[idom];
12232 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12233 for ( ; elemItr != domain.end(); ++elemItr )
12235 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
12236 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
12239 // MESSAGE("aFace=" << aFace->GetID());
12240 bool isQuad = aFace->IsQuadratic();
12241 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12243 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12245 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12246 while (nodeIt->more())
12248 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12249 bool isMedium = isQuad && (aFace->IsMediumNode(node));
12251 ln2.push_back(node);
12253 ln0.push_back(node);
12255 const SMDS_MeshNode* clone = 0;
12256 if (!clonedNodes.count(node))
12258 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12259 copyPosition( node, clone );
12260 clonedNodes[node] = clone;
12263 clone = clonedNodes[node];
12266 ln3.push_back(clone);
12268 ln1.push_back(clone);
12270 const SMDS_MeshNode* inter = 0;
12271 if (isQuad && (!isMedium))
12273 if (!intermediateNodes.count(node))
12275 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12276 copyPosition( node, inter );
12277 intermediateNodes[node] = inter;
12280 inter = intermediateNodes[node];
12281 ln4.push_back(inter);
12285 // --- extrude the face
12287 vector<const SMDS_MeshNode*> ln;
12288 SMDS_MeshVolume* vol = 0;
12289 vtkIdType aType = aFace->GetVtkType();
12293 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12294 // MESSAGE("vol prism " << vol->GetID());
12295 ln.push_back(ln1[0]);
12296 ln.push_back(ln1[1]);
12297 ln.push_back(ln1[2]);
12300 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12301 // MESSAGE("vol hexa " << vol->GetID());
12302 ln.push_back(ln1[0]);
12303 ln.push_back(ln1[1]);
12304 ln.push_back(ln1[2]);
12305 ln.push_back(ln1[3]);
12307 case VTK_QUADRATIC_TRIANGLE:
12308 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12309 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12310 // MESSAGE("vol quad prism " << vol->GetID());
12311 ln.push_back(ln1[0]);
12312 ln.push_back(ln1[1]);
12313 ln.push_back(ln1[2]);
12314 ln.push_back(ln3[0]);
12315 ln.push_back(ln3[1]);
12316 ln.push_back(ln3[2]);
12318 case VTK_QUADRATIC_QUAD:
12319 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12320 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12321 // ln4[0], ln4[1], ln4[2], ln4[3]);
12322 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12323 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12324 ln4[0], ln4[1], ln4[2], ln4[3]);
12325 // MESSAGE("vol quad hexa " << vol->GetID());
12326 ln.push_back(ln1[0]);
12327 ln.push_back(ln1[1]);
12328 ln.push_back(ln1[2]);
12329 ln.push_back(ln1[3]);
12330 ln.push_back(ln3[0]);
12331 ln.push_back(ln3[1]);
12332 ln.push_back(ln3[2]);
12333 ln.push_back(ln3[3]);
12343 stringstream grpname;
12347 string namegrp = grpname.str();
12348 if (!mapOfJunctionGroups.count(namegrp))
12349 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12350 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12352 sgrp->Add(vol->GetID());
12355 // --- modify the face
12357 aFace->ChangeNodes(&ln[0], ln.size());
12364 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12365 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12366 * groups of faces to remove inside the object, (idem edges).
12367 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12369 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12370 const TopoDS_Shape& theShape,
12371 SMESH_NodeSearcher* theNodeSearcher,
12372 const char* groupName,
12373 std::vector<double>& nodesCoords,
12374 std::vector<std::vector<int> >& listOfListOfNodes)
12376 // MESSAGE("--------------------------------");
12377 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12378 // MESSAGE("--------------------------------");
12380 // --- zone of volumes to remove is given :
12381 // 1 either by a geom shape (one or more vertices) and a radius,
12382 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12383 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12384 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12385 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12386 // defined by it's name.
12388 SMESHDS_GroupBase* groupDS = 0;
12389 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12390 while ( groupIt->more() )
12393 SMESH_Group * group = groupIt->next();
12394 if ( !group ) continue;
12395 groupDS = group->GetGroupDS();
12396 if ( !groupDS || groupDS->IsEmpty() ) continue;
12397 std::string grpName = group->GetName();
12398 //MESSAGE("grpName=" << grpName);
12399 if (grpName == groupName)
12405 bool isNodeGroup = false;
12406 bool isNodeCoords = false;
12409 if (groupDS->GetType() != SMDSAbs_Node)
12411 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12414 if (nodesCoords.size() > 0)
12415 isNodeCoords = true; // a list o nodes given by their coordinates
12416 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12418 // --- define groups to build
12420 int idg; // --- group of SMDS volumes
12421 string grpvName = groupName;
12422 grpvName += "_vol";
12423 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12426 MESSAGE("group not created " << grpvName);
12429 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12431 int idgs; // --- group of SMDS faces on the skin
12432 string grpsName = groupName;
12433 grpsName += "_skin";
12434 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12437 MESSAGE("group not created " << grpsName);
12440 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12442 int idgi; // --- group of SMDS faces internal (several shapes)
12443 string grpiName = groupName;
12444 grpiName += "_internalFaces";
12445 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12448 MESSAGE("group not created " << grpiName);
12451 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12453 int idgei; // --- group of SMDS faces internal (several shapes)
12454 string grpeiName = groupName;
12455 grpeiName += "_internalEdges";
12456 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12459 MESSAGE("group not created " << grpeiName);
12462 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12464 // --- build downward connectivity
12466 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12467 meshDS->BuildDownWardConnectivity(true);
12468 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12470 // --- set of volumes detected inside
12472 std::set<int> setOfInsideVol;
12473 std::set<int> setOfVolToCheck;
12475 std::vector<gp_Pnt> gpnts;
12478 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12480 //MESSAGE("group of nodes provided");
12481 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12482 while ( elemIt->more() )
12484 const SMDS_MeshElement* elem = elemIt->next();
12487 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12490 SMDS_MeshElement* vol = 0;
12491 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12492 while (volItr->more())
12494 vol = (SMDS_MeshElement*)volItr->next();
12495 setOfInsideVol.insert(vol->getVtkId());
12496 sgrp->Add(vol->GetID());
12500 else if (isNodeCoords)
12502 //MESSAGE("list of nodes coordinates provided");
12505 while ( i < nodesCoords.size()-2 )
12507 double x = nodesCoords[i++];
12508 double y = nodesCoords[i++];
12509 double z = nodesCoords[i++];
12510 gp_Pnt p = gp_Pnt(x, y ,z);
12511 gpnts.push_back(p);
12512 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12516 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12518 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12519 TopTools_IndexedMapOfShape vertexMap;
12520 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12521 gp_Pnt p = gp_Pnt(0,0,0);
12522 if (vertexMap.Extent() < 1)
12525 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12527 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12528 p = BRep_Tool::Pnt(vertex);
12529 gpnts.push_back(p);
12530 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12534 if (gpnts.size() > 0)
12536 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12537 //MESSAGE("startNode->nodeId " << nodeId);
12539 double radius2 = radius*radius;
12540 //MESSAGE("radius2 " << radius2);
12542 // --- volumes on start node
12544 setOfVolToCheck.clear();
12545 SMDS_MeshElement* startVol = 0;
12546 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12547 while (volItr->more())
12549 startVol = (SMDS_MeshElement*)volItr->next();
12550 setOfVolToCheck.insert(startVol->getVtkId());
12552 if (setOfVolToCheck.empty())
12554 MESSAGE("No volumes found");
12558 // --- starting with central volumes then their neighbors, check if they are inside
12559 // or outside the domain, until no more new neighbor volume is inside.
12560 // Fill the group of inside volumes
12562 std::map<int, double> mapOfNodeDistance2;
12563 mapOfNodeDistance2.clear();
12564 std::set<int> setOfOutsideVol;
12565 while (!setOfVolToCheck.empty())
12567 std::set<int>::iterator it = setOfVolToCheck.begin();
12569 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12570 bool volInside = false;
12571 vtkIdType npts = 0;
12572 vtkIdType* pts = 0;
12573 grid->GetCellPoints(vtkId, npts, pts);
12574 for (int i=0; i<npts; i++)
12576 double distance2 = 0;
12577 if (mapOfNodeDistance2.count(pts[i]))
12579 distance2 = mapOfNodeDistance2[pts[i]];
12580 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12584 double *coords = grid->GetPoint(pts[i]);
12585 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12587 for ( size_t j = 0; j < gpnts.size(); j++ )
12589 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12590 if (d2 < distance2)
12593 if (distance2 < radius2)
12597 mapOfNodeDistance2[pts[i]] = distance2;
12598 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12600 if (distance2 < radius2)
12602 volInside = true; // one or more nodes inside the domain
12603 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12609 setOfInsideVol.insert(vtkId);
12610 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12611 int neighborsVtkIds[NBMAXNEIGHBORS];
12612 int downIds[NBMAXNEIGHBORS];
12613 unsigned char downTypes[NBMAXNEIGHBORS];
12614 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12615 for (int n = 0; n < nbNeighbors; n++)
12616 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12617 setOfVolToCheck.insert(neighborsVtkIds[n]);
12621 setOfOutsideVol.insert(vtkId);
12622 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12624 setOfVolToCheck.erase(vtkId);
12628 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12629 // If yes, add the volume to the inside set
12631 bool addedInside = true;
12632 std::set<int> setOfVolToReCheck;
12633 while (addedInside)
12635 //MESSAGE(" --------------------------- re check");
12636 addedInside = false;
12637 std::set<int>::iterator itv = setOfInsideVol.begin();
12638 for (; itv != setOfInsideVol.end(); ++itv)
12641 int neighborsVtkIds[NBMAXNEIGHBORS];
12642 int downIds[NBMAXNEIGHBORS];
12643 unsigned char downTypes[NBMAXNEIGHBORS];
12644 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12645 for (int n = 0; n < nbNeighbors; n++)
12646 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12647 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12649 setOfVolToCheck = setOfVolToReCheck;
12650 setOfVolToReCheck.clear();
12651 while (!setOfVolToCheck.empty())
12653 std::set<int>::iterator it = setOfVolToCheck.begin();
12655 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12657 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12658 int countInside = 0;
12659 int neighborsVtkIds[NBMAXNEIGHBORS];
12660 int downIds[NBMAXNEIGHBORS];
12661 unsigned char downTypes[NBMAXNEIGHBORS];
12662 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12663 for (int n = 0; n < nbNeighbors; n++)
12664 if (setOfInsideVol.count(neighborsVtkIds[n]))
12666 //MESSAGE("countInside " << countInside);
12667 if (countInside > 1)
12669 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12670 setOfInsideVol.insert(vtkId);
12671 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12672 addedInside = true;
12675 setOfVolToReCheck.insert(vtkId);
12677 setOfVolToCheck.erase(vtkId);
12681 // --- map of Downward faces at the boundary, inside the global volume
12682 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12683 // fill group of SMDS faces inside the volume (when several volume shapes)
12684 // fill group of SMDS faces on the skin of the global volume (if skin)
12686 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12687 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12688 std::set<int>::iterator it = setOfInsideVol.begin();
12689 for (; it != setOfInsideVol.end(); ++it)
12692 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12693 int neighborsVtkIds[NBMAXNEIGHBORS];
12694 int downIds[NBMAXNEIGHBORS];
12695 unsigned char downTypes[NBMAXNEIGHBORS];
12696 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12697 for (int n = 0; n < nbNeighbors; n++)
12699 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12700 if (neighborDim == 3)
12702 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12704 DownIdType face(downIds[n], downTypes[n]);
12705 boundaryFaces[face] = vtkId;
12707 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12708 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12709 if (vtkFaceId >= 0)
12711 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12712 // find also the smds edges on this face
12713 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12714 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12715 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12716 for (int i = 0; i < nbEdges; i++)
12718 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12719 if (vtkEdgeId >= 0)
12720 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12724 else if (neighborDim == 2) // skin of the volume
12726 DownIdType face(downIds[n], downTypes[n]);
12727 skinFaces[face] = vtkId;
12728 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12729 if (vtkFaceId >= 0)
12730 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12735 // --- identify the edges constituting the wire of each subshape on the skin
12736 // define polylines with the nodes of edges, equivalent to wires
12737 // project polylines on subshapes, and partition, to get geom faces
12739 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12740 std::set<int> emptySet;
12742 std::set<int> shapeIds;
12744 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12745 while (itelem->more())
12747 const SMDS_MeshElement *elem = itelem->next();
12748 int shapeId = elem->getshapeId();
12749 int vtkId = elem->getVtkId();
12750 if (!shapeIdToVtkIdSet.count(shapeId))
12752 shapeIdToVtkIdSet[shapeId] = emptySet;
12753 shapeIds.insert(shapeId);
12755 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12758 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12759 std::set<DownIdType, DownIdCompare> emptyEdges;
12760 emptyEdges.clear();
12762 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12763 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12765 int shapeId = itShape->first;
12766 //MESSAGE(" --- Shape ID --- "<< shapeId);
12767 shapeIdToEdges[shapeId] = emptyEdges;
12769 std::vector<int> nodesEdges;
12771 std::set<int>::iterator its = itShape->second.begin();
12772 for (; its != itShape->second.end(); ++its)
12775 //MESSAGE(" " << vtkId);
12776 int neighborsVtkIds[NBMAXNEIGHBORS];
12777 int downIds[NBMAXNEIGHBORS];
12778 unsigned char downTypes[NBMAXNEIGHBORS];
12779 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12780 for (int n = 0; n < nbNeighbors; n++)
12782 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12784 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12785 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12786 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12788 DownIdType edge(downIds[n], downTypes[n]);
12789 if (!shapeIdToEdges[shapeId].count(edge))
12791 shapeIdToEdges[shapeId].insert(edge);
12793 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12794 nodesEdges.push_back(vtkNodeId[0]);
12795 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12796 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12802 std::list<int> order;
12804 if (nodesEdges.size() > 0)
12806 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12807 nodesEdges[0] = -1;
12808 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12809 nodesEdges[1] = -1; // do not reuse this edge
12813 int nodeTofind = order.back(); // try first to push back
12815 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12816 if (nodesEdges[i] == nodeTofind)
12818 if ( i == (int) nodesEdges.size() )
12819 found = false; // no follower found on back
12822 if (i%2) // odd ==> use the previous one
12823 if (nodesEdges[i-1] < 0)
12827 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12828 nodesEdges[i-1] = -1;
12830 else // even ==> use the next one
12831 if (nodesEdges[i+1] < 0)
12835 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12836 nodesEdges[i+1] = -1;
12841 // try to push front
12843 nodeTofind = order.front(); // try to push front
12844 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12845 if ( nodesEdges[i] == nodeTofind )
12847 if ( i == (int)nodesEdges.size() )
12849 found = false; // no predecessor found on front
12852 if (i%2) // odd ==> use the previous one
12853 if (nodesEdges[i-1] < 0)
12857 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12858 nodesEdges[i-1] = -1;
12860 else // even ==> use the next one
12861 if (nodesEdges[i+1] < 0)
12865 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12866 nodesEdges[i+1] = -1;
12872 std::vector<int> nodes;
12873 nodes.push_back(shapeId);
12874 std::list<int>::iterator itl = order.begin();
12875 for (; itl != order.end(); itl++)
12877 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12878 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12880 listOfListOfNodes.push_back(nodes);
12883 // partition geom faces with blocFissure
12884 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12885 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12891 //================================================================================
12893 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12894 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12895 * \return TRUE if operation has been completed successfully, FALSE otherwise
12897 //================================================================================
12899 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12901 // iterates on volume elements and detect all free faces on them
12902 SMESHDS_Mesh* aMesh = GetMeshDS();
12906 ElemFeatures faceType( SMDSAbs_Face );
12907 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12908 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12911 const SMDS_MeshVolume* volume = vIt->next();
12912 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12913 vTool.SetExternalNormal();
12914 const int iQuad = volume->IsQuadratic();
12915 faceType.SetQuad( iQuad );
12916 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12918 if (!vTool.IsFreeFace(iface))
12921 vector<const SMDS_MeshNode *> nodes;
12922 int nbFaceNodes = vTool.NbFaceNodes(iface);
12923 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12925 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12926 nodes.push_back(faceNodes[inode]);
12928 if (iQuad) // add medium nodes
12930 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12931 nodes.push_back(faceNodes[inode]);
12932 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12933 nodes.push_back(faceNodes[8]);
12935 // add new face based on volume nodes
12936 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12938 nbExisted++; // face already exsist
12942 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12947 return ( nbFree == ( nbExisted + nbCreated ));
12952 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12954 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12956 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12959 //================================================================================
12961 * \brief Creates missing boundary elements
12962 * \param elements - elements whose boundary is to be checked
12963 * \param dimension - defines type of boundary elements to create
12964 * \param group - a group to store created boundary elements in
12965 * \param targetMesh - a mesh to store created boundary elements in
12966 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12967 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12968 * boundary elements will be copied into the targetMesh
12969 * \param toAddExistingBondary - if true, not only new but also pre-existing
12970 * boundary elements will be added into the new group
12971 * \param aroundElements - if true, elements will be created on boundary of given
12972 * elements else, on boundary of the whole mesh.
12973 * \return nb of added boundary elements
12975 //================================================================================
12977 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12978 Bnd_Dimension dimension,
12979 SMESH_Group* group/*=0*/,
12980 SMESH_Mesh* targetMesh/*=0*/,
12981 bool toCopyElements/*=false*/,
12982 bool toCopyExistingBoundary/*=false*/,
12983 bool toAddExistingBondary/*= false*/,
12984 bool aroundElements/*= false*/)
12986 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12987 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12988 // hope that all elements are of the same type, do not check them all
12989 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12990 throw SALOME_Exception(LOCALIZED("wrong element type"));
12993 toCopyElements = toCopyExistingBoundary = false;
12995 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12996 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12997 int nbAddedBnd = 0;
12999 // editor adding present bnd elements and optionally holding elements to add to the group
13000 SMESH_MeshEditor* presentEditor;
13001 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
13002 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
13004 SMESH_MesherHelper helper( *myMesh );
13005 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
13006 SMDS_VolumeTool vTool;
13007 TIDSortedElemSet avoidSet;
13008 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
13011 typedef vector<const SMDS_MeshNode*> TConnectivity;
13012 TConnectivity tgtNodes;
13013 ElemFeatures elemKind( missType ), elemToCopy;
13015 vector<const SMDS_MeshElement*> presentBndElems;
13016 vector<TConnectivity> missingBndElems;
13017 vector<int> freeFacets;
13018 TConnectivity nodes, elemNodes;
13020 SMDS_ElemIteratorPtr eIt;
13021 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13022 else eIt = elemSetIterator( elements );
13024 while (eIt->more())
13026 const SMDS_MeshElement* elem = eIt->next();
13027 const int iQuad = elem->IsQuadratic();
13028 elemKind.SetQuad( iQuad );
13030 // ------------------------------------------------------------------------------------
13031 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
13032 // ------------------------------------------------------------------------------------
13033 presentBndElems.clear();
13034 missingBndElems.clear();
13035 freeFacets.clear(); nodes.clear(); elemNodes.clear();
13036 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
13038 const SMDS_MeshElement* otherVol = 0;
13039 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
13041 if ( !vTool.IsFreeFace(iface, &otherVol) &&
13042 ( !aroundElements || elements.count( otherVol )))
13044 freeFacets.push_back( iface );
13046 if ( missType == SMDSAbs_Face )
13047 vTool.SetExternalNormal();
13048 for ( size_t i = 0; i < freeFacets.size(); ++i )
13050 int iface = freeFacets[i];
13051 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
13052 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
13053 if ( missType == SMDSAbs_Edge ) // boundary edges
13055 nodes.resize( 2+iQuad );
13056 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
13058 for ( size_t j = 0; j < nodes.size(); ++j )
13059 nodes[ j ] = nn[ i+j ];
13060 if ( const SMDS_MeshElement* edge =
13061 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
13062 presentBndElems.push_back( edge );
13064 missingBndElems.push_back( nodes );
13067 else // boundary face
13070 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13071 nodes.push_back( nn[inode] ); // add corner nodes
13073 for ( inode = 1; inode < nbFaceNodes; inode += 2)
13074 nodes.push_back( nn[inode] ); // add medium nodes
13075 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
13077 nodes.push_back( vTool.GetNodes()[ iCenter ] );
13079 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
13080 SMDSAbs_Face, /*noMedium=*/false ))
13081 presentBndElems.push_back( f );
13083 missingBndElems.push_back( nodes );
13085 if ( targetMesh != myMesh )
13087 // add 1D elements on face boundary to be added to a new mesh
13088 const SMDS_MeshElement* edge;
13089 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13092 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
13094 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
13095 if ( edge && avoidSet.insert( edge ).second )
13096 presentBndElems.push_back( edge );
13102 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
13104 avoidSet.clear(), avoidSet.insert( elem );
13105 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
13106 SMDS_MeshElement::iterator() );
13107 elemNodes.push_back( elemNodes[0] );
13108 nodes.resize( 2 + iQuad );
13109 const int nbLinks = elem->NbCornerNodes();
13110 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
13112 nodes[0] = elemNodes[iN];
13113 nodes[1] = elemNodes[iN+1+iQuad];
13114 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
13115 continue; // not free link
13117 if ( iQuad ) nodes[2] = elemNodes[iN+1];
13118 if ( const SMDS_MeshElement* edge =
13119 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
13120 presentBndElems.push_back( edge );
13122 missingBndElems.push_back( nodes );
13126 // ---------------------------------
13127 // 2. Add missing boundary elements
13128 // ---------------------------------
13129 if ( targetMesh != myMesh )
13130 // instead of making a map of nodes in this mesh and targetMesh,
13131 // we create nodes with same IDs.
13132 for ( size_t i = 0; i < missingBndElems.size(); ++i )
13134 TConnectivity& srcNodes = missingBndElems[i];
13135 tgtNodes.resize( srcNodes.size() );
13136 for ( inode = 0; inode < srcNodes.size(); ++inode )
13137 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
13138 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
13140 /*noMedium=*/false))
13142 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
13146 for ( size_t i = 0; i < missingBndElems.size(); ++i )
13148 TConnectivity& nodes = missingBndElems[ i ];
13149 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
13151 /*noMedium=*/false))
13153 SMDS_MeshElement* newElem =
13154 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
13155 nbAddedBnd += bool( newElem );
13157 // try to set a new element to a shape
13158 if ( myMesh->HasShapeToMesh() )
13161 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
13162 const size_t nbN = nodes.size() / (iQuad+1 );
13163 for ( inode = 0; inode < nbN && ok; ++inode )
13165 pair<int, TopAbs_ShapeEnum> i_stype =
13166 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
13167 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
13168 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
13170 if ( ok && mediumShapes.size() > 1 )
13172 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13173 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13174 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13176 if (( ok = ( stype_i->first != stype_i_0.first )))
13177 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13178 aMesh->IndexToShape( stype_i_0.second ));
13181 if ( ok && mediumShapes.begin()->first == missShapeType )
13182 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13186 // ----------------------------------
13187 // 3. Copy present boundary elements
13188 // ----------------------------------
13189 if ( toCopyExistingBoundary )
13190 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13192 const SMDS_MeshElement* e = presentBndElems[i];
13193 tgtNodes.resize( e->NbNodes() );
13194 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13195 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13196 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13198 else // store present elements to add them to a group
13199 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13201 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
13204 } // loop on given elements
13206 // ---------------------------------------------
13207 // 4. Fill group with boundary elements
13208 // ---------------------------------------------
13211 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13212 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
13213 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
13215 tgtEditor.myLastCreatedElems.Clear();
13216 tgtEditor2.myLastCreatedElems.Clear();
13218 // -----------------------
13219 // 5. Copy given elements
13220 // -----------------------
13221 if ( toCopyElements && targetMesh != myMesh )
13223 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13224 else eIt = elemSetIterator( elements );
13225 while (eIt->more())
13227 const SMDS_MeshElement* elem = eIt->next();
13228 tgtNodes.resize( elem->NbNodes() );
13229 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13230 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13231 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13233 tgtEditor.myLastCreatedElems.Clear();
13239 //================================================================================
13241 * \brief Copy node position and set \a to node on the same geometry
13243 //================================================================================
13245 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13246 const SMDS_MeshNode* to )
13248 if ( !from || !to ) return;
13250 SMDS_PositionPtr pos = from->GetPosition();
13251 if ( !pos || from->getshapeId() < 1 ) return;
13253 switch ( pos->GetTypeOfPosition() )
13255 case SMDS_TOP_3DSPACE: break;
13257 case SMDS_TOP_FACE:
13259 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
13260 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13261 fPos->GetUParameter(), fPos->GetVParameter() );
13264 case SMDS_TOP_EDGE:
13266 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13267 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
13268 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13271 case SMDS_TOP_VERTEX:
13273 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13276 case SMDS_TOP_UNSPEC:
13281 namespace // utils for MakePolyLine
13283 //================================================================================
13285 * \brief Sequence of found points and a current point data
13289 std::vector< gp_XYZ > myPoints;
13292 int mySrcPntInd; //!< start point index
13293 const SMDS_MeshElement* myFace;
13294 SMESH_NodeXYZ myNode1;
13295 SMESH_NodeXYZ myNode2;
13300 TIDSortedElemSet myElemSet, myAvoidSet;
13302 Path(): myLength(0.0), myFace(0) {}
13304 bool SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13305 const SMDS_MeshElement* face,
13306 const gp_XYZ& plnNorm,
13307 const gp_XYZ& plnOrig );
13309 void AddPoint( const gp_XYZ& p );
13311 bool Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig );
13313 bool ReachSamePoint( const Path& other );
13315 static void Remove( std::vector< Path > & paths, size_t& i );
13318 //================================================================================
13320 * \brief Return true if this Path meats another
13322 //================================================================================
13324 bool Path::ReachSamePoint( const Path& other )
13326 return ( mySrcPntInd != other.mySrcPntInd &&
13327 myFace == other.myFace );
13330 //================================================================================
13332 * \brief Remove a path from a vector
13334 //================================================================================
13336 void Path::Remove( std::vector< Path > & paths, size_t& i )
13338 if ( paths.size() > 1 )
13340 size_t j = paths.size() - 1; // last item to be removed
13343 paths[ i ].myPoints.swap( paths[ j ].myPoints );
13344 paths[ i ].myLength = paths[ j ].myLength;
13345 paths[ i ].mySrcPntInd = paths[ j ].mySrcPntInd;
13346 paths[ i ].myFace = paths[ j ].myFace;
13347 paths[ i ].myNode1 = paths[ j ].myNode1;
13348 paths[ i ].myNode2 = paths[ j ].myNode2;
13349 paths[ i ].myNodeInd1 = paths[ j ].myNodeInd1;
13350 paths[ i ].myNodeInd2 = paths[ j ].myNodeInd2;
13351 paths[ i ].myDot1 = paths[ j ].myDot1;
13352 paths[ i ].myDot2 = paths[ j ].myDot2;
13360 //================================================================================
13362 * \brief Store a point that is at a node of a face if the face is intersected by plane.
13363 * Return false if the node is a sole intersection point of the face and the plane
13365 //================================================================================
13367 bool Path::SetCutAtCorner( const SMESH_NodeXYZ& cornerNode,
13368 const SMDS_MeshElement* face,
13369 const gp_XYZ& plnNorm,
13370 const gp_XYZ& plnOrig )
13372 if ( face == myFace )
13374 myNodeInd1 = face->GetNodeIndex( cornerNode._node );
13375 myNodeInd2 = ( myNodeInd1 + 1 ) % face->NbCornerNodes();
13376 int ind3 = ( myNodeInd1 + 2 ) % face->NbCornerNodes();
13377 myNode1.Set( face->GetNode( ind3 ));
13378 myNode2.Set( face->GetNode( myNodeInd2 ));
13380 myDot1 = plnNorm * ( myNode1 - plnOrig );
13381 myDot2 = plnNorm * ( myNode2 - plnOrig );
13383 bool ok = ( myDot1 * myDot2 < 0 );
13384 if ( !ok && myDot1 * myDot2 == 0 )
13386 ok = ( myDot1 != myDot2 );
13387 if ( ok && myFace )
13388 ok = ( myFace->GetNodeIndex(( myDot1 == 0 ? myNode1 : myNode2 )._node ) < 0 );
13394 AddPoint( cornerNode );
13399 //================================================================================
13401 * \brief Store a point and update myLength
13403 //================================================================================
13405 void Path::AddPoint( const gp_XYZ& p )
13407 if ( !myPoints.empty() )
13408 myLength += ( p - myPoints.back() ).Modulus();
13411 myPoints.push_back( p );
13414 //================================================================================
13416 * \brief Try to find the next point
13417 * \param [in] plnNorm - cutting plane normal
13418 * \param [in] plnOrig - cutting plane origin
13420 //================================================================================
13422 bool Path::Extend( const gp_XYZ& plnNorm, const gp_XYZ& plnOrig )
13424 int nodeInd3 = ( myNodeInd1 + 1 ) % myFace->NbCornerNodes();
13425 if ( myNodeInd2 == nodeInd3 )
13426 nodeInd3 = ( myNodeInd1 + 2 ) % myFace->NbCornerNodes();
13428 SMESH_NodeXYZ node3 = myFace->GetNode( nodeInd3 );
13429 double dot3 = plnNorm * ( node3 - plnOrig );
13431 if ( dot3 * myDot1 < 0. )
13434 myNodeInd2 = nodeInd3;
13437 else if ( dot3 * myDot2 < 0. )
13440 myNodeInd1 = nodeInd3;
13443 else if ( dot3 == 0. )
13445 SMDS_ElemIteratorPtr fIt = node3._node->GetInverseElementIterator(SMDSAbs_Face);
13446 while ( fIt->more() )
13447 if ( SetCutAtCorner( node3, fIt->next(), plnNorm, plnOrig ))
13451 else if ( myDot2 == 0. )
13453 SMESH_NodeXYZ node2 = myNode2; // copy as myNode2 changes in SetCutAtCorner()
13454 SMDS_ElemIteratorPtr fIt = node2._node->GetInverseElementIterator(SMDSAbs_Face);
13455 while ( fIt->more() )
13456 if ( SetCutAtCorner( node2, fIt->next(), plnNorm, plnOrig ))
13461 double r = Abs( myDot1 / ( myDot2 - myDot1 ));
13462 AddPoint( myNode1 * ( 1 - r ) + myNode2 * r );
13464 myAvoidSet.clear();
13465 myAvoidSet.insert( myFace );
13466 myFace = SMESH_MeshAlgos::FindFaceInSet( myNode1._node, myNode2._node,
13467 myElemSet, myAvoidSet,
13468 &myNodeInd1, &myNodeInd2 );
13472 //================================================================================
13474 * \brief Compute a path between two points of PolySegment
13476 struct PolyPathCompute
13478 SMESH_MeshEditor::TListOfPolySegments& mySegments; //!< inout PolySegment's
13479 std::vector< Path >& myPaths; //!< path of each of segments to compute
13480 SMESH_Mesh* myMesh;
13481 mutable std::vector< std::string > myErrors;
13483 PolyPathCompute( SMESH_MeshEditor::TListOfPolySegments& theSegments,
13484 std::vector< Path >& thePaths,
13485 SMESH_Mesh* theMesh):
13486 mySegments( theSegments ),
13487 myPaths( thePaths ),
13489 myErrors( theSegments.size() )
13492 #undef SMESH_CAUGHT
13493 #define SMESH_CAUGHT myErrors[i] =
13494 void operator() ( const int i ) const
13497 const_cast< PolyPathCompute* >( this )->Compute( i );
13498 SMESH_CATCH( SMESH::returnError );
13500 #undef SMESH_CAUGHT
13501 //================================================================================
13503 * \brief Compute a path of a given segment
13505 //================================================================================
13507 void Compute( const int iSeg )
13509 SMESH_MeshEditor::PolySegment& polySeg = mySegments[ iSeg ];
13511 // get a cutting plane
13513 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13514 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13515 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13516 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13518 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13519 gp_XYZ plnOrig = p2;
13521 // find paths connecting the 2 end points of polySeg
13523 std::vector< Path > paths; paths.reserve(10);
13525 // initialize paths
13527 for ( int iP = 0; iP < 2; ++iP ) // loop on the polySeg end points
13530 path.mySrcPntInd = iP;
13531 size_t nbPaths = paths.size();
13533 if ( polySeg.myNode2[ iP ] && polySeg.myNode2[ iP ] != polySeg.myNode1[ iP ] )
13535 while (( path.myFace = SMESH_MeshAlgos::FindFaceInSet( polySeg.myNode1[ iP ],
13536 polySeg.myNode2[ iP ],
13540 &path.myNodeInd2 )))
13542 path.myNode1.Set( polySeg.myNode1[ iP ]);
13543 path.myNode2.Set( polySeg.myNode2[ iP ]);
13544 path.myDot1 = plnNorm * ( path.myNode1 - plnOrig );
13545 path.myDot2 = plnNorm * ( path.myNode2 - plnOrig );
13546 path.myPoints.clear();
13547 path.AddPoint( 0.5 * ( path.myNode1 + path.myNode2 ));
13548 path.myAvoidSet.insert( path.myFace );
13549 paths.push_back( path );
13551 if ( nbPaths == paths.size() )
13552 throw SALOME_Exception ( SMESH_Comment("No face edge found by point ") << iP+1
13553 << " in a PolySegment " << iSeg );
13555 else // an end point is at node
13557 std::set<const SMDS_MeshNode* > nodes;
13558 SMDS_ElemIteratorPtr fIt = polySeg.myNode1[ iP ]->GetInverseElementIterator(SMDSAbs_Face);
13559 while ( fIt->more() )
13561 path.myPoints.clear();
13562 if ( path.SetCutAtCorner( polySeg.myNode1[ iP ], fIt->next(), plnNorm, plnOrig ))
13564 if (( path.myDot1 * path.myDot2 != 0 ) ||
13565 ( nodes.insert( path.myDot1 == 0 ? path.myNode1._node : path.myNode2._node ).second ))
13566 paths.push_back( path );
13571 // look for a one-segment path
13572 for ( size_t i = 0; i < nbPaths; ++i )
13573 for ( size_t j = nbPaths; j < paths.size(); ++j )
13574 if ( paths[i].myFace == paths[j].myFace )
13576 myPaths[ iSeg ].myPoints.push_back( paths[i].myPoints[0] );
13577 myPaths[ iSeg ].myPoints.push_back( paths[j].myPoints[0] );
13584 myPaths[ iSeg ].myLength = 1e100;
13586 while ( paths.size() >= 2 )
13588 for ( size_t i = 0; i < paths.size(); ++i )
13590 Path& path = paths[ i ];
13591 if ( !path.Extend( plnNorm, plnOrig ) || // path reached a mesh boundary
13592 path.myLength > myPaths[ iSeg ].myLength ) // path is longer than others
13594 Path::Remove( paths, i );
13598 // join paths that reach same point
13599 for ( size_t j = 0; j < paths.size(); ++j )
13601 if ( i != j && paths[i].ReachSamePoint( paths[j] ))
13603 double distLast = ( paths[i].myPoints.back() - paths[j].myPoints.back() ).Modulus();
13604 double fullLength = ( paths[i].myLength + paths[j].myLength + distLast );
13605 if ( fullLength < myPaths[ iSeg ].myLength )
13607 myPaths[ iSeg ].myLength = fullLength;
13608 std::vector< gp_XYZ > & allPoints = myPaths[ iSeg ].myPoints;
13609 allPoints.swap( paths[i].myPoints );
13610 allPoints.insert( allPoints.end(),
13611 paths[j].myPoints.rbegin(),
13612 paths[j].myPoints.rend() );
13614 Path::Remove( paths, i );
13615 Path::Remove( paths, j );
13619 if ( !paths.empty() && (int) paths[0].myPoints.size() > myMesh->NbFaces() )
13620 throw SALOME_Exception(LOCALIZED( "Infinite loop in MakePolyLine()"));
13623 if ( myPaths[ iSeg ].myPoints.empty() )
13624 throw SALOME_Exception( SMESH_Comment("Can't find a full path for PolySegment #") << iSeg );
13626 } // PolyPathCompute::Compute()
13628 }; // struct PolyPathCompute
13632 //=======================================================================
13633 //function : MakePolyLine
13634 //purpose : Create a polyline consisting of 1D mesh elements each lying on a 2D element of
13635 // the initial mesh
13636 //=======================================================================
13638 void SMESH_MeshEditor::MakePolyLine( TListOfPolySegments& theSegments,
13639 SMESHDS_Group* theGroup,
13640 SMESH_ElementSearcher* theSearcher)
13642 std::vector< Path > segPaths( theSegments.size() ); // path of each of segments
13644 SMESH_ElementSearcher* searcher = theSearcher;
13645 SMESHUtils::Deleter<SMESH_ElementSearcher> delSearcher;
13648 searcher = SMESH_MeshAlgos::GetElementSearcher( *GetMeshDS() );
13649 delSearcher._obj = searcher;
13652 // get cutting planes
13654 std::vector< bool > isVectorOK( theSegments.size(), true );
13655 const double planarCoef = 0.333; // plane height in planar case
13657 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13659 PolySegment& polySeg = theSegments[ iSeg ];
13661 gp_XYZ p1 = SMESH_NodeXYZ( polySeg.myNode1[0] );
13662 gp_XYZ p2 = SMESH_NodeXYZ( polySeg.myNode1[1] );
13663 if ( polySeg.myNode2[0] ) p1 = 0.5 * ( p1 + SMESH_NodeXYZ( polySeg.myNode2[0] ));
13664 if ( polySeg.myNode2[1] ) p2 = 0.5 * ( p2 + SMESH_NodeXYZ( polySeg.myNode2[1] ));
13666 gp_XYZ plnNorm = ( p1 - p2 ) ^ polySeg.myVector.XYZ();
13668 isVectorOK[ iSeg ] = ( plnNorm.Modulus() > std::numeric_limits<double>::min() );
13669 if ( !isVectorOK[ iSeg ])
13671 gp_XYZ pMid = 0.5 * ( p1 + p2 );
13672 const SMDS_MeshElement* face;
13673 polySeg.myMidProjPoint = searcher->Project( pMid, SMDSAbs_Face, &face );
13674 polySeg.myVector = polySeg.myMidProjPoint.XYZ() - pMid;
13677 SMESH_MeshAlgos::FaceNormal( face, faceNorm );
13679 if ( polySeg.myVector.Magnitude() < Precision::Confusion() ||
13680 polySeg.myVector * faceNorm < Precision::Confusion() )
13682 polySeg.myVector = faceNorm;
13683 polySeg.myMidProjPoint = pMid + faceNorm * ( p1 - p2 ).Modulus() * planarCoef;
13688 polySeg.myVector = plnNorm ^ ( p1 - p2 );
13692 // assure that inverse elements are constructed, avoid their concurrent building in threads
13693 GetMeshDS()->nodesIterator()->next()->NbInverseElements();
13697 PolyPathCompute algo( theSegments, segPaths, myMesh );
13698 OSD_Parallel::For( 0, theSegments.size(), algo, theSegments.size() == 1 );
13700 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13701 if ( !algo.myErrors[ iSeg ].empty() )
13702 throw SALOME_Exception( algo.myErrors[ iSeg ].c_str() );
13704 // create an 1D mesh
13706 const SMDS_MeshNode *n, *nPrev = 0;
13707 SMESHDS_Mesh* mesh = GetMeshDS();
13709 for ( size_t iSeg = 0; iSeg < theSegments.size(); ++iSeg )
13711 const Path& path = segPaths[iSeg];
13712 if ( path.myPoints.size() < 2 )
13715 double tol = path.myLength / path.myPoints.size() / 1000.;
13716 if ( !nPrev || ( SMESH_NodeXYZ( nPrev ) - path.myPoints[0] ).SquareModulus() > tol*tol )
13718 nPrev = mesh->AddNode( path.myPoints[0].X(), path.myPoints[0].Y(), path.myPoints[0].Z() );
13719 myLastCreatedNodes.Append( nPrev );
13721 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13723 n = mesh->AddNode( path.myPoints[iP].X(), path.myPoints[iP].Y(), path.myPoints[iP].Z() );
13724 myLastCreatedNodes.Append( n );
13726 const SMDS_MeshElement* elem = mesh->AddEdge( nPrev, n );
13727 myLastCreatedElems.Append( elem );
13729 theGroup->Add( elem );
13736 gp_XYZ pMid = 0.5 * ( path.myPoints[0] + path.myPoints.back() );
13737 if ( isVectorOK[ iSeg ])
13739 // find the most distance point of a path
13740 double maxDist = 0;
13741 for ( size_t iP = 1; iP < path.myPoints.size(); ++iP )
13743 double dist = Abs( theSegments[iSeg].myVector * ( path.myPoints[iP] - path.myPoints[0] ));
13744 if ( dist > maxDist )
13747 theSegments[iSeg].myMidProjPoint = path.myPoints[iP];
13750 if ( maxDist < Precision::Confusion() ) // planar case
13751 theSegments[iSeg].myMidProjPoint =
13752 pMid + theSegments[iSeg].myVector.XYZ().Normalized() * path.myLength * planarCoef;
13754 theSegments[iSeg].myVector = gp_Vec( pMid, theSegments[iSeg].myMidProjPoint );