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 SMDS_ElemIteratorPtr elemIt;
7983 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7984 else elemIt = elemSetIterator( theElements );
7986 vector< TGroupOfElems > arrayOfGroups;
7987 TGroupOfElems groupOfElems;
7988 TMapOfNodeSet mapOfNodeSet;
7990 for ( int iGroup = 0; elemIt->more(); )
7992 const SMDS_MeshElement* curElem = elemIt->next();
7993 SortableElement SE(curElem);
7995 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, iGroup));
7996 if ( !pp.second ) { // one more coincident elem
7997 TMapOfNodeSet::iterator& itSE = pp.first;
7998 int iG = itSE->second;
7999 arrayOfGroups[ iG ].push_back( curElem->GetID() );
8002 arrayOfGroups.push_back( groupOfElems );
8003 arrayOfGroups.back().push_back( curElem->GetID() );
8008 groupOfElems.clear();
8009 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8010 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8012 if ( groupIt->size() > 1 ) {
8013 //groupOfElems.sort(); -- theElements is sorted already
8014 theGroupsOfElementsID.push_back( groupOfElems );
8015 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8020 //=======================================================================
8021 //function : MergeElements
8022 //purpose : In each given group, substitute all elements by the first one.
8023 //=======================================================================
8025 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8027 myLastCreatedElems.Clear();
8028 myLastCreatedNodes.Clear();
8030 typedef list<int> TListOfIDs;
8031 TListOfIDs rmElemIds; // IDs of elems to remove
8033 SMESHDS_Mesh* aMesh = GetMeshDS();
8035 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8036 while ( groupsIt != theGroupsOfElementsID.end() ) {
8037 TListOfIDs& aGroupOfElemID = *groupsIt;
8038 aGroupOfElemID.sort();
8039 int elemIDToKeep = aGroupOfElemID.front();
8040 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8041 aGroupOfElemID.pop_front();
8042 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8043 while ( idIt != aGroupOfElemID.end() ) {
8044 int elemIDToRemove = *idIt;
8045 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8046 // add the kept element in groups of removed one (PAL15188)
8047 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8048 rmElemIds.push_back( elemIDToRemove );
8054 Remove( rmElemIds, false );
8057 //=======================================================================
8058 //function : MergeEqualElements
8059 //purpose : Remove all but one of elements built on the same nodes.
8060 //=======================================================================
8062 void SMESH_MeshEditor::MergeEqualElements()
8064 TIDSortedElemSet aMeshElements; /* empty input ==
8065 to merge equal elements in the whole mesh */
8066 TListOfListOfElementsID aGroupsOfElementsID;
8067 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8068 MergeElements(aGroupsOfElementsID);
8071 //=======================================================================
8072 //function : findAdjacentFace
8074 //=======================================================================
8076 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8077 const SMDS_MeshNode* n2,
8078 const SMDS_MeshElement* elem)
8080 TIDSortedElemSet elemSet, avoidSet;
8082 avoidSet.insert ( elem );
8083 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8086 //=======================================================================
8087 //function : findSegment
8088 //purpose : Return a mesh segment by two nodes one of which can be medium
8089 //=======================================================================
8091 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8092 const SMDS_MeshNode* n2)
8094 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8095 while ( it->more() )
8097 const SMDS_MeshElement* seg = it->next();
8098 if ( seg->GetNodeIndex( n2 ) >= 0 )
8104 //=======================================================================
8105 //function : FindFreeBorder
8107 //=======================================================================
8109 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8111 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8112 const SMDS_MeshNode* theSecondNode,
8113 const SMDS_MeshNode* theLastNode,
8114 list< const SMDS_MeshNode* > & theNodes,
8115 list< const SMDS_MeshElement* >& theFaces)
8117 if ( !theFirstNode || !theSecondNode )
8119 // find border face between theFirstNode and theSecondNode
8120 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8124 theFaces.push_back( curElem );
8125 theNodes.push_back( theFirstNode );
8126 theNodes.push_back( theSecondNode );
8128 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8129 TIDSortedElemSet foundElems;
8130 bool needTheLast = ( theLastNode != 0 );
8132 while ( nStart != theLastNode ) {
8133 if ( nStart == theFirstNode )
8134 return !needTheLast;
8136 // find all free border faces sharing form nStart
8138 list< const SMDS_MeshElement* > curElemList;
8139 list< const SMDS_MeshNode* > nStartList;
8140 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8141 while ( invElemIt->more() ) {
8142 const SMDS_MeshElement* e = invElemIt->next();
8143 if ( e == curElem || foundElems.insert( e ).second ) {
8145 int iNode = 0, nbNodes = e->NbNodes();
8146 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8148 if ( e->IsQuadratic() ) {
8149 const SMDS_VtkFace* F =
8150 dynamic_cast<const SMDS_VtkFace*>(e);
8151 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8152 // use special nodes iterator
8153 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8154 while( anIter->more() ) {
8155 nodes[ iNode++ ] = cast2Node(anIter->next());
8159 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8160 while ( nIt->more() )
8161 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8163 nodes[ iNode ] = nodes[ 0 ];
8165 for ( iNode = 0; iNode < nbNodes; iNode++ )
8166 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8167 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8168 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8170 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8171 curElemList.push_back( e );
8175 // analyse the found
8177 int nbNewBorders = curElemList.size();
8178 if ( nbNewBorders == 0 ) {
8179 // no free border furthermore
8180 return !needTheLast;
8182 else if ( nbNewBorders == 1 ) {
8183 // one more element found
8185 nStart = nStartList.front();
8186 curElem = curElemList.front();
8187 theFaces.push_back( curElem );
8188 theNodes.push_back( nStart );
8191 // several continuations found
8192 list< const SMDS_MeshElement* >::iterator curElemIt;
8193 list< const SMDS_MeshNode* >::iterator nStartIt;
8194 // check if one of them reached the last node
8195 if ( needTheLast ) {
8196 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8197 curElemIt!= curElemList.end();
8198 curElemIt++, nStartIt++ )
8199 if ( *nStartIt == theLastNode ) {
8200 theFaces.push_back( *curElemIt );
8201 theNodes.push_back( *nStartIt );
8205 // find the best free border by the continuations
8206 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8207 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8208 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8209 curElemIt!= curElemList.end();
8210 curElemIt++, nStartIt++ )
8212 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8213 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8214 // find one more free border
8215 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8219 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8220 // choice: clear a worse one
8221 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8222 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8223 contNodes[ iWorse ].clear();
8224 contFaces[ iWorse ].clear();
8227 if ( contNodes[0].empty() && contNodes[1].empty() )
8230 // append the best free border
8231 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8232 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8233 theNodes.pop_back(); // remove nIgnore
8234 theNodes.pop_back(); // remove nStart
8235 theFaces.pop_back(); // remove curElem
8236 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8237 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8238 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8239 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8242 } // several continuations found
8243 } // while ( nStart != theLastNode )
8248 //=======================================================================
8249 //function : CheckFreeBorderNodes
8250 //purpose : Return true if the tree nodes are on a free border
8251 //=======================================================================
8253 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8254 const SMDS_MeshNode* theNode2,
8255 const SMDS_MeshNode* theNode3)
8257 list< const SMDS_MeshNode* > nodes;
8258 list< const SMDS_MeshElement* > faces;
8259 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8262 //=======================================================================
8263 //function : SewFreeBorder
8265 //warning : for border-to-side sewing theSideSecondNode is considered as
8266 // the last side node and theSideThirdNode is not used
8267 //=======================================================================
8269 SMESH_MeshEditor::Sew_Error
8270 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8271 const SMDS_MeshNode* theBordSecondNode,
8272 const SMDS_MeshNode* theBordLastNode,
8273 const SMDS_MeshNode* theSideFirstNode,
8274 const SMDS_MeshNode* theSideSecondNode,
8275 const SMDS_MeshNode* theSideThirdNode,
8276 const bool theSideIsFreeBorder,
8277 const bool toCreatePolygons,
8278 const bool toCreatePolyedrs)
8280 myLastCreatedElems.Clear();
8281 myLastCreatedNodes.Clear();
8283 Sew_Error aResult = SEW_OK;
8285 // ====================================
8286 // find side nodes and elements
8287 // ====================================
8289 list< const SMDS_MeshNode* > nSide[ 2 ];
8290 list< const SMDS_MeshElement* > eSide[ 2 ];
8291 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8292 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8296 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8297 nSide[0], eSide[0])) {
8298 MESSAGE(" Free Border 1 not found " );
8299 aResult = SEW_BORDER1_NOT_FOUND;
8301 if (theSideIsFreeBorder) {
8304 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8305 nSide[1], eSide[1])) {
8306 MESSAGE(" Free Border 2 not found " );
8307 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8310 if ( aResult != SEW_OK )
8313 if (!theSideIsFreeBorder) {
8317 // -------------------------------------------------------------------------
8319 // 1. If nodes to merge are not coincident, move nodes of the free border
8320 // from the coord sys defined by the direction from the first to last
8321 // nodes of the border to the correspondent sys of the side 2
8322 // 2. On the side 2, find the links most co-directed with the correspondent
8323 // links of the free border
8324 // -------------------------------------------------------------------------
8326 // 1. Since sewing may break if there are volumes to split on the side 2,
8327 // we won't move nodes but just compute new coordinates for them
8328 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8329 TNodeXYZMap nBordXYZ;
8330 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8331 list< const SMDS_MeshNode* >::iterator nBordIt;
8333 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8334 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8335 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8336 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8337 double tol2 = 1.e-8;
8338 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8339 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8340 // Need node movement.
8342 // find X and Z axes to create trsf
8343 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8345 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8347 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8350 gp_Ax3 toBordAx( Pb1, Zb, X );
8351 gp_Ax3 fromSideAx( Ps1, Zs, X );
8352 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8354 gp_Trsf toBordSys, fromSide2Sys;
8355 toBordSys.SetTransformation( toBordAx );
8356 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8357 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8360 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8361 const SMDS_MeshNode* n = *nBordIt;
8362 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8363 toBordSys.Transforms( xyz );
8364 fromSide2Sys.Transforms( xyz );
8365 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8369 // just insert nodes XYZ in the nBordXYZ map
8370 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8371 const SMDS_MeshNode* n = *nBordIt;
8372 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8376 // 2. On the side 2, find the links most co-directed with the correspondent
8377 // links of the free border
8379 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8380 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8381 sideNodes.push_back( theSideFirstNode );
8383 bool hasVolumes = false;
8384 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8385 set<long> foundSideLinkIDs, checkedLinkIDs;
8386 SMDS_VolumeTool volume;
8387 //const SMDS_MeshNode* faceNodes[ 4 ];
8389 const SMDS_MeshNode* sideNode;
8390 const SMDS_MeshElement* sideElem = 0;
8391 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8392 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8393 nBordIt = bordNodes.begin();
8395 // border node position and border link direction to compare with
8396 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8397 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8398 // choose next side node by link direction or by closeness to
8399 // the current border node:
8400 bool searchByDir = ( *nBordIt != theBordLastNode );
8402 // find the next node on the Side 2
8404 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8406 checkedLinkIDs.clear();
8407 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8409 // loop on inverse elements of current node (prevSideNode) on the Side 2
8410 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8411 while ( invElemIt->more() )
8413 const SMDS_MeshElement* elem = invElemIt->next();
8414 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8415 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8416 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8417 bool isVolume = volume.Set( elem );
8418 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8419 if ( isVolume ) // --volume
8421 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8422 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8423 if(elem->IsQuadratic()) {
8424 const SMDS_VtkFace* F =
8425 dynamic_cast<const SMDS_VtkFace*>(elem);
8426 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8427 // use special nodes iterator
8428 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8429 while( anIter->more() ) {
8430 nodes[ iNode ] = cast2Node(anIter->next());
8431 if ( nodes[ iNode++ ] == prevSideNode )
8432 iPrevNode = iNode - 1;
8436 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8437 while ( nIt->more() ) {
8438 nodes[ iNode ] = cast2Node( nIt->next() );
8439 if ( nodes[ iNode++ ] == prevSideNode )
8440 iPrevNode = iNode - 1;
8443 // there are 2 links to check
8448 // loop on links, to be precise, on the second node of links
8449 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8450 const SMDS_MeshNode* n = nodes[ iNode ];
8452 if ( !volume.IsLinked( n, prevSideNode ))
8456 if ( iNode ) // a node before prevSideNode
8457 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8458 else // a node after prevSideNode
8459 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8461 // check if this link was already used
8462 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8463 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8464 if (!isJustChecked &&
8465 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8467 // test a link geometrically
8468 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8469 bool linkIsBetter = false;
8470 double dot = 0.0, dist = 0.0;
8471 if ( searchByDir ) { // choose most co-directed link
8472 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8473 linkIsBetter = ( dot > maxDot );
8475 else { // choose link with the node closest to bordPos
8476 dist = ( nextXYZ - bordPos ).SquareModulus();
8477 linkIsBetter = ( dist < minDist );
8479 if ( linkIsBetter ) {
8488 } // loop on inverse elements of prevSideNode
8491 MESSAGE(" Can't find path by links of the Side 2 ");
8492 return SEW_BAD_SIDE_NODES;
8494 sideNodes.push_back( sideNode );
8495 sideElems.push_back( sideElem );
8496 foundSideLinkIDs.insert ( linkID );
8497 prevSideNode = sideNode;
8499 if ( *nBordIt == theBordLastNode )
8500 searchByDir = false;
8502 // find the next border link to compare with
8503 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8504 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8505 // move to next border node if sideNode is before forward border node (bordPos)
8506 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8507 prevBordNode = *nBordIt;
8509 bordPos = nBordXYZ[ *nBordIt ];
8510 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8511 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8515 while ( sideNode != theSideSecondNode );
8517 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8518 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8519 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8521 } // end nodes search on the side 2
8523 // ============================
8524 // sew the border to the side 2
8525 // ============================
8527 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8528 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8530 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8531 if ( toMergeConformal && toCreatePolygons )
8533 // do not merge quadrangles if polygons are OK (IPAL0052824)
8534 eIt[0] = eSide[0].begin();
8535 eIt[1] = eSide[1].begin();
8536 bool allQuads[2] = { true, true };
8537 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8538 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8539 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8541 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8544 TListOfListOfNodes nodeGroupsToMerge;
8545 if (( toMergeConformal ) ||
8546 ( theSideIsFreeBorder && !theSideThirdNode )) {
8548 // all nodes are to be merged
8550 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8551 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8552 nIt[0]++, nIt[1]++ )
8554 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8555 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8556 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8561 // insert new nodes into the border and the side to get equal nb of segments
8563 // get normalized parameters of nodes on the borders
8564 vector< double > param[ 2 ];
8565 param[0].resize( maxNbNodes );
8566 param[1].resize( maxNbNodes );
8568 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8569 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8570 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8571 const SMDS_MeshNode* nPrev = *nIt;
8572 double bordLength = 0;
8573 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8574 const SMDS_MeshNode* nCur = *nIt;
8575 gp_XYZ segment (nCur->X() - nPrev->X(),
8576 nCur->Y() - nPrev->Y(),
8577 nCur->Z() - nPrev->Z());
8578 double segmentLen = segment.Modulus();
8579 bordLength += segmentLen;
8580 param[ iBord ][ iNode ] = bordLength;
8583 // normalize within [0,1]
8584 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8585 param[ iBord ][ iNode ] /= bordLength;
8589 // loop on border segments
8590 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8591 int i[ 2 ] = { 0, 0 };
8592 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8593 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8595 TElemOfNodeListMap insertMap;
8596 TElemOfNodeListMap::iterator insertMapIt;
8598 // key: elem to insert nodes into
8599 // value: 2 nodes to insert between + nodes to be inserted
8601 bool next[ 2 ] = { false, false };
8603 // find min adjacent segment length after sewing
8604 double nextParam = 10., prevParam = 0;
8605 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8606 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8607 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8608 if ( i[ iBord ] > 0 )
8609 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8611 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8612 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8613 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8615 // choose to insert or to merge nodes
8616 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8617 if ( Abs( du ) <= minSegLen * 0.2 ) {
8620 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8621 const SMDS_MeshNode* n0 = *nIt[0];
8622 const SMDS_MeshNode* n1 = *nIt[1];
8623 nodeGroupsToMerge.back().push_back( n1 );
8624 nodeGroupsToMerge.back().push_back( n0 );
8625 // position of node of the border changes due to merge
8626 param[ 0 ][ i[0] ] += du;
8627 // move n1 for the sake of elem shape evaluation during insertion.
8628 // n1 will be removed by MergeNodes() anyway
8629 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8630 next[0] = next[1] = true;
8635 int intoBord = ( du < 0 ) ? 0 : 1;
8636 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8637 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8638 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8639 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8640 if ( intoBord == 1 ) {
8641 // move node of the border to be on a link of elem of the side
8642 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8643 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8644 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8645 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8646 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8648 insertMapIt = insertMap.find( elem );
8649 bool notFound = ( insertMapIt == insertMap.end() );
8650 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8652 // insert into another link of the same element:
8653 // 1. perform insertion into the other link of the elem
8654 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8655 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8656 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8657 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8658 // 2. perform insertion into the link of adjacent faces
8659 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8660 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8662 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8663 InsertNodesIntoLink( seg, n12, n22, nodeList );
8665 if (toCreatePolyedrs) {
8666 // perform insertion into the links of adjacent volumes
8667 UpdateVolumes(n12, n22, nodeList);
8669 // 3. find an element appeared on n1 and n2 after the insertion
8670 insertMap.erase( elem );
8671 elem = findAdjacentFace( n1, n2, 0 );
8673 if ( notFound || otherLink ) {
8674 // add element and nodes of the side into the insertMap
8675 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8676 (*insertMapIt).second.push_back( n1 );
8677 (*insertMapIt).second.push_back( n2 );
8679 // add node to be inserted into elem
8680 (*insertMapIt).second.push_back( nIns );
8681 next[ 1 - intoBord ] = true;
8684 // go to the next segment
8685 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8686 if ( next[ iBord ] ) {
8687 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8689 nPrev[ iBord ] = *nIt[ iBord ];
8690 nIt[ iBord ]++; i[ iBord ]++;
8694 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8696 // perform insertion of nodes into elements
8698 for (insertMapIt = insertMap.begin();
8699 insertMapIt != insertMap.end();
8702 const SMDS_MeshElement* elem = (*insertMapIt).first;
8703 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8704 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8705 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8707 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8709 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8710 InsertNodesIntoLink( seg, n1, n2, nodeList );
8713 if ( !theSideIsFreeBorder ) {
8714 // look for and insert nodes into the faces adjacent to elem
8715 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8716 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8719 if (toCreatePolyedrs) {
8720 // perform insertion into the links of adjacent volumes
8721 UpdateVolumes(n1, n2, nodeList);
8724 } // end: insert new nodes
8726 MergeNodes ( nodeGroupsToMerge );
8729 // Remove coincident segments
8732 TIDSortedElemSet segments;
8733 SMESH_SequenceOfElemPtr newFaces;
8734 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8736 if ( !myLastCreatedElems(i) ) continue;
8737 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8738 segments.insert( segments.end(), myLastCreatedElems(i) );
8740 newFaces.Append( myLastCreatedElems(i) );
8742 // get segments adjacent to merged nodes
8743 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8744 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8746 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8747 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8748 while ( segIt->more() )
8749 segments.insert( segIt->next() );
8753 TListOfListOfElementsID equalGroups;
8754 if ( !segments.empty() )
8755 FindEqualElements( segments, equalGroups );
8756 if ( !equalGroups.empty() )
8758 // remove from segments those that will be removed
8759 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8760 for ( ; itGroups != equalGroups.end(); ++itGroups )
8762 list< int >& group = *itGroups;
8763 list< int >::iterator id = group.begin();
8764 for ( ++id; id != group.end(); ++id )
8765 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8766 segments.erase( seg );
8768 // remove equal segments
8769 MergeElements( equalGroups );
8771 // restore myLastCreatedElems
8772 myLastCreatedElems = newFaces;
8773 TIDSortedElemSet::iterator seg = segments.begin();
8774 for ( ; seg != segments.end(); ++seg )
8775 myLastCreatedElems.Append( *seg );
8781 //=======================================================================
8782 //function : InsertNodesIntoLink
8783 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8784 // and theBetweenNode2 and split theElement
8785 //=======================================================================
8787 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8788 const SMDS_MeshNode* theBetweenNode1,
8789 const SMDS_MeshNode* theBetweenNode2,
8790 list<const SMDS_MeshNode*>& theNodesToInsert,
8791 const bool toCreatePoly)
8793 if ( !theElement ) return;
8795 SMESHDS_Mesh *aMesh = GetMeshDS();
8796 vector<const SMDS_MeshElement*> newElems;
8798 if ( theElement->GetType() == SMDSAbs_Edge )
8800 theNodesToInsert.push_front( theBetweenNode1 );
8801 theNodesToInsert.push_back ( theBetweenNode2 );
8802 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8803 const SMDS_MeshNode* n1 = *n;
8804 for ( ++n; n != theNodesToInsert.end(); ++n )
8806 const SMDS_MeshNode* n2 = *n;
8807 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8808 AddToSameGroups( seg, theElement, aMesh );
8810 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8813 theNodesToInsert.pop_front();
8814 theNodesToInsert.pop_back();
8816 if ( theElement->IsQuadratic() ) // add a not split part
8818 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8819 theElement->end_nodes() );
8820 int iOther = 0, nbN = nodes.size();
8821 for ( ; iOther < nbN; ++iOther )
8822 if ( nodes[iOther] != theBetweenNode1 &&
8823 nodes[iOther] != theBetweenNode2 )
8827 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8828 AddToSameGroups( seg, theElement, aMesh );
8830 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8832 else if ( iOther == 2 )
8834 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8835 AddToSameGroups( seg, theElement, aMesh );
8837 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8840 // treat new elements
8841 for ( size_t i = 0; i < newElems.size(); ++i )
8844 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8845 myLastCreatedElems.Append( newElems[i] );
8847 ReplaceElemInGroups( theElement, newElems, aMesh );
8848 aMesh->RemoveElement( theElement );
8851 } // if ( theElement->GetType() == SMDSAbs_Edge )
8853 const SMDS_MeshElement* theFace = theElement;
8854 if ( theFace->GetType() != SMDSAbs_Face ) return;
8856 // find indices of 2 link nodes and of the rest nodes
8857 int iNode = 0, il1, il2, i3, i4;
8858 il1 = il2 = i3 = i4 = -1;
8859 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8861 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8862 while ( nodeIt->more() ) {
8863 const SMDS_MeshNode* n = nodeIt->next();
8864 if ( n == theBetweenNode1 )
8866 else if ( n == theBetweenNode2 )
8872 nodes[ iNode++ ] = n;
8874 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8877 // arrange link nodes to go one after another regarding the face orientation
8878 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8879 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8884 aNodesToInsert.reverse();
8886 // check that not link nodes of a quadrangles are in good order
8887 int nbFaceNodes = theFace->NbNodes();
8888 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8894 if (toCreatePoly || theFace->IsPoly()) {
8897 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8899 // add nodes of face up to first node of link
8902 if ( theFace->IsQuadratic() ) {
8903 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8904 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8905 // use special nodes iterator
8906 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8907 while( anIter->more() && !isFLN ) {
8908 const SMDS_MeshNode* n = cast2Node(anIter->next());
8909 poly_nodes[iNode++] = n;
8910 if (n == nodes[il1]) {
8914 // add nodes to insert
8915 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8916 for (; nIt != aNodesToInsert.end(); nIt++) {
8917 poly_nodes[iNode++] = *nIt;
8919 // add nodes of face starting from last node of link
8920 while ( anIter->more() ) {
8921 poly_nodes[iNode++] = cast2Node(anIter->next());
8925 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8926 while ( nodeIt->more() && !isFLN ) {
8927 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8928 poly_nodes[iNode++] = n;
8929 if (n == nodes[il1]) {
8933 // add nodes to insert
8934 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8935 for (; nIt != aNodesToInsert.end(); nIt++) {
8936 poly_nodes[iNode++] = *nIt;
8938 // add nodes of face starting from last node of link
8939 while ( nodeIt->more() ) {
8940 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8941 poly_nodes[iNode++] = n;
8946 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8949 else if ( !theFace->IsQuadratic() )
8951 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8952 int nbLinkNodes = 2 + aNodesToInsert.size();
8953 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8954 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8955 linkNodes[ 0 ] = nodes[ il1 ];
8956 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8957 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8958 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8959 linkNodes[ iNode++ ] = *nIt;
8961 // decide how to split a quadrangle: compare possible variants
8962 // and choose which of splits to be a quadrangle
8963 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8964 if ( nbFaceNodes == 3 ) {
8965 iBestQuad = nbSplits;
8968 else if ( nbFaceNodes == 4 ) {
8969 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8970 double aBestRate = DBL_MAX;
8971 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8973 double aBadRate = 0;
8974 // evaluate elements quality
8975 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8976 if ( iSplit == iQuad ) {
8977 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8981 aBadRate += getBadRate( &quad, aCrit );
8984 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8986 nodes[ iSplit < iQuad ? i4 : i3 ]);
8987 aBadRate += getBadRate( &tria, aCrit );
8991 if ( aBadRate < aBestRate ) {
8993 aBestRate = aBadRate;
8998 // create new elements
9000 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9002 if ( iSplit == iBestQuad )
9003 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9008 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9010 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9013 const SMDS_MeshNode* newNodes[ 4 ];
9014 newNodes[ 0 ] = linkNodes[ i1 ];
9015 newNodes[ 1 ] = linkNodes[ i2 ];
9016 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9017 newNodes[ 3 ] = nodes[ i4 ];
9018 if (iSplit == iBestQuad)
9019 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9021 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9023 } // end if(!theFace->IsQuadratic())
9025 else { // theFace is quadratic
9026 // we have to split theFace on simple triangles and one simple quadrangle
9028 int nbshift = tmp*2;
9029 // shift nodes in nodes[] by nbshift
9031 for(i=0; i<nbshift; i++) {
9032 const SMDS_MeshNode* n = nodes[0];
9033 for(j=0; j<nbFaceNodes-1; j++) {
9034 nodes[j] = nodes[j+1];
9036 nodes[nbFaceNodes-1] = n;
9038 il1 = il1 - nbshift;
9039 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9040 // n0 n1 n2 n0 n1 n2
9041 // +-----+-----+ +-----+-----+
9050 // create new elements
9052 if ( nbFaceNodes == 6 ) { // quadratic triangle
9053 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9054 if ( theFace->IsMediumNode(nodes[il1]) ) {
9055 // create quadrangle
9056 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9062 // create quadrangle
9063 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9069 else { // nbFaceNodes==8 - quadratic quadrangle
9070 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9071 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9072 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9073 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9074 // create quadrangle
9075 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9081 // create quadrangle
9082 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9088 // create needed triangles using n1,n2,n3 and inserted nodes
9089 int nbn = 2 + aNodesToInsert.size();
9090 vector<const SMDS_MeshNode*> aNodes(nbn);
9091 aNodes[0 ] = nodes[n1];
9092 aNodes[nbn-1] = nodes[n2];
9093 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9094 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9095 aNodes[iNode++] = *nIt;
9097 for ( i = 1; i < nbn; i++ )
9098 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9101 // remove the old face
9102 for ( size_t i = 0; i < newElems.size(); ++i )
9105 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9106 myLastCreatedElems.Append( newElems[i] );
9108 ReplaceElemInGroups( theFace, newElems, aMesh );
9109 aMesh->RemoveElement(theFace);
9111 } // InsertNodesIntoLink()
9113 //=======================================================================
9114 //function : UpdateVolumes
9116 //=======================================================================
9118 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9119 const SMDS_MeshNode* theBetweenNode2,
9120 list<const SMDS_MeshNode*>& theNodesToInsert)
9122 myLastCreatedElems.Clear();
9123 myLastCreatedNodes.Clear();
9125 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9126 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9127 const SMDS_MeshElement* elem = invElemIt->next();
9129 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9130 SMDS_VolumeTool aVolume (elem);
9131 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9134 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9135 int iface, nbFaces = aVolume.NbFaces();
9136 vector<const SMDS_MeshNode *> poly_nodes;
9137 vector<int> quantities (nbFaces);
9139 for (iface = 0; iface < nbFaces; iface++) {
9140 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9141 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9142 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9144 for (int inode = 0; inode < nbFaceNodes; inode++) {
9145 poly_nodes.push_back(faceNodes[inode]);
9147 if (nbInserted == 0) {
9148 if (faceNodes[inode] == theBetweenNode1) {
9149 if (faceNodes[inode + 1] == theBetweenNode2) {
9150 nbInserted = theNodesToInsert.size();
9152 // add nodes to insert
9153 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9154 for (; nIt != theNodesToInsert.end(); nIt++) {
9155 poly_nodes.push_back(*nIt);
9159 else if (faceNodes[inode] == theBetweenNode2) {
9160 if (faceNodes[inode + 1] == theBetweenNode1) {
9161 nbInserted = theNodesToInsert.size();
9163 // add nodes to insert in reversed order
9164 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9166 for (; nIt != theNodesToInsert.begin(); nIt--) {
9167 poly_nodes.push_back(*nIt);
9169 poly_nodes.push_back(*nIt);
9176 quantities[iface] = nbFaceNodes + nbInserted;
9179 // Replace the volume
9180 SMESHDS_Mesh *aMesh = GetMeshDS();
9182 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9184 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9185 myLastCreatedElems.Append( newElem );
9186 ReplaceElemInGroups( elem, newElem, aMesh );
9188 aMesh->RemoveElement( elem );
9194 //================================================================================
9196 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9198 //================================================================================
9200 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9201 vector<const SMDS_MeshNode *> & nodes,
9202 vector<int> & nbNodeInFaces )
9205 nbNodeInFaces.clear();
9206 SMDS_VolumeTool vTool ( elem );
9207 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9209 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9210 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9211 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9216 //=======================================================================
9218 * \brief Convert elements contained in a sub-mesh to quadratic
9219 * \return int - nb of checked elements
9221 //=======================================================================
9223 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9224 SMESH_MesherHelper& theHelper,
9225 const bool theForce3d)
9227 //MESSAGE("convertElemToQuadratic");
9229 if( !theSm ) return nbElem;
9231 vector<int> nbNodeInFaces;
9232 vector<const SMDS_MeshNode *> nodes;
9233 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9234 while(ElemItr->more())
9237 const SMDS_MeshElement* elem = ElemItr->next();
9238 if( !elem ) continue;
9240 // analyse a necessity of conversion
9241 const SMDSAbs_ElementType aType = elem->GetType();
9242 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9244 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9245 bool hasCentralNodes = false;
9246 if ( elem->IsQuadratic() )
9249 switch ( aGeomType ) {
9250 case SMDSEntity_Quad_Triangle:
9251 case SMDSEntity_Quad_Quadrangle:
9252 case SMDSEntity_Quad_Hexa:
9253 case SMDSEntity_Quad_Penta:
9254 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9256 case SMDSEntity_BiQuad_Triangle:
9257 case SMDSEntity_BiQuad_Quadrangle:
9258 case SMDSEntity_TriQuad_Hexa:
9259 case SMDSEntity_BiQuad_Penta:
9260 alreadyOK = theHelper.GetIsBiQuadratic();
9261 hasCentralNodes = true;
9266 // take into account already present medium nodes
9268 case SMDSAbs_Volume:
9269 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9271 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9273 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9279 // get elem data needed to re-create it
9281 const int id = elem->GetID();
9282 const int nbNodes = elem->NbCornerNodes();
9283 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9284 if ( aGeomType == SMDSEntity_Polyhedra )
9285 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9286 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9287 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9289 // remove a linear element
9290 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9292 // remove central nodes of biquadratic elements (biquad->quad conversion)
9293 if ( hasCentralNodes )
9294 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9295 if ( nodes[i]->NbInverseElements() == 0 )
9296 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9298 const SMDS_MeshElement* NewElem = 0;
9304 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9312 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9315 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9318 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9322 case SMDSAbs_Volume :
9326 case SMDSEntity_Tetra:
9327 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9329 case SMDSEntity_Pyramid:
9330 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9332 case SMDSEntity_Penta:
9333 case SMDSEntity_Quad_Penta:
9334 case SMDSEntity_BiQuad_Penta:
9335 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9337 case SMDSEntity_Hexa:
9338 case SMDSEntity_Quad_Hexa:
9339 case SMDSEntity_TriQuad_Hexa:
9340 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9341 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9343 case SMDSEntity_Hexagonal_Prism:
9345 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9352 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9353 if( NewElem && NewElem->getshapeId() < 1 )
9354 theSm->AddElement( NewElem );
9358 //=======================================================================
9359 //function : ConvertToQuadratic
9361 //=======================================================================
9363 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9365 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9366 SMESHDS_Mesh* meshDS = GetMeshDS();
9368 SMESH_MesherHelper aHelper(*myMesh);
9370 aHelper.SetIsQuadratic( true );
9371 aHelper.SetIsBiQuadratic( theToBiQuad );
9372 aHelper.SetElementsOnShape(true);
9373 aHelper.ToFixNodeParameters( true );
9375 // convert elements assigned to sub-meshes
9376 int nbCheckedElems = 0;
9377 if ( myMesh->HasShapeToMesh() )
9379 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9381 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9382 while ( smIt->more() ) {
9383 SMESH_subMesh* sm = smIt->next();
9384 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9385 aHelper.SetSubShape( sm->GetSubShape() );
9386 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9392 // convert elements NOT assigned to sub-meshes
9393 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9394 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9396 aHelper.SetElementsOnShape(false);
9397 SMESHDS_SubMesh *smDS = 0;
9400 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9401 while( aEdgeItr->more() )
9403 const SMDS_MeshEdge* edge = aEdgeItr->next();
9404 if ( !edge->IsQuadratic() )
9406 int id = edge->GetID();
9407 const SMDS_MeshNode* n1 = edge->GetNode(0);
9408 const SMDS_MeshNode* n2 = edge->GetNode(1);
9410 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9412 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9413 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9417 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9422 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9423 while( aFaceItr->more() )
9425 const SMDS_MeshFace* face = aFaceItr->next();
9426 if ( !face ) continue;
9428 const SMDSAbs_EntityType type = face->GetEntityType();
9432 case SMDSEntity_Quad_Triangle:
9433 case SMDSEntity_Quad_Quadrangle:
9434 alreadyOK = !theToBiQuad;
9435 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9437 case SMDSEntity_BiQuad_Triangle:
9438 case SMDSEntity_BiQuad_Quadrangle:
9439 alreadyOK = theToBiQuad;
9440 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9442 default: alreadyOK = false;
9447 const int id = face->GetID();
9448 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9450 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9452 SMDS_MeshFace * NewFace = 0;
9455 case SMDSEntity_Triangle:
9456 case SMDSEntity_Quad_Triangle:
9457 case SMDSEntity_BiQuad_Triangle:
9458 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9459 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9460 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9463 case SMDSEntity_Quadrangle:
9464 case SMDSEntity_Quad_Quadrangle:
9465 case SMDSEntity_BiQuad_Quadrangle:
9466 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9467 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9468 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9472 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9474 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9478 vector<int> nbNodeInFaces;
9479 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9480 while(aVolumeItr->more())
9482 const SMDS_MeshVolume* volume = aVolumeItr->next();
9483 if ( !volume ) continue;
9485 const SMDSAbs_EntityType type = volume->GetEntityType();
9486 if ( volume->IsQuadratic() )
9491 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9492 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9493 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9494 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9495 default: alreadyOK = true;
9499 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9503 const int id = volume->GetID();
9504 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9505 if ( type == SMDSEntity_Polyhedra )
9506 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9507 else if ( type == SMDSEntity_Hexagonal_Prism )
9508 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9510 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9512 SMDS_MeshVolume * NewVolume = 0;
9515 case SMDSEntity_Tetra:
9516 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9518 case SMDSEntity_Hexa:
9519 case SMDSEntity_Quad_Hexa:
9520 case SMDSEntity_TriQuad_Hexa:
9521 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9522 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9523 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9524 if ( nodes[i]->NbInverseElements() == 0 )
9525 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9527 case SMDSEntity_Pyramid:
9528 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9529 nodes[3], nodes[4], id, theForce3d);
9531 case SMDSEntity_Penta:
9532 case SMDSEntity_Quad_Penta:
9533 case SMDSEntity_BiQuad_Penta:
9534 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9535 nodes[3], nodes[4], nodes[5], id, theForce3d);
9536 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9537 if ( nodes[i]->NbInverseElements() == 0 )
9538 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9540 case SMDSEntity_Hexagonal_Prism:
9542 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9544 ReplaceElemInGroups(volume, NewVolume, meshDS);
9549 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9550 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9551 // aHelper.FixQuadraticElements(myError);
9552 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9556 //================================================================================
9558 * \brief Makes given elements quadratic
9559 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9560 * \param theElements - elements to make quadratic
9562 //================================================================================
9564 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9565 TIDSortedElemSet& theElements,
9566 const bool theToBiQuad)
9568 if ( theElements.empty() ) return;
9570 // we believe that all theElements are of the same type
9571 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9573 // get all nodes shared by theElements
9574 TIDSortedNodeSet allNodes;
9575 TIDSortedElemSet::iterator eIt = theElements.begin();
9576 for ( ; eIt != theElements.end(); ++eIt )
9577 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9579 // complete theElements with elements of lower dim whose all nodes are in allNodes
9581 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9582 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9583 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9584 for ( ; nIt != allNodes.end(); ++nIt )
9586 const SMDS_MeshNode* n = *nIt;
9587 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9588 while ( invIt->more() )
9590 const SMDS_MeshElement* e = invIt->next();
9591 const SMDSAbs_ElementType type = e->GetType();
9592 if ( e->IsQuadratic() )
9594 quadAdjacentElems[ type ].insert( e );
9597 switch ( e->GetEntityType() ) {
9598 case SMDSEntity_Quad_Triangle:
9599 case SMDSEntity_Quad_Quadrangle:
9600 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9601 case SMDSEntity_BiQuad_Triangle:
9602 case SMDSEntity_BiQuad_Quadrangle:
9603 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9604 default: alreadyOK = true;
9609 if ( type >= elemType )
9610 continue; // same type or more complex linear element
9612 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9613 continue; // e is already checked
9617 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9618 while ( nodeIt->more() && allIn )
9619 allIn = allNodes.count( nodeIt->next() );
9621 theElements.insert(e );
9625 SMESH_MesherHelper helper(*myMesh);
9626 helper.SetIsQuadratic( true );
9627 helper.SetIsBiQuadratic( theToBiQuad );
9629 // add links of quadratic adjacent elements to the helper
9631 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9632 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9633 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9635 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9637 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9638 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9639 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9641 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9643 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9644 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9645 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9647 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9650 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9652 SMESHDS_Mesh* meshDS = GetMeshDS();
9653 SMESHDS_SubMesh* smDS = 0;
9654 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9656 const SMDS_MeshElement* elem = *eIt;
9659 int nbCentralNodes = 0;
9660 switch ( elem->GetEntityType() ) {
9661 // linear convertible
9662 case SMDSEntity_Edge:
9663 case SMDSEntity_Triangle:
9664 case SMDSEntity_Quadrangle:
9665 case SMDSEntity_Tetra:
9666 case SMDSEntity_Pyramid:
9667 case SMDSEntity_Hexa:
9668 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9669 // quadratic that can become bi-quadratic
9670 case SMDSEntity_Quad_Triangle:
9671 case SMDSEntity_Quad_Quadrangle:
9672 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9674 case SMDSEntity_BiQuad_Triangle:
9675 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9676 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9678 default: alreadyOK = true;
9680 if ( alreadyOK ) continue;
9682 const SMDSAbs_ElementType type = elem->GetType();
9683 const int id = elem->GetID();
9684 const int nbNodes = elem->NbCornerNodes();
9685 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9687 helper.SetSubShape( elem->getshapeId() );
9689 if ( !smDS || !smDS->Contains( elem ))
9690 smDS = meshDS->MeshElements( elem->getshapeId() );
9691 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9693 SMDS_MeshElement * newElem = 0;
9696 case 4: // cases for most frequently used element types go first (for optimization)
9697 if ( type == SMDSAbs_Volume )
9698 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9700 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9703 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9704 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9707 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9710 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9713 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9714 nodes[4], id, theForce3d);
9717 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9718 nodes[4], nodes[5], id, theForce3d);
9722 ReplaceElemInGroups( elem, newElem, meshDS);
9723 if( newElem && smDS )
9724 smDS->AddElement( newElem );
9726 // remove central nodes
9727 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9728 if ( nodes[i]->NbInverseElements() == 0 )
9729 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9731 } // loop on theElements
9734 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9735 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9736 // helper.FixQuadraticElements( myError );
9737 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9741 //=======================================================================
9743 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9744 * \return int - nb of checked elements
9746 //=======================================================================
9748 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9749 SMDS_ElemIteratorPtr theItr,
9750 const int theShapeID)
9753 SMESHDS_Mesh* meshDS = GetMeshDS();
9754 ElemFeatures elemType;
9755 vector<const SMDS_MeshNode *> nodes;
9757 while( theItr->more() )
9759 const SMDS_MeshElement* elem = theItr->next();
9761 if( elem && elem->IsQuadratic())
9764 int nbCornerNodes = elem->NbCornerNodes();
9765 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9767 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9769 //remove a quadratic element
9770 if ( !theSm || !theSm->Contains( elem ))
9771 theSm = meshDS->MeshElements( elem->getshapeId() );
9772 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9774 // remove medium nodes
9775 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9776 if ( nodes[i]->NbInverseElements() == 0 )
9777 meshDS->RemoveFreeNode( nodes[i], theSm );
9779 // add a linear element
9780 nodes.resize( nbCornerNodes );
9781 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9782 ReplaceElemInGroups(elem, newElem, meshDS);
9783 if( theSm && newElem )
9784 theSm->AddElement( newElem );
9790 //=======================================================================
9791 //function : ConvertFromQuadratic
9793 //=======================================================================
9795 bool SMESH_MeshEditor::ConvertFromQuadratic()
9797 int nbCheckedElems = 0;
9798 if ( myMesh->HasShapeToMesh() )
9800 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9802 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9803 while ( smIt->more() ) {
9804 SMESH_subMesh* sm = smIt->next();
9805 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9806 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9812 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9813 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9815 SMESHDS_SubMesh *aSM = 0;
9816 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9824 //================================================================================
9826 * \brief Return true if all medium nodes of the element are in the node set
9828 //================================================================================
9830 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9832 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9833 if ( !nodeSet.count( elem->GetNode(i) ))
9839 //================================================================================
9841 * \brief Makes given elements linear
9843 //================================================================================
9845 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9847 if ( theElements.empty() ) return;
9849 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9850 set<int> mediumNodeIDs;
9851 TIDSortedElemSet::iterator eIt = theElements.begin();
9852 for ( ; eIt != theElements.end(); ++eIt )
9854 const SMDS_MeshElement* e = *eIt;
9855 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9856 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9859 // replace given elements by linear ones
9860 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9861 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9863 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9864 // except those elements sharing medium nodes of quadratic element whose medium nodes
9865 // are not all in mediumNodeIDs
9867 // get remaining medium nodes
9868 TIDSortedNodeSet mediumNodes;
9869 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9870 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9871 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9872 mediumNodes.insert( mediumNodes.end(), n );
9874 // find more quadratic elements to convert
9875 TIDSortedElemSet moreElemsToConvert;
9876 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9877 for ( ; nIt != mediumNodes.end(); ++nIt )
9879 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9880 while ( invIt->more() )
9882 const SMDS_MeshElement* e = invIt->next();
9883 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9885 // find a more complex element including e and
9886 // whose medium nodes are not in mediumNodes
9887 bool complexFound = false;
9888 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9890 SMDS_ElemIteratorPtr invIt2 =
9891 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9892 while ( invIt2->more() )
9894 const SMDS_MeshElement* eComplex = invIt2->next();
9895 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9897 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9898 if ( nbCommonNodes == e->NbNodes())
9900 complexFound = true;
9901 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9907 if ( !complexFound )
9908 moreElemsToConvert.insert( e );
9912 elemIt = elemSetIterator( moreElemsToConvert );
9913 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9916 //=======================================================================
9917 //function : SewSideElements
9919 //=======================================================================
9921 SMESH_MeshEditor::Sew_Error
9922 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9923 TIDSortedElemSet& theSide2,
9924 const SMDS_MeshNode* theFirstNode1,
9925 const SMDS_MeshNode* theFirstNode2,
9926 const SMDS_MeshNode* theSecondNode1,
9927 const SMDS_MeshNode* theSecondNode2)
9929 myLastCreatedElems.Clear();
9930 myLastCreatedNodes.Clear();
9932 if ( theSide1.size() != theSide2.size() )
9933 return SEW_DIFF_NB_OF_ELEMENTS;
9935 Sew_Error aResult = SEW_OK;
9937 // 1. Build set of faces representing each side
9938 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9939 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9941 // =======================================================================
9942 // 1. Build set of faces representing each side:
9943 // =======================================================================
9944 // a. build set of nodes belonging to faces
9945 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9946 // c. create temporary faces representing side of volumes if correspondent
9947 // face does not exist
9949 SMESHDS_Mesh* aMesh = GetMeshDS();
9950 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9951 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9952 TIDSortedElemSet faceSet1, faceSet2;
9953 set<const SMDS_MeshElement*> volSet1, volSet2;
9954 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9955 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9956 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9957 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9958 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9959 int iSide, iFace, iNode;
9961 list<const SMDS_MeshElement* > tempFaceList;
9962 for ( iSide = 0; iSide < 2; iSide++ ) {
9963 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9964 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9965 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9966 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9967 set<const SMDS_MeshElement*>::iterator vIt;
9968 TIDSortedElemSet::iterator eIt;
9969 set<const SMDS_MeshNode*>::iterator nIt;
9971 // check that given nodes belong to given elements
9972 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9973 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9974 int firstIndex = -1, secondIndex = -1;
9975 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9976 const SMDS_MeshElement* elem = *eIt;
9977 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9978 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9979 if ( firstIndex > -1 && secondIndex > -1 ) break;
9981 if ( firstIndex < 0 || secondIndex < 0 ) {
9982 // we can simply return until temporary faces created
9983 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9986 // -----------------------------------------------------------
9987 // 1a. Collect nodes of existing faces
9988 // and build set of face nodes in order to detect missing
9989 // faces corresponding to sides of volumes
9990 // -----------------------------------------------------------
9992 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9994 // loop on the given element of a side
9995 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9996 //const SMDS_MeshElement* elem = *eIt;
9997 const SMDS_MeshElement* elem = *eIt;
9998 if ( elem->GetType() == SMDSAbs_Face ) {
9999 faceSet->insert( elem );
10000 set <const SMDS_MeshNode*> faceNodeSet;
10001 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10002 while ( nodeIt->more() ) {
10003 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10004 nodeSet->insert( n );
10005 faceNodeSet.insert( n );
10007 setOfFaceNodeSet.insert( faceNodeSet );
10009 else if ( elem->GetType() == SMDSAbs_Volume )
10010 volSet->insert( elem );
10012 // ------------------------------------------------------------------------------
10013 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10014 // ------------------------------------------------------------------------------
10016 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10017 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10018 while ( fIt->more() ) { // loop on faces sharing a node
10019 const SMDS_MeshElement* f = fIt->next();
10020 if ( faceSet->find( f ) == faceSet->end() ) {
10021 // check if all nodes are in nodeSet and
10022 // complete setOfFaceNodeSet if they are
10023 set <const SMDS_MeshNode*> faceNodeSet;
10024 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10025 bool allInSet = true;
10026 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10027 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10028 if ( nodeSet->find( n ) == nodeSet->end() )
10031 faceNodeSet.insert( n );
10034 faceSet->insert( f );
10035 setOfFaceNodeSet.insert( faceNodeSet );
10041 // -------------------------------------------------------------------------
10042 // 1c. Create temporary faces representing sides of volumes if correspondent
10043 // face does not exist
10044 // -------------------------------------------------------------------------
10046 if ( !volSet->empty() ) {
10047 //int nodeSetSize = nodeSet->size();
10049 // loop on given volumes
10050 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10051 SMDS_VolumeTool vol (*vIt);
10052 // loop on volume faces: find free faces
10053 // --------------------------------------
10054 list<const SMDS_MeshElement* > freeFaceList;
10055 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10056 if ( !vol.IsFreeFace( iFace ))
10058 // check if there is already a face with same nodes in a face set
10059 const SMDS_MeshElement* aFreeFace = 0;
10060 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10061 int nbNodes = vol.NbFaceNodes( iFace );
10062 set <const SMDS_MeshNode*> faceNodeSet;
10063 vol.GetFaceNodes( iFace, faceNodeSet );
10064 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10066 // no such a face is given but it still can exist, check it
10067 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10068 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10070 if ( !aFreeFace ) {
10071 // create a temporary face
10072 if ( nbNodes == 3 ) {
10073 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10074 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10076 else if ( nbNodes == 4 ) {
10077 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10078 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10081 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10082 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10083 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10086 tempFaceList.push_back( aFreeFace );
10090 freeFaceList.push_back( aFreeFace );
10092 } // loop on faces of a volume
10094 // choose one of several free faces of a volume
10095 // --------------------------------------------
10096 if ( freeFaceList.size() > 1 ) {
10097 // choose a face having max nb of nodes shared by other elems of a side
10098 int maxNbNodes = -1;
10099 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10100 while ( fIt != freeFaceList.end() ) { // loop on free faces
10101 int nbSharedNodes = 0;
10102 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10103 while ( nodeIt->more() ) { // loop on free face nodes
10104 const SMDS_MeshNode* n =
10105 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10106 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10107 while ( invElemIt->more() ) {
10108 const SMDS_MeshElement* e = invElemIt->next();
10109 nbSharedNodes += faceSet->count( e );
10110 nbSharedNodes += elemSet->count( e );
10113 if ( nbSharedNodes > maxNbNodes ) {
10114 maxNbNodes = nbSharedNodes;
10115 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10117 else if ( nbSharedNodes == maxNbNodes ) {
10121 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10124 if ( freeFaceList.size() > 1 )
10126 // could not choose one face, use another way
10127 // choose a face most close to the bary center of the opposite side
10128 gp_XYZ aBC( 0., 0., 0. );
10129 set <const SMDS_MeshNode*> addedNodes;
10130 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10131 eIt = elemSet2->begin();
10132 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10133 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10134 while ( nodeIt->more() ) { // loop on free face nodes
10135 const SMDS_MeshNode* n =
10136 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10137 if ( addedNodes.insert( n ).second )
10138 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10141 aBC /= addedNodes.size();
10142 double minDist = DBL_MAX;
10143 fIt = freeFaceList.begin();
10144 while ( fIt != freeFaceList.end() ) { // loop on free faces
10146 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10147 while ( nodeIt->more() ) { // loop on free face nodes
10148 const SMDS_MeshNode* n =
10149 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10150 gp_XYZ p( n->X(),n->Y(),n->Z() );
10151 dist += ( aBC - p ).SquareModulus();
10153 if ( dist < minDist ) {
10155 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10158 fIt = freeFaceList.erase( fIt++ );
10161 } // choose one of several free faces of a volume
10163 if ( freeFaceList.size() == 1 ) {
10164 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10165 faceSet->insert( aFreeFace );
10166 // complete a node set with nodes of a found free face
10167 // for ( iNode = 0; iNode < ; iNode++ )
10168 // nodeSet->insert( fNodes[ iNode ] );
10171 } // loop on volumes of a side
10173 // // complete a set of faces if new nodes in a nodeSet appeared
10174 // // ----------------------------------------------------------
10175 // if ( nodeSetSize != nodeSet->size() ) {
10176 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10177 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10178 // while ( fIt->more() ) { // loop on faces sharing a node
10179 // const SMDS_MeshElement* f = fIt->next();
10180 // if ( faceSet->find( f ) == faceSet->end() ) {
10181 // // check if all nodes are in nodeSet and
10182 // // complete setOfFaceNodeSet if they are
10183 // set <const SMDS_MeshNode*> faceNodeSet;
10184 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10185 // bool allInSet = true;
10186 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10187 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10188 // if ( nodeSet->find( n ) == nodeSet->end() )
10189 // allInSet = false;
10191 // faceNodeSet.insert( n );
10193 // if ( allInSet ) {
10194 // faceSet->insert( f );
10195 // setOfFaceNodeSet.insert( faceNodeSet );
10201 } // Create temporary faces, if there are volumes given
10204 if ( faceSet1.size() != faceSet2.size() ) {
10205 // delete temporary faces: they are in reverseElements of actual nodes
10206 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10207 // while ( tmpFaceIt->more() )
10208 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10209 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10210 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10211 // aMesh->RemoveElement(*tmpFaceIt);
10212 MESSAGE("Diff nb of faces");
10213 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10216 // ============================================================
10217 // 2. Find nodes to merge:
10218 // bind a node to remove to a node to put instead
10219 // ============================================================
10221 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10222 if ( theFirstNode1 != theFirstNode2 )
10223 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10224 if ( theSecondNode1 != theSecondNode2 )
10225 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10227 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10228 set< long > linkIdSet; // links to process
10229 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10231 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10232 list< NLink > linkList[2];
10233 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10234 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10235 // loop on links in linkList; find faces by links and append links
10236 // of the found faces to linkList
10237 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10238 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10240 NLink link[] = { *linkIt[0], *linkIt[1] };
10241 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10242 if ( !linkIdSet.count( linkID ) )
10245 // by links, find faces in the face sets,
10246 // and find indices of link nodes in the found faces;
10247 // in a face set, there is only one or no face sharing a link
10248 // ---------------------------------------------------------------
10250 const SMDS_MeshElement* face[] = { 0, 0 };
10251 vector<const SMDS_MeshNode*> fnodes[2];
10252 int iLinkNode[2][2];
10253 TIDSortedElemSet avoidSet;
10254 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10255 const SMDS_MeshNode* n1 = link[iSide].first;
10256 const SMDS_MeshNode* n2 = link[iSide].second;
10257 //cout << "Side " << iSide << " ";
10258 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10259 // find a face by two link nodes
10260 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10261 *faceSetPtr[ iSide ], avoidSet,
10262 &iLinkNode[iSide][0],
10263 &iLinkNode[iSide][1] );
10264 if ( face[ iSide ])
10266 //cout << " F " << face[ iSide]->GetID() <<endl;
10267 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10268 // put face nodes to fnodes
10269 if ( face[ iSide ]->IsQuadratic() )
10271 // use interlaced nodes iterator
10272 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10273 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10274 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10275 while ( nIter->more() )
10276 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10280 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10281 face[ iSide ]->end_nodes() );
10283 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10287 // check similarity of elements of the sides
10288 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10289 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10290 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10291 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10294 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10296 break; // do not return because it's necessary to remove tmp faces
10299 // set nodes to merge
10300 // -------------------
10302 if ( face[0] && face[1] ) {
10303 const int nbNodes = face[0]->NbNodes();
10304 if ( nbNodes != face[1]->NbNodes() ) {
10305 MESSAGE("Diff nb of face nodes");
10306 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10307 break; // do not return because it s necessary to remove tmp faces
10309 bool reverse[] = { false, false }; // order of nodes in the link
10310 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10311 // analyse link orientation in faces
10312 int i1 = iLinkNode[ iSide ][ 0 ];
10313 int i2 = iLinkNode[ iSide ][ 1 ];
10314 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10316 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10317 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10318 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10320 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10321 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10324 // add other links of the faces to linkList
10325 // -----------------------------------------
10327 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10328 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10329 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10330 if ( !iter_isnew.second ) { // already in a set: no need to process
10331 linkIdSet.erase( iter_isnew.first );
10333 else // new in set == encountered for the first time: add
10335 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10336 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10337 linkList[0].push_back ( NLink( n1, n2 ));
10338 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10343 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10346 } // loop on link lists
10348 if ( aResult == SEW_OK &&
10349 ( //linkIt[0] != linkList[0].end() ||
10350 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10351 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10352 " " << (faceSetPtr[1]->empty()));
10353 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10356 // ====================================================================
10357 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10358 // ====================================================================
10360 // delete temporary faces
10361 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10362 // while ( tmpFaceIt->more() )
10363 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10364 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10365 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10366 aMesh->RemoveElement(*tmpFaceIt);
10368 if ( aResult != SEW_OK)
10371 list< int > nodeIDsToRemove;
10372 vector< const SMDS_MeshNode*> nodes;
10373 ElemFeatures elemType;
10375 // loop on nodes replacement map
10376 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10377 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10378 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10380 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10381 nodeIDsToRemove.push_back( nToRemove->GetID() );
10382 // loop on elements sharing nToRemove
10383 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10384 while ( invElemIt->more() ) {
10385 const SMDS_MeshElement* e = invElemIt->next();
10386 // get a new suite of nodes: make replacement
10387 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10388 nodes.resize( nbNodes );
10389 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10390 while ( nIt->more() ) {
10391 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10392 nnIt = nReplaceMap.find( n );
10393 if ( nnIt != nReplaceMap.end() ) {
10395 n = (*nnIt).second;
10399 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10400 // elemIDsToRemove.push_back( e->GetID() );
10404 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10405 aMesh->RemoveElement( e );
10407 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10409 AddToSameGroups( newElem, e, aMesh );
10410 if ( int aShapeId = e->getshapeId() )
10411 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10417 Remove( nodeIDsToRemove, true );
10422 //================================================================================
10424 * \brief Find corresponding nodes in two sets of faces
10425 * \param theSide1 - first face set
10426 * \param theSide2 - second first face
10427 * \param theFirstNode1 - a boundary node of set 1
10428 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10429 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10430 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10431 * \param nReplaceMap - output map of corresponding nodes
10432 * \return bool - is a success or not
10434 //================================================================================
10437 //#define DEBUG_MATCHING_NODES
10440 SMESH_MeshEditor::Sew_Error
10441 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10442 set<const SMDS_MeshElement*>& theSide2,
10443 const SMDS_MeshNode* theFirstNode1,
10444 const SMDS_MeshNode* theFirstNode2,
10445 const SMDS_MeshNode* theSecondNode1,
10446 const SMDS_MeshNode* theSecondNode2,
10447 TNodeNodeMap & nReplaceMap)
10449 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10451 nReplaceMap.clear();
10452 if ( theFirstNode1 != theFirstNode2 )
10453 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10454 if ( theSecondNode1 != theSecondNode2 )
10455 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10457 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10458 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10460 list< NLink > linkList[2];
10461 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10462 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10464 // loop on links in linkList; find faces by links and append links
10465 // of the found faces to linkList
10466 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10467 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10468 NLink link[] = { *linkIt[0], *linkIt[1] };
10469 if ( linkSet.find( link[0] ) == linkSet.end() )
10472 // by links, find faces in the face sets,
10473 // and find indices of link nodes in the found faces;
10474 // in a face set, there is only one or no face sharing a link
10475 // ---------------------------------------------------------------
10477 const SMDS_MeshElement* face[] = { 0, 0 };
10478 list<const SMDS_MeshNode*> notLinkNodes[2];
10479 //bool reverse[] = { false, false }; // order of notLinkNodes
10481 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10483 const SMDS_MeshNode* n1 = link[iSide].first;
10484 const SMDS_MeshNode* n2 = link[iSide].second;
10485 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10486 set< const SMDS_MeshElement* > facesOfNode1;
10487 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10489 // during a loop of the first node, we find all faces around n1,
10490 // during a loop of the second node, we find one face sharing both n1 and n2
10491 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10492 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10493 while ( fIt->more() ) { // loop on faces sharing a node
10494 const SMDS_MeshElement* f = fIt->next();
10495 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10496 ! facesOfNode1.insert( f ).second ) // f encounters twice
10498 if ( face[ iSide ] ) {
10499 MESSAGE( "2 faces per link " );
10500 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10503 faceSet->erase( f );
10505 // get not link nodes
10506 int nbN = f->NbNodes();
10507 if ( f->IsQuadratic() )
10509 nbNodes[ iSide ] = nbN;
10510 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10511 int i1 = f->GetNodeIndex( n1 );
10512 int i2 = f->GetNodeIndex( n2 );
10513 int iEnd = nbN, iBeg = -1, iDelta = 1;
10514 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10516 std::swap( iEnd, iBeg ); iDelta = -1;
10521 if ( i == iEnd ) i = iBeg + iDelta;
10522 if ( i == i1 ) break;
10523 nodes.push_back ( f->GetNode( i ) );
10529 // check similarity of elements of the sides
10530 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10531 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10532 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10533 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10536 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10540 // set nodes to merge
10541 // -------------------
10543 if ( face[0] && face[1] ) {
10544 if ( nbNodes[0] != nbNodes[1] ) {
10545 MESSAGE("Diff nb of face nodes");
10546 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10548 #ifdef DEBUG_MATCHING_NODES
10549 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10550 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10551 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10553 int nbN = nbNodes[0];
10555 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10556 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10557 for ( int i = 0 ; i < nbN - 2; ++i ) {
10558 #ifdef DEBUG_MATCHING_NODES
10559 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10561 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10565 // add other links of the face 1 to linkList
10566 // -----------------------------------------
10568 const SMDS_MeshElement* f0 = face[0];
10569 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10570 for ( int i = 0; i < nbN; i++ )
10572 const SMDS_MeshNode* n2 = f0->GetNode( i );
10573 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10574 linkSet.insert( SMESH_TLink( n1, n2 ));
10575 if ( !iter_isnew.second ) { // already in a set: no need to process
10576 linkSet.erase( iter_isnew.first );
10578 else // new in set == encountered for the first time: add
10580 #ifdef DEBUG_MATCHING_NODES
10581 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10582 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10584 linkList[0].push_back ( NLink( n1, n2 ));
10585 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10590 } // loop on link lists
10595 namespace // automatically find theAffectedElems for DoubleNodes()
10597 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10599 //--------------------------------------------------------------------------------
10600 // Nodes shared by adjacent FissureBorder's.
10601 // 1 node if FissureBorder separates faces
10602 // 2 nodes if FissureBorder separates volumes
10605 const SMDS_MeshNode* _nodes[2];
10608 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10612 _nbNodes = bool( n1 ) + bool( n2 );
10613 if ( _nbNodes == 2 && n1 > n2 )
10614 std::swap( _nodes[0], _nodes[1] );
10616 bool operator<( const SubBorder& other ) const
10618 for ( int i = 0; i < _nbNodes; ++i )
10620 if ( _nodes[i] < other._nodes[i] ) return true;
10621 if ( _nodes[i] > other._nodes[i] ) return false;
10627 //--------------------------------------------------------------------------------
10628 // Map a SubBorder to all FissureBorder it bounds
10629 struct FissureBorder;
10630 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10631 typedef TBorderLinks::iterator TMappedSub;
10633 //--------------------------------------------------------------------------------
10635 * \brief Element border (volume facet or face edge) at a fissure
10637 struct FissureBorder
10639 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10640 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10642 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10643 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10645 FissureBorder( FissureBorder && from ) // move constructor
10647 std::swap( _nodes, from._nodes );
10648 std::swap( _sortedNodes, from._sortedNodes );
10649 _elems[0] = from._elems[0];
10650 _elems[1] = from._elems[1];
10653 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10654 std::vector< const SMDS_MeshElement* > & adjElems)
10655 : _nodes( elemToDuplicate->NbCornerNodes() )
10657 for ( size_t i = 0; i < _nodes.size(); ++i )
10658 _nodes[i] = elemToDuplicate->GetNode( i );
10660 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10661 findAdjacent( type, adjElems );
10664 FissureBorder( const SMDS_MeshNode** nodes,
10665 const size_t nbNodes,
10666 const SMDSAbs_ElementType adjElemsType,
10667 std::vector< const SMDS_MeshElement* > & adjElems)
10668 : _nodes( nodes, nodes + nbNodes )
10670 findAdjacent( adjElemsType, adjElems );
10673 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10674 std::vector< const SMDS_MeshElement* > & adjElems)
10676 _elems[0] = _elems[1] = 0;
10678 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10679 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10680 _elems[i] = adjElems[i];
10683 bool operator<( const FissureBorder& other ) const
10685 return GetSortedNodes() < other.GetSortedNodes();
10688 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10690 if ( _sortedNodes.empty() && !_nodes.empty() )
10692 FissureBorder* me = const_cast<FissureBorder*>( this );
10693 me->_sortedNodes = me->_nodes;
10694 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10696 return _sortedNodes;
10699 size_t NbSub() const
10701 return _nodes.size();
10704 SubBorder Sub(size_t i) const
10706 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10709 void AddSelfTo( TBorderLinks& borderLinks )
10711 _mappedSubs.resize( NbSub() );
10712 for ( size_t i = 0; i < NbSub(); ++i )
10714 TBorderLinks::iterator s2b =
10715 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10716 s2b->second.push_back( this );
10717 _mappedSubs[ i ] = s2b;
10726 const SMDS_MeshElement* GetMarkedElem() const
10728 if ( _nodes.empty() ) return 0; // cleared
10729 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10730 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10734 gp_XYZ GetNorm() const // normal to the border
10737 if ( _nodes.size() == 2 )
10739 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10740 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10742 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10745 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10746 norm = bordDir ^ avgNorm;
10750 SMESH_NodeXYZ p0( _nodes[0] );
10751 SMESH_NodeXYZ p1( _nodes[1] );
10752 SMESH_NodeXYZ p2( _nodes[2] );
10753 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10755 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10761 void ChooseSide() // mark an _elem located at positive side of fissure
10763 _elems[0]->setIsMarked( true );
10764 gp_XYZ norm = GetNorm();
10765 double maxX = norm.Coord(1);
10766 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10767 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10770 _elems[0]->setIsMarked( false );
10771 _elems[1]->setIsMarked( true );
10775 }; // struct FissureBorder
10777 //--------------------------------------------------------------------------------
10779 * \brief Classifier of elements at fissure edge
10781 class FissureNormal
10783 std::vector< gp_XYZ > _normals;
10787 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10790 _normals.reserve(2);
10791 _normals.push_back( bord.GetNorm() );
10792 if ( _normals.size() == 2 )
10793 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10796 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10799 switch ( _normals.size() ) {
10802 isIn = !isOut( n, _normals[0], elem );
10807 bool in1 = !isOut( n, _normals[0], elem );
10808 bool in2 = !isOut( n, _normals[1], elem );
10809 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10816 //================================================================================
10818 * \brief Classify an element by a plane passing through a node
10820 //================================================================================
10822 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10824 SMESH_NodeXYZ p = n;
10826 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10828 SMESH_NodeXYZ pi = elem->GetNode( i );
10829 sumDot += norm * ( pi - p );
10831 return sumDot < -1e-100;
10834 //================================================================================
10836 * \brief Find FissureBorder's by nodes to duplicate
10838 //================================================================================
10840 void findFissureBorders( const TIDSortedElemSet& theNodes,
10841 std::vector< FissureBorder > & theFissureBorders )
10843 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10844 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10846 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10847 if ( n->NbInverseElements( elemType ) == 0 )
10849 elemType = SMDSAbs_Face;
10850 if ( n->NbInverseElements( elemType ) == 0 )
10853 // unmark elements touching the fissure
10854 for ( ; nIt != theNodes.end(); ++nIt )
10855 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10857 // loop on elements touching the fissure to get their borders belonging to the fissure
10858 std::set< FissureBorder > fissureBorders;
10859 std::vector< const SMDS_MeshElement* > adjElems;
10860 std::vector< const SMDS_MeshNode* > nodes;
10861 SMDS_VolumeTool volTool;
10862 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10864 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10865 while ( invIt->more() )
10867 const SMDS_MeshElement* eInv = invIt->next();
10868 if ( eInv->isMarked() ) continue;
10869 eInv->setIsMarked( true );
10871 if ( elemType == SMDSAbs_Volume )
10873 volTool.Set( eInv );
10874 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10875 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10877 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10878 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10880 bool allOnFissure = true;
10881 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10882 if (( allOnFissure = theNodes.count( nn[ iN ])))
10883 nodes.push_back( nn[ iN ]);
10884 if ( allOnFissure )
10885 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10886 elemType, adjElems )));
10889 else // elemType == SMDSAbs_Face
10891 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10892 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10893 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10895 nn[1] = eInv->GetNode( iN );
10896 onFissure1 = theNodes.count( nn[1] );
10897 if ( onFissure0 && onFissure1 )
10898 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10900 onFissure0 = onFissure1;
10906 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10907 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10908 for ( ; bord != fissureBorders.end(); ++bord )
10910 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10913 } // findFissureBorders()
10915 //================================================================================
10917 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10918 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10919 * \param [in] theNodesNot - nodes not to duplicate
10920 * \param [out] theAffectedElems - the found elements
10922 //================================================================================
10924 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10925 TIDSortedElemSet& theAffectedElems)
10927 if ( theElemsOrNodes.empty() ) return;
10929 // find FissureBorder's
10931 std::vector< FissureBorder > fissure;
10932 std::vector< const SMDS_MeshElement* > elemsByFacet;
10934 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10935 if ( (*elIt)->GetType() == SMDSAbs_Node )
10937 findFissureBorders( theElemsOrNodes, fissure );
10941 fissure.reserve( theElemsOrNodes.size() );
10942 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10943 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10945 if ( fissure.empty() )
10948 // fill borderLinks
10950 TBorderLinks borderLinks;
10952 for ( size_t i = 0; i < fissure.size(); ++i )
10954 fissure[i].AddSelfTo( borderLinks );
10957 // get theAffectedElems
10959 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10960 for ( size_t i = 0; i < fissure.size(); ++i )
10961 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10963 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10964 false, /*markElem=*/true );
10967 std::vector<const SMDS_MeshNode *> facetNodes;
10968 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10969 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10971 // choose a side of fissure
10972 fissure[0].ChooseSide();
10973 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10975 size_t nbCheckedBorders = 0;
10976 while ( nbCheckedBorders < fissure.size() )
10978 // find a FissureBorder to treat
10979 FissureBorder* bord = 0;
10980 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10981 if ( fissure[i].GetMarkedElem() )
10982 bord = & fissure[i];
10983 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10984 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10986 bord = & fissure[i];
10987 bord->ChooseSide();
10988 theAffectedElems.insert( bord->GetMarkedElem() );
10990 if ( !bord ) return;
10991 ++nbCheckedBorders;
10993 // treat FissureBorder's linked to bord
10994 fissureNodes.clear();
10995 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10996 for ( size_t i = 0; i < bord->NbSub(); ++i )
10998 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10999 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
11000 std::vector< FissureBorder* >& linkedBorders = l2b->second;
11001 const SubBorder& sb = l2b->first;
11002 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
11004 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
11006 for ( int j = 0; j < sb._nbNodes; ++j )
11007 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
11011 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
11012 // until an elem adjacent to a neighbour FissureBorder is found
11013 facetNodes.clear();
11014 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
11015 facetNodes.resize( sb._nbNodes + 1 );
11019 // check if bordElem is adjacent to a neighbour FissureBorder
11020 for ( size_t j = 0; j < linkedBorders.size(); ++j )
11022 FissureBorder* bord2 = linkedBorders[j];
11023 if ( bord2 == bord ) continue;
11024 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
11027 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
11032 // find the next bordElem
11033 const SMDS_MeshElement* nextBordElem = 0;
11034 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
11036 const SMDS_MeshNode* n = bordElem->GetNode( iN );
11037 if ( fissureNodes.count( n )) continue;
11039 facetNodes[ sb._nbNodes ] = n;
11040 elemsByFacet.clear();
11041 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
11043 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
11044 if ( elemsByFacet[ iE ] != bordElem &&
11045 !elemsByFacet[ iE ]->isMarked() )
11047 theAffectedElems.insert( elemsByFacet[ iE ]);
11048 elemsByFacet[ iE ]->setIsMarked( true );
11049 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
11050 nextBordElem = elemsByFacet[ iE ];
11054 bordElem = nextBordElem;
11056 } // while ( bordElem )
11058 linkedBorders.clear(); // not to treat this link any more
11060 } // loop on SubBorder's of a FissureBorder
11064 } // loop on FissureBorder's
11067 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11069 // mark nodes of theAffectedElems
11070 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11072 // unmark nodes of the fissure
11073 elIt = theElemsOrNodes.begin();
11074 if ( (*elIt)->GetType() == SMDSAbs_Node )
11075 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11077 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11079 std::vector< gp_XYZ > normVec;
11081 // loop on nodes of the fissure, add elements having marked nodes
11082 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11084 const SMDS_MeshElement* e = (*elIt);
11085 if ( e->GetType() != SMDSAbs_Node )
11086 e->setIsMarked( true ); // avoid adding a fissure element
11088 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11090 const SMDS_MeshNode* n = e->GetNode( iN );
11091 if ( fissEdgeNodes2Norm.count( n ))
11094 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11095 while ( invIt->more() )
11097 const SMDS_MeshElement* eInv = invIt->next();
11098 if ( eInv->isMarked() ) continue;
11099 eInv->setIsMarked( true );
11101 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11102 while( nIt->more() )
11103 if ( nIt->next()->isMarked())
11105 theAffectedElems.insert( eInv );
11106 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11107 n->setIsMarked( false );
11114 // add elements on the fissure edge
11115 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11116 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11118 const SMDS_MeshNode* edgeNode = n2N->first;
11119 const FissureNormal & normals = n2N->second;
11121 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11122 while ( invIt->more() )
11124 const SMDS_MeshElement* eInv = invIt->next();
11125 if ( eInv->isMarked() ) continue;
11126 eInv->setIsMarked( true );
11128 // classify eInv using normals
11129 bool toAdd = normals.IsIn( edgeNode, eInv );
11130 if ( toAdd ) // check if all nodes lie on the fissure edge
11132 bool notOnEdge = false;
11133 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
11134 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11139 theAffectedElems.insert( eInv );
11145 } // findAffectedElems()
11148 //================================================================================
11150 * \brief Create elements equal (on same nodes) to given ones
11151 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
11152 * elements of the uppest dimension are duplicated.
11154 //================================================================================
11156 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11158 ClearLastCreated();
11159 SMESHDS_Mesh* mesh = GetMeshDS();
11161 // get an element type and an iterator over elements
11163 SMDSAbs_ElementType type = SMDSAbs_All;
11164 SMDS_ElemIteratorPtr elemIt;
11165 if ( theElements.empty() )
11167 if ( mesh->NbNodes() == 0 )
11169 // get most complex type
11170 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11171 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11172 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11174 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11175 if ( mesh->GetMeshInfo().NbElements( types[i] ))
11180 elemIt = mesh->elementsIterator( type );
11184 type = (*theElements.begin())->GetType();
11185 elemIt = elemSetIterator( theElements );
11188 // un-mark all elements to avoid duplicating just created elements
11189 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11191 // duplicate elements
11193 ElemFeatures elemType;
11195 vector< const SMDS_MeshNode* > nodes;
11196 while ( elemIt->more() )
11198 const SMDS_MeshElement* elem = elemIt->next();
11199 if ( elem->GetType() != type || elem->isMarked() )
11202 elemType.Init( elem, /*basicOnly=*/false );
11203 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11205 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11206 newElem->setIsMarked( true );
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 );