1 // Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
86 #include <gp_Trsf.hxx>
100 #include <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
109 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
111 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
113 //=======================================================================
114 //function : SMESH_MeshEditor
116 //=======================================================================
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119 :myMesh( theMesh ) // theMesh may be NULL
123 //================================================================================
125 * \brief Clears myLastCreatedNodes and myLastCreatedElems
127 //================================================================================
129 void SMESH_MeshEditor::CrearLastCreated()
131 myLastCreatedNodes.Clear();
132 myLastCreatedElems.Clear();
136 //=======================================================================
140 //=======================================================================
143 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
144 const SMDSAbs_ElementType type,
147 const double ballDiameter)
149 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
150 SMDS_MeshElement* e = 0;
151 int nbnode = node.size();
152 SMESHDS_Mesh* mesh = GetMeshDS();
157 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
158 else e = mesh->AddFace (node[0], node[1], node[2] );
160 else if (nbnode == 4) {
161 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
162 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
164 else if (nbnode == 6) {
165 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
166 node[4], node[5], ID);
167 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
170 else if (nbnode == 8) {
171 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
172 node[4], node[5], node[6], node[7], ID);
173 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
174 node[4], node[5], node[6], node[7] );
176 else if (nbnode == 9) {
177 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
178 node[4], node[5], node[6], node[7], node[8], ID);
179 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
180 node[4], node[5], node[6], node[7], node[8] );
183 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
184 else e = mesh->AddPolygonalFace (node );
191 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
192 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
194 else if (nbnode == 5) {
195 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
197 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
200 else if (nbnode == 6) {
201 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
202 node[4], node[5], ID);
203 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
206 else if (nbnode == 8) {
207 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208 node[4], node[5], node[6], node[7], ID);
209 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], node[7] );
212 else if (nbnode == 10) {
213 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6], node[7],
215 node[8], node[9], ID);
216 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7],
220 else if (nbnode == 12) {
221 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7],
223 node[8], node[9], node[10], node[11], ID);
224 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7],
226 node[8], node[9], node[10], node[11] );
228 else if (nbnode == 13) {
229 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
230 node[4], node[5], node[6], node[7],
231 node[8], node[9], node[10],node[11],
233 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
234 node[4], node[5], node[6], node[7],
235 node[8], node[9], node[10],node[11],
238 else if (nbnode == 15) {
239 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240 node[4], node[5], node[6], node[7],
241 node[8], node[9], node[10],node[11],
242 node[12],node[13],node[14],ID);
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
244 node[4], node[5], node[6], node[7],
245 node[8], node[9], node[10],node[11],
246 node[12],node[13],node[14] );
248 else if (nbnode == 20) {
249 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 node[4], node[5], node[6], node[7],
251 node[8], node[9], node[10],node[11],
252 node[12],node[13],node[14],node[15],
253 node[16],node[17],node[18],node[19],ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
255 node[4], node[5], node[6], node[7],
256 node[8], node[9], node[10],node[11],
257 node[12],node[13],node[14],node[15],
258 node[16],node[17],node[18],node[19] );
260 else if (nbnode == 27) {
261 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262 node[4], node[5], node[6], node[7],
263 node[8], node[9], node[10],node[11],
264 node[12],node[13],node[14],node[15],
265 node[16],node[17],node[18],node[19],
266 node[20],node[21],node[22],node[23],
267 node[24],node[25],node[26], ID);
268 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7],
270 node[8], node[9], node[10],node[11],
271 node[12],node[13],node[14],node[15],
272 node[16],node[17],node[18],node[19],
273 node[20],node[21],node[22],node[23],
274 node[24],node[25],node[26] );
281 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
282 else e = mesh->AddEdge (node[0], node[1] );
284 else if ( nbnode == 3 ) {
285 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
286 else e = mesh->AddEdge (node[0], node[1], node[2] );
290 case SMDSAbs_0DElement:
292 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
293 else e = mesh->Add0DElement (node[0] );
298 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
299 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
303 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
304 else e = mesh->AddBall (node[0], ballDiameter);
309 if ( e ) myLastCreatedElems.Append( e );
313 //=======================================================================
317 //=======================================================================
319 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
320 const SMDSAbs_ElementType type,
324 vector<const SMDS_MeshNode*> nodes;
325 nodes.reserve( nodeIDs.size() );
326 vector<int>::const_iterator id = nodeIDs.begin();
327 while ( id != nodeIDs.end() ) {
328 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
329 nodes.push_back( node );
333 return AddElement( nodes, type, isPoly, ID );
336 //=======================================================================
338 //purpose : Remove a node or an element.
339 // Modify a compute state of sub-meshes which become empty
340 //=======================================================================
342 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
345 myLastCreatedElems.Clear();
346 myLastCreatedNodes.Clear();
348 SMESHDS_Mesh* aMesh = GetMeshDS();
349 set< SMESH_subMesh *> smmap;
352 list<int>::const_iterator it = theIDs.begin();
353 for ( ; it != theIDs.end(); it++ ) {
354 const SMDS_MeshElement * elem;
356 elem = aMesh->FindNode( *it );
358 elem = aMesh->FindElement( *it );
362 // Notify VERTEX sub-meshes about modification
364 const SMDS_MeshNode* node = cast2Node( elem );
365 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
366 if ( int aShapeID = node->getshapeId() )
367 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
370 // Find sub-meshes to notify about modification
371 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
372 // while ( nodeIt->more() ) {
373 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
374 // const SMDS_PositionPtr& aPosition = node->GetPosition();
375 // if ( aPosition.get() ) {
376 // if ( int aShapeID = aPosition->GetShapeId() ) {
377 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378 // smmap.insert( sm );
385 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
387 aMesh->RemoveElement( elem );
391 // Notify sub-meshes about modification
392 if ( !smmap.empty() ) {
393 set< SMESH_subMesh *>::iterator smIt;
394 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
395 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
398 // // Check if the whole mesh becomes empty
399 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
400 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
405 //================================================================================
407 * \brief Create 0D elements on all nodes of the given object except those
408 * nodes on which a 0D element already exists.
409 * \param elements - Elements on whose nodes to create 0D elements; if empty,
410 * the all mesh is treated
411 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
413 //================================================================================
415 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
416 TIDSortedElemSet& all0DElems )
418 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
419 SMDS_ElemIteratorPtr elemIt;
420 if ( elements.empty() )
421 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
423 elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
425 while ( elemIt->more() )
427 const SMDS_MeshElement* e = elemIt->next();
428 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
429 while ( nodeIt->more() )
431 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
432 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
434 all0DElems.insert( it0D->next() );
436 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
437 all0DElems.insert( myLastCreatedElems.Last() );
443 //=======================================================================
444 //function : FindShape
445 //purpose : Return an index of the shape theElem is on
446 // or zero if a shape not found
447 //=======================================================================
449 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
451 myLastCreatedElems.Clear();
452 myLastCreatedNodes.Clear();
454 SMESHDS_Mesh * aMesh = GetMeshDS();
455 if ( aMesh->ShapeToMesh().IsNull() )
458 int aShapeID = theElem->getshapeId();
462 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
463 if ( sm->Contains( theElem ))
466 if ( theElem->GetType() == SMDSAbs_Node ) {
467 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
470 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
473 TopoDS_Shape aShape; // the shape a node of theElem is on
474 if ( theElem->GetType() != SMDSAbs_Node )
476 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
477 while ( nodeIt->more() ) {
478 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
479 if ((aShapeID = node->getshapeId()) > 0) {
480 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
481 if ( sm->Contains( theElem ))
483 if ( aShape.IsNull() )
484 aShape = aMesh->IndexToShape( aShapeID );
490 // None of nodes is on a proper shape,
491 // find the shape among ancestors of aShape on which a node is
492 if ( !aShape.IsNull() ) {
493 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
494 for ( ; ancIt.More(); ancIt.Next() ) {
495 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
496 if ( sm && sm->Contains( theElem ))
497 return aMesh->ShapeToIndex( ancIt.Value() );
502 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
503 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
504 for ( ; id_sm != id2sm.end(); ++id_sm )
505 if ( id_sm->second->Contains( theElem ))
509 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
513 //=======================================================================
514 //function : IsMedium
516 //=======================================================================
518 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
519 const SMDSAbs_ElementType typeToCheck)
521 bool isMedium = false;
522 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
523 while (it->more() && !isMedium ) {
524 const SMDS_MeshElement* elem = it->next();
525 isMedium = elem->IsMediumNode(node);
530 //=======================================================================
531 //function : ShiftNodesQuadTria
533 // Shift nodes in the array corresponded to quadratic triangle
534 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
535 //=======================================================================
536 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
538 const SMDS_MeshNode* nd1 = aNodes[0];
539 aNodes[0] = aNodes[1];
540 aNodes[1] = aNodes[2];
542 const SMDS_MeshNode* nd2 = aNodes[3];
543 aNodes[3] = aNodes[4];
544 aNodes[4] = aNodes[5];
548 //=======================================================================
549 //function : edgeConnectivity
551 // return number of the edges connected with the theNode.
552 // if theEdges has connections with the other type of the
553 // elements, return -1
554 //=======================================================================
555 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
557 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
559 while(elemIt->more()) {
567 //=======================================================================
568 //function : GetNodesFromTwoTria
570 // Shift nodes in the array corresponded to quadratic triangle
571 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
572 //=======================================================================
573 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
574 const SMDS_MeshElement * theTria2,
575 const SMDS_MeshNode* N1[],
576 const SMDS_MeshNode* N2[])
578 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
581 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
584 if(it->more()) return false;
585 it = theTria2->nodesIterator();
588 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
591 if(it->more()) return false;
593 int sames[3] = {-1,-1,-1};
605 if(nbsames!=2) return false;
607 ShiftNodesQuadTria(N1);
609 ShiftNodesQuadTria(N1);
612 i = sames[0] + sames[1] + sames[2];
614 ShiftNodesQuadTria(N2);
616 // now we receive following N1 and N2 (using numeration as above image)
617 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
618 // i.e. first nodes from both arrays determ new diagonal
622 //=======================================================================
623 //function : InverseDiag
624 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
625 // but having other common link.
626 // Return False if args are improper
627 //=======================================================================
629 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
630 const SMDS_MeshElement * theTria2 )
632 MESSAGE("InverseDiag");
633 myLastCreatedElems.Clear();
634 myLastCreatedNodes.Clear();
636 if (!theTria1 || !theTria2)
639 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
640 if (!F1) return false;
641 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
642 if (!F2) return false;
643 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
644 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
646 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
647 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
651 // put nodes in array and find out indices of the same ones
652 const SMDS_MeshNode* aNodes [6];
653 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
655 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
656 while ( it->more() ) {
657 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
659 if ( i > 2 ) // theTria2
660 // find same node of theTria1
661 for ( int j = 0; j < 3; j++ )
662 if ( aNodes[ i ] == aNodes[ j ]) {
671 return false; // theTria1 is not a triangle
672 it = theTria2->nodesIterator();
674 if ( i == 6 && it->more() )
675 return false; // theTria2 is not a triangle
678 // find indices of 1,2 and of A,B in theTria1
679 int iA = 0, iB = 0, i1 = 0, i2 = 0;
680 for ( i = 0; i < 6; i++ ) {
681 if ( sameInd [ i ] == 0 ) {
690 // nodes 1 and 2 should not be the same
691 if ( aNodes[ i1 ] == aNodes[ i2 ] )
695 aNodes[ iA ] = aNodes[ i2 ];
697 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
699 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
700 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
704 } // end if(F1 && F2)
706 // check case of quadratic faces
707 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
709 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
713 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
714 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
722 const SMDS_MeshNode* N1 [6];
723 const SMDS_MeshNode* N2 [6];
724 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
726 // now we receive following N1 and N2 (using numeration as above image)
727 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
728 // i.e. first nodes from both arrays determ new diagonal
730 const SMDS_MeshNode* N1new [6];
731 const SMDS_MeshNode* N2new [6];
744 // replaces nodes in faces
745 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
746 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
751 //=======================================================================
752 //function : findTriangles
753 //purpose : find triangles sharing theNode1-theNode2 link
754 //=======================================================================
756 static bool findTriangles(const SMDS_MeshNode * theNode1,
757 const SMDS_MeshNode * theNode2,
758 const SMDS_MeshElement*& theTria1,
759 const SMDS_MeshElement*& theTria2)
761 if ( !theNode1 || !theNode2 ) return false;
763 theTria1 = theTria2 = 0;
765 set< const SMDS_MeshElement* > emap;
766 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
768 const SMDS_MeshElement* elem = it->next();
769 if ( elem->NbNodes() == 3 )
772 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
774 const SMDS_MeshElement* elem = it->next();
775 if ( emap.find( elem ) != emap.end() ) {
777 // theTria1 must be element with minimum ID
778 if( theTria1->GetID() < elem->GetID() ) {
792 return ( theTria1 && theTria2 );
795 //=======================================================================
796 //function : InverseDiag
797 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
798 // with ones built on the same 4 nodes but having other common link.
799 // Return false if proper faces not found
800 //=======================================================================
802 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
803 const SMDS_MeshNode * theNode2)
805 myLastCreatedElems.Clear();
806 myLastCreatedNodes.Clear();
808 MESSAGE( "::InverseDiag()" );
810 const SMDS_MeshElement *tr1, *tr2;
811 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
814 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
815 if (!F1) return false;
816 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
817 if (!F2) return false;
818 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
819 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
821 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
822 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
826 // put nodes in array
827 // and find indices of 1,2 and of A in tr1 and of B in tr2
828 int i, iA1 = 0, i1 = 0;
829 const SMDS_MeshNode* aNodes1 [3];
830 SMDS_ElemIteratorPtr it;
831 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
832 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
833 if ( aNodes1[ i ] == theNode1 )
834 iA1 = i; // node A in tr1
835 else if ( aNodes1[ i ] != theNode2 )
839 const SMDS_MeshNode* aNodes2 [3];
840 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
841 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
842 if ( aNodes2[ i ] == theNode2 )
843 iB2 = i; // node B in tr2
844 else if ( aNodes2[ i ] != theNode1 )
848 // nodes 1 and 2 should not be the same
849 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
853 aNodes1[ iA1 ] = aNodes2[ i2 ];
855 aNodes2[ iB2 ] = aNodes1[ i1 ];
857 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
858 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
863 // check case of quadratic faces
864 return InverseDiag(tr1,tr2);
867 //=======================================================================
868 //function : getQuadrangleNodes
869 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
870 // fusion of triangles tr1 and tr2 having shared link on
871 // theNode1 and theNode2
872 //=======================================================================
874 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
875 const SMDS_MeshNode * theNode1,
876 const SMDS_MeshNode * theNode2,
877 const SMDS_MeshElement * tr1,
878 const SMDS_MeshElement * tr2 )
880 if( tr1->NbNodes() != tr2->NbNodes() )
882 // find the 4-th node to insert into tr1
883 const SMDS_MeshNode* n4 = 0;
884 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
886 while ( !n4 && i<3 ) {
887 const SMDS_MeshNode * n = cast2Node( it->next() );
889 bool isDiag = ( n == theNode1 || n == theNode2 );
893 // Make an array of nodes to be in a quadrangle
894 int iNode = 0, iFirstDiag = -1;
895 it = tr1->nodesIterator();
898 const SMDS_MeshNode * n = cast2Node( it->next() );
900 bool isDiag = ( n == theNode1 || n == theNode2 );
902 if ( iFirstDiag < 0 )
904 else if ( iNode - iFirstDiag == 1 )
905 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
907 else if ( n == n4 ) {
908 return false; // tr1 and tr2 should not have all the same nodes
910 theQuadNodes[ iNode++ ] = n;
912 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
913 theQuadNodes[ iNode ] = n4;
918 //=======================================================================
919 //function : DeleteDiag
920 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
921 // with a quadrangle built on the same 4 nodes.
922 // Return false if proper faces not found
923 //=======================================================================
925 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
926 const SMDS_MeshNode * theNode2)
928 myLastCreatedElems.Clear();
929 myLastCreatedNodes.Clear();
931 MESSAGE( "::DeleteDiag()" );
933 const SMDS_MeshElement *tr1, *tr2;
934 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
937 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
938 if (!F1) return false;
939 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
940 if (!F2) return false;
941 SMESHDS_Mesh * aMesh = GetMeshDS();
943 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
944 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
946 const SMDS_MeshNode* aNodes [ 4 ];
947 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
950 const SMDS_MeshElement* newElem = 0;
951 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
952 myLastCreatedElems.Append(newElem);
953 AddToSameGroups( newElem, tr1, aMesh );
954 int aShapeId = tr1->getshapeId();
957 aMesh->SetMeshElementOnShape( newElem, aShapeId );
959 aMesh->RemoveElement( tr1 );
960 aMesh->RemoveElement( tr2 );
965 // check case of quadratic faces
966 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
968 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
972 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
973 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
981 const SMDS_MeshNode* N1 [6];
982 const SMDS_MeshNode* N2 [6];
983 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
985 // now we receive following N1 and N2 (using numeration as above image)
986 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
987 // i.e. first nodes from both arrays determ new diagonal
989 const SMDS_MeshNode* aNodes[8];
999 const SMDS_MeshElement* newElem = 0;
1000 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1001 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1002 myLastCreatedElems.Append(newElem);
1003 AddToSameGroups( newElem, tr1, aMesh );
1004 int aShapeId = tr1->getshapeId();
1007 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1009 aMesh->RemoveElement( tr1 );
1010 aMesh->RemoveElement( tr2 );
1012 // remove middle node (9)
1013 GetMeshDS()->RemoveNode( N1[4] );
1018 //=======================================================================
1019 //function : Reorient
1020 //purpose : Reverse theElement orientation
1021 //=======================================================================
1023 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1025 MESSAGE("Reorient");
1026 myLastCreatedElems.Clear();
1027 myLastCreatedNodes.Clear();
1031 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1032 if ( !it || !it->more() )
1035 switch ( theElem->GetType() ) {
1038 case SMDSAbs_Face: {
1039 if(!theElem->IsQuadratic()) {
1040 int i = theElem->NbNodes();
1041 vector<const SMDS_MeshNode*> aNodes( i );
1042 while ( it->more() )
1043 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1044 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1047 // quadratic elements
1048 if(theElem->GetType()==SMDSAbs_Edge) {
1049 vector<const SMDS_MeshNode*> aNodes(3);
1050 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1051 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1052 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1053 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1056 int nbn = theElem->NbNodes();
1057 vector<const SMDS_MeshNode*> aNodes(nbn);
1058 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1060 for(; i<nbn/2; i++) {
1061 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1063 for(i=0; i<nbn/2; i++) {
1064 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1066 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1070 case SMDSAbs_Volume: {
1071 if (theElem->IsPoly()) {
1072 // TODO reorient vtk polyhedron
1073 MESSAGE("reorient vtk polyhedron ?");
1074 const SMDS_VtkVolume* aPolyedre =
1075 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1077 MESSAGE("Warning: bad volumic element");
1081 int nbFaces = aPolyedre->NbFaces();
1082 vector<const SMDS_MeshNode *> poly_nodes;
1083 vector<int> quantities (nbFaces);
1085 // reverse each face of the polyedre
1086 for (int iface = 1; iface <= nbFaces; iface++) {
1087 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1088 quantities[iface - 1] = nbFaceNodes;
1090 for (inode = nbFaceNodes; inode >= 1; inode--) {
1091 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1092 poly_nodes.push_back(curNode);
1096 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100 SMDS_VolumeTool vTool;
1101 if ( !vTool.Set( theElem ))
1104 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1105 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1114 //================================================================================
1116 * \brief Reorient faces.
1117 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1118 * \param theDirection - desired direction of normal of \a theFace
1119 * \param theFace - one of \a theFaces that sould be orientated according to
1120 * \a theDirection and whose orientation defines orientation of other faces
1121 * \return number of reoriented faces.
1123 //================================================================================
1125 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1126 const gp_Dir& theDirection,
1127 const SMDS_MeshElement * theFace)
1130 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1132 if ( theFaces.empty() )
1134 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1135 while ( fIt->more() )
1136 theFaces.insert( theFaces.end(), fIt->next() );
1139 // orient theFace according to theDirection
1141 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1142 if ( normal * theDirection.XYZ() < 0 )
1143 nbReori += Reorient( theFace );
1145 // Orient other faces
1147 set< const SMDS_MeshElement* > startFaces;
1148 TIDSortedElemSet avoidSet;
1149 set< SMESH_TLink > checkedLinks;
1150 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1152 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1153 theFaces.erase( theFace );
1154 startFaces.insert( theFace );
1156 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1157 while ( startFace != startFaces.end() )
1159 theFace = *startFace;
1160 const int nbNodes = theFace->NbCornerNodes();
1163 avoidSet.insert(theFace);
1165 NLink link( theFace->GetNode( 0 ), 0 );
1166 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1168 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1169 linkIt_isNew = checkedLinks.insert( link );
1170 if ( !linkIt_isNew.second )
1172 // link has already been checked and won't be encountered more
1173 // if the group (theFaces) is manifold
1174 checkedLinks.erase( linkIt_isNew.first );
1178 int nodeInd1, nodeInd2;
1179 const SMDS_MeshElement* otherFace = FindFaceInSet( link.first, link.second,
1181 & nodeInd1, & nodeInd2);
1182 if ( otherFace && otherFace != theFace)
1184 // link must be reversed in otherFace if orientation ot otherFace
1185 // is same as that of theFace
1186 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1188 // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1189 // << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1190 nbReori += Reorient( otherFace );
1192 startFaces.insert( otherFace );
1193 if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1194 theFaces.erase( otherFace );
1197 std::swap( link.first, link.second );
1199 startFaces.erase( startFace );
1200 startFace = startFaces.begin();
1205 //=======================================================================
1206 //function : getBadRate
1208 //=======================================================================
1210 static double getBadRate (const SMDS_MeshElement* theElem,
1211 SMESH::Controls::NumericalFunctorPtr& theCrit)
1213 SMESH::Controls::TSequenceOfXYZ P;
1214 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1216 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1217 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1220 //=======================================================================
1221 //function : QuadToTri
1222 //purpose : Cut quadrangles into triangles.
1223 // theCrit is used to select a diagonal to cut
1224 //=======================================================================
1226 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1227 SMESH::Controls::NumericalFunctorPtr theCrit)
1229 myLastCreatedElems.Clear();
1230 myLastCreatedNodes.Clear();
1232 MESSAGE( "::QuadToTri()" );
1234 if ( !theCrit.get() )
1237 SMESHDS_Mesh * aMesh = GetMeshDS();
1239 Handle(Geom_Surface) surface;
1240 SMESH_MesherHelper helper( *GetMesh() );
1242 TIDSortedElemSet::iterator itElem;
1243 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1244 const SMDS_MeshElement* elem = *itElem;
1245 if ( !elem || elem->GetType() != SMDSAbs_Face )
1247 if ( elem->NbCornerNodes() != 4 )
1250 // retrieve element nodes
1251 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1253 // compare two sets of possible triangles
1254 double aBadRate1, aBadRate2; // to what extent a set is bad
1255 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1256 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1257 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1259 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1260 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1261 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1263 int aShapeId = FindShape( elem );
1264 const SMDS_MeshElement* newElem1 = 0;
1265 const SMDS_MeshElement* newElem2 = 0;
1267 if( !elem->IsQuadratic() ) {
1269 // split liner quadrangle
1271 if ( aBadRate1 <= aBadRate2 ) {
1272 // tr1 + tr2 is better
1273 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1274 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1277 // tr3 + tr4 is better
1278 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1279 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1284 // split quadratic quadrangle
1286 // get surface elem is on
1287 if ( aShapeId != helper.GetSubShapeID() ) {
1291 shape = aMesh->IndexToShape( aShapeId );
1292 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1293 TopoDS_Face face = TopoDS::Face( shape );
1294 surface = BRep_Tool::Surface( face );
1295 if ( !surface.IsNull() )
1296 helper.SetSubShape( shape );
1299 // find middle point for (0,1,2,3)
1300 // and create a node in this point;
1301 const SMDS_MeshNode* newN = 0;
1302 if ( aNodes.size() == 9 )
1304 // SMDSEntity_BiQuad_Quadrangle
1305 newN = aNodes.back();
1310 if ( surface.IsNull() )
1312 for ( int i = 0; i < 4; i++ )
1313 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1318 const SMDS_MeshNode* inFaceNode = 0;
1319 if ( helper.GetNodeUVneedInFaceNode() )
1320 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1321 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1322 inFaceNode = aNodes[ i ];
1324 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1326 for ( int i = 0; i < 4; i++ )
1327 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1329 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1331 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1332 myLastCreatedNodes.Append(newN);
1334 // create a new element
1335 if ( aBadRate1 <= aBadRate2 ) {
1336 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1337 aNodes[6], aNodes[7], newN );
1338 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1339 newN, aNodes[4], aNodes[5] );
1342 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1343 aNodes[7], aNodes[4], newN );
1344 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1345 newN, aNodes[5], aNodes[6] );
1349 // care of a new element
1351 myLastCreatedElems.Append(newElem1);
1352 myLastCreatedElems.Append(newElem2);
1353 AddToSameGroups( newElem1, elem, aMesh );
1354 AddToSameGroups( newElem2, elem, aMesh );
1356 // put a new triangle on the same shape
1359 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1360 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1362 aMesh->RemoveElement( elem );
1367 //=======================================================================
1368 //function : BestSplit
1369 //purpose : Find better diagonal for cutting.
1370 //=======================================================================
1372 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1373 SMESH::Controls::NumericalFunctorPtr theCrit)
1375 myLastCreatedElems.Clear();
1376 myLastCreatedNodes.Clear();
1381 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1384 if( theQuad->NbNodes()==4 ||
1385 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1387 // retrieve element nodes
1388 const SMDS_MeshNode* aNodes [4];
1389 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1391 //while (itN->more())
1393 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1395 // compare two sets of possible triangles
1396 double aBadRate1, aBadRate2; // to what extent a set is bad
1397 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1398 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1399 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1401 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1402 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1403 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1405 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1406 return 1; // diagonal 1-3
1408 return 2; // diagonal 2-4
1415 // Methods of splitting volumes into tetra
1417 const int theHexTo5_1[5*4+1] =
1419 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1421 const int theHexTo5_2[5*4+1] =
1423 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1425 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1427 const int theHexTo6_1[6*4+1] =
1429 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
1431 const int theHexTo6_2[6*4+1] =
1433 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
1435 const int theHexTo6_3[6*4+1] =
1437 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
1439 const int theHexTo6_4[6*4+1] =
1441 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
1443 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1445 const int thePyraTo2_1[2*4+1] =
1447 0, 1, 2, 4, 0, 2, 3, 4, -1
1449 const int thePyraTo2_2[2*4+1] =
1451 1, 2, 3, 4, 1, 3, 0, 4, -1
1453 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1455 const int thePentaTo3_1[3*4+1] =
1457 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1459 const int thePentaTo3_2[3*4+1] =
1461 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1463 const int thePentaTo3_3[3*4+1] =
1465 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1467 const int thePentaTo3_4[3*4+1] =
1469 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1471 const int thePentaTo3_5[3*4+1] =
1473 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1475 const int thePentaTo3_6[3*4+1] =
1477 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1479 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1480 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1482 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1485 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1486 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1487 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1492 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1493 bool _baryNode; //!< additional node is to be created at cell barycenter
1494 bool _ownConn; //!< to delete _connectivity in destructor
1495 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1497 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1498 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1499 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1500 bool hasFacet( const TTriangleFacet& facet ) const
1502 const int* tetConn = _connectivity;
1503 for ( ; tetConn[0] >= 0; tetConn += 4 )
1504 if (( facet.contains( tetConn[0] ) +
1505 facet.contains( tetConn[1] ) +
1506 facet.contains( tetConn[2] ) +
1507 facet.contains( tetConn[3] )) == 3 )
1513 //=======================================================================
1515 * \brief return TSplitMethod for the given element
1517 //=======================================================================
1519 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1521 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1523 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1524 // an edge and a face barycenter; tertaherdons are based on triangles and
1525 // a volume barycenter
1526 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1528 // Find out how adjacent volumes are split
1530 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1531 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1532 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1534 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1535 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1536 if ( nbNodes < 4 ) continue;
1538 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1539 const int* nInd = vol.GetFaceNodesIndices( iF );
1542 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1543 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1544 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1545 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1549 int iCom = 0; // common node of triangle faces to split into
1550 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1552 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1553 nInd[ iQ * ( (iCom+1)%nbNodes )],
1554 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1555 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1556 nInd[ iQ * ( (iCom+2)%nbNodes )],
1557 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1558 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1560 triaSplits.push_back( t012 );
1561 triaSplits.push_back( t023 );
1566 if ( !triaSplits.empty() )
1567 hasAdjacentSplits = true;
1570 // Among variants of split method select one compliant with adjacent volumes
1572 TSplitMethod method;
1573 if ( !vol.Element()->IsPoly() && !is24TetMode )
1575 int nbVariants = 2, nbTet = 0;
1576 const int** connVariants = 0;
1577 switch ( vol.Element()->GetEntityType() )
1579 case SMDSEntity_Hexa:
1580 case SMDSEntity_Quad_Hexa:
1581 case SMDSEntity_TriQuad_Hexa:
1582 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1583 connVariants = theHexTo5, nbTet = 5;
1585 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1587 case SMDSEntity_Pyramid:
1588 case SMDSEntity_Quad_Pyramid:
1589 connVariants = thePyraTo2; nbTet = 2;
1591 case SMDSEntity_Penta:
1592 case SMDSEntity_Quad_Penta:
1593 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1598 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1600 // check method compliancy with adjacent tetras,
1601 // all found splits must be among facets of tetras described by this method
1602 method = TSplitMethod( nbTet, connVariants[variant] );
1603 if ( hasAdjacentSplits && method._nbTetra > 0 )
1605 bool facetCreated = true;
1606 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1608 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1609 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1610 facetCreated = method.hasFacet( *facet );
1612 if ( !facetCreated )
1613 method = TSplitMethod(0); // incompatible method
1617 if ( method._nbTetra < 1 )
1619 // No standard method is applicable, use a generic solution:
1620 // each facet of a volume is split into triangles and
1621 // each of triangles and a volume barycenter form a tetrahedron.
1623 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1625 int* connectivity = new int[ maxTetConnSize + 1 ];
1626 method._connectivity = connectivity;
1627 method._ownConn = true;
1628 method._baryNode = !isHex27; // to create central node or not
1631 int baryCenInd = vol.NbNodes() - int( isHex27 );
1632 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1634 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1635 const int* nInd = vol.GetFaceNodesIndices( iF );
1636 // find common node of triangle facets of tetra to create
1637 int iCommon = 0; // index in linear numeration
1638 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1639 if ( !triaSplits.empty() )
1642 const TTriangleFacet* facet = &triaSplits.front();
1643 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1644 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1645 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1648 else if ( nbNodes > 3 && !is24TetMode )
1650 // find the best method of splitting into triangles by aspect ratio
1651 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1652 map< double, int > badness2iCommon;
1653 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1654 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1655 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1658 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1660 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1661 nodes[ iQ*((iLast-1)%nbNodes)],
1662 nodes[ iQ*((iLast )%nbNodes)]);
1663 badness += getBadRate( &tria, aspectRatio );
1665 badness2iCommon.insert( make_pair( badness, iCommon ));
1667 // use iCommon with lowest badness
1668 iCommon = badness2iCommon.begin()->second;
1670 if ( iCommon >= nbNodes )
1671 iCommon = 0; // something wrong
1673 // fill connectivity of tetrahedra based on a current face
1674 int nbTet = nbNodes - 2;
1675 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1680 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1681 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1685 method._faceBaryNode[ iF ] = 0;
1686 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1689 for ( int i = 0; i < nbTet; ++i )
1691 int i1 = i, i2 = (i+1) % nbNodes;
1692 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1693 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1694 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1695 connectivity[ connSize++ ] = faceBaryCenInd;
1696 connectivity[ connSize++ ] = baryCenInd;
1701 for ( int i = 0; i < nbTet; ++i )
1703 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1704 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1705 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1706 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1707 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1708 connectivity[ connSize++ ] = baryCenInd;
1711 method._nbTetra += nbTet;
1713 } // loop on volume faces
1715 connectivity[ connSize++ ] = -1;
1717 } // end of generic solution
1721 //================================================================================
1723 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1725 //================================================================================
1727 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1729 // find the tetrahedron including the three nodes of facet
1730 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1731 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1732 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1733 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1734 while ( volIt1->more() )
1736 const SMDS_MeshElement* v = volIt1->next();
1737 SMDSAbs_EntityType type = v->GetEntityType();
1738 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1740 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1741 continue; // medium node not allowed
1742 const int ind2 = v->GetNodeIndex( n2 );
1743 if ( ind2 < 0 || 3 < ind2 )
1745 const int ind3 = v->GetNodeIndex( n3 );
1746 if ( ind3 < 0 || 3 < ind3 )
1753 //=======================================================================
1755 * \brief A key of a face of volume
1757 //=======================================================================
1759 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1761 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1763 TIDSortedNodeSet sortedNodes;
1764 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1765 int nbNodes = vol.NbFaceNodes( iF );
1766 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1767 for ( int i = 0; i < nbNodes; i += iQ )
1768 sortedNodes.insert( fNodes[i] );
1769 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1770 first.first = (*(n++))->GetID();
1771 first.second = (*(n++))->GetID();
1772 second.first = (*(n++))->GetID();
1773 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1778 //=======================================================================
1779 //function : SplitVolumesIntoTetra
1780 //purpose : Split volume elements into tetrahedra.
1781 //=======================================================================
1783 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1784 const int theMethodFlags)
1786 // std-like iterator on coordinates of nodes of mesh element
1787 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1788 NXyzIterator xyzEnd;
1790 SMDS_VolumeTool volTool;
1791 SMESH_MesherHelper helper( *GetMesh());
1793 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1794 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1796 SMESH_SequenceOfElemPtr newNodes, newElems;
1798 // map face of volume to it's baricenrtic node
1799 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1802 TIDSortedElemSet::const_iterator elem = theElems.begin();
1803 for ( ; elem != theElems.end(); ++elem )
1805 if ( (*elem)->GetType() != SMDSAbs_Volume )
1807 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1808 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1811 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1813 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1814 if ( splitMethod._nbTetra < 1 ) continue;
1816 // find submesh to add new tetras to
1817 if ( !subMesh || !subMesh->Contains( *elem ))
1819 int shapeID = FindShape( *elem );
1820 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1821 subMesh = GetMeshDS()->MeshElements( shapeID );
1824 if ( (*elem)->IsQuadratic() )
1827 // add quadratic links to the helper
1828 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1830 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1831 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1832 for ( int iN = 0; iN < nbN; iN += iQ )
1833 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1835 helper.SetIsQuadratic( true );
1840 helper.SetIsQuadratic( false );
1842 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1843 helper.SetElementsOnShape( true );
1844 if ( splitMethod._baryNode )
1846 // make a node at barycenter
1847 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1848 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1849 nodes.push_back( gcNode );
1850 newNodes.Append( gcNode );
1852 if ( !splitMethod._faceBaryNode.empty() )
1854 // make or find baricentric nodes of faces
1855 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1856 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1858 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1859 volFace2BaryNode.insert
1860 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1863 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1864 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1866 nodes.push_back( iF_n->second = f_n->second );
1871 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1872 const int* tetConn = splitMethod._connectivity;
1873 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1874 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1875 nodes[ tetConn[1] ],
1876 nodes[ tetConn[2] ],
1877 nodes[ tetConn[3] ]));
1879 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1881 // Split faces on sides of the split volume
1883 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1884 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1886 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1887 if ( nbNodes < 4 ) continue;
1889 // find an existing face
1890 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1891 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1892 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1893 /*noMedium=*/false))
1896 helper.SetElementsOnShape( false );
1897 vector< const SMDS_MeshElement* > triangles;
1899 // find submesh to add new triangles in
1900 if ( !fSubMesh || !fSubMesh->Contains( face ))
1902 int shapeID = FindShape( face );
1903 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1905 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1906 if ( iF_n != splitMethod._faceBaryNode.end() )
1908 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1910 const SMDS_MeshNode* n1 = fNodes[iN];
1911 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1912 const SMDS_MeshNode *n3 = iF_n->second;
1913 if ( !volTool.IsFaceExternal( iF ))
1915 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1917 if ( fSubMesh && n3->getshapeId() < 1 )
1918 fSubMesh->AddNode( n3 );
1923 // among possible triangles create ones discribed by split method
1924 const int* nInd = volTool.GetFaceNodesIndices( iF );
1925 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1926 int iCom = 0; // common node of triangle faces to split into
1927 list< TTriangleFacet > facets;
1928 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1930 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1931 nInd[ iQ * ( (iCom+1)%nbNodes )],
1932 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1933 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1934 nInd[ iQ * ( (iCom+2)%nbNodes )],
1935 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1936 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1938 facets.push_back( t012 );
1939 facets.push_back( t023 );
1940 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1941 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1942 nInd[ iQ * ((iLast-1)%nbNodes )],
1943 nInd[ iQ * ((iLast )%nbNodes )]));
1947 list< TTriangleFacet >::iterator facet = facets.begin();
1948 for ( ; facet != facets.end(); ++facet )
1950 if ( !volTool.IsFaceExternal( iF ))
1951 swap( facet->_n2, facet->_n3 );
1952 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1953 volNodes[ facet->_n2 ],
1954 volNodes[ facet->_n3 ]));
1957 for ( int i = 0; i < triangles.size(); ++i )
1959 if ( !triangles[i] ) continue;
1961 fSubMesh->AddElement( triangles[i]);
1962 newElems.Append( triangles[i] );
1964 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1965 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1968 } // loop on volume faces to split them into triangles
1970 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1972 if ( geomType == SMDSEntity_TriQuad_Hexa )
1974 // remove medium nodes that could become free
1975 for ( int i = 20; i < volTool.NbNodes(); ++i )
1976 if ( volNodes[i]->NbInverseElements() == 0 )
1977 GetMeshDS()->RemoveNode( volNodes[i] );
1979 } // loop on volumes to split
1981 myLastCreatedNodes = newNodes;
1982 myLastCreatedElems = newElems;
1985 //=======================================================================
1986 //function : AddToSameGroups
1987 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1988 //=======================================================================
1990 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1991 const SMDS_MeshElement* elemInGroups,
1992 SMESHDS_Mesh * aMesh)
1994 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1995 if (!groups.empty()) {
1996 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1997 for ( ; grIt != groups.end(); grIt++ ) {
1998 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1999 if ( group && group->Contains( elemInGroups ))
2000 group->SMDSGroup().Add( elemToAdd );
2006 //=======================================================================
2007 //function : RemoveElemFromGroups
2008 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2009 //=======================================================================
2010 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2011 SMESHDS_Mesh * aMesh)
2013 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2014 if (!groups.empty())
2016 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2017 for (; GrIt != groups.end(); GrIt++)
2019 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2020 if (!grp || grp->IsEmpty()) continue;
2021 grp->SMDSGroup().Remove(removeelem);
2026 //================================================================================
2028 * \brief Replace elemToRm by elemToAdd in the all groups
2030 //================================================================================
2032 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2033 const SMDS_MeshElement* elemToAdd,
2034 SMESHDS_Mesh * aMesh)
2036 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2037 if (!groups.empty()) {
2038 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2039 for ( ; grIt != groups.end(); grIt++ ) {
2040 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2041 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2042 group->SMDSGroup().Add( elemToAdd );
2047 //================================================================================
2049 * \brief Replace elemToRm by elemToAdd in the all groups
2051 //================================================================================
2053 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2054 const vector<const SMDS_MeshElement*>& elemToAdd,
2055 SMESHDS_Mesh * aMesh)
2057 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2058 if (!groups.empty())
2060 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2061 for ( ; grIt != groups.end(); grIt++ ) {
2062 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2063 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2064 for ( int i = 0; i < elemToAdd.size(); ++i )
2065 group->SMDSGroup().Add( elemToAdd[ i ] );
2070 //=======================================================================
2071 //function : QuadToTri
2072 //purpose : Cut quadrangles into triangles.
2073 // theCrit is used to select a diagonal to cut
2074 //=======================================================================
2076 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2077 const bool the13Diag)
2079 myLastCreatedElems.Clear();
2080 myLastCreatedNodes.Clear();
2082 MESSAGE( "::QuadToTri()" );
2084 SMESHDS_Mesh * aMesh = GetMeshDS();
2086 Handle(Geom_Surface) surface;
2087 SMESH_MesherHelper helper( *GetMesh() );
2089 TIDSortedElemSet::iterator itElem;
2090 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2091 const SMDS_MeshElement* elem = *itElem;
2092 if ( !elem || elem->GetType() != SMDSAbs_Face )
2094 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2095 if(!isquad) continue;
2097 if(elem->NbNodes()==4) {
2098 // retrieve element nodes
2099 const SMDS_MeshNode* aNodes [4];
2100 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2102 while ( itN->more() )
2103 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2105 int aShapeId = FindShape( elem );
2106 const SMDS_MeshElement* newElem1 = 0;
2107 const SMDS_MeshElement* newElem2 = 0;
2109 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2110 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2113 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2114 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2116 myLastCreatedElems.Append(newElem1);
2117 myLastCreatedElems.Append(newElem2);
2118 // put a new triangle on the same shape and add to the same groups
2121 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2122 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2124 AddToSameGroups( newElem1, elem, aMesh );
2125 AddToSameGroups( newElem2, elem, aMesh );
2126 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2127 aMesh->RemoveElement( elem );
2130 // Quadratic quadrangle
2132 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2134 // get surface elem is on
2135 int aShapeId = FindShape( elem );
2136 if ( aShapeId != helper.GetSubShapeID() ) {
2140 shape = aMesh->IndexToShape( aShapeId );
2141 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2142 TopoDS_Face face = TopoDS::Face( shape );
2143 surface = BRep_Tool::Surface( face );
2144 if ( !surface.IsNull() )
2145 helper.SetSubShape( shape );
2149 const SMDS_MeshNode* aNodes [8];
2150 const SMDS_MeshNode* inFaceNode = 0;
2151 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2153 while ( itN->more() ) {
2154 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2155 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2156 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2158 inFaceNode = aNodes[ i-1 ];
2162 // find middle point for (0,1,2,3)
2163 // and create a node in this point;
2165 if ( surface.IsNull() ) {
2167 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2171 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2174 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2176 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2178 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2179 myLastCreatedNodes.Append(newN);
2181 // create a new element
2182 const SMDS_MeshElement* newElem1 = 0;
2183 const SMDS_MeshElement* newElem2 = 0;
2185 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2186 aNodes[6], aNodes[7], newN );
2187 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2188 newN, aNodes[4], aNodes[5] );
2191 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2192 aNodes[7], aNodes[4], newN );
2193 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2194 newN, aNodes[5], aNodes[6] );
2196 myLastCreatedElems.Append(newElem1);
2197 myLastCreatedElems.Append(newElem2);
2198 // put a new triangle on the same shape and add to the same groups
2201 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2202 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2204 AddToSameGroups( newElem1, elem, aMesh );
2205 AddToSameGroups( newElem2, elem, aMesh );
2206 aMesh->RemoveElement( elem );
2213 //=======================================================================
2214 //function : getAngle
2216 //=======================================================================
2218 double getAngle(const SMDS_MeshElement * tr1,
2219 const SMDS_MeshElement * tr2,
2220 const SMDS_MeshNode * n1,
2221 const SMDS_MeshNode * n2)
2223 double angle = 2. * M_PI; // bad angle
2226 SMESH::Controls::TSequenceOfXYZ P1, P2;
2227 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2228 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2231 if(!tr1->IsQuadratic())
2232 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2234 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2235 if ( N1.SquareMagnitude() <= gp::Resolution() )
2237 if(!tr2->IsQuadratic())
2238 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2240 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2241 if ( N2.SquareMagnitude() <= gp::Resolution() )
2244 // find the first diagonal node n1 in the triangles:
2245 // take in account a diagonal link orientation
2246 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2247 for ( int t = 0; t < 2; t++ ) {
2248 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2249 int i = 0, iDiag = -1;
2250 while ( it->more()) {
2251 const SMDS_MeshElement *n = it->next();
2252 if ( n == n1 || n == n2 ) {
2256 if ( i - iDiag == 1 )
2257 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2266 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2269 angle = N1.Angle( N2 );
2274 // =================================================
2275 // class generating a unique ID for a pair of nodes
2276 // and able to return nodes by that ID
2277 // =================================================
2281 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2282 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2285 long GetLinkID (const SMDS_MeshNode * n1,
2286 const SMDS_MeshNode * n2) const
2288 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2291 bool GetNodes (const long theLinkID,
2292 const SMDS_MeshNode* & theNode1,
2293 const SMDS_MeshNode* & theNode2) const
2295 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2296 if ( !theNode1 ) return false;
2297 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2298 if ( !theNode2 ) return false;
2304 const SMESHDS_Mesh* myMesh;
2309 //=======================================================================
2310 //function : TriToQuad
2311 //purpose : Fuse neighbour triangles into quadrangles.
2312 // theCrit is used to select a neighbour to fuse with.
2313 // theMaxAngle is a max angle between element normals at which
2314 // fusion is still performed.
2315 //=======================================================================
2317 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2318 SMESH::Controls::NumericalFunctorPtr theCrit,
2319 const double theMaxAngle)
2321 myLastCreatedElems.Clear();
2322 myLastCreatedNodes.Clear();
2324 MESSAGE( "::TriToQuad()" );
2326 if ( !theCrit.get() )
2329 SMESHDS_Mesh * aMesh = GetMeshDS();
2331 // Prepare data for algo: build
2332 // 1. map of elements with their linkIDs
2333 // 2. map of linkIDs with their elements
2335 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2336 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2337 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2338 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2340 TIDSortedElemSet::iterator itElem;
2341 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2342 const SMDS_MeshElement* elem = *itElem;
2343 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2344 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2345 if(!IsTria) continue;
2347 // retrieve element nodes
2348 const SMDS_MeshNode* aNodes [4];
2349 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2352 aNodes[ i++ ] = cast2Node( itN->next() );
2353 aNodes[ 3 ] = aNodes[ 0 ];
2356 for ( i = 0; i < 3; i++ ) {
2357 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2358 // check if elements sharing a link can be fused
2359 itLE = mapLi_listEl.find( link );
2360 if ( itLE != mapLi_listEl.end() ) {
2361 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2363 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2364 //if ( FindShape( elem ) != FindShape( elem2 ))
2365 // continue; // do not fuse triangles laying on different shapes
2366 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2367 continue; // avoid making badly shaped quads
2368 (*itLE).second.push_back( elem );
2371 mapLi_listEl[ link ].push_back( elem );
2373 mapEl_setLi [ elem ].insert( link );
2376 // Clean the maps from the links shared by a sole element, ie
2377 // links to which only one element is bound in mapLi_listEl
2379 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2380 int nbElems = (*itLE).second.size();
2381 if ( nbElems < 2 ) {
2382 const SMDS_MeshElement* elem = (*itLE).second.front();
2383 SMESH_TLink link = (*itLE).first;
2384 mapEl_setLi[ elem ].erase( link );
2385 if ( mapEl_setLi[ elem ].empty() )
2386 mapEl_setLi.erase( elem );
2390 // Algo: fuse triangles into quadrangles
2392 while ( ! mapEl_setLi.empty() ) {
2393 // Look for the start element:
2394 // the element having the least nb of shared links
2395 const SMDS_MeshElement* startElem = 0;
2397 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2398 int nbLinks = (*itEL).second.size();
2399 if ( nbLinks < minNbLinks ) {
2400 startElem = (*itEL).first;
2401 minNbLinks = nbLinks;
2402 if ( minNbLinks == 1 )
2407 // search elements to fuse starting from startElem or links of elements
2408 // fused earlyer - startLinks
2409 list< SMESH_TLink > startLinks;
2410 while ( startElem || !startLinks.empty() ) {
2411 while ( !startElem && !startLinks.empty() ) {
2412 // Get an element to start, by a link
2413 SMESH_TLink linkId = startLinks.front();
2414 startLinks.pop_front();
2415 itLE = mapLi_listEl.find( linkId );
2416 if ( itLE != mapLi_listEl.end() ) {
2417 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2418 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2419 for ( ; itE != listElem.end() ; itE++ )
2420 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2422 mapLi_listEl.erase( itLE );
2427 // Get candidates to be fused
2428 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2429 const SMESH_TLink *link12, *link13;
2431 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2432 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2433 ASSERT( !setLi.empty() );
2434 set< SMESH_TLink >::iterator itLi;
2435 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2437 const SMESH_TLink & link = (*itLi);
2438 itLE = mapLi_listEl.find( link );
2439 if ( itLE == mapLi_listEl.end() )
2442 const SMDS_MeshElement* elem = (*itLE).second.front();
2444 elem = (*itLE).second.back();
2445 mapLi_listEl.erase( itLE );
2446 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2457 // add other links of elem to list of links to re-start from
2458 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2459 set< SMESH_TLink >::iterator it;
2460 for ( it = links.begin(); it != links.end(); it++ ) {
2461 const SMESH_TLink& link2 = (*it);
2462 if ( link2 != link )
2463 startLinks.push_back( link2 );
2467 // Get nodes of possible quadrangles
2468 const SMDS_MeshNode *n12 [4], *n13 [4];
2469 bool Ok12 = false, Ok13 = false;
2470 const SMDS_MeshNode *linkNode1, *linkNode2;
2472 linkNode1 = link12->first;
2473 linkNode2 = link12->second;
2474 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2478 linkNode1 = link13->first;
2479 linkNode2 = link13->second;
2480 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2484 // Choose a pair to fuse
2485 if ( Ok12 && Ok13 ) {
2486 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2487 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2488 double aBadRate12 = getBadRate( &quad12, theCrit );
2489 double aBadRate13 = getBadRate( &quad13, theCrit );
2490 if ( aBadRate13 < aBadRate12 )
2497 // and remove fused elems and removed links from the maps
2498 mapEl_setLi.erase( tr1 );
2500 mapEl_setLi.erase( tr2 );
2501 mapLi_listEl.erase( *link12 );
2502 if(tr1->NbNodes()==3) {
2503 const SMDS_MeshElement* newElem = 0;
2504 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2505 myLastCreatedElems.Append(newElem);
2506 AddToSameGroups( newElem, tr1, aMesh );
2507 int aShapeId = tr1->getshapeId();
2510 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2512 aMesh->RemoveElement( tr1 );
2513 aMesh->RemoveElement( tr2 );
2516 const SMDS_MeshNode* N1 [6];
2517 const SMDS_MeshNode* N2 [6];
2518 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2519 // now we receive following N1 and N2 (using numeration as above image)
2520 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2521 // i.e. first nodes from both arrays determ new diagonal
2522 const SMDS_MeshNode* aNodes[8];
2531 const SMDS_MeshElement* newElem = 0;
2532 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2533 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2534 myLastCreatedElems.Append(newElem);
2535 AddToSameGroups( newElem, tr1, aMesh );
2536 int aShapeId = tr1->getshapeId();
2539 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2541 aMesh->RemoveElement( tr1 );
2542 aMesh->RemoveElement( tr2 );
2543 // remove middle node (9)
2544 GetMeshDS()->RemoveNode( N1[4] );
2548 mapEl_setLi.erase( tr3 );
2549 mapLi_listEl.erase( *link13 );
2550 if(tr1->NbNodes()==3) {
2551 const SMDS_MeshElement* newElem = 0;
2552 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2553 myLastCreatedElems.Append(newElem);
2554 AddToSameGroups( newElem, tr1, aMesh );
2555 int aShapeId = tr1->getshapeId();
2558 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2560 aMesh->RemoveElement( tr1 );
2561 aMesh->RemoveElement( tr3 );
2564 const SMDS_MeshNode* N1 [6];
2565 const SMDS_MeshNode* N2 [6];
2566 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2567 // now we receive following N1 and N2 (using numeration as above image)
2568 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2569 // i.e. first nodes from both arrays determ new diagonal
2570 const SMDS_MeshNode* aNodes[8];
2579 const SMDS_MeshElement* newElem = 0;
2580 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2581 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2582 myLastCreatedElems.Append(newElem);
2583 AddToSameGroups( newElem, tr1, aMesh );
2584 int aShapeId = tr1->getshapeId();
2587 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2589 aMesh->RemoveElement( tr1 );
2590 aMesh->RemoveElement( tr3 );
2591 // remove middle node (9)
2592 GetMeshDS()->RemoveNode( N1[4] );
2596 // Next element to fuse: the rejected one
2598 startElem = Ok12 ? tr3 : tr2;
2600 } // if ( startElem )
2601 } // while ( startElem || !startLinks.empty() )
2602 } // while ( ! mapEl_setLi.empty() )
2608 /*#define DUMPSO(txt) \
2609 // cout << txt << endl;
2610 //=============================================================================
2614 //=============================================================================
2615 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2619 int tmp = idNodes[ i1 ];
2620 idNodes[ i1 ] = idNodes[ i2 ];
2621 idNodes[ i2 ] = tmp;
2622 gp_Pnt Ptmp = P[ i1 ];
2625 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2628 //=======================================================================
2629 //function : SortQuadNodes
2630 //purpose : Set 4 nodes of a quadrangle face in a good order.
2631 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2633 //=======================================================================
2635 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2640 for ( i = 0; i < 4; i++ ) {
2641 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2643 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2646 gp_Vec V1(P[0], P[1]);
2647 gp_Vec V2(P[0], P[2]);
2648 gp_Vec V3(P[0], P[3]);
2650 gp_Vec Cross1 = V1 ^ V2;
2651 gp_Vec Cross2 = V2 ^ V3;
2654 if (Cross1.Dot(Cross2) < 0)
2659 if (Cross1.Dot(Cross2) < 0)
2663 swap ( i, i + 1, idNodes, P );
2665 // for ( int ii = 0; ii < 4; ii++ ) {
2666 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2667 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2673 //=======================================================================
2674 //function : SortHexaNodes
2675 //purpose : Set 8 nodes of a hexahedron in a good order.
2676 // Return success status
2677 //=======================================================================
2679 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2684 DUMPSO( "INPUT: ========================================");
2685 for ( i = 0; i < 8; i++ ) {
2686 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2687 if ( !n ) return false;
2688 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2689 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2691 DUMPSO( "========================================");
2694 set<int> faceNodes; // ids of bottom face nodes, to be found
2695 set<int> checkedId1; // ids of tried 2-nd nodes
2696 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2697 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2698 int iMin, iLoop1 = 0;
2700 // Loop to try the 2-nd nodes
2702 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2704 // Find not checked 2-nd node
2705 for ( i = 1; i < 8; i++ )
2706 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2707 int id1 = idNodes[i];
2708 swap ( 1, i, idNodes, P );
2709 checkedId1.insert ( id1 );
2713 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2714 // ie that all but meybe one (id3 which is on the same face) nodes
2715 // lay on the same side from the triangle plane.
2717 bool manyInPlane = false; // more than 4 nodes lay in plane
2719 while ( ++iLoop2 < 6 ) {
2721 // get 1-2-3 plane coeffs
2722 Standard_Real A, B, C, D;
2723 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2724 if ( N.SquareMagnitude() > gp::Resolution() )
2726 gp_Pln pln ( P[0], N );
2727 pln.Coefficients( A, B, C, D );
2729 // find the node (iMin) closest to pln
2730 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2732 for ( i = 3; i < 8; i++ ) {
2733 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2734 if ( fabs( dist[i] ) < minDist ) {
2735 minDist = fabs( dist[i] );
2738 if ( fabs( dist[i] ) <= tol )
2739 idInPln.insert( idNodes[i] );
2742 // there should not be more than 4 nodes in bottom plane
2743 if ( idInPln.size() > 1 )
2745 DUMPSO( "### idInPln.size() = " << idInPln.size());
2746 // idInPlane does not contain the first 3 nodes
2747 if ( manyInPlane || idInPln.size() == 5)
2748 return false; // all nodes in one plane
2751 // set the 1-st node to be not in plane
2752 for ( i = 3; i < 8; i++ ) {
2753 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2754 DUMPSO( "### Reset 0-th node");
2755 swap( 0, i, idNodes, P );
2760 // reset to re-check second nodes
2761 leastDist = DBL_MAX;
2765 break; // from iLoop2;
2768 // check that the other 4 nodes are on the same side
2769 bool sameSide = true;
2770 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2771 for ( i = 3; sameSide && i < 8; i++ ) {
2773 sameSide = ( isNeg == dist[i] <= 0.);
2776 // keep best solution
2777 if ( sameSide && minDist < leastDist ) {
2778 leastDist = minDist;
2780 faceNodes.insert( idNodes[ 1 ] );
2781 faceNodes.insert( idNodes[ 2 ] );
2782 faceNodes.insert( idNodes[ iMin ] );
2783 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2784 << " leastDist = " << leastDist);
2785 if ( leastDist <= DBL_MIN )
2790 // set next 3-d node to check
2791 int iNext = 2 + iLoop2;
2793 DUMPSO( "Try 2-nd");
2794 swap ( 2, iNext, idNodes, P );
2796 } // while ( iLoop2 < 6 )
2799 if ( faceNodes.empty() ) return false;
2801 // Put the faceNodes in proper places
2802 for ( i = 4; i < 8; i++ ) {
2803 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2804 // find a place to put
2806 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2808 DUMPSO( "Set faceNodes");
2809 swap ( iTo, i, idNodes, P );
2814 // Set nodes of the found bottom face in good order
2815 DUMPSO( " Found bottom face: ");
2816 i = SortQuadNodes( theMesh, idNodes );
2818 gp_Pnt Ptmp = P[ i ];
2823 // for ( int ii = 0; ii < 4; ii++ ) {
2824 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2825 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2828 // Gravity center of the top and bottom faces
2829 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2830 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2832 // Get direction from the bottom to the top face
2833 gp_Vec upDir ( aGCb, aGCt );
2834 Standard_Real upDirSize = upDir.Magnitude();
2835 if ( upDirSize <= gp::Resolution() ) return false;
2838 // Assure that the bottom face normal points up
2839 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2840 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2841 if ( Nb.Dot( upDir ) < 0 ) {
2842 DUMPSO( "Reverse bottom face");
2843 swap( 1, 3, idNodes, P );
2846 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2847 Standard_Real minDist = DBL_MAX;
2848 for ( i = 4; i < 8; i++ ) {
2849 // projection of P[i] to the plane defined by P[0] and upDir
2850 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2851 Standard_Real sqDist = P[0].SquareDistance( Pp );
2852 if ( sqDist < minDist ) {
2857 DUMPSO( "Set 4-th");
2858 swap ( 4, iMin, idNodes, P );
2860 // Set nodes of the top face in good order
2861 DUMPSO( "Sort top face");
2862 i = SortQuadNodes( theMesh, &idNodes[4] );
2865 gp_Pnt Ptmp = P[ i ];
2870 // Assure that direction of the top face normal is from the bottom face
2871 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2872 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2873 if ( Nt.Dot( upDir ) < 0 ) {
2874 DUMPSO( "Reverse top face");
2875 swap( 5, 7, idNodes, P );
2878 // DUMPSO( "OUTPUT: ========================================");
2879 // for ( i = 0; i < 8; i++ ) {
2880 // float *p = ugrid->GetPoint(idNodes[i]);
2881 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2887 //================================================================================
2889 * \brief Return nodes linked to the given one
2890 * \param theNode - the node
2891 * \param linkedNodes - the found nodes
2892 * \param type - the type of elements to check
2894 * Medium nodes are ignored
2896 //================================================================================
2898 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2899 TIDSortedElemSet & linkedNodes,
2900 SMDSAbs_ElementType type )
2902 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2903 while ( elemIt->more() )
2905 const SMDS_MeshElement* elem = elemIt->next();
2906 if(elem->GetType() == SMDSAbs_0DElement)
2909 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2910 if ( elem->GetType() == SMDSAbs_Volume )
2912 SMDS_VolumeTool vol( elem );
2913 while ( nodeIt->more() ) {
2914 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2915 if ( theNode != n && vol.IsLinked( theNode, n ))
2916 linkedNodes.insert( n );
2921 for ( int i = 0; nodeIt->more(); ++i ) {
2922 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2923 if ( n == theNode ) {
2924 int iBefore = i - 1;
2926 if ( elem->IsQuadratic() ) {
2927 int nb = elem->NbNodes() / 2;
2928 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2929 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2931 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2932 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2939 //=======================================================================
2940 //function : laplacianSmooth
2941 //purpose : pulls theNode toward the center of surrounding nodes directly
2942 // connected to that node along an element edge
2943 //=======================================================================
2945 void laplacianSmooth(const SMDS_MeshNode* theNode,
2946 const Handle(Geom_Surface)& theSurface,
2947 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2949 // find surrounding nodes
2951 TIDSortedElemSet nodeSet;
2952 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2954 // compute new coodrs
2956 double coord[] = { 0., 0., 0. };
2957 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2958 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2959 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2960 if ( theSurface.IsNull() ) { // smooth in 3D
2961 coord[0] += node->X();
2962 coord[1] += node->Y();
2963 coord[2] += node->Z();
2965 else { // smooth in 2D
2966 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2967 gp_XY* uv = theUVMap[ node ];
2968 coord[0] += uv->X();
2969 coord[1] += uv->Y();
2972 int nbNodes = nodeSet.size();
2975 coord[0] /= nbNodes;
2976 coord[1] /= nbNodes;
2978 if ( !theSurface.IsNull() ) {
2979 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2980 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2981 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2987 coord[2] /= nbNodes;
2991 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2994 //=======================================================================
2995 //function : centroidalSmooth
2996 //purpose : pulls theNode toward the element-area-weighted centroid of the
2997 // surrounding elements
2998 //=======================================================================
3000 void centroidalSmooth(const SMDS_MeshNode* theNode,
3001 const Handle(Geom_Surface)& theSurface,
3002 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3004 gp_XYZ aNewXYZ(0.,0.,0.);
3005 SMESH::Controls::Area anAreaFunc;
3006 double totalArea = 0.;
3011 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3012 while ( elemIt->more() )
3014 const SMDS_MeshElement* elem = elemIt->next();
3017 gp_XYZ elemCenter(0.,0.,0.);
3018 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3019 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3020 int nn = elem->NbNodes();
3021 if(elem->IsQuadratic()) nn = nn/2;
3023 //while ( itN->more() ) {
3025 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3027 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3028 aNodePoints.push_back( aP );
3029 if ( !theSurface.IsNull() ) { // smooth in 2D
3030 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3031 gp_XY* uv = theUVMap[ aNode ];
3032 aP.SetCoord( uv->X(), uv->Y(), 0. );
3036 double elemArea = anAreaFunc.GetValue( aNodePoints );
3037 totalArea += elemArea;
3039 aNewXYZ += elemCenter * elemArea;
3041 aNewXYZ /= totalArea;
3042 if ( !theSurface.IsNull() ) {
3043 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3044 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3049 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3052 //=======================================================================
3053 //function : getClosestUV
3054 //purpose : return UV of closest projection
3055 //=======================================================================
3057 static bool getClosestUV (Extrema_GenExtPS& projector,
3058 const gp_Pnt& point,
3061 projector.Perform( point );
3062 if ( projector.IsDone() ) {
3063 double u, v, minVal = DBL_MAX;
3064 for ( int i = projector.NbExt(); i > 0; i-- )
3065 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3066 if ( projector.SquareDistance( i ) < minVal ) {
3067 minVal = projector.SquareDistance( i );
3069 if ( projector.Value( i ) < minVal ) {
3070 minVal = projector.Value( i );
3072 projector.Point( i ).Parameter( u, v );
3074 result.SetCoord( u, v );
3080 //=======================================================================
3082 //purpose : Smooth theElements during theNbIterations or until a worst
3083 // element has aspect ratio <= theTgtAspectRatio.
3084 // Aspect Ratio varies in range [1.0, inf].
3085 // If theElements is empty, the whole mesh is smoothed.
3086 // theFixedNodes contains additionally fixed nodes. Nodes built
3087 // on edges and boundary nodes are always fixed.
3088 //=======================================================================
3090 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3091 set<const SMDS_MeshNode*> & theFixedNodes,
3092 const SmoothMethod theSmoothMethod,
3093 const int theNbIterations,
3094 double theTgtAspectRatio,
3097 myLastCreatedElems.Clear();
3098 myLastCreatedNodes.Clear();
3100 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3102 if ( theTgtAspectRatio < 1.0 )
3103 theTgtAspectRatio = 1.0;
3105 const double disttol = 1.e-16;
3107 SMESH::Controls::AspectRatio aQualityFunc;
3109 SMESHDS_Mesh* aMesh = GetMeshDS();
3111 if ( theElems.empty() ) {
3112 // add all faces to theElems
3113 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3114 while ( fIt->more() ) {
3115 const SMDS_MeshElement* face = fIt->next();
3116 theElems.insert( face );
3119 // get all face ids theElems are on
3120 set< int > faceIdSet;
3121 TIDSortedElemSet::iterator itElem;
3123 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3124 int fId = FindShape( *itElem );
3125 // check that corresponding submesh exists and a shape is face
3127 faceIdSet.find( fId ) == faceIdSet.end() &&
3128 aMesh->MeshElements( fId )) {
3129 TopoDS_Shape F = aMesh->IndexToShape( fId );
3130 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3131 faceIdSet.insert( fId );
3134 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3136 // ===============================================
3137 // smooth elements on each TopoDS_Face separately
3138 // ===============================================
3140 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3141 for ( ; fId != faceIdSet.rend(); ++fId ) {
3142 // get face surface and submesh
3143 Handle(Geom_Surface) surface;
3144 SMESHDS_SubMesh* faceSubMesh = 0;
3146 double fToler2 = 0, f,l;
3147 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3148 bool isUPeriodic = false, isVPeriodic = false;
3150 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3151 surface = BRep_Tool::Surface( face );
3152 faceSubMesh = aMesh->MeshElements( *fId );
3153 fToler2 = BRep_Tool::Tolerance( face );
3154 fToler2 *= fToler2 * 10.;
3155 isUPeriodic = surface->IsUPeriodic();
3158 isVPeriodic = surface->IsVPeriodic();
3161 surface->Bounds( u1, u2, v1, v2 );
3163 // ---------------------------------------------------------
3164 // for elements on a face, find movable and fixed nodes and
3165 // compute UV for them
3166 // ---------------------------------------------------------
3167 bool checkBoundaryNodes = false;
3168 bool isQuadratic = false;
3169 set<const SMDS_MeshNode*> setMovableNodes;
3170 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3171 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3172 list< const SMDS_MeshElement* > elemsOnFace;
3174 Extrema_GenExtPS projector;
3175 GeomAdaptor_Surface surfAdaptor;
3176 if ( !surface.IsNull() ) {
3177 surfAdaptor.Load( surface );
3178 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3180 int nbElemOnFace = 0;
3181 itElem = theElems.begin();
3182 // loop on not yet smoothed elements: look for elems on a face
3183 while ( itElem != theElems.end() ) {
3184 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3185 break; // all elements found
3187 const SMDS_MeshElement* elem = *itElem;
3188 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3189 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3193 elemsOnFace.push_back( elem );
3194 theElems.erase( itElem++ );
3198 isQuadratic = elem->IsQuadratic();
3200 // get movable nodes of elem
3201 const SMDS_MeshNode* node;
3202 SMDS_TypeOfPosition posType;
3203 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3204 int nn = 0, nbn = elem->NbNodes();
3205 if(elem->IsQuadratic())
3207 while ( nn++ < nbn ) {
3208 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3209 const SMDS_PositionPtr& pos = node->GetPosition();
3210 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3211 if (posType != SMDS_TOP_EDGE &&
3212 posType != SMDS_TOP_VERTEX &&
3213 theFixedNodes.find( node ) == theFixedNodes.end())
3215 // check if all faces around the node are on faceSubMesh
3216 // because a node on edge may be bound to face
3217 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3219 if ( faceSubMesh ) {
3220 while ( eIt->more() && all ) {
3221 const SMDS_MeshElement* e = eIt->next();
3222 all = faceSubMesh->Contains( e );
3226 setMovableNodes.insert( node );
3228 checkBoundaryNodes = true;
3230 if ( posType == SMDS_TOP_3DSPACE )
3231 checkBoundaryNodes = true;
3234 if ( surface.IsNull() )
3237 // get nodes to check UV
3238 list< const SMDS_MeshNode* > uvCheckNodes;
3239 itN = elem->nodesIterator();
3240 nn = 0; nbn = elem->NbNodes();
3241 if(elem->IsQuadratic())
3243 while ( nn++ < nbn ) {
3244 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3245 if ( uvMap.find( node ) == uvMap.end() )
3246 uvCheckNodes.push_back( node );
3247 // add nodes of elems sharing node
3248 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3249 // while ( eIt->more() ) {
3250 // const SMDS_MeshElement* e = eIt->next();
3251 // if ( e != elem ) {
3252 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3253 // while ( nIt->more() ) {
3254 // const SMDS_MeshNode* n =
3255 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3256 // if ( uvMap.find( n ) == uvMap.end() )
3257 // uvCheckNodes.push_back( n );
3263 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3264 for ( ; n != uvCheckNodes.end(); ++n ) {
3267 const SMDS_PositionPtr& pos = node->GetPosition();
3268 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3270 switch ( posType ) {
3271 case SMDS_TOP_FACE: {
3272 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3273 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3276 case SMDS_TOP_EDGE: {
3277 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3278 Handle(Geom2d_Curve) pcurve;
3279 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3280 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3281 if ( !pcurve.IsNull() ) {
3282 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3283 uv = pcurve->Value( u ).XY();
3287 case SMDS_TOP_VERTEX: {
3288 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3289 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3290 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3295 // check existing UV
3296 bool project = true;
3297 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3298 double dist1 = DBL_MAX, dist2 = 0;
3299 if ( posType != SMDS_TOP_3DSPACE ) {
3300 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3301 project = dist1 > fToler2;
3303 if ( project ) { // compute new UV
3305 if ( !getClosestUV( projector, pNode, newUV )) {
3306 MESSAGE("Node Projection Failed " << node);
3310 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3312 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3314 if ( posType != SMDS_TOP_3DSPACE )
3315 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3316 if ( dist2 < dist1 )
3320 // store UV in the map
3321 listUV.push_back( uv );
3322 uvMap.insert( make_pair( node, &listUV.back() ));
3324 } // loop on not yet smoothed elements
3326 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3327 checkBoundaryNodes = true;
3329 // fix nodes on mesh boundary
3331 if ( checkBoundaryNodes ) {
3332 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3333 map< SMESH_TLink, int >::iterator link_nb;
3334 // put all elements links to linkNbMap
3335 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3336 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3337 const SMDS_MeshElement* elem = (*elemIt);
3338 int nbn = elem->NbCornerNodes();
3339 // loop on elem links: insert them in linkNbMap
3340 for ( int iN = 0; iN < nbn; ++iN ) {
3341 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3342 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3343 SMESH_TLink link( n1, n2 );
3344 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3348 // remove nodes that are in links encountered only once from setMovableNodes
3349 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3350 if ( link_nb->second == 1 ) {
3351 setMovableNodes.erase( link_nb->first.node1() );
3352 setMovableNodes.erase( link_nb->first.node2() );
3357 // -----------------------------------------------------
3358 // for nodes on seam edge, compute one more UV ( uvMap2 );
3359 // find movable nodes linked to nodes on seam and which
3360 // are to be smoothed using the second UV ( uvMap2 )
3361 // -----------------------------------------------------
3363 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3364 if ( !surface.IsNull() ) {
3365 TopExp_Explorer eExp( face, TopAbs_EDGE );
3366 for ( ; eExp.More(); eExp.Next() ) {
3367 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3368 if ( !BRep_Tool::IsClosed( edge, face ))
3370 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3371 if ( !sm ) continue;
3372 // find out which parameter varies for a node on seam
3375 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3376 if ( pcurve.IsNull() ) continue;
3377 uv1 = pcurve->Value( f );
3379 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3380 if ( pcurve.IsNull() ) continue;
3381 uv2 = pcurve->Value( f );
3382 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3384 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3385 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3387 // get nodes on seam and its vertices
3388 list< const SMDS_MeshNode* > seamNodes;
3389 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3390 while ( nSeamIt->more() ) {
3391 const SMDS_MeshNode* node = nSeamIt->next();
3392 if ( !isQuadratic || !IsMedium( node ))
3393 seamNodes.push_back( node );
3395 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3396 for ( ; vExp.More(); vExp.Next() ) {
3397 sm = aMesh->MeshElements( vExp.Current() );
3399 nSeamIt = sm->GetNodes();
3400 while ( nSeamIt->more() )
3401 seamNodes.push_back( nSeamIt->next() );
3404 // loop on nodes on seam
3405 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3406 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3407 const SMDS_MeshNode* nSeam = *noSeIt;
3408 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3409 if ( n_uv == uvMap.end() )
3412 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3413 // set the second UV
3414 listUV.push_back( *n_uv->second );
3415 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3416 if ( uvMap2.empty() )
3417 uvMap2 = uvMap; // copy the uvMap contents
3418 uvMap2[ nSeam ] = &listUV.back();
3420 // collect movable nodes linked to ones on seam in nodesNearSeam
3421 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3422 while ( eIt->more() ) {
3423 const SMDS_MeshElement* e = eIt->next();
3424 int nbUseMap1 = 0, nbUseMap2 = 0;
3425 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3426 int nn = 0, nbn = e->NbNodes();
3427 if(e->IsQuadratic()) nbn = nbn/2;
3428 while ( nn++ < nbn )
3430 const SMDS_MeshNode* n =
3431 static_cast<const SMDS_MeshNode*>( nIt->next() );
3433 setMovableNodes.find( n ) == setMovableNodes.end() )
3435 // add only nodes being closer to uv2 than to uv1
3436 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3437 0.5 * ( n->Y() + nSeam->Y() ),
3438 0.5 * ( n->Z() + nSeam->Z() ));
3440 getClosestUV( projector, pMid, uv );
3441 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3442 nodesNearSeam.insert( n );
3448 // for centroidalSmooth all element nodes must
3449 // be on one side of a seam
3450 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3451 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3453 while ( nn++ < nbn ) {
3454 const SMDS_MeshNode* n =
3455 static_cast<const SMDS_MeshNode*>( nIt->next() );
3456 setMovableNodes.erase( n );
3460 } // loop on nodes on seam
3461 } // loop on edge of a face
3462 } // if ( !face.IsNull() )
3464 if ( setMovableNodes.empty() ) {
3465 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3466 continue; // goto next face
3474 double maxRatio = -1., maxDisplacement = -1.;
3475 set<const SMDS_MeshNode*>::iterator nodeToMove;
3476 for ( it = 0; it < theNbIterations; it++ ) {
3477 maxDisplacement = 0.;
3478 nodeToMove = setMovableNodes.begin();
3479 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3480 const SMDS_MeshNode* node = (*nodeToMove);
3481 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3484 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3485 if ( theSmoothMethod == LAPLACIAN )
3486 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3488 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3490 // node displacement
3491 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3492 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3493 if ( aDispl > maxDisplacement )
3494 maxDisplacement = aDispl;
3496 // no node movement => exit
3497 //if ( maxDisplacement < 1.e-16 ) {
3498 if ( maxDisplacement < disttol ) {
3499 MESSAGE("-- no node movement --");
3503 // check elements quality
3505 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3506 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3507 const SMDS_MeshElement* elem = (*elemIt);
3508 if ( !elem || elem->GetType() != SMDSAbs_Face )
3510 SMESH::Controls::TSequenceOfXYZ aPoints;
3511 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3512 double aValue = aQualityFunc.GetValue( aPoints );
3513 if ( aValue > maxRatio )
3517 if ( maxRatio <= theTgtAspectRatio ) {
3518 MESSAGE("-- quality achived --");
3521 if (it+1 == theNbIterations) {
3522 MESSAGE("-- Iteration limit exceeded --");
3524 } // smoothing iterations
3526 MESSAGE(" Face id: " << *fId <<
3527 " Nb iterstions: " << it <<
3528 " Displacement: " << maxDisplacement <<
3529 " Aspect Ratio " << maxRatio);
3531 // ---------------------------------------
3532 // new nodes positions are computed,
3533 // record movement in DS and set new UV
3534 // ---------------------------------------
3535 nodeToMove = setMovableNodes.begin();
3536 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3537 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3538 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3539 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3540 if ( node_uv != uvMap.end() ) {
3541 gp_XY* uv = node_uv->second;
3543 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3547 // move medium nodes of quadratic elements
3550 SMESH_MesherHelper helper( *GetMesh() );
3551 if ( !face.IsNull() )
3552 helper.SetSubShape( face );
3553 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3554 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3555 const SMDS_VtkFace* QF =
3556 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3557 if(QF && QF->IsQuadratic()) {
3558 vector<const SMDS_MeshNode*> Ns;
3559 Ns.reserve(QF->NbNodes()+1);
3560 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3561 while ( anIter->more() )
3562 Ns.push_back( cast2Node(anIter->next()) );
3563 Ns.push_back( Ns[0] );
3565 for(int i=0; i<QF->NbNodes(); i=i+2) {
3566 if ( !surface.IsNull() ) {
3567 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3568 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3569 gp_XY uv = ( uv1 + uv2 ) / 2.;
3570 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3571 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3574 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3575 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3576 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3578 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3579 fabs( Ns[i+1]->Y() - y ) > disttol ||
3580 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3581 // we have to move i+1 node
3582 aMesh->MoveNode( Ns[i+1], x, y, z );
3589 } // loop on face ids
3593 //=======================================================================
3594 //function : isReverse
3595 //purpose : Return true if normal of prevNodes is not co-directied with
3596 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3597 // iNotSame is where prevNodes and nextNodes are different.
3598 // If result is true then future volume orientation is OK
3599 //=======================================================================
3601 static bool isReverse(const SMDS_MeshElement* face,
3602 const vector<const SMDS_MeshNode*>& prevNodes,
3603 const vector<const SMDS_MeshNode*>& nextNodes,
3607 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3608 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3609 gp_XYZ extrDir( pN - pP ), faceNorm;
3610 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3612 return faceNorm * extrDir < 0.0;
3615 //=======================================================================
3617 * \brief Create elements by sweeping an element
3618 * \param elem - element to sweep
3619 * \param newNodesItVec - nodes generated from each node of the element
3620 * \param newElems - generated elements
3621 * \param nbSteps - number of sweeping steps
3622 * \param srcElements - to append elem for each generated element
3624 //=======================================================================
3626 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3627 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3628 list<const SMDS_MeshElement*>& newElems,
3630 SMESH_SequenceOfElemPtr& srcElements)
3632 //MESSAGE("sweepElement " << nbSteps);
3633 SMESHDS_Mesh* aMesh = GetMeshDS();
3635 const int nbNodes = elem->NbNodes();
3636 const int nbCorners = elem->NbCornerNodes();
3637 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3638 polyhedron creation !!! */
3639 // Loop on elem nodes:
3640 // find new nodes and detect same nodes indices
3641 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3642 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3643 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3644 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3646 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3647 vector<int> sames(nbNodes);
3648 vector<bool> isSingleNode(nbNodes);
3650 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3651 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3652 const SMDS_MeshNode* node = nnIt->first;
3653 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3654 if ( listNewNodes.empty() )
3657 itNN [ iNode ] = listNewNodes.begin();
3658 prevNod[ iNode ] = node;
3659 nextNod[ iNode ] = listNewNodes.front();
3661 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3662 corner node of linear */
3663 if ( prevNod[ iNode ] != nextNod [ iNode ])
3664 nbDouble += !isSingleNode[iNode];
3666 if( iNode < nbCorners ) { // check corners only
3667 if ( prevNod[ iNode ] == nextNod [ iNode ])
3668 sames[nbSame++] = iNode;
3670 iNotSameNode = iNode;
3674 if ( nbSame == nbNodes || nbSame > 2) {
3675 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3679 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3681 // fix nodes order to have bottom normal external
3682 if ( baseType == SMDSEntity_Polygon )
3684 std::reverse( itNN.begin(), itNN.end() );
3685 std::reverse( prevNod.begin(), prevNod.end() );
3686 std::reverse( midlNod.begin(), midlNod.end() );
3687 std::reverse( nextNod.begin(), nextNod.end() );
3688 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3692 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3693 SMDS_MeshCell::applyInterlace( ind, itNN );
3694 SMDS_MeshCell::applyInterlace( ind, prevNod );
3695 SMDS_MeshCell::applyInterlace( ind, nextNod );
3696 SMDS_MeshCell::applyInterlace( ind, midlNod );
3697 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3700 sames[nbSame] = iNotSameNode;
3701 for ( int j = 0; j <= nbSame; ++j )
3702 for ( size_t i = 0; i < ind.size(); ++i )
3703 if ( ind[i] == sames[j] )
3708 iNotSameNode = sames[nbSame];
3713 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3715 iSameNode = sames[ nbSame-1 ];
3716 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3717 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3718 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3721 // make new elements
3722 for (int iStep = 0; iStep < nbSteps; iStep++ )
3725 for ( iNode = 0; iNode < nbNodes; iNode++ )
3727 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3728 nextNod[ iNode ] = *itNN[ iNode ]++;
3731 SMDS_MeshElement* aNewElem = 0;
3732 /*if(!elem->IsPoly())*/ {
3733 switch ( baseType ) {
3735 case SMDSEntity_Node: { // sweep NODE
3736 if ( nbSame == 0 ) {
3737 if ( isSingleNode[0] )
3738 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3740 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3746 case SMDSEntity_Edge: { // sweep EDGE
3747 if ( nbDouble == 0 )
3749 if ( nbSame == 0 ) // ---> quadrangle
3750 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3751 nextNod[ 1 ], nextNod[ 0 ] );
3752 else // ---> triangle
3753 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3754 nextNod[ iNotSameNode ] );
3756 else // ---> polygon
3758 vector<const SMDS_MeshNode*> poly_nodes;
3759 poly_nodes.push_back( prevNod[0] );
3760 poly_nodes.push_back( prevNod[1] );
3761 if ( prevNod[1] != nextNod[1] )
3763 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3764 poly_nodes.push_back( nextNod[1] );
3766 if ( prevNod[0] != nextNod[0] )
3768 poly_nodes.push_back( nextNod[0] );
3769 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3771 switch ( poly_nodes.size() ) {
3773 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3776 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3777 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3780 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3785 case SMDSEntity_Triangle: // TRIANGLE --->
3787 if ( nbDouble > 0 ) break;
3788 if ( nbSame == 0 ) // ---> pentahedron
3789 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3790 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3792 else if ( nbSame == 1 ) // ---> pyramid
3793 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3794 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3795 nextNod[ iSameNode ]);
3797 else // 2 same nodes: ---> tetrahedron
3798 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3799 nextNod[ iNotSameNode ]);
3802 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3806 if ( nbDouble+nbSame == 2 )
3808 if(nbSame==0) { // ---> quadratic quadrangle
3809 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3810 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3812 else { //(nbSame==1) // ---> quadratic triangle
3814 return; // medium node on axis
3816 else if(sames[0]==0)
3817 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3818 nextNod[2], midlNod[1], prevNod[2]);
3820 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3821 midlNod[0], nextNod[2], prevNod[2]);
3824 else if ( nbDouble == 3 )
3826 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3827 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3828 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3835 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3836 if ( nbDouble > 0 ) break;
3838 if ( nbSame == 0 ) // ---> hexahedron
3839 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3840 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3842 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3843 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3844 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3845 nextNod[ iSameNode ]);
3846 newElems.push_back( aNewElem );
3847 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3848 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3849 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3851 else if ( nbSame == 2 ) { // ---> pentahedron
3852 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3853 // iBeforeSame is same too
3854 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3855 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3856 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3858 // iAfterSame is same too
3859 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3860 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3861 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3865 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3866 if ( nbDouble+nbSame != 3 ) break;
3868 // ---> pentahedron with 15 nodes
3869 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3870 nextNod[0], nextNod[1], nextNod[2],
3871 prevNod[3], prevNod[4], prevNod[5],
3872 nextNod[3], nextNod[4], nextNod[5],
3873 midlNod[0], midlNod[1], midlNod[2]);
3875 else if(nbSame==1) {
3876 // ---> 2d order pyramid of 13 nodes
3877 int apex = iSameNode;
3878 int i0 = ( apex + 1 ) % nbCorners;
3879 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3883 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3884 nextNod[i0], nextNod[i1], prevNod[apex],
3885 prevNod[i01], midlNod[i0],
3886 nextNod[i01], midlNod[i1],
3887 prevNod[i1a], prevNod[i0a],
3888 nextNod[i0a], nextNod[i1a]);
3890 else if(nbSame==2) {
3891 // ---> 2d order tetrahedron of 10 nodes
3892 int n1 = iNotSameNode;
3893 int n2 = ( n1 + 1 ) % nbCorners;
3894 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3898 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3899 prevNod[n12], prevNod[n23], prevNod[n31],
3900 midlNod[n1], nextNod[n12], nextNod[n31]);
3904 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3906 if ( nbDouble != 4 ) break;
3907 // ---> hexahedron with 20 nodes
3908 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3909 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3910 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3911 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3912 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3914 else if(nbSame==1) {
3915 // ---> pyramid + pentahedron - can not be created since it is needed
3916 // additional middle node at the center of face
3917 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3920 else if( nbSame == 2 ) {
3921 if ( nbDouble != 2 ) break;
3922 // ---> 2d order Pentahedron with 15 nodes
3924 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3925 // iBeforeSame is same too
3932 // iAfterSame is same too
3942 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3943 prevNod[n4], prevNod[n5], nextNod[n5],
3944 prevNod[n12], midlNod[n2], nextNod[n12],
3945 prevNod[n45], midlNod[n5], nextNod[n45],
3946 prevNod[n14], prevNod[n25], nextNod[n25]);
3950 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3952 if( nbSame == 0 && nbDouble == 9 ) {
3953 // ---> tri-quadratic hexahedron with 27 nodes
3954 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3955 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3956 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3957 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3958 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3959 prevNod[8], // bottom center
3960 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3961 nextNod[8], // top center
3962 midlNod[8]);// elem center
3970 case SMDSEntity_Polygon: { // sweep POLYGON
3972 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3973 // ---> hexagonal prism
3974 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3975 prevNod[3], prevNod[4], prevNod[5],
3976 nextNod[0], nextNod[1], nextNod[2],
3977 nextNod[3], nextNod[4], nextNod[5]);
3981 case SMDSEntity_Ball:
3989 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3991 if ( baseType != SMDSEntity_Polygon )
3993 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3994 SMDS_MeshCell::applyInterlace( ind, prevNod );
3995 SMDS_MeshCell::applyInterlace( ind, nextNod );
3996 SMDS_MeshCell::applyInterlace( ind, midlNod );
3997 SMDS_MeshCell::applyInterlace( ind, itNN );
3998 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3999 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4001 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4002 vector<int> quantities (nbNodes + 2);
4003 polyedre_nodes.clear();
4007 for (int inode = 0; inode < nbNodes; inode++)
4008 polyedre_nodes.push_back( prevNod[inode] );
4009 quantities.push_back( nbNodes );
4012 polyedre_nodes.push_back( nextNod[0] );
4013 for (int inode = nbNodes; inode-1; --inode )
4014 polyedre_nodes.push_back( nextNod[inode-1] );
4015 quantities.push_back( nbNodes );
4018 for (int iface = 0; iface < nbNodes; iface++)
4020 const int prevNbNodes = polyedre_nodes.size();
4021 int inextface = (iface+1) % nbNodes;
4022 polyedre_nodes.push_back( prevNod[inextface] );
4023 polyedre_nodes.push_back( prevNod[iface] );
4024 if ( prevNod[iface] != nextNod[iface] )
4026 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4027 polyedre_nodes.push_back( nextNod[iface] );
4029 if ( prevNod[inextface] != nextNod[inextface] )
4031 polyedre_nodes.push_back( nextNod[inextface] );
4032 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4034 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4035 if ( nbFaceNodes > 2 )
4036 quantities.push_back( nbFaceNodes );
4037 else // degenerated face
4038 polyedre_nodes.resize( prevNbNodes );
4040 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4044 newElems.push_back( aNewElem );
4045 myLastCreatedElems.Append(aNewElem);
4046 srcElements.Append( elem );
4049 // set new prev nodes
4050 for ( iNode = 0; iNode < nbNodes; iNode++ )
4051 prevNod[ iNode ] = nextNod[ iNode ];
4056 //=======================================================================
4058 * \brief Create 1D and 2D elements around swept elements
4059 * \param mapNewNodes - source nodes and ones generated from them
4060 * \param newElemsMap - source elements and ones generated from them
4061 * \param elemNewNodesMap - nodes generated from each node of each element
4062 * \param elemSet - all swept elements
4063 * \param nbSteps - number of sweeping steps
4064 * \param srcElements - to append elem for each generated element
4066 //=======================================================================
4068 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4069 TElemOfElemListMap & newElemsMap,
4070 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4071 TIDSortedElemSet& elemSet,
4073 SMESH_SequenceOfElemPtr& srcElements)
4075 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4076 SMESHDS_Mesh* aMesh = GetMeshDS();
4078 // Find nodes belonging to only one initial element - sweep them to get edges.
4080 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4081 for ( ; nList != mapNewNodes.end(); nList++ )
4083 const SMDS_MeshNode* node =
4084 static_cast<const SMDS_MeshNode*>( nList->first );
4085 if ( newElemsMap.count( node ))
4086 continue; // node was extruded into edge
4087 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4088 int nbInitElems = 0;
4089 const SMDS_MeshElement* el = 0;
4090 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4091 while ( eIt->more() && nbInitElems < 2 ) {
4093 SMDSAbs_ElementType type = el->GetType();
4094 if ( type == SMDSAbs_Volume || type < highType ) continue;
4095 if ( type > highType ) {
4099 nbInitElems += elemSet.count(el);
4101 if ( nbInitElems < 2 ) {
4102 bool NotCreateEdge = el && el->IsMediumNode(node);
4103 if(!NotCreateEdge) {
4104 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4105 list<const SMDS_MeshElement*> newEdges;
4106 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4111 // Make a ceiling for each element ie an equal element of last new nodes.
4112 // Find free links of faces - make edges and sweep them into faces.
4114 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4115 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4116 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4118 const SMDS_MeshElement* elem = itElem->first;
4119 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4121 if(itElem->second.size()==0) continue;
4123 const bool isQuadratic = elem->IsQuadratic();
4125 if ( elem->GetType() == SMDSAbs_Edge ) {
4126 // create a ceiling edge
4127 if ( !isQuadratic ) {
4128 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4129 vecNewNodes[ 1 ]->second.back())) {
4130 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4131 vecNewNodes[ 1 ]->second.back()));
4132 srcElements.Append( myLastCreatedElems.Last() );
4136 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4137 vecNewNodes[ 1 ]->second.back(),
4138 vecNewNodes[ 2 ]->second.back())) {
4139 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4140 vecNewNodes[ 1 ]->second.back(),
4141 vecNewNodes[ 2 ]->second.back()));
4142 srcElements.Append( myLastCreatedElems.Last() );
4146 if ( elem->GetType() != SMDSAbs_Face )
4149 bool hasFreeLinks = false;
4151 TIDSortedElemSet avoidSet;
4152 avoidSet.insert( elem );
4154 set<const SMDS_MeshNode*> aFaceLastNodes;
4155 int iNode, nbNodes = vecNewNodes.size();
4156 if ( !isQuadratic ) {
4157 // loop on the face nodes
4158 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4159 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4160 // look for free links of the face
4161 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4162 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4163 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4164 // check if a link is free
4165 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4166 hasFreeLinks = true;
4167 // make an edge and a ceiling for a new edge
4168 if ( !aMesh->FindEdge( n1, n2 )) {
4169 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4170 srcElements.Append( myLastCreatedElems.Last() );
4172 n1 = vecNewNodes[ iNode ]->second.back();
4173 n2 = vecNewNodes[ iNext ]->second.back();
4174 if ( !aMesh->FindEdge( n1, n2 )) {
4175 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4176 srcElements.Append( myLastCreatedElems.Last() );
4181 else { // elem is quadratic face
4182 int nbn = nbNodes/2;
4183 for ( iNode = 0; iNode < nbn; iNode++ ) {
4184 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4185 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4186 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4187 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4188 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4189 // check if a link is free
4190 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4191 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4192 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4193 hasFreeLinks = true;
4194 // make an edge and a ceiling for a new edge
4196 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4197 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4198 srcElements.Append( myLastCreatedElems.Last() );
4200 n1 = vecNewNodes[ iNode ]->second.back();
4201 n2 = vecNewNodes[ iNext ]->second.back();
4202 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4203 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4204 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4205 srcElements.Append( myLastCreatedElems.Last() );
4209 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4210 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4214 // sweep free links into faces
4216 if ( hasFreeLinks ) {
4217 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4218 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4220 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4221 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4222 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4223 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4225 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4226 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4227 std::advance( v, volNb );
4228 // find indices of free faces of a volume and their source edges
4229 list< int > freeInd;
4230 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4231 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4232 int iF, nbF = vTool.NbFaces();
4233 for ( iF = 0; iF < nbF; iF ++ ) {
4234 if (vTool.IsFreeFace( iF ) &&
4235 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4236 initNodeSet != faceNodeSet) // except an initial face
4238 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4240 freeInd.push_back( iF );
4241 // find source edge of a free face iF
4242 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4243 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4244 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4245 initNodeSet.begin(), initNodeSet.end(),
4246 commonNodes.begin());
4247 if ( (*v)->IsQuadratic() )
4248 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4250 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4252 if ( !srcEdges.back() )
4254 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4255 << iF << " of volume #" << vTool.ID() << endl;
4260 if ( freeInd.empty() )
4263 // create faces for all steps;
4264 // if such a face has been already created by sweep of edge,
4265 // assure that its orientation is OK
4266 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4267 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4268 vTool.SetExternalNormal();
4269 const int nextShift = vTool.IsForward() ? +1 : -1;
4270 list< int >::iterator ind = freeInd.begin();
4271 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4272 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4274 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4275 int nbn = vTool.NbFaceNodes( *ind );
4276 const SMDS_MeshElement * f = 0;
4277 if ( nbn == 3 ) ///// triangle
4279 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4281 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4283 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4285 nodes[ 1 + nextShift ] };
4287 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4289 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4293 else if ( nbn == 4 ) ///// quadrangle
4295 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4297 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4299 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4300 nodes[ 2 ], nodes[ 2+nextShift ] };
4302 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4304 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4305 newOrder[ 2 ], newOrder[ 3 ]));
4308 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4310 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4312 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4314 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4316 nodes[2 + 2*nextShift],
4317 nodes[3 - 2*nextShift],
4319 nodes[3 + 2*nextShift]};
4321 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4323 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4331 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4333 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4334 nodes[1], nodes[3], nodes[5], nodes[7] );
4336 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4338 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4339 nodes[4 - 2*nextShift],
4341 nodes[4 + 2*nextShift],
4343 nodes[5 - 2*nextShift],
4345 nodes[5 + 2*nextShift] };
4347 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4349 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4350 newOrder[ 2 ], newOrder[ 3 ],
4351 newOrder[ 4 ], newOrder[ 5 ],
4352 newOrder[ 6 ], newOrder[ 7 ]));
4355 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4357 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4358 SMDSAbs_Face, /*noMedium=*/false);
4360 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4362 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4363 nodes[4 - 2*nextShift],
4365 nodes[4 + 2*nextShift],
4367 nodes[5 - 2*nextShift],
4369 nodes[5 + 2*nextShift],
4372 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4374 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4375 newOrder[ 2 ], newOrder[ 3 ],
4376 newOrder[ 4 ], newOrder[ 5 ],
4377 newOrder[ 6 ], newOrder[ 7 ],
4381 else //////// polygon
4383 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4384 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4386 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4388 if ( !vTool.IsForward() )
4389 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4391 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4393 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4397 while ( srcElements.Length() < myLastCreatedElems.Length() )
4398 srcElements.Append( *srcEdge );
4400 } // loop on free faces
4402 // go to the next volume
4404 while ( iVol++ < nbVolumesByStep ) v++;
4407 } // loop on volumes of one step
4408 } // sweep free links into faces
4410 // Make a ceiling face with a normal external to a volume
4412 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4414 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4416 lastVol.SetExternalNormal();
4417 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4418 int nbn = lastVol.NbFaceNodes( iF );
4420 if (!hasFreeLinks ||
4421 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4422 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4424 else if ( nbn == 4 )
4426 if (!hasFreeLinks ||
4427 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4428 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4430 else if ( nbn == 6 && isQuadratic )
4432 if (!hasFreeLinks ||
4433 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4434 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4435 nodes[1], nodes[3], nodes[5]));
4437 else if ( nbn == 8 && isQuadratic )
4439 if (!hasFreeLinks ||
4440 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4441 nodes[1], nodes[3], nodes[5], nodes[7]) )
4442 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4443 nodes[1], nodes[3], nodes[5], nodes[7]));
4445 else if ( nbn == 9 && isQuadratic )
4447 if (!hasFreeLinks ||
4448 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4449 SMDSAbs_Face, /*noMedium=*/false) )
4450 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4451 nodes[1], nodes[3], nodes[5], nodes[7],
4455 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4456 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4457 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4460 while ( srcElements.Length() < myLastCreatedElems.Length() )
4461 srcElements.Append( myLastCreatedElems.Last() );
4463 } // loop on swept elements
4466 //=======================================================================
4467 //function : RotationSweep
4469 //=======================================================================
4471 SMESH_MeshEditor::PGroupIDs
4472 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4473 const gp_Ax1& theAxis,
4474 const double theAngle,
4475 const int theNbSteps,
4476 const double theTol,
4477 const bool theMakeGroups,
4478 const bool theMakeWalls)
4480 myLastCreatedElems.Clear();
4481 myLastCreatedNodes.Clear();
4483 // source elements for each generated one
4484 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4486 MESSAGE( "RotationSweep()");
4488 aTrsf.SetRotation( theAxis, theAngle );
4490 aTrsf2.SetRotation( theAxis, theAngle/2. );
4492 gp_Lin aLine( theAxis );
4493 double aSqTol = theTol * theTol;
4495 SMESHDS_Mesh* aMesh = GetMeshDS();
4497 TNodeOfNodeListMap mapNewNodes;
4498 TElemOfVecOfNnlmiMap mapElemNewNodes;
4499 TElemOfElemListMap newElemsMap;
4501 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4502 myMesh->NbFaces(ORDER_QUADRATIC) +
4503 myMesh->NbVolumes(ORDER_QUADRATIC) );
4505 TIDSortedElemSet::iterator itElem;
4506 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4507 const SMDS_MeshElement* elem = *itElem;
4508 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4510 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4511 newNodesItVec.reserve( elem->NbNodes() );
4513 // loop on elem nodes
4514 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4515 while ( itN->more() )
4517 // check if a node has been already sweeped
4518 const SMDS_MeshNode* node = cast2Node( itN->next() );
4520 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4522 aXYZ.Coord( coord[0], coord[1], coord[2] );
4523 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4525 TNodeOfNodeListMapItr nIt =
4526 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4527 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4528 if ( listNewNodes.empty() )
4530 // check if we are to create medium nodes between corner ones
4531 bool needMediumNodes = false;
4532 if ( isQuadraticMesh )
4534 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4535 while (it->more() && !needMediumNodes )
4537 const SMDS_MeshElement* invElem = it->next();
4538 if ( invElem != elem && !theElems.count( invElem )) continue;
4539 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4540 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4541 needMediumNodes = true;
4546 const SMDS_MeshNode * newNode = node;
4547 for ( int i = 0; i < theNbSteps; i++ ) {
4549 if ( needMediumNodes ) // create a medium node
4551 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4552 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4553 myLastCreatedNodes.Append(newNode);
4554 srcNodes.Append( node );
4555 listNewNodes.push_back( newNode );
4556 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4559 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4561 // create a corner node
4562 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4563 myLastCreatedNodes.Append(newNode);
4564 srcNodes.Append( node );
4565 listNewNodes.push_back( newNode );
4568 listNewNodes.push_back( newNode );
4569 // if ( needMediumNodes )
4570 // listNewNodes.push_back( newNode );
4574 newNodesItVec.push_back( nIt );
4576 // make new elements
4577 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4581 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4583 PGroupIDs newGroupIDs;
4584 if ( theMakeGroups )
4585 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4591 //=======================================================================
4592 //function : CreateNode
4594 //=======================================================================
4595 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4598 const double tolnode,
4599 SMESH_SequenceOfNode& aNodes)
4601 // myLastCreatedElems.Clear();
4602 // myLastCreatedNodes.Clear();
4605 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4607 // try to search in sequence of existing nodes
4608 // if aNodes.Length()>0 we 'nave to use given sequence
4609 // else - use all nodes of mesh
4610 if(aNodes.Length()>0) {
4612 for(i=1; i<=aNodes.Length(); i++) {
4613 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4614 if(P1.Distance(P2)<tolnode)
4615 return aNodes.Value(i);
4619 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4620 while(itn->more()) {
4621 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4622 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4623 if(P1.Distance(P2)<tolnode)
4628 // create new node and return it
4629 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4630 //myLastCreatedNodes.Append(NewNode);
4635 //=======================================================================
4636 //function : ExtrusionSweep
4638 //=======================================================================
4640 SMESH_MeshEditor::PGroupIDs
4641 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4642 const gp_Vec& theStep,
4643 const int theNbSteps,
4644 TElemOfElemListMap& newElemsMap,
4645 const bool theMakeGroups,
4647 const double theTolerance)
4649 ExtrusParam aParams;
4650 aParams.myDir = gp_Dir(theStep);
4651 aParams.myNodes.Clear();
4652 aParams.mySteps = new TColStd_HSequenceOfReal;
4654 for(i=1; i<=theNbSteps; i++)
4655 aParams.mySteps->Append(theStep.Magnitude());
4658 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4662 //=======================================================================
4663 //function : ExtrusionSweep
4665 //=======================================================================
4667 SMESH_MeshEditor::PGroupIDs
4668 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4669 ExtrusParam& theParams,
4670 TElemOfElemListMap& newElemsMap,
4671 const bool theMakeGroups,
4673 const double theTolerance)
4675 myLastCreatedElems.Clear();
4676 myLastCreatedNodes.Clear();
4678 // source elements for each generated one
4679 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4681 SMESHDS_Mesh* aMesh = GetMeshDS();
4683 int nbsteps = theParams.mySteps->Length();
4685 TNodeOfNodeListMap mapNewNodes;
4686 //TNodeOfNodeVecMap mapNewNodes;
4687 TElemOfVecOfNnlmiMap mapElemNewNodes;
4688 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4690 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4691 myMesh->NbFaces(ORDER_QUADRATIC) +
4692 myMesh->NbVolumes(ORDER_QUADRATIC) );
4694 TIDSortedElemSet::iterator itElem;
4695 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4696 // check element type
4697 const SMDS_MeshElement* elem = *itElem;
4698 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4701 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4702 newNodesItVec.reserve( elem->NbNodes() );
4704 // loop on elem nodes
4705 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4706 while ( itN->more() )
4708 // check if a node has been already sweeped
4709 const SMDS_MeshNode* node = cast2Node( itN->next() );
4710 TNodeOfNodeListMap::iterator nIt =
4711 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4712 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4713 if ( listNewNodes.empty() )
4717 // check if we are to create medium nodes between corner ones
4718 bool needMediumNodes = false;
4719 if ( isQuadraticMesh )
4721 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4722 while (it->more() && !needMediumNodes )
4724 const SMDS_MeshElement* invElem = it->next();
4725 if ( invElem != elem && !theElems.count( invElem )) continue;
4726 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4727 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4728 needMediumNodes = true;
4732 double coord[] = { node->X(), node->Y(), node->Z() };
4733 for ( int i = 0; i < nbsteps; i++ )
4735 if ( needMediumNodes ) // create a medium node
4737 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4738 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4739 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4740 if( theFlags & EXTRUSION_FLAG_SEW ) {
4741 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4742 theTolerance, theParams.myNodes);
4743 listNewNodes.push_back( newNode );
4746 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4747 myLastCreatedNodes.Append(newNode);
4748 srcNodes.Append( node );
4749 listNewNodes.push_back( newNode );
4752 // create a corner node
4753 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4754 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4755 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4756 if( theFlags & EXTRUSION_FLAG_SEW ) {
4757 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4758 theTolerance, theParams.myNodes);
4759 listNewNodes.push_back( newNode );
4762 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4763 myLastCreatedNodes.Append(newNode);
4764 srcNodes.Append( node );
4765 listNewNodes.push_back( newNode );
4769 newNodesItVec.push_back( nIt );
4771 // make new elements
4772 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4775 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4776 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4778 PGroupIDs newGroupIDs;
4779 if ( theMakeGroups )
4780 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4785 //=======================================================================
4786 //function : ExtrusionAlongTrack
4788 //=======================================================================
4789 SMESH_MeshEditor::Extrusion_Error
4790 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4791 SMESH_subMesh* theTrack,
4792 const SMDS_MeshNode* theN1,
4793 const bool theHasAngles,
4794 list<double>& theAngles,
4795 const bool theLinearVariation,
4796 const bool theHasRefPoint,
4797 const gp_Pnt& theRefPoint,
4798 const bool theMakeGroups)
4800 MESSAGE("ExtrusionAlongTrack");
4801 myLastCreatedElems.Clear();
4802 myLastCreatedNodes.Clear();
4805 std::list<double> aPrms;
4806 TIDSortedElemSet::iterator itElem;
4809 TopoDS_Edge aTrackEdge;
4810 TopoDS_Vertex aV1, aV2;
4812 SMDS_ElemIteratorPtr aItE;
4813 SMDS_NodeIteratorPtr aItN;
4814 SMDSAbs_ElementType aTypeE;
4816 TNodeOfNodeListMap mapNewNodes;
4819 aNbE = theElements.size();
4822 return EXTR_NO_ELEMENTS;
4824 // 1.1 Track Pattern
4827 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4829 aItE = pSubMeshDS->GetElements();
4830 while ( aItE->more() ) {
4831 const SMDS_MeshElement* pE = aItE->next();
4832 aTypeE = pE->GetType();
4833 // Pattern must contain links only
4834 if ( aTypeE != SMDSAbs_Edge )
4835 return EXTR_PATH_NOT_EDGE;
4838 list<SMESH_MeshEditor_PathPoint> fullList;
4840 const TopoDS_Shape& aS = theTrack->GetSubShape();
4841 // Sub-shape for the Pattern must be an Edge or Wire
4842 if( aS.ShapeType() == TopAbs_EDGE ) {
4843 aTrackEdge = TopoDS::Edge( aS );
4844 // the Edge must not be degenerated
4845 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4846 return EXTR_BAD_PATH_SHAPE;
4847 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4848 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4849 const SMDS_MeshNode* aN1 = aItN->next();
4850 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4851 const SMDS_MeshNode* aN2 = aItN->next();
4852 // starting node must be aN1 or aN2
4853 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4854 return EXTR_BAD_STARTING_NODE;
4855 aItN = pSubMeshDS->GetNodes();
4856 while ( aItN->more() ) {
4857 const SMDS_MeshNode* pNode = aItN->next();
4858 const SMDS_EdgePosition* pEPos =
4859 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4860 double aT = pEPos->GetUParameter();
4861 aPrms.push_back( aT );
4863 //Extrusion_Error err =
4864 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4865 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4866 list< SMESH_subMesh* > LSM;
4867 TopTools_SequenceOfShape Edges;
4868 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4869 while(itSM->more()) {
4870 SMESH_subMesh* SM = itSM->next();
4872 const TopoDS_Shape& aS = SM->GetSubShape();
4875 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4876 int startNid = theN1->GetID();
4877 TColStd_MapOfInteger UsedNums;
4879 int NbEdges = Edges.Length();
4881 for(; i<=NbEdges; i++) {
4883 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4884 for(; itLSM!=LSM.end(); itLSM++) {
4886 if(UsedNums.Contains(k)) continue;
4887 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4888 SMESH_subMesh* locTrack = *itLSM;
4889 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4890 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4891 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4892 const SMDS_MeshNode* aN1 = aItN->next();
4893 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4894 const SMDS_MeshNode* aN2 = aItN->next();
4895 // starting node must be aN1 or aN2
4896 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4897 // 2. Collect parameters on the track edge
4899 aItN = locMeshDS->GetNodes();
4900 while ( aItN->more() ) {
4901 const SMDS_MeshNode* pNode = aItN->next();
4902 const SMDS_EdgePosition* pEPos =
4903 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4904 double aT = pEPos->GetUParameter();
4905 aPrms.push_back( aT );
4907 list<SMESH_MeshEditor_PathPoint> LPP;
4908 //Extrusion_Error err =
4909 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4910 LLPPs.push_back(LPP);
4912 // update startN for search following egde
4913 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4914 else startNid = aN1->GetID();
4918 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4919 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4920 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4921 for(; itPP!=firstList.end(); itPP++) {
4922 fullList.push_back( *itPP );
4924 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4925 fullList.pop_back();
4927 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4928 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4929 itPP = currList.begin();
4930 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4931 gp_Dir D1 = PP1.Tangent();
4932 gp_Dir D2 = PP2.Tangent();
4933 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4934 (D1.Z()+D2.Z())/2 ) );
4935 PP1.SetTangent(Dnew);
4936 fullList.push_back(PP1);
4938 for(; itPP!=firstList.end(); itPP++) {
4939 fullList.push_back( *itPP );
4941 PP1 = fullList.back();
4942 fullList.pop_back();
4944 // if wire not closed
4945 fullList.push_back(PP1);
4949 return EXTR_BAD_PATH_SHAPE;
4952 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4953 theHasRefPoint, theRefPoint, theMakeGroups);
4957 //=======================================================================
4958 //function : ExtrusionAlongTrack
4960 //=======================================================================
4961 SMESH_MeshEditor::Extrusion_Error
4962 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4963 SMESH_Mesh* theTrack,
4964 const SMDS_MeshNode* theN1,
4965 const bool theHasAngles,
4966 list<double>& theAngles,
4967 const bool theLinearVariation,
4968 const bool theHasRefPoint,
4969 const gp_Pnt& theRefPoint,
4970 const bool theMakeGroups)
4972 myLastCreatedElems.Clear();
4973 myLastCreatedNodes.Clear();
4976 std::list<double> aPrms;
4977 TIDSortedElemSet::iterator itElem;
4980 TopoDS_Edge aTrackEdge;
4981 TopoDS_Vertex aV1, aV2;
4983 SMDS_ElemIteratorPtr aItE;
4984 SMDS_NodeIteratorPtr aItN;
4985 SMDSAbs_ElementType aTypeE;
4987 TNodeOfNodeListMap mapNewNodes;
4990 aNbE = theElements.size();
4993 return EXTR_NO_ELEMENTS;
4995 // 1.1 Track Pattern
4998 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5000 aItE = pMeshDS->elementsIterator();
5001 while ( aItE->more() ) {
5002 const SMDS_MeshElement* pE = aItE->next();
5003 aTypeE = pE->GetType();
5004 // Pattern must contain links only
5005 if ( aTypeE != SMDSAbs_Edge )
5006 return EXTR_PATH_NOT_EDGE;
5009 list<SMESH_MeshEditor_PathPoint> fullList;
5011 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5013 if( aS == SMESH_Mesh::PseudoShape() ) {
5014 //Mesh without shape
5015 const SMDS_MeshNode* currentNode = NULL;
5016 const SMDS_MeshNode* prevNode = theN1;
5017 std::vector<const SMDS_MeshNode*> aNodesList;
5018 aNodesList.push_back(theN1);
5019 int nbEdges = 0, conn=0;
5020 const SMDS_MeshElement* prevElem = NULL;
5021 const SMDS_MeshElement* currentElem = NULL;
5022 int totalNbEdges = theTrack->NbEdges();
5023 SMDS_ElemIteratorPtr nIt;
5026 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5027 return EXTR_BAD_STARTING_NODE;
5030 conn = nbEdgeConnectivity(theN1);
5032 return EXTR_PATH_NOT_EDGE;
5034 aItE = theN1->GetInverseElementIterator();
5035 prevElem = aItE->next();
5036 currentElem = prevElem;
5038 if(totalNbEdges == 1 ) {
5039 nIt = currentElem->nodesIterator();
5040 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5041 if(currentNode == prevNode)
5042 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5043 aNodesList.push_back(currentNode);
5045 nIt = currentElem->nodesIterator();
5046 while( nIt->more() ) {
5047 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5048 if(currentNode == prevNode)
5049 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5050 aNodesList.push_back(currentNode);
5052 //case of the closed mesh
5053 if(currentNode == theN1) {
5058 conn = nbEdgeConnectivity(currentNode);
5060 return EXTR_PATH_NOT_EDGE;
5061 }else if( conn == 1 && nbEdges > 0 ) {
5066 prevNode = currentNode;
5067 aItE = currentNode->GetInverseElementIterator();
5068 currentElem = aItE->next();
5069 if( currentElem == prevElem)
5070 currentElem = aItE->next();
5071 nIt = currentElem->nodesIterator();
5072 prevElem = currentElem;
5078 if(nbEdges != totalNbEdges)
5079 return EXTR_PATH_NOT_EDGE;
5081 TopTools_SequenceOfShape Edges;
5082 double x1,x2,y1,y2,z1,z2;
5083 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5084 int startNid = theN1->GetID();
5085 for(int i = 1; i < aNodesList.size(); i++) {
5086 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5087 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5088 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5089 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5090 list<SMESH_MeshEditor_PathPoint> LPP;
5092 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5093 LLPPs.push_back(LPP);
5094 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5095 else startNid = aNodesList[i-1]->GetID();
5099 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5100 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5101 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5102 for(; itPP!=firstList.end(); itPP++) {
5103 fullList.push_back( *itPP );
5106 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5107 SMESH_MeshEditor_PathPoint PP2;
5108 fullList.pop_back();
5110 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5111 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5112 itPP = currList.begin();
5113 PP2 = currList.front();
5114 gp_Dir D1 = PP1.Tangent();
5115 gp_Dir D2 = PP2.Tangent();
5116 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5117 (D1.Z()+D2.Z())/2 ) );
5118 PP1.SetTangent(Dnew);
5119 fullList.push_back(PP1);
5121 for(; itPP!=currList.end(); itPP++) {
5122 fullList.push_back( *itPP );
5124 PP1 = fullList.back();
5125 fullList.pop_back();
5127 fullList.push_back(PP1);
5129 } // Sub-shape for the Pattern must be an Edge or Wire
5130 else if( aS.ShapeType() == TopAbs_EDGE ) {
5131 aTrackEdge = TopoDS::Edge( aS );
5132 // the Edge must not be degenerated
5133 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5134 return EXTR_BAD_PATH_SHAPE;
5135 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5136 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5137 const SMDS_MeshNode* aN1 = aItN->next();
5138 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5139 const SMDS_MeshNode* aN2 = aItN->next();
5140 // starting node must be aN1 or aN2
5141 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5142 return EXTR_BAD_STARTING_NODE;
5143 aItN = pMeshDS->nodesIterator();
5144 while ( aItN->more() ) {
5145 const SMDS_MeshNode* pNode = aItN->next();
5146 if( pNode==aN1 || pNode==aN2 ) continue;
5147 const SMDS_EdgePosition* pEPos =
5148 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5149 double aT = pEPos->GetUParameter();
5150 aPrms.push_back( aT );
5152 //Extrusion_Error err =
5153 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5155 else if( aS.ShapeType() == TopAbs_WIRE ) {
5156 list< SMESH_subMesh* > LSM;
5157 TopTools_SequenceOfShape Edges;
5158 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5159 for(; eExp.More(); eExp.Next()) {
5160 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5161 if( BRep_Tool::Degenerated(E) ) continue;
5162 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5168 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5169 int startNid = theN1->GetID();
5170 TColStd_MapOfInteger UsedNums;
5171 int NbEdges = Edges.Length();
5173 for(; i<=NbEdges; i++) {
5175 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5176 for(; itLSM!=LSM.end(); itLSM++) {
5178 if(UsedNums.Contains(k)) continue;
5179 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5180 SMESH_subMesh* locTrack = *itLSM;
5181 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5182 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5183 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5184 const SMDS_MeshNode* aN1 = aItN->next();
5185 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5186 const SMDS_MeshNode* aN2 = aItN->next();
5187 // starting node must be aN1 or aN2
5188 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5189 // 2. Collect parameters on the track edge
5191 aItN = locMeshDS->GetNodes();
5192 while ( aItN->more() ) {
5193 const SMDS_MeshNode* pNode = aItN->next();
5194 const SMDS_EdgePosition* pEPos =
5195 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5196 double aT = pEPos->GetUParameter();
5197 aPrms.push_back( aT );
5199 list<SMESH_MeshEditor_PathPoint> LPP;
5200 //Extrusion_Error err =
5201 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5202 LLPPs.push_back(LPP);
5204 // update startN for search following egde
5205 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5206 else startNid = aN1->GetID();
5210 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5211 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5212 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5213 for(; itPP!=firstList.end(); itPP++) {
5214 fullList.push_back( *itPP );
5216 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5217 fullList.pop_back();
5219 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5220 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5221 itPP = currList.begin();
5222 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5223 gp_Dir D1 = PP1.Tangent();
5224 gp_Dir D2 = PP2.Tangent();
5225 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5226 (D1.Z()+D2.Z())/2 ) );
5227 PP1.SetTangent(Dnew);
5228 fullList.push_back(PP1);
5230 for(; itPP!=currList.end(); itPP++) {
5231 fullList.push_back( *itPP );
5233 PP1 = fullList.back();
5234 fullList.pop_back();
5236 // if wire not closed
5237 fullList.push_back(PP1);
5241 return EXTR_BAD_PATH_SHAPE;
5244 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5245 theHasRefPoint, theRefPoint, theMakeGroups);
5249 //=======================================================================
5250 //function : MakeEdgePathPoints
5251 //purpose : auxilary for ExtrusionAlongTrack
5252 //=======================================================================
5253 SMESH_MeshEditor::Extrusion_Error
5254 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5255 const TopoDS_Edge& aTrackEdge,
5257 list<SMESH_MeshEditor_PathPoint>& LPP)
5259 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5261 aTolVec2=aTolVec*aTolVec;
5263 TopoDS_Vertex aV1, aV2;
5264 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5265 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5266 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5267 // 2. Collect parameters on the track edge
5268 aPrms.push_front( aT1 );
5269 aPrms.push_back( aT2 );
5272 if( FirstIsStart ) {
5283 SMESH_MeshEditor_PathPoint aPP;
5284 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5285 std::list<double>::iterator aItD = aPrms.begin();
5286 for(; aItD != aPrms.end(); ++aItD) {
5290 aC3D->D1( aT, aP3D, aVec );
5291 aL2 = aVec.SquareMagnitude();
5292 if ( aL2 < aTolVec2 )
5293 return EXTR_CANT_GET_TANGENT;
5294 gp_Dir aTgt( aVec );
5296 aPP.SetTangent( aTgt );
5297 aPP.SetParameter( aT );
5304 //=======================================================================
5305 //function : MakeExtrElements
5306 //purpose : auxilary for ExtrusionAlongTrack
5307 //=======================================================================
5308 SMESH_MeshEditor::Extrusion_Error
5309 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5310 list<SMESH_MeshEditor_PathPoint>& fullList,
5311 const bool theHasAngles,
5312 list<double>& theAngles,
5313 const bool theLinearVariation,
5314 const bool theHasRefPoint,
5315 const gp_Pnt& theRefPoint,
5316 const bool theMakeGroups)
5318 MESSAGE("MakeExtrElements");
5319 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5320 int aNbTP = fullList.size();
5321 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5323 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5324 LinearAngleVariation(aNbTP-1, theAngles);
5326 vector<double> aAngles( aNbTP );
5328 for(; j<aNbTP; ++j) {
5331 if ( theHasAngles ) {
5333 std::list<double>::iterator aItD = theAngles.begin();
5334 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5336 aAngles[j] = anAngle;
5339 // fill vector of path points with angles
5340 //aPPs.resize(fullList.size());
5342 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5343 for(; itPP!=fullList.end(); itPP++) {
5345 SMESH_MeshEditor_PathPoint PP = *itPP;
5346 PP.SetAngle(aAngles[j]);
5350 TNodeOfNodeListMap mapNewNodes;
5351 TElemOfVecOfNnlmiMap mapElemNewNodes;
5352 TElemOfElemListMap newElemsMap;
5353 TIDSortedElemSet::iterator itElem;
5356 SMDSAbs_ElementType aTypeE;
5357 // source elements for each generated one
5358 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5360 // 3. Center of rotation aV0
5361 gp_Pnt aV0 = theRefPoint;
5363 if ( !theHasRefPoint ) {
5365 aGC.SetCoord( 0.,0.,0. );
5367 itElem = theElements.begin();
5368 for ( ; itElem != theElements.end(); itElem++ ) {
5369 const SMDS_MeshElement* elem = *itElem;
5371 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5372 while ( itN->more() ) {
5373 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5378 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5379 list<const SMDS_MeshNode*> aLNx;
5380 mapNewNodes[node] = aLNx;
5382 gp_XYZ aXYZ( aX, aY, aZ );
5390 } // if (!theHasRefPoint) {
5391 mapNewNodes.clear();
5393 // 4. Processing the elements
5394 SMESHDS_Mesh* aMesh = GetMeshDS();
5396 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5397 // check element type
5398 const SMDS_MeshElement* elem = *itElem;
5399 aTypeE = elem->GetType();
5400 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5403 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5404 newNodesItVec.reserve( elem->NbNodes() );
5406 // loop on elem nodes
5408 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5409 while ( itN->more() )
5412 // check if a node has been already processed
5413 const SMDS_MeshNode* node =
5414 static_cast<const SMDS_MeshNode*>( itN->next() );
5415 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5416 if ( nIt == mapNewNodes.end() ) {
5417 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5418 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5421 aX = node->X(); aY = node->Y(); aZ = node->Z();
5423 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5424 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5425 gp_Ax1 anAx1, anAxT1T0;
5426 gp_Dir aDT1x, aDT0x, aDT1T0;
5431 aPN0.SetCoord(aX, aY, aZ);
5433 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5435 aDT0x= aPP0.Tangent();
5436 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5438 for ( j = 1; j < aNbTP; ++j ) {
5439 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5441 aDT1x = aPP1.Tangent();
5442 aAngle1x = aPP1.Angle();
5444 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5446 gp_Vec aV01x( aP0x, aP1x );
5447 aTrsf.SetTranslation( aV01x );
5450 aV1x = aV0x.Transformed( aTrsf );
5451 aPN1 = aPN0.Transformed( aTrsf );
5453 // rotation 1 [ T1,T0 ]
5454 aAngleT1T0=-aDT1x.Angle( aDT0x );
5455 if (fabs(aAngleT1T0) > aTolAng) {
5457 anAxT1T0.SetLocation( aV1x );
5458 anAxT1T0.SetDirection( aDT1T0 );
5459 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5461 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5465 if ( theHasAngles ) {
5466 anAx1.SetLocation( aV1x );
5467 anAx1.SetDirection( aDT1x );
5468 aTrsfRot.SetRotation( anAx1, aAngle1x );
5470 aPN1 = aPN1.Transformed( aTrsfRot );
5474 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5475 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5476 // create additional node
5477 double x = ( aPN1.X() + aPN0.X() )/2.;
5478 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5479 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5480 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5481 myLastCreatedNodes.Append(newNode);
5482 srcNodes.Append( node );
5483 listNewNodes.push_back( newNode );
5488 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5489 myLastCreatedNodes.Append(newNode);
5490 srcNodes.Append( node );
5491 listNewNodes.push_back( newNode );
5501 // if current elem is quadratic and current node is not medium
5502 // we have to check - may be it is needed to insert additional nodes
5503 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5504 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5505 if(listNewNodes.size()==aNbTP-1) {
5506 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5507 gp_XYZ P(node->X(), node->Y(), node->Z());
5508 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5510 for(i=0; i<aNbTP-1; i++) {
5511 const SMDS_MeshNode* N = *it;
5512 double x = ( N->X() + P.X() )/2.;
5513 double y = ( N->Y() + P.Y() )/2.;
5514 double z = ( N->Z() + P.Z() )/2.;
5515 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5516 srcNodes.Append( node );
5517 myLastCreatedNodes.Append(newN);
5520 P = gp_XYZ(N->X(),N->Y(),N->Z());
5522 listNewNodes.clear();
5523 for(i=0; i<2*(aNbTP-1); i++) {
5524 listNewNodes.push_back(aNodes[i]);
5530 newNodesItVec.push_back( nIt );
5532 // make new elements
5533 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5534 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5535 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5538 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5540 if ( theMakeGroups )
5541 generateGroups( srcNodes, srcElems, "extruded");
5547 //=======================================================================
5548 //function : LinearAngleVariation
5549 //purpose : auxilary for ExtrusionAlongTrack
5550 //=======================================================================
5551 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5552 list<double>& Angles)
5554 int nbAngles = Angles.size();
5555 if( nbSteps > nbAngles ) {
5556 vector<double> theAngles(nbAngles);
5557 list<double>::iterator it = Angles.begin();
5559 for(; it!=Angles.end(); it++) {
5561 theAngles[i] = (*it);
5564 double rAn2St = double( nbAngles ) / double( nbSteps );
5565 double angPrev = 0, angle;
5566 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5567 double angCur = rAn2St * ( iSt+1 );
5568 double angCurFloor = floor( angCur );
5569 double angPrevFloor = floor( angPrev );
5570 if ( angPrevFloor == angCurFloor )
5571 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5573 int iP = int( angPrevFloor );
5574 double angPrevCeil = ceil(angPrev);
5575 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5577 int iC = int( angCurFloor );
5578 if ( iC < nbAngles )
5579 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5581 iP = int( angPrevCeil );
5583 angle += theAngles[ iC ];
5585 res.push_back(angle);
5590 for(; it!=res.end(); it++)
5591 Angles.push_back( *it );
5596 //================================================================================
5598 * \brief Move or copy theElements applying theTrsf to their nodes
5599 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5600 * \param theTrsf - transformation to apply
5601 * \param theCopy - if true, create translated copies of theElems
5602 * \param theMakeGroups - if true and theCopy, create translated groups
5603 * \param theTargetMesh - mesh to copy translated elements into
5604 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5606 //================================================================================
5608 SMESH_MeshEditor::PGroupIDs
5609 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5610 const gp_Trsf& theTrsf,
5612 const bool theMakeGroups,
5613 SMESH_Mesh* theTargetMesh)
5615 myLastCreatedElems.Clear();
5616 myLastCreatedNodes.Clear();
5618 bool needReverse = false;
5619 string groupPostfix;
5620 switch ( theTrsf.Form() ) {
5622 MESSAGE("gp_PntMirror");
5624 groupPostfix = "mirrored";
5627 MESSAGE("gp_Ax1Mirror");
5628 groupPostfix = "mirrored";
5631 MESSAGE("gp_Ax2Mirror");
5633 groupPostfix = "mirrored";
5636 MESSAGE("gp_Rotation");
5637 groupPostfix = "rotated";
5639 case gp_Translation:
5640 MESSAGE("gp_Translation");
5641 groupPostfix = "translated";
5644 MESSAGE("gp_Scale");
5645 groupPostfix = "scaled";
5647 case gp_CompoundTrsf: // different scale by axis
5648 MESSAGE("gp_CompoundTrsf");
5649 groupPostfix = "scaled";
5653 needReverse = false;
5654 groupPostfix = "transformed";
5657 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5658 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5659 SMESHDS_Mesh* aMesh = GetMeshDS();
5662 // map old node to new one
5663 TNodeNodeMap nodeMap;
5665 // elements sharing moved nodes; those of them which have all
5666 // nodes mirrored but are not in theElems are to be reversed
5667 TIDSortedElemSet inverseElemSet;
5669 // source elements for each generated one
5670 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5672 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5673 TIDSortedElemSet orphanNode;
5675 if ( theElems.empty() ) // transform the whole mesh
5678 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5679 while ( eIt->more() ) theElems.insert( eIt->next() );
5681 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5682 while ( nIt->more() )
5684 const SMDS_MeshNode* node = nIt->next();
5685 if ( node->NbInverseElements() == 0)
5686 orphanNode.insert( node );
5690 // loop on elements to transform nodes : first orphan nodes then elems
5691 TIDSortedElemSet::iterator itElem;
5692 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5693 for (int i=0; i<2; i++)
5694 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5695 const SMDS_MeshElement* elem = *itElem;
5699 // loop on elem nodes
5700 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5701 while ( itN->more() ) {
5703 const SMDS_MeshNode* node = cast2Node( itN->next() );
5704 // check if a node has been already transformed
5705 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5706 nodeMap.insert( make_pair ( node, node ));
5707 if ( !n2n_isnew.second )
5711 coord[0] = node->X();
5712 coord[1] = node->Y();
5713 coord[2] = node->Z();
5714 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5715 if ( theTargetMesh ) {
5716 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5717 n2n_isnew.first->second = newNode;
5718 myLastCreatedNodes.Append(newNode);
5719 srcNodes.Append( node );
5721 else if ( theCopy ) {
5722 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5723 n2n_isnew.first->second = newNode;
5724 myLastCreatedNodes.Append(newNode);
5725 srcNodes.Append( node );
5728 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5729 // node position on shape becomes invalid
5730 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5731 ( SMDS_SpacePosition::originSpacePosition() );
5734 // keep inverse elements
5735 if ( !theCopy && !theTargetMesh && needReverse ) {
5736 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5737 while ( invElemIt->more() ) {
5738 const SMDS_MeshElement* iel = invElemIt->next();
5739 inverseElemSet.insert( iel );
5745 // either create new elements or reverse mirrored ones
5746 if ( !theCopy && !needReverse && !theTargetMesh )
5749 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5750 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5751 theElems.insert( *invElemIt );
5753 // Replicate or reverse elements
5755 std::vector<int> iForw;
5756 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5758 const SMDS_MeshElement* elem = *itElem;
5759 if ( !elem ) continue;
5761 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5762 int nbNodes = elem->NbNodes();
5763 if ( geomType == SMDSGeom_NONE ) continue; // node
5765 switch ( geomType ) {
5767 case SMDSGeom_POLYGON: // ---------------------- polygon
5769 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5771 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5772 while (itN->more()) {
5773 const SMDS_MeshNode* node =
5774 static_cast<const SMDS_MeshNode*>(itN->next());
5775 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5776 if (nodeMapIt == nodeMap.end())
5777 break; // not all nodes transformed
5779 // reverse mirrored faces and volumes
5780 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5782 poly_nodes[iNode] = (*nodeMapIt).second;
5786 if ( iNode != nbNodes )
5787 continue; // not all nodes transformed
5789 if ( theTargetMesh ) {
5790 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5791 srcElems.Append( elem );
5793 else if ( theCopy ) {
5794 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5795 srcElems.Append( elem );
5798 aMesh->ChangePolygonNodes(elem, poly_nodes);
5803 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5805 const SMDS_VtkVolume* aPolyedre =
5806 dynamic_cast<const SMDS_VtkVolume*>( elem );
5808 MESSAGE("Warning: bad volumic element");
5812 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5813 vector<int> quantities; quantities.reserve( nbNodes );
5815 bool allTransformed = true;
5816 int nbFaces = aPolyedre->NbFaces();
5817 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5818 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5819 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5820 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5821 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5822 if (nodeMapIt == nodeMap.end()) {
5823 allTransformed = false; // not all nodes transformed
5825 poly_nodes.push_back((*nodeMapIt).second);
5827 if ( needReverse && allTransformed )
5828 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5830 quantities.push_back(nbFaceNodes);
5832 if ( !allTransformed )
5833 continue; // not all nodes transformed
5835 if ( theTargetMesh ) {
5836 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5837 srcElems.Append( elem );
5839 else if ( theCopy ) {
5840 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5841 srcElems.Append( elem );
5844 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5849 case SMDSGeom_BALL: // -------------------- Ball
5851 if ( !theCopy && !theTargetMesh ) continue;
5853 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5854 if (nodeMapIt == nodeMap.end())
5855 continue; // not all nodes transformed
5857 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5858 if ( theTargetMesh ) {
5859 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5860 srcElems.Append( elem );
5863 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5864 srcElems.Append( elem );
5869 default: // ----------------------- Regular elements
5871 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5872 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5873 const std::vector<int>& i = needReverse ? iRev : iForw;
5875 // find transformed nodes
5876 vector<const SMDS_MeshNode*> nodes(nbNodes);
5878 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5879 while ( itN->more() ) {
5880 const SMDS_MeshNode* node =
5881 static_cast<const SMDS_MeshNode*>( itN->next() );
5882 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5883 if ( nodeMapIt == nodeMap.end() )
5884 break; // not all nodes transformed
5885 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5887 if ( iNode != nbNodes )
5888 continue; // not all nodes transformed
5890 if ( theTargetMesh ) {
5891 if ( SMDS_MeshElement* copy =
5892 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5893 myLastCreatedElems.Append( copy );
5894 srcElems.Append( elem );
5897 else if ( theCopy ) {
5898 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5899 srcElems.Append( elem );
5902 // reverse element as it was reversed by transformation
5904 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5906 } // switch ( geomType )
5908 } // loop on elements
5910 PGroupIDs newGroupIDs;
5912 if ( ( theMakeGroups && theCopy ) ||
5913 ( theMakeGroups && theTargetMesh ) )
5914 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5919 //=======================================================================
5921 * \brief Create groups of elements made during transformation
5922 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5923 * \param elemGens - elements making corresponding myLastCreatedElems
5924 * \param postfix - to append to names of new groups
5926 //=======================================================================
5928 SMESH_MeshEditor::PGroupIDs
5929 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5930 const SMESH_SequenceOfElemPtr& elemGens,
5931 const std::string& postfix,
5932 SMESH_Mesh* targetMesh)
5934 PGroupIDs newGroupIDs( new list<int> );
5935 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5937 // Sort existing groups by types and collect their names
5939 // to store an old group and a generated new one
5940 typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5941 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5942 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5944 set< string > groupNames;
5946 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5947 if ( !groupIt->more() ) return newGroupIDs;
5949 int newGroupID = mesh->GetGroupIds().back()+1;
5950 while ( groupIt->more() )
5952 SMESH_Group * group = groupIt->next();
5953 if ( !group ) continue;
5954 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5955 if ( !groupDS || groupDS->IsEmpty() ) continue;
5956 groupNames.insert( group->GetName() );
5957 groupDS->SetStoreName( group->GetName() );
5958 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5959 groupDS->GetType() );
5960 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5961 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5964 // Loop on nodes and elements to add them in new groups
5966 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5968 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5969 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5970 if ( gens.Length() != elems.Length() )
5971 throw SALOME_Exception(LOCALIZED("invalid args"));
5973 // loop on created elements
5974 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5976 const SMDS_MeshElement* sourceElem = gens( iElem );
5977 if ( !sourceElem ) {
5978 MESSAGE("generateGroups(): NULL source element");
5981 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5982 if ( groupsOldNew.empty() ) { // no groups of this type at all
5983 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5984 ++iElem; // skip all elements made by sourceElem
5987 // collect all elements made by sourceElem
5988 list< const SMDS_MeshElement* > resultElems;
5989 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5990 if ( resElem != sourceElem )
5991 resultElems.push_back( resElem );
5992 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5993 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5994 if ( resElem != sourceElem )
5995 resultElems.push_back( resElem );
5997 // add resultElems to groups made by ones the sourceElem belongs to
5998 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5999 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6001 SMESHDS_GroupBase* oldGroup = gOldNew->first;
6002 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6004 // fill in a new group
6005 SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6006 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6007 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6008 newGroup.Add( *resElemIt );
6011 } // loop on created elements
6012 }// loop on nodes and elements
6014 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6016 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6018 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6019 SMESHDS_Group* newGroupDS = orderedOldNewGroups[i]->second;
6020 if ( newGroupDS->IsEmpty() )
6022 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6027 string name = oldGroupDS->GetStoreName();
6028 if ( !targetMesh ) {
6032 while ( !groupNames.insert( name ).second ) // name exists
6033 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6035 newGroupDS->SetStoreName( name.c_str() );
6037 // make a SMESH_Groups
6038 mesh->AddGroup( newGroupDS );
6039 newGroupIDs->push_back( newGroupDS->GetID() );
6042 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6049 //================================================================================
6051 * \brief Return list of group of nodes close to each other within theTolerance
6052 * Search among theNodes or in the whole mesh if theNodes is empty using
6053 * an Octree algorithm
6055 //================================================================================
6057 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6058 const double theTolerance,
6059 TListOfListOfNodes & theGroupsOfNodes)
6061 myLastCreatedElems.Clear();
6062 myLastCreatedNodes.Clear();
6064 if ( theNodes.empty() )
6065 { // get all nodes in the mesh
6066 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6067 while ( nIt->more() )
6068 theNodes.insert( theNodes.end(),nIt->next());
6071 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6075 //=======================================================================
6077 * \brief Implementation of search for the node closest to point
6079 //=======================================================================
6081 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6083 //---------------------------------------------------------------------
6085 * \brief Constructor
6087 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6089 myMesh = ( SMESHDS_Mesh* ) theMesh;
6091 TIDSortedNodeSet nodes;
6093 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6094 while ( nIt->more() )
6095 nodes.insert( nodes.end(), nIt->next() );
6097 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6099 // get max size of a leaf box
6100 SMESH_OctreeNode* tree = myOctreeNode;
6101 while ( !tree->isLeaf() )
6103 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6107 myHalfLeafSize = tree->maxSize() / 2.;
6110 //---------------------------------------------------------------------
6112 * \brief Move node and update myOctreeNode accordingly
6114 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6116 myOctreeNode->UpdateByMoveNode( node, toPnt );
6117 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6120 //---------------------------------------------------------------------
6122 * \brief Do it's job
6124 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6126 map<double, const SMDS_MeshNode*> dist2Nodes;
6127 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6128 if ( !dist2Nodes.empty() )
6129 return dist2Nodes.begin()->second;
6130 list<const SMDS_MeshNode*> nodes;
6131 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6133 double minSqDist = DBL_MAX;
6134 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6136 // sort leafs by their distance from thePnt
6137 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6138 TDistTreeMap treeMap;
6139 list< SMESH_OctreeNode* > treeList;
6140 list< SMESH_OctreeNode* >::iterator trIt;
6141 treeList.push_back( myOctreeNode );
6143 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6144 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6145 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6147 SMESH_OctreeNode* tree = *trIt;
6148 if ( !tree->isLeaf() ) // put children to the queue
6150 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6151 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6152 while ( cIt->more() )
6153 treeList.push_back( cIt->next() );
6155 else if ( tree->NbNodes() ) // put a tree to the treeMap
6157 const Bnd_B3d& box = *tree->getBox();
6158 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6159 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6160 if ( !it_in.second ) // not unique distance to box center
6161 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6164 // find distance after which there is no sense to check tree's
6165 double sqLimit = DBL_MAX;
6166 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6167 if ( treeMap.size() > 5 ) {
6168 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6169 const Bnd_B3d& box = *closestTree->getBox();
6170 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6171 sqLimit = limit * limit;
6173 // get all nodes from trees
6174 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6175 if ( sqDist_tree->first > sqLimit )
6177 SMESH_OctreeNode* tree = sqDist_tree->second;
6178 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6181 // find closest among nodes
6182 minSqDist = DBL_MAX;
6183 const SMDS_MeshNode* closestNode = 0;
6184 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6185 for ( ; nIt != nodes.end(); ++nIt ) {
6186 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6187 if ( minSqDist > sqDist ) {
6195 //---------------------------------------------------------------------
6199 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6201 //---------------------------------------------------------------------
6203 * \brief Return the node tree
6205 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6208 SMESH_OctreeNode* myOctreeNode;
6209 SMESHDS_Mesh* myMesh;
6210 double myHalfLeafSize; // max size of a leaf box
6213 //=======================================================================
6215 * \brief Return SMESH_NodeSearcher
6217 //=======================================================================
6219 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6221 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6224 // ========================================================================
6225 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6227 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6228 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6229 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6231 //=======================================================================
6233 * \brief Octal tree of bounding boxes of elements
6235 //=======================================================================
6237 class ElementBndBoxTree : public SMESH_Octree
6241 ElementBndBoxTree(const SMDS_Mesh& mesh,
6242 SMDSAbs_ElementType elemType,
6243 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6244 double tolerance = NodeRadius );
6245 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6246 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6247 void getElementsInSphere ( const gp_XYZ& center,
6248 const double radius, TIDSortedElemSet& foundElems);
6249 size_t getSize() { return std::max( _size, _elements.size() ); }
6250 ~ElementBndBoxTree();
6253 ElementBndBoxTree():_size(0) {}
6254 SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6255 void buildChildrenData();
6256 Bnd_B3d* buildRootBox();
6258 //!< Bounding box of element
6259 struct ElementBox : public Bnd_B3d
6261 const SMDS_MeshElement* _element;
6262 int _refCount; // an ElementBox can be included in several tree branches
6263 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6265 vector< ElementBox* > _elements;
6269 //================================================================================
6271 * \brief ElementBndBoxTree creation
6273 //================================================================================
6275 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6276 :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6278 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6279 _elements.reserve( nbElems );
6281 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6282 while ( elemIt->more() )
6283 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6288 //================================================================================
6292 //================================================================================
6294 ElementBndBoxTree::~ElementBndBoxTree()
6296 for ( int i = 0; i < _elements.size(); ++i )
6297 if ( --_elements[i]->_refCount <= 0 )
6298 delete _elements[i];
6301 //================================================================================
6303 * \brief Return the maximal box
6305 //================================================================================
6307 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6309 Bnd_B3d* box = new Bnd_B3d;
6310 for ( int i = 0; i < _elements.size(); ++i )
6311 box->Add( *_elements[i] );
6315 //================================================================================
6317 * \brief Redistrubute element boxes among children
6319 //================================================================================
6321 void ElementBndBoxTree::buildChildrenData()
6323 for ( int i = 0; i < _elements.size(); ++i )
6325 for (int j = 0; j < 8; j++)
6327 if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6329 _elements[i]->_refCount++;
6330 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6333 _elements[i]->_refCount--;
6335 _size = _elements.size();
6336 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6338 for (int j = 0; j < 8; j++)
6340 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6341 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6342 child->myIsLeaf = true;
6344 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6345 SMESHUtils::CompactVector( child->_elements );
6349 //================================================================================
6351 * \brief Return elements which can include the point
6353 //================================================================================
6355 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6356 TIDSortedElemSet& foundElems)
6358 if ( getBox()->IsOut( point.XYZ() ))
6363 for ( int i = 0; i < _elements.size(); ++i )
6364 if ( !_elements[i]->IsOut( point.XYZ() ))
6365 foundElems.insert( _elements[i]->_element );
6369 for (int i = 0; i < 8; i++)
6370 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6374 //================================================================================
6376 * \brief Return elements which can be intersected by the line
6378 //================================================================================
6380 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6381 TIDSortedElemSet& foundElems)
6383 if ( getBox()->IsOut( line ))
6388 for ( int i = 0; i < _elements.size(); ++i )
6389 if ( !_elements[i]->IsOut( line ))
6390 foundElems.insert( _elements[i]->_element );
6394 for (int i = 0; i < 8; i++)
6395 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6399 //================================================================================
6401 * \brief Return elements from leaves intersecting the sphere
6403 //================================================================================
6405 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6406 const double radius,
6407 TIDSortedElemSet& foundElems)
6409 if ( getBox()->IsOut( center, radius ))
6414 for ( int i = 0; i < _elements.size(); ++i )
6415 if ( !_elements[i]->IsOut( center, radius ))
6416 foundElems.insert( _elements[i]->_element );
6420 for (int i = 0; i < 8; i++)
6421 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6425 //================================================================================
6427 * \brief Construct the element box
6429 //================================================================================
6431 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6435 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6436 while ( nIt->more() )
6437 Add( SMESH_TNodeXYZ( nIt->next() ));
6438 Enlarge( tolerance );
6443 //=======================================================================
6445 * \brief Implementation of search for the elements by point and
6446 * of classification of point in 2D mesh
6448 //=======================================================================
6450 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6452 SMESHDS_Mesh* _mesh;
6453 SMDS_ElemIteratorPtr _meshPartIt;
6454 ElementBndBoxTree* _ebbTree;
6455 SMESH_NodeSearcherImpl* _nodeSearcher;
6456 SMDSAbs_ElementType _elementType;
6458 bool _outerFacesFound;
6459 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6461 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6462 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6463 ~SMESH_ElementSearcherImpl()
6465 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6466 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6468 virtual int FindElementsByPoint(const gp_Pnt& point,
6469 SMDSAbs_ElementType type,
6470 vector< const SMDS_MeshElement* >& foundElements);
6471 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6472 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6473 SMDSAbs_ElementType type );
6475 void GetElementsNearLine( const gp_Ax1& line,
6476 SMDSAbs_ElementType type,
6477 vector< const SMDS_MeshElement* >& foundElems);
6478 double getTolerance();
6479 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6480 const double tolerance, double & param);
6481 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6482 bool isOuterBoundary(const SMDS_MeshElement* face) const
6484 return _outerFaces.empty() || _outerFaces.count(face);
6486 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6488 const SMDS_MeshElement* _face;
6490 bool _coincides; //!< the line lays in face plane
6491 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6492 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6494 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6497 TIDSortedElemSet _faces;
6498 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6499 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6503 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6505 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6506 << ", _coincides="<<i._coincides << ")";
6509 //=======================================================================
6511 * \brief define tolerance for search
6513 //=======================================================================
6515 double SMESH_ElementSearcherImpl::getTolerance()
6517 if ( _tolerance < 0 )
6519 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6522 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6524 double boxSize = _nodeSearcher->getTree()->maxSize();
6525 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6527 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6529 double boxSize = _ebbTree->maxSize();
6530 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6532 if ( _tolerance == 0 )
6534 // define tolerance by size of a most complex element
6535 int complexType = SMDSAbs_Volume;
6536 while ( complexType > SMDSAbs_All &&
6537 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6539 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6541 if ( complexType == int( SMDSAbs_Node ))
6543 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6545 if ( meshInfo.NbNodes() > 2 )
6546 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6550 SMDS_ElemIteratorPtr elemIt =
6551 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6552 const SMDS_MeshElement* elem = elemIt->next();
6553 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6554 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6556 while ( nodeIt->more() )
6558 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6559 elemSize = max( dist, elemSize );
6562 _tolerance = 1e-4 * elemSize;
6568 //================================================================================
6570 * \brief Find intersection of the line and an edge of face and return parameter on line
6572 //================================================================================
6574 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6575 const SMDS_MeshElement* face,
6582 GeomAPI_ExtremaCurveCurve anExtCC;
6583 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6585 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6586 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6588 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6589 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6590 anExtCC.Init( lineCurve, edge);
6591 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6593 Quantity_Parameter pl, pe;
6594 anExtCC.LowerDistanceParameters( pl, pe );
6596 if ( ++nbInts == 2 )
6600 if ( nbInts > 0 ) param /= nbInts;
6603 //================================================================================
6605 * \brief Find all faces belonging to the outer boundary of mesh
6607 //================================================================================
6609 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6611 if ( _outerFacesFound ) return;
6613 // Collect all outer faces by passing from one outer face to another via their links
6614 // and BTW find out if there are internal faces at all.
6616 // checked links and links where outer boundary meets internal one
6617 set< SMESH_TLink > visitedLinks, seamLinks;
6619 // links to treat with already visited faces sharing them
6620 list < TFaceLink > startLinks;
6622 // load startLinks with the first outerFace
6623 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6624 _outerFaces.insert( outerFace );
6626 TIDSortedElemSet emptySet;
6627 while ( !startLinks.empty() )
6629 const SMESH_TLink& link = startLinks.front()._link;
6630 TIDSortedElemSet& faces = startLinks.front()._faces;
6632 outerFace = *faces.begin();
6633 // find other faces sharing the link
6634 const SMDS_MeshElement* f;
6635 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6638 // select another outer face among the found
6639 const SMDS_MeshElement* outerFace2 = 0;
6640 if ( faces.size() == 2 )
6642 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6644 else if ( faces.size() > 2 )
6646 seamLinks.insert( link );
6648 // link direction within the outerFace
6649 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6650 SMESH_TNodeXYZ( link.node2()));
6651 int i1 = outerFace->GetNodeIndex( link.node1() );
6652 int i2 = outerFace->GetNodeIndex( link.node2() );
6653 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6654 if ( rev ) n1n2.Reverse();
6656 gp_XYZ ofNorm, fNorm;
6657 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6659 // direction from the link inside outerFace
6660 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6661 // sort all other faces by angle with the dirInOF
6662 map< double, const SMDS_MeshElement* > angle2Face;
6663 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6664 for ( ; face != faces.end(); ++face )
6666 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6668 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6669 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6670 if ( angle < 0 ) angle += 2. * M_PI;
6671 angle2Face.insert( make_pair( angle, *face ));
6673 if ( !angle2Face.empty() )
6674 outerFace2 = angle2Face.begin()->second;
6677 // store the found outer face and add its links to continue seaching from
6680 _outerFaces.insert( outerFace );
6681 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6682 for ( int i = 0; i < nbNodes; ++i )
6684 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6685 if ( visitedLinks.insert( link2 ).second )
6686 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6689 startLinks.pop_front();
6691 _outerFacesFound = true;
6693 if ( !seamLinks.empty() )
6695 // There are internal boundaries touching the outher one,
6696 // find all faces of internal boundaries in order to find
6697 // faces of boundaries of holes, if any.
6702 _outerFaces.clear();
6706 //=======================================================================
6708 * \brief Find elements of given type where the given point is IN or ON.
6709 * Returns nb of found elements and elements them-selves.
6711 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6713 //=======================================================================
6715 int SMESH_ElementSearcherImpl::
6716 FindElementsByPoint(const gp_Pnt& point,
6717 SMDSAbs_ElementType type,
6718 vector< const SMDS_MeshElement* >& foundElements)
6720 foundElements.clear();
6722 double tolerance = getTolerance();
6724 // =================================================================================
6725 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6727 if ( !_nodeSearcher )
6728 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6730 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6731 if ( !closeNode ) return foundElements.size();
6733 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6734 return foundElements.size(); // to far from any node
6736 if ( type == SMDSAbs_Node )
6738 foundElements.push_back( closeNode );
6742 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6743 while ( elemIt->more() )
6744 foundElements.push_back( elemIt->next() );
6747 // =================================================================================
6748 else // elements more complex than 0D
6750 if ( !_ebbTree || _elementType != type )
6752 if ( _ebbTree ) delete _ebbTree;
6753 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6755 TIDSortedElemSet suspectElems;
6756 _ebbTree->getElementsNearPoint( point, suspectElems );
6757 TIDSortedElemSet::iterator elem = suspectElems.begin();
6758 for ( ; elem != suspectElems.end(); ++elem )
6759 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6760 foundElements.push_back( *elem );
6762 return foundElements.size();
6765 //=======================================================================
6767 * \brief Find an element of given type most close to the given point
6769 * WARNING: Only face search is implemeneted so far
6771 //=======================================================================
6773 const SMDS_MeshElement*
6774 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6775 SMDSAbs_ElementType type )
6777 const SMDS_MeshElement* closestElem = 0;
6779 if ( type == SMDSAbs_Face )
6781 if ( !_ebbTree || _elementType != type )
6783 if ( _ebbTree ) delete _ebbTree;
6784 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6786 TIDSortedElemSet suspectElems;
6787 _ebbTree->getElementsNearPoint( point, suspectElems );
6789 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6791 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6792 _ebbTree->getBox()->CornerMax() );
6794 if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6795 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6797 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6798 while ( suspectElems.empty() )
6800 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6804 double minDist = std::numeric_limits<double>::max();
6805 multimap< double, const SMDS_MeshElement* > dist2face;
6806 TIDSortedElemSet::iterator elem = suspectElems.begin();
6807 for ( ; elem != suspectElems.end(); ++elem )
6809 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6811 if ( dist < minDist + 1e-10)
6814 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6817 if ( !dist2face.empty() )
6819 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6820 closestElem = d2f->second;
6821 // if there are several elements at the same distance, select one
6822 // with GC closest to the point
6823 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6824 double minDistToGC = 0;
6825 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6827 if ( minDistToGC == 0 )
6830 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6831 TXyzIterator(), gc ) / closestElem->NbNodes();
6832 minDistToGC = point.SquareDistance( gc );
6835 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6836 TXyzIterator(), gc ) / d2f->second->NbNodes();
6837 double d = point.SquareDistance( gc );
6838 if ( d < minDistToGC )
6841 closestElem = d2f->second;
6844 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6845 // <<closestElem->GetID() << " DIST " << minDist << endl;
6850 // NOT IMPLEMENTED SO FAR
6856 //================================================================================
6858 * \brief Classify the given point in the closed 2D mesh
6860 //================================================================================
6862 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6864 double tolerance = getTolerance();
6865 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6867 if ( _ebbTree ) delete _ebbTree;
6868 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6870 // Algo: analyse transition of a line starting at the point through mesh boundary;
6871 // try three lines parallel to axis of the coordinate system and perform rough
6872 // analysis. If solution is not clear perform thorough analysis.
6874 const int nbAxes = 3;
6875 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6876 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6877 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6878 multimap< int, int > nbInt2Axis; // to find the simplest case
6879 for ( int axis = 0; axis < nbAxes; ++axis )
6881 gp_Ax1 lineAxis( point, axisDir[axis]);
6882 gp_Lin line ( lineAxis );
6884 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6885 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6887 // Intersect faces with the line
6889 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6890 TIDSortedElemSet::iterator face = suspectFaces.begin();
6891 for ( ; face != suspectFaces.end(); ++face )
6895 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6896 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6898 // perform intersection
6899 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6900 if ( !intersection.IsDone() )
6902 if ( intersection.IsInQuadric() )
6904 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6906 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6908 gp_Pnt intersectionPoint = intersection.Point(1);
6909 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6910 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6913 // Analyse intersections roughly
6915 int nbInter = u2inters.size();
6919 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6920 if ( nbInter == 1 ) // not closed mesh
6921 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6923 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6926 if ( (f<0) == (l<0) )
6929 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6930 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6931 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6934 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6936 if ( _outerFacesFound ) break; // pass to thorough analysis
6938 } // three attempts - loop on CS axes
6940 // Analyse intersections thoroughly.
6941 // We make two loops maximum, on the first one we only exclude touching intersections,
6942 // on the second, if situation is still unclear, we gather and use information on
6943 // position of faces (internal or outer). If faces position is already gathered,
6944 // we make the second loop right away.
6946 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6948 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6949 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6951 int axis = nb_axis->second;
6952 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6954 gp_Ax1 lineAxis( point, axisDir[axis]);
6955 gp_Lin line ( lineAxis );
6957 // add tangent intersections to u2inters
6959 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6960 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6961 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6962 u2inters.insert(make_pair( param, *tgtInt ));
6963 tangentInters[ axis ].clear();
6965 // Count intersections before and after the point excluding touching ones.
6966 // If hasPositionInfo we count intersections of outer boundary only
6968 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6969 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6970 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6971 bool ok = ! u_int1->second._coincides;
6972 while ( ok && u_int1 != u2inters.end() )
6974 double u = u_int1->first;
6975 bool touchingInt = false;
6976 if ( ++u_int2 != u2inters.end() )
6978 // skip intersections at the same point (if the line passes through edge or node)
6980 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6986 // skip tangent intersections
6988 const SMDS_MeshElement* prevFace = u_int1->second._face;
6989 while ( ok && u_int2->second._coincides )
6991 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6997 ok = ( u_int2 != u2inters.end() );
7002 // skip intersections at the same point after tangent intersections
7005 double u2 = u_int2->first;
7007 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7013 // decide if we skipped a touching intersection
7014 if ( nbSamePnt + nbTgt > 0 )
7016 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7017 map< double, TInters >::iterator u_int = u_int1;
7018 for ( ; u_int != u_int2; ++u_int )
7020 if ( u_int->second._coincides ) continue;
7021 double dot = u_int->second._faceNorm * line.Direction();
7022 if ( dot > maxDot ) maxDot = dot;
7023 if ( dot < minDot ) minDot = dot;
7025 touchingInt = ( minDot*maxDot < 0 );
7030 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7041 u_int1 = u_int2; // to next intersection
7043 } // loop on intersections with one line
7047 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7050 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7053 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7054 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7056 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7059 if ( (f<0) == (l<0) )
7062 if ( hasPositionInfo )
7063 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7065 } // loop on intersections of the tree lines - thorough analysis
7067 if ( !hasPositionInfo )
7069 // gather info on faces position - is face in the outer boundary or not
7070 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7071 findOuterBoundary( u2inters.begin()->second._face );
7074 } // two attempts - with and w/o faces position info in the mesh
7076 return TopAbs_UNKNOWN;
7079 //=======================================================================
7081 * \brief Return elements possibly intersecting the line
7083 //=======================================================================
7085 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7086 SMDSAbs_ElementType type,
7087 vector< const SMDS_MeshElement* >& foundElems)
7089 if ( !_ebbTree || _elementType != type )
7091 if ( _ebbTree ) delete _ebbTree;
7092 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7094 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7095 _ebbTree->getElementsNearLine( line, suspectFaces );
7096 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7099 //=======================================================================
7101 * \brief Return SMESH_ElementSearcher
7103 //=======================================================================
7105 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7107 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7110 //=======================================================================
7112 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7114 //=======================================================================
7116 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7118 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7121 //=======================================================================
7123 * \brief Return true if the point is IN or ON of the element
7125 //=======================================================================
7127 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7129 if ( element->GetType() == SMDSAbs_Volume)
7131 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7134 // get ordered nodes
7136 vector< gp_XYZ > xyz;
7137 vector<const SMDS_MeshNode*> nodeList;
7139 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7140 if ( element->IsQuadratic() ) {
7141 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7142 nodeIt = f->interlacedNodesElemIterator();
7143 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7144 nodeIt = e->interlacedNodesElemIterator();
7146 while ( nodeIt->more() )
7148 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7149 xyz.push_back( SMESH_TNodeXYZ(node) );
7150 nodeList.push_back(node);
7153 int i, nbNodes = element->NbNodes();
7155 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7157 // compute face normal
7158 gp_Vec faceNorm(0,0,0);
7159 xyz.push_back( xyz.front() );
7160 nodeList.push_back( nodeList.front() );
7161 for ( i = 0; i < nbNodes; ++i )
7163 gp_Vec edge1( xyz[i+1], xyz[i]);
7164 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7165 faceNorm += edge1 ^ edge2;
7167 double normSize = faceNorm.Magnitude();
7168 if ( normSize <= tol )
7170 // degenerated face: point is out if it is out of all face edges
7171 for ( i = 0; i < nbNodes; ++i )
7173 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7174 if ( !IsOut( &edge, point, tol ))
7179 faceNorm /= normSize;
7181 // check if the point lays on face plane
7182 gp_Vec n2p( xyz[0], point );
7183 if ( fabs( n2p * faceNorm ) > tol )
7184 return true; // not on face plane
7186 // check if point is out of face boundary:
7187 // define it by closest transition of a ray point->infinity through face boundary
7188 // on the face plane.
7189 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7190 // to find intersections of the ray with the boundary.
7192 gp_Vec plnNorm = ray ^ faceNorm;
7193 normSize = plnNorm.Magnitude();
7194 if ( normSize <= tol ) return false; // point coincides with the first node
7195 plnNorm /= normSize;
7196 // for each node of the face, compute its signed distance to the plane
7197 vector<double> dist( nbNodes + 1);
7198 for ( i = 0; i < nbNodes; ++i )
7200 gp_Vec n2p( xyz[i], point );
7201 dist[i] = n2p * plnNorm;
7203 dist.back() = dist.front();
7204 // find the closest intersection
7206 double rClosest, distClosest = 1e100;;
7208 for ( i = 0; i < nbNodes; ++i )
7211 if ( fabs( dist[i]) < tol )
7213 else if ( fabs( dist[i+1]) < tol )
7215 else if ( dist[i] * dist[i+1] < 0 )
7216 r = dist[i] / ( dist[i] - dist[i+1] );
7218 continue; // no intersection
7219 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7220 gp_Vec p2int ( point, pInt);
7221 if ( p2int * ray > -tol ) // right half-space
7223 double intDist = p2int.SquareMagnitude();
7224 if ( intDist < distClosest )
7229 distClosest = intDist;
7234 return true; // no intesections - out
7236 // analyse transition
7237 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7238 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7239 gp_Vec p2int ( point, pClosest );
7240 bool out = (edgeNorm * p2int) < -tol;
7241 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7244 // ray pass through a face node; analyze transition through an adjacent edge
7245 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7246 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7247 gp_Vec edgeAdjacent( p1, p2 );
7248 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7249 bool out2 = (edgeNorm2 * p2int) < -tol;
7251 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7252 return covexCorner ? (out || out2) : (out && out2);
7254 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7256 // point is out of edge if it is NOT ON any straight part of edge
7257 // (we consider quadratic edge as being composed of two straight parts)
7258 for ( i = 1; i < nbNodes; ++i )
7260 gp_Vec edge( xyz[i-1], xyz[i]);
7261 gp_Vec n1p ( xyz[i-1], point);
7262 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7265 gp_Vec n2p( xyz[i], point );
7266 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7268 return false; // point is ON this part
7272 // Node or 0D element -------------------------------------------------------------------------
7274 gp_Vec n2p ( xyz[0], point );
7275 return n2p.Magnitude() <= tol;
7280 //=======================================================================
7284 // Position of a point relative to a segment
7288 // VERTEX 1 o----ON-----> VERTEX 2
7292 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7293 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7297 int _index; // index of vertex or segment
7299 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7300 bool operator < (const PointPos& other ) const
7302 if ( _name == other._name )
7303 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7304 return _name < other._name;
7308 //================================================================================
7310 * \brief Return of a point relative to a segment
7311 * \param point2D - the point to analyze position of
7312 * \param xyVec - end points of segments
7313 * \param index0 - 0-based index of the first point of segment
7314 * \param posToFindOut - flags of positions to detect
7315 * \retval PointPos - point position
7317 //================================================================================
7319 PointPos getPointPosition( const gp_XY& point2D,
7320 const gp_XY* segEnds,
7321 const int index0 = 0,
7322 const int posToFindOut = POS_ALL)
7324 const gp_XY& p1 = segEnds[ index0 ];
7325 const gp_XY& p2 = segEnds[ index0+1 ];
7326 const gp_XY grad = p2 - p1;
7328 if ( posToFindOut & POS_VERTEX )
7330 // check if the point2D is at "vertex 1" zone
7331 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7332 p1.Y() + grad.X() ) };
7333 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7334 return PointPos( POS_VERTEX, index0 );
7336 // check if the point2D is at "vertex 2" zone
7337 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7338 p2.Y() + grad.X() ) };
7339 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7340 return PointPos( POS_VERTEX, index0 + 1);
7342 double edgeEquation =
7343 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7344 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7348 //=======================================================================
7350 * \brief Return minimal distance from a point to a face
7352 * Currently we ignore non-planarity and 2nd order of face
7354 //=======================================================================
7356 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7357 const gp_Pnt& point )
7359 double badDistance = -1;
7360 if ( !face ) return badDistance;
7362 // coordinates of nodes (medium nodes, if any, ignored)
7363 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7364 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7365 xyz.resize( face->NbCornerNodes()+1 );
7367 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7368 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7370 gp_Vec OZ ( xyz[0], xyz[1] );
7371 gp_Vec OX ( xyz[0], xyz[2] );
7372 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7374 if ( xyz.size() < 4 ) return badDistance;
7375 OZ = gp_Vec ( xyz[0], xyz[2] );
7376 OX = gp_Vec ( xyz[0], xyz[3] );
7380 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7382 catch ( Standard_Failure ) {
7385 trsf.SetTransformation( tgtCS );
7387 // move all the nodes to 2D
7388 vector<gp_XY> xy( xyz.size() );
7389 for ( size_t i = 0;i < xyz.size()-1; ++i )
7391 gp_XYZ p3d = xyz[i];
7392 trsf.Transforms( p3d );
7393 xy[i].SetCoord( p3d.X(), p3d.Z() );
7395 xyz.back() = xyz.front();
7396 xy.back() = xy.front();
7398 // // move the point in 2D
7399 gp_XYZ tmpPnt = point.XYZ();
7400 trsf.Transforms( tmpPnt );
7401 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7403 // loop on segments of the face to analyze point position ralative to the face
7404 set< PointPos > pntPosSet;
7405 for ( size_t i = 1; i < xy.size(); ++i )
7407 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7408 pntPosSet.insert( pos );
7412 PointPos pos = *pntPosSet.begin();
7413 // cout << "Face " << face->GetID() << " DIST: ";
7414 switch ( pos._name )
7417 // point is most close to a segment
7418 gp_Vec p0p1( point, xyz[ pos._index ] );
7419 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7421 double projDist = p0p1 * p1p2; // distance projected to the segment
7422 gp_Vec projVec = p1p2 * projDist;
7423 gp_Vec distVec = p0p1 - projVec;
7424 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7425 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7426 return distVec.Magnitude();
7429 // point is inside the face
7430 double distToFacePlane = tmpPnt.Y();
7431 // cout << distToFacePlane << ", INSIDE " << endl;
7432 return Abs( distToFacePlane );
7435 // point is most close to a node
7436 gp_Vec distVec( point, xyz[ pos._index ]);
7437 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7438 return distVec.Magnitude();
7444 //=======================================================================
7445 //function : SimplifyFace
7447 //=======================================================================
7448 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7449 vector<const SMDS_MeshNode *>& poly_nodes,
7450 vector<int>& quantities) const
7452 int nbNodes = faceNodes.size();
7457 set<const SMDS_MeshNode*> nodeSet;
7459 // get simple seq of nodes
7460 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7461 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7462 int iSimple = 0, nbUnique = 0;
7464 simpleNodes[iSimple++] = faceNodes[0];
7466 for (int iCur = 1; iCur < nbNodes; iCur++) {
7467 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7468 simpleNodes[iSimple++] = faceNodes[iCur];
7469 if (nodeSet.insert( faceNodes[iCur] ).second)
7473 int nbSimple = iSimple;
7474 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7484 bool foundLoop = (nbSimple > nbUnique);
7487 set<const SMDS_MeshNode*> loopSet;
7488 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7489 const SMDS_MeshNode* n = simpleNodes[iSimple];
7490 if (!loopSet.insert( n ).second) {
7494 int iC = 0, curLast = iSimple;
7495 for (; iC < curLast; iC++) {
7496 if (simpleNodes[iC] == n) break;
7498 int loopLen = curLast - iC;
7500 // create sub-element
7502 quantities.push_back(loopLen);
7503 for (; iC < curLast; iC++) {
7504 poly_nodes.push_back(simpleNodes[iC]);
7507 // shift the rest nodes (place from the first loop position)
7508 for (iC = curLast + 1; iC < nbSimple; iC++) {
7509 simpleNodes[iC - loopLen] = simpleNodes[iC];
7511 nbSimple -= loopLen;
7514 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7515 } // while (foundLoop)
7519 quantities.push_back(iSimple);
7520 for (int i = 0; i < iSimple; i++)
7521 poly_nodes.push_back(simpleNodes[i]);
7527 //=======================================================================
7528 //function : MergeNodes
7529 //purpose : In each group, the cdr of nodes are substituted by the first one
7531 //=======================================================================
7533 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7535 MESSAGE("MergeNodes");
7536 myLastCreatedElems.Clear();
7537 myLastCreatedNodes.Clear();
7539 SMESHDS_Mesh* aMesh = GetMeshDS();
7541 TNodeNodeMap nodeNodeMap; // node to replace - new node
7542 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7543 list< int > rmElemIds, rmNodeIds;
7545 // Fill nodeNodeMap and elems
7547 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7548 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7549 list<const SMDS_MeshNode*>& nodes = *grIt;
7550 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7551 const SMDS_MeshNode* nToKeep = *nIt;
7552 //MESSAGE("node to keep " << nToKeep->GetID());
7553 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7554 const SMDS_MeshNode* nToRemove = *nIt;
7555 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7556 if ( nToRemove != nToKeep ) {
7557 //MESSAGE(" node to remove " << nToRemove->GetID());
7558 rmNodeIds.push_back( nToRemove->GetID() );
7559 AddToSameGroups( nToKeep, nToRemove, aMesh );
7562 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7563 while ( invElemIt->more() ) {
7564 const SMDS_MeshElement* elem = invElemIt->next();
7569 // Change element nodes or remove an element
7571 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7572 for ( ; eIt != elems.end(); eIt++ ) {
7573 const SMDS_MeshElement* elem = *eIt;
7574 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7575 int nbNodes = elem->NbNodes();
7576 int aShapeId = FindShape( elem );
7578 set<const SMDS_MeshNode*> nodeSet;
7579 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7580 int iUnique = 0, iCur = 0, nbRepl = 0;
7581 vector<int> iRepl( nbNodes );
7583 // get new seq of nodes
7584 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7585 while ( itN->more() ) {
7586 const SMDS_MeshNode* n =
7587 static_cast<const SMDS_MeshNode*>( itN->next() );
7589 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7590 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7592 // BUG 0020185: begin
7594 bool stopRecur = false;
7595 set<const SMDS_MeshNode*> nodesRecur;
7596 nodesRecur.insert(n);
7597 while (!stopRecur) {
7598 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7599 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7600 n = (*nnIt_i).second;
7601 if (!nodesRecur.insert(n).second) {
7602 // error: recursive dependancy
7612 curNodes[ iCur ] = n;
7613 bool isUnique = nodeSet.insert( n ).second;
7615 uniqueNodes[ iUnique++ ] = n;
7617 iRepl[ nbRepl++ ] = iCur;
7621 // Analyse element topology after replacement
7624 int nbUniqueNodes = nodeSet.size();
7625 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7626 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7627 // Polygons and Polyhedral volumes
7628 if (elem->IsPoly()) {
7630 if (elem->GetType() == SMDSAbs_Face) {
7632 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7634 for (; inode < nbNodes; inode++) {
7635 face_nodes[inode] = curNodes[inode];
7638 vector<const SMDS_MeshNode *> polygons_nodes;
7639 vector<int> quantities;
7640 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7643 for (int iface = 0; iface < nbNew; iface++) {
7644 int nbNodes = quantities[iface];
7645 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7646 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7647 poly_nodes[ii] = polygons_nodes[inode];
7649 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7650 myLastCreatedElems.Append(newElem);
7652 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7655 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7656 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7657 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7659 if (nbNew > 0) quid = nbNew - 1;
7660 vector<int> newquant(quantities.begin()+quid, quantities.end());
7661 const SMDS_MeshElement* newElem = 0;
7662 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7663 myLastCreatedElems.Append(newElem);
7664 if ( aShapeId && newElem )
7665 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7666 rmElemIds.push_back(elem->GetID());
7669 rmElemIds.push_back(elem->GetID());
7673 else if (elem->GetType() == SMDSAbs_Volume) {
7674 // Polyhedral volume
7675 if (nbUniqueNodes < 4) {
7676 rmElemIds.push_back(elem->GetID());
7679 // each face has to be analyzed in order to check volume validity
7680 const SMDS_VtkVolume* aPolyedre =
7681 dynamic_cast<const SMDS_VtkVolume*>( elem );
7683 int nbFaces = aPolyedre->NbFaces();
7685 vector<const SMDS_MeshNode *> poly_nodes;
7686 vector<int> quantities;
7688 for (int iface = 1; iface <= nbFaces; iface++) {
7689 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7690 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7692 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7693 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7694 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7695 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7696 faceNode = (*nnIt).second;
7698 faceNodes[inode - 1] = faceNode;
7701 SimplifyFace(faceNodes, poly_nodes, quantities);
7704 if (quantities.size() > 3) {
7705 // to be done: remove coincident faces
7708 if (quantities.size() > 3)
7710 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7711 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7712 const SMDS_MeshElement* newElem = 0;
7713 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7714 myLastCreatedElems.Append(newElem);
7715 if ( aShapeId && newElem )
7716 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7717 rmElemIds.push_back(elem->GetID());
7721 rmElemIds.push_back(elem->GetID());
7732 // TODO not all the possible cases are solved. Find something more generic?
7733 switch ( nbNodes ) {
7734 case 2: ///////////////////////////////////// EDGE
7735 isOk = false; break;
7736 case 3: ///////////////////////////////////// TRIANGLE
7737 isOk = false; break;
7739 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7741 else { //////////////////////////////////// QUADRANGLE
7742 if ( nbUniqueNodes < 3 )
7744 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7745 isOk = false; // opposite nodes stick
7746 //MESSAGE("isOk " << isOk);
7749 case 6: ///////////////////////////////////// PENTAHEDRON
7750 if ( nbUniqueNodes == 4 ) {
7751 // ---------------------------------> tetrahedron
7753 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7754 // all top nodes stick: reverse a bottom
7755 uniqueNodes[ 0 ] = curNodes [ 1 ];
7756 uniqueNodes[ 1 ] = curNodes [ 0 ];
7758 else if (nbRepl == 3 &&
7759 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7760 // all bottom nodes stick: set a top before
7761 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7762 uniqueNodes[ 0 ] = curNodes [ 3 ];
7763 uniqueNodes[ 1 ] = curNodes [ 4 ];
7764 uniqueNodes[ 2 ] = curNodes [ 5 ];
7766 else if (nbRepl == 4 &&
7767 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7768 // a lateral face turns into a line: reverse a bottom
7769 uniqueNodes[ 0 ] = curNodes [ 1 ];
7770 uniqueNodes[ 1 ] = curNodes [ 0 ];
7775 else if ( nbUniqueNodes == 5 ) {
7776 // PENTAHEDRON --------------------> 2 tetrahedrons
7777 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7778 // a bottom node sticks with a linked top one
7780 SMDS_MeshElement* newElem =
7781 aMesh->AddVolume(curNodes[ 3 ],
7784 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7785 myLastCreatedElems.Append(newElem);
7787 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7788 // 2. : reverse a bottom
7789 uniqueNodes[ 0 ] = curNodes [ 1 ];
7790 uniqueNodes[ 1 ] = curNodes [ 0 ];
7800 if(elem->IsQuadratic()) { // Quadratic quadrangle
7812 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7815 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7817 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7818 uniqueNodes[0] = curNodes[0];
7819 uniqueNodes[1] = curNodes[2];
7820 uniqueNodes[2] = curNodes[3];
7821 uniqueNodes[3] = curNodes[5];
7822 uniqueNodes[4] = curNodes[6];
7823 uniqueNodes[5] = curNodes[7];
7826 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7827 uniqueNodes[0] = curNodes[0];
7828 uniqueNodes[1] = curNodes[1];
7829 uniqueNodes[2] = curNodes[2];
7830 uniqueNodes[3] = curNodes[4];
7831 uniqueNodes[4] = curNodes[5];
7832 uniqueNodes[5] = curNodes[6];
7835 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7836 uniqueNodes[0] = curNodes[1];
7837 uniqueNodes[1] = curNodes[2];
7838 uniqueNodes[2] = curNodes[3];
7839 uniqueNodes[3] = curNodes[5];
7840 uniqueNodes[4] = curNodes[6];
7841 uniqueNodes[5] = curNodes[0];
7844 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7845 uniqueNodes[0] = curNodes[0];
7846 uniqueNodes[1] = curNodes[1];
7847 uniqueNodes[2] = curNodes[3];
7848 uniqueNodes[3] = curNodes[4];
7849 uniqueNodes[4] = curNodes[6];
7850 uniqueNodes[5] = curNodes[7];
7853 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7854 uniqueNodes[0] = curNodes[0];
7855 uniqueNodes[1] = curNodes[2];
7856 uniqueNodes[2] = curNodes[3];
7857 uniqueNodes[3] = curNodes[1];
7858 uniqueNodes[4] = curNodes[6];
7859 uniqueNodes[5] = curNodes[7];
7862 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7863 uniqueNodes[0] = curNodes[0];
7864 uniqueNodes[1] = curNodes[1];
7865 uniqueNodes[2] = curNodes[2];
7866 uniqueNodes[3] = curNodes[4];
7867 uniqueNodes[4] = curNodes[5];
7868 uniqueNodes[5] = curNodes[7];
7871 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7872 uniqueNodes[0] = curNodes[0];
7873 uniqueNodes[1] = curNodes[1];
7874 uniqueNodes[2] = curNodes[3];
7875 uniqueNodes[3] = curNodes[4];
7876 uniqueNodes[4] = curNodes[2];
7877 uniqueNodes[5] = curNodes[7];
7880 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7881 uniqueNodes[0] = curNodes[0];
7882 uniqueNodes[1] = curNodes[1];
7883 uniqueNodes[2] = curNodes[2];
7884 uniqueNodes[3] = curNodes[4];
7885 uniqueNodes[4] = curNodes[5];
7886 uniqueNodes[5] = curNodes[3];
7891 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7894 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7898 //////////////////////////////////// HEXAHEDRON
7900 SMDS_VolumeTool hexa (elem);
7901 hexa.SetExternalNormal();
7902 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7903 //////////////////////// HEX ---> 1 tetrahedron
7904 for ( int iFace = 0; iFace < 6; iFace++ ) {
7905 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7906 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7907 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7908 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7909 // one face turns into a point ...
7910 int iOppFace = hexa.GetOppFaceIndex( iFace );
7911 ind = hexa.GetFaceNodesIndices( iOppFace );
7913 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7914 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7917 if ( nbStick == 1 ) {
7918 // ... and the opposite one - into a triangle.
7920 ind = hexa.GetFaceNodesIndices( iFace );
7921 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7928 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7929 //////////////////////// HEX ---> 1 prism
7930 int nbTria = 0, iTria[3];
7931 const int *ind; // indices of face nodes
7932 // look for triangular faces
7933 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7934 ind = hexa.GetFaceNodesIndices( iFace );
7935 TIDSortedNodeSet faceNodes;
7936 for ( iCur = 0; iCur < 4; iCur++ )
7937 faceNodes.insert( curNodes[ind[iCur]] );
7938 if ( faceNodes.size() == 3 )
7939 iTria[ nbTria++ ] = iFace;
7941 // check if triangles are opposite
7942 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7945 // set nodes of the bottom triangle
7946 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7948 for ( iCur = 0; iCur < 4; iCur++ )
7949 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7950 indB.push_back( ind[iCur] );
7951 if ( !hexa.IsForward() )
7952 std::swap( indB[0], indB[2] );
7953 for ( iCur = 0; iCur < 3; iCur++ )
7954 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7955 // set nodes of the top triangle
7956 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7957 for ( iCur = 0; iCur < 3; ++iCur )
7958 for ( int j = 0; j < 4; ++j )
7959 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7961 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7967 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7968 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7969 for ( int iFace = 0; iFace < 6; iFace++ ) {
7970 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7971 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7972 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7973 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7974 // one face turns into a point ...
7975 int iOppFace = hexa.GetOppFaceIndex( iFace );
7976 ind = hexa.GetFaceNodesIndices( iOppFace );
7978 iUnique = 2; // reverse a tetrahedron 1 bottom
7979 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7980 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7982 else if ( iUnique >= 0 )
7983 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7985 if ( nbStick == 0 ) {
7986 // ... and the opposite one is a quadrangle
7988 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7989 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7992 SMDS_MeshElement* newElem =
7993 aMesh->AddVolume(curNodes[ind[ 0 ]],
7996 curNodes[indTop[ 0 ]]);
7997 myLastCreatedElems.Append(newElem);
7999 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8006 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8007 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8008 // find indices of quad and tri faces
8009 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8010 for ( iFace = 0; iFace < 6; iFace++ ) {
8011 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8013 for ( iCur = 0; iCur < 4; iCur++ )
8014 nodeSet.insert( curNodes[ind[ iCur ]] );
8015 nbUniqueNodes = nodeSet.size();
8016 if ( nbUniqueNodes == 3 )
8017 iTriFace[ nbTri++ ] = iFace;
8018 else if ( nbUniqueNodes == 4 )
8019 iQuadFace[ nbQuad++ ] = iFace;
8021 if (nbQuad == 2 && nbTri == 4 &&
8022 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8023 // 2 opposite quadrangles stuck with a diagonal;
8024 // sample groups of merged indices: (0-4)(2-6)
8025 // --------------------------------------------> 2 tetrahedrons
8026 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8027 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8028 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8029 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8030 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8031 // stuck with 0-2 diagonal
8039 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8040 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8041 // stuck with 1-3 diagonal
8053 uniqueNodes[ 0 ] = curNodes [ i0 ];
8054 uniqueNodes[ 1 ] = curNodes [ i1d ];
8055 uniqueNodes[ 2 ] = curNodes [ i3d ];
8056 uniqueNodes[ 3 ] = curNodes [ i0t ];
8059 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8063 myLastCreatedElems.Append(newElem);
8065 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8068 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8069 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8070 // --------------------------------------------> prism
8071 // find 2 opposite triangles
8073 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8074 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8075 // find indices of kept and replaced nodes
8076 // and fill unique nodes of 2 opposite triangles
8077 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8078 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8079 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8080 // fill unique nodes
8083 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8084 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8085 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8087 // iCur of a linked node of the opposite face (make normals co-directed):
8088 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8089 // check that correspondent corners of triangles are linked
8090 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8093 uniqueNodes[ iUnique ] = n;
8094 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8103 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8106 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8113 } // switch ( nbNodes )
8115 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8117 if ( isOk ) { // the elem remains valid after sticking nodes
8118 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8120 // Change nodes of polyedre
8121 const SMDS_VtkVolume* aPolyedre =
8122 dynamic_cast<const SMDS_VtkVolume*>( elem );
8124 int nbFaces = aPolyedre->NbFaces();
8126 vector<const SMDS_MeshNode *> poly_nodes;
8127 vector<int> quantities (nbFaces);
8129 for (int iface = 1; iface <= nbFaces; iface++) {
8130 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8131 quantities[iface - 1] = nbFaceNodes;
8133 for (inode = 1; inode <= nbFaceNodes; inode++) {
8134 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8136 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8137 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8138 curNode = (*nnIt).second;
8140 poly_nodes.push_back(curNode);
8143 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8146 else // replace non-polyhedron elements
8148 const SMDSAbs_ElementType etyp = elem->GetType();
8149 const int elemId = elem->GetID();
8150 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8151 uniqueNodes.resize(nbUniqueNodes);
8153 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8155 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8156 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8157 if ( sm && newElem )
8158 sm->AddElement( newElem );
8159 if ( elem != newElem )
8160 ReplaceElemInGroups( elem, newElem, aMesh );
8164 // Remove invalid regular element or invalid polygon
8165 rmElemIds.push_back( elem->GetID() );
8168 } // loop on elements
8170 // Remove bad elements, then equal nodes (order important)
8172 Remove( rmElemIds, false );
8173 Remove( rmNodeIds, true );
8178 // ========================================================
8179 // class : SortableElement
8180 // purpose : allow sorting elements basing on their nodes
8181 // ========================================================
8182 class SortableElement : public set <const SMDS_MeshElement*>
8186 SortableElement( const SMDS_MeshElement* theElem )
8189 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8190 while ( nodeIt->more() )
8191 this->insert( nodeIt->next() );
8194 const SMDS_MeshElement* Get() const
8197 void Set(const SMDS_MeshElement* e) const
8202 mutable const SMDS_MeshElement* myElem;
8205 //=======================================================================
8206 //function : FindEqualElements
8207 //purpose : Return list of group of elements built on the same nodes.
8208 // Search among theElements or in the whole mesh if theElements is empty
8209 //=======================================================================
8211 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
8212 TListOfListOfElementsID & theGroupsOfElementsID)
8214 myLastCreatedElems.Clear();
8215 myLastCreatedNodes.Clear();
8217 typedef map< SortableElement, int > TMapOfNodeSet;
8218 typedef list<int> TGroupOfElems;
8220 if ( theElements.empty() )
8221 { // get all elements in the mesh
8222 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8223 while ( eIt->more() )
8224 theElements.insert( theElements.end(), eIt->next());
8227 vector< TGroupOfElems > arrayOfGroups;
8228 TGroupOfElems groupOfElems;
8229 TMapOfNodeSet mapOfNodeSet;
8231 TIDSortedElemSet::iterator elemIt = theElements.begin();
8232 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8233 const SMDS_MeshElement* curElem = *elemIt;
8234 SortableElement SE(curElem);
8237 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8238 if( !(pp.second) ) {
8239 TMapOfNodeSet::iterator& itSE = pp.first;
8240 ind = (*itSE).second;
8241 arrayOfGroups[ind].push_back(curElem->GetID());
8244 groupOfElems.clear();
8245 groupOfElems.push_back(curElem->GetID());
8246 arrayOfGroups.push_back(groupOfElems);
8251 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8252 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8253 groupOfElems = *groupIt;
8254 if ( groupOfElems.size() > 1 ) {
8255 groupOfElems.sort();
8256 theGroupsOfElementsID.push_back(groupOfElems);
8261 //=======================================================================
8262 //function : MergeElements
8263 //purpose : In each given group, substitute all elements by the first one.
8264 //=======================================================================
8266 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8268 myLastCreatedElems.Clear();
8269 myLastCreatedNodes.Clear();
8271 typedef list<int> TListOfIDs;
8272 TListOfIDs rmElemIds; // IDs of elems to remove
8274 SMESHDS_Mesh* aMesh = GetMeshDS();
8276 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8277 while ( groupsIt != theGroupsOfElementsID.end() ) {
8278 TListOfIDs& aGroupOfElemID = *groupsIt;
8279 aGroupOfElemID.sort();
8280 int elemIDToKeep = aGroupOfElemID.front();
8281 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8282 aGroupOfElemID.pop_front();
8283 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8284 while ( idIt != aGroupOfElemID.end() ) {
8285 int elemIDToRemove = *idIt;
8286 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8287 // add the kept element in groups of removed one (PAL15188)
8288 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8289 rmElemIds.push_back( elemIDToRemove );
8295 Remove( rmElemIds, false );
8298 //=======================================================================
8299 //function : MergeEqualElements
8300 //purpose : Remove all but one of elements built on the same nodes.
8301 //=======================================================================
8303 void SMESH_MeshEditor::MergeEqualElements()
8305 TIDSortedElemSet aMeshElements; /* empty input ==
8306 to merge equal elements in the whole mesh */
8307 TListOfListOfElementsID aGroupsOfElementsID;
8308 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8309 MergeElements(aGroupsOfElementsID);
8312 //=======================================================================
8313 //function : FindFaceInSet
8314 //purpose : Return a face having linked nodes n1 and n2 and which is
8315 // - not in avoidSet,
8316 // - in elemSet provided that !elemSet.empty()
8317 // i1 and i2 optionally returns indices of n1 and n2
8318 //=======================================================================
8320 const SMDS_MeshElement*
8321 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8322 const SMDS_MeshNode* n2,
8323 const TIDSortedElemSet& elemSet,
8324 const TIDSortedElemSet& avoidSet,
8330 const SMDS_MeshElement* face = 0;
8332 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8333 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8334 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8336 //MESSAGE("in while ( invElemIt->more() && !face )");
8337 const SMDS_MeshElement* elem = invElemIt->next();
8338 if (avoidSet.count( elem ))
8340 if ( !elemSet.empty() && !elemSet.count( elem ))
8343 i1 = elem->GetNodeIndex( n1 );
8344 // find a n2 linked to n1
8345 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8346 for ( int di = -1; di < 2 && !face; di += 2 )
8348 i2 = (i1+di+nbN) % nbN;
8349 if ( elem->GetNode( i2 ) == n2 )
8352 if ( !face && elem->IsQuadratic())
8354 // analysis for quadratic elements using all nodes
8355 const SMDS_VtkFace* F =
8356 dynamic_cast<const SMDS_VtkFace*>(elem);
8357 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8358 // use special nodes iterator
8359 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8360 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8361 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8363 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8364 if ( n1 == prevN && n2 == n )
8368 else if ( n2 == prevN && n1 == n )
8370 face = elem; swap( i1, i2 );
8376 if ( n1ind ) *n1ind = i1;
8377 if ( n2ind ) *n2ind = i2;
8381 //=======================================================================
8382 //function : findAdjacentFace
8384 //=======================================================================
8386 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8387 const SMDS_MeshNode* n2,
8388 const SMDS_MeshElement* elem)
8390 TIDSortedElemSet elemSet, avoidSet;
8392 avoidSet.insert ( elem );
8393 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8396 //=======================================================================
8397 //function : FindFreeBorder
8399 //=======================================================================
8401 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8403 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8404 const SMDS_MeshNode* theSecondNode,
8405 const SMDS_MeshNode* theLastNode,
8406 list< const SMDS_MeshNode* > & theNodes,
8407 list< const SMDS_MeshElement* >& theFaces)
8409 if ( !theFirstNode || !theSecondNode )
8411 // find border face between theFirstNode and theSecondNode
8412 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8416 theFaces.push_back( curElem );
8417 theNodes.push_back( theFirstNode );
8418 theNodes.push_back( theSecondNode );
8420 //vector<const SMDS_MeshNode*> nodes;
8421 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8422 TIDSortedElemSet foundElems;
8423 bool needTheLast = ( theLastNode != 0 );
8425 while ( nStart != theLastNode ) {
8426 if ( nStart == theFirstNode )
8427 return !needTheLast;
8429 // find all free border faces sharing form nStart
8431 list< const SMDS_MeshElement* > curElemList;
8432 list< const SMDS_MeshNode* > nStartList;
8433 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8434 while ( invElemIt->more() ) {
8435 const SMDS_MeshElement* e = invElemIt->next();
8436 if ( e == curElem || foundElems.insert( e ).second ) {
8438 int iNode = 0, nbNodes = e->NbNodes();
8439 //const SMDS_MeshNode* nodes[nbNodes+1];
8440 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8442 if(e->IsQuadratic()) {
8443 const SMDS_VtkFace* F =
8444 dynamic_cast<const SMDS_VtkFace*>(e);
8445 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8446 // use special nodes iterator
8447 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8448 while( anIter->more() ) {
8449 nodes[ iNode++ ] = cast2Node(anIter->next());
8453 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8454 while ( nIt->more() )
8455 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8457 nodes[ iNode ] = nodes[ 0 ];
8459 for ( iNode = 0; iNode < nbNodes; iNode++ )
8460 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8461 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8462 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8464 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8465 curElemList.push_back( e );
8469 // analyse the found
8471 int nbNewBorders = curElemList.size();
8472 if ( nbNewBorders == 0 ) {
8473 // no free border furthermore
8474 return !needTheLast;
8476 else if ( nbNewBorders == 1 ) {
8477 // one more element found
8479 nStart = nStartList.front();
8480 curElem = curElemList.front();
8481 theFaces.push_back( curElem );
8482 theNodes.push_back( nStart );
8485 // several continuations found
8486 list< const SMDS_MeshElement* >::iterator curElemIt;
8487 list< const SMDS_MeshNode* >::iterator nStartIt;
8488 // check if one of them reached the last node
8489 if ( needTheLast ) {
8490 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8491 curElemIt!= curElemList.end();
8492 curElemIt++, nStartIt++ )
8493 if ( *nStartIt == theLastNode ) {
8494 theFaces.push_back( *curElemIt );
8495 theNodes.push_back( *nStartIt );
8499 // find the best free border by the continuations
8500 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8501 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8502 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8503 curElemIt!= curElemList.end();
8504 curElemIt++, nStartIt++ )
8506 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8507 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8508 // find one more free border
8509 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8513 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8514 // choice: clear a worse one
8515 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8516 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8517 contNodes[ iWorse ].clear();
8518 contFaces[ iWorse ].clear();
8521 if ( contNodes[0].empty() && contNodes[1].empty() )
8524 // append the best free border
8525 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8526 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8527 theNodes.pop_back(); // remove nIgnore
8528 theNodes.pop_back(); // remove nStart
8529 theFaces.pop_back(); // remove curElem
8530 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8531 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8532 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8533 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8536 } // several continuations found
8537 } // while ( nStart != theLastNode )
8542 //=======================================================================
8543 //function : CheckFreeBorderNodes
8544 //purpose : Return true if the tree nodes are on a free border
8545 //=======================================================================
8547 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8548 const SMDS_MeshNode* theNode2,
8549 const SMDS_MeshNode* theNode3)
8551 list< const SMDS_MeshNode* > nodes;
8552 list< const SMDS_MeshElement* > faces;
8553 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8556 //=======================================================================
8557 //function : SewFreeBorder
8559 //=======================================================================
8561 SMESH_MeshEditor::Sew_Error
8562 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8563 const SMDS_MeshNode* theBordSecondNode,
8564 const SMDS_MeshNode* theBordLastNode,
8565 const SMDS_MeshNode* theSideFirstNode,
8566 const SMDS_MeshNode* theSideSecondNode,
8567 const SMDS_MeshNode* theSideThirdNode,
8568 const bool theSideIsFreeBorder,
8569 const bool toCreatePolygons,
8570 const bool toCreatePolyedrs)
8572 myLastCreatedElems.Clear();
8573 myLastCreatedNodes.Clear();
8575 MESSAGE("::SewFreeBorder()");
8576 Sew_Error aResult = SEW_OK;
8578 // ====================================
8579 // find side nodes and elements
8580 // ====================================
8582 list< const SMDS_MeshNode* > nSide[ 2 ];
8583 list< const SMDS_MeshElement* > eSide[ 2 ];
8584 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8585 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8589 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8590 nSide[0], eSide[0])) {
8591 MESSAGE(" Free Border 1 not found " );
8592 aResult = SEW_BORDER1_NOT_FOUND;
8594 if (theSideIsFreeBorder) {
8597 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8598 nSide[1], eSide[1])) {
8599 MESSAGE(" Free Border 2 not found " );
8600 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8603 if ( aResult != SEW_OK )
8606 if (!theSideIsFreeBorder) {
8610 // -------------------------------------------------------------------------
8612 // 1. If nodes to merge are not coincident, move nodes of the free border
8613 // from the coord sys defined by the direction from the first to last
8614 // nodes of the border to the correspondent sys of the side 2
8615 // 2. On the side 2, find the links most co-directed with the correspondent
8616 // links of the free border
8617 // -------------------------------------------------------------------------
8619 // 1. Since sewing may break if there are volumes to split on the side 2,
8620 // we wont move nodes but just compute new coordinates for them
8621 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8622 TNodeXYZMap nBordXYZ;
8623 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8624 list< const SMDS_MeshNode* >::iterator nBordIt;
8626 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8627 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8628 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8629 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8630 double tol2 = 1.e-8;
8631 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8632 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8633 // Need node movement.
8635 // find X and Z axes to create trsf
8636 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8638 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8640 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8643 gp_Ax3 toBordAx( Pb1, Zb, X );
8644 gp_Ax3 fromSideAx( Ps1, Zs, X );
8645 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8647 gp_Trsf toBordSys, fromSide2Sys;
8648 toBordSys.SetTransformation( toBordAx );
8649 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8650 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8653 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8654 const SMDS_MeshNode* n = *nBordIt;
8655 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8656 toBordSys.Transforms( xyz );
8657 fromSide2Sys.Transforms( xyz );
8658 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8662 // just insert nodes XYZ in the nBordXYZ map
8663 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8664 const SMDS_MeshNode* n = *nBordIt;
8665 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8669 // 2. On the side 2, find the links most co-directed with the correspondent
8670 // links of the free border
8672 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8673 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8674 sideNodes.push_back( theSideFirstNode );
8676 bool hasVolumes = false;
8677 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8678 set<long> foundSideLinkIDs, checkedLinkIDs;
8679 SMDS_VolumeTool volume;
8680 //const SMDS_MeshNode* faceNodes[ 4 ];
8682 const SMDS_MeshNode* sideNode;
8683 const SMDS_MeshElement* sideElem;
8684 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8685 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8686 nBordIt = bordNodes.begin();
8688 // border node position and border link direction to compare with
8689 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8690 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8691 // choose next side node by link direction or by closeness to
8692 // the current border node:
8693 bool searchByDir = ( *nBordIt != theBordLastNode );
8695 // find the next node on the Side 2
8697 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8699 checkedLinkIDs.clear();
8700 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8702 // loop on inverse elements of current node (prevSideNode) on the Side 2
8703 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8704 while ( invElemIt->more() )
8706 const SMDS_MeshElement* elem = invElemIt->next();
8707 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8708 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8709 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8710 bool isVolume = volume.Set( elem );
8711 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8712 if ( isVolume ) // --volume
8714 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8715 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8716 if(elem->IsQuadratic()) {
8717 const SMDS_VtkFace* F =
8718 dynamic_cast<const SMDS_VtkFace*>(elem);
8719 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8720 // use special nodes iterator
8721 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8722 while( anIter->more() ) {
8723 nodes[ iNode ] = cast2Node(anIter->next());
8724 if ( nodes[ iNode++ ] == prevSideNode )
8725 iPrevNode = iNode - 1;
8729 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8730 while ( nIt->more() ) {
8731 nodes[ iNode ] = cast2Node( nIt->next() );
8732 if ( nodes[ iNode++ ] == prevSideNode )
8733 iPrevNode = iNode - 1;
8736 // there are 2 links to check
8741 // loop on links, to be precise, on the second node of links
8742 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8743 const SMDS_MeshNode* n = nodes[ iNode ];
8745 if ( !volume.IsLinked( n, prevSideNode ))
8749 if ( iNode ) // a node before prevSideNode
8750 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8751 else // a node after prevSideNode
8752 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8754 // check if this link was already used
8755 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8756 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8757 if (!isJustChecked &&
8758 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8760 // test a link geometrically
8761 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8762 bool linkIsBetter = false;
8763 double dot = 0.0, dist = 0.0;
8764 if ( searchByDir ) { // choose most co-directed link
8765 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8766 linkIsBetter = ( dot > maxDot );
8768 else { // choose link with the node closest to bordPos
8769 dist = ( nextXYZ - bordPos ).SquareModulus();
8770 linkIsBetter = ( dist < minDist );
8772 if ( linkIsBetter ) {
8781 } // loop on inverse elements of prevSideNode
8784 MESSAGE(" Cant find path by links of the Side 2 ");
8785 return SEW_BAD_SIDE_NODES;
8787 sideNodes.push_back( sideNode );
8788 sideElems.push_back( sideElem );
8789 foundSideLinkIDs.insert ( linkID );
8790 prevSideNode = sideNode;
8792 if ( *nBordIt == theBordLastNode )
8793 searchByDir = false;
8795 // find the next border link to compare with
8796 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8797 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8798 // move to next border node if sideNode is before forward border node (bordPos)
8799 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8800 prevBordNode = *nBordIt;
8802 bordPos = nBordXYZ[ *nBordIt ];
8803 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8804 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8808 while ( sideNode != theSideSecondNode );
8810 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8811 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8812 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8814 } // end nodes search on the side 2
8816 // ============================
8817 // sew the border to the side 2
8818 // ============================
8820 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8821 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8823 TListOfListOfNodes nodeGroupsToMerge;
8824 if ( nbNodes[0] == nbNodes[1] ||
8825 ( theSideIsFreeBorder && !theSideThirdNode)) {
8827 // all nodes are to be merged
8829 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8830 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8831 nIt[0]++, nIt[1]++ )
8833 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8834 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8835 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8840 // insert new nodes into the border and the side to get equal nb of segments
8842 // get normalized parameters of nodes on the borders
8843 //double param[ 2 ][ maxNbNodes ];
8845 param[0] = new double [ maxNbNodes ];
8846 param[1] = new double [ maxNbNodes ];
8848 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8849 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8850 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8851 const SMDS_MeshNode* nPrev = *nIt;
8852 double bordLength = 0;
8853 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8854 const SMDS_MeshNode* nCur = *nIt;
8855 gp_XYZ segment (nCur->X() - nPrev->X(),
8856 nCur->Y() - nPrev->Y(),
8857 nCur->Z() - nPrev->Z());
8858 double segmentLen = segment.Modulus();
8859 bordLength += segmentLen;
8860 param[ iBord ][ iNode ] = bordLength;
8863 // normalize within [0,1]
8864 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8865 param[ iBord ][ iNode ] /= bordLength;
8869 // loop on border segments
8870 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8871 int i[ 2 ] = { 0, 0 };
8872 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8873 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8875 TElemOfNodeListMap insertMap;
8876 TElemOfNodeListMap::iterator insertMapIt;
8878 // key: elem to insert nodes into
8879 // value: 2 nodes to insert between + nodes to be inserted
8881 bool next[ 2 ] = { false, false };
8883 // find min adjacent segment length after sewing
8884 double nextParam = 10., prevParam = 0;
8885 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8886 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8887 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8888 if ( i[ iBord ] > 0 )
8889 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8891 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8892 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8893 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8895 // choose to insert or to merge nodes
8896 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8897 if ( Abs( du ) <= minSegLen * 0.2 ) {
8900 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8901 const SMDS_MeshNode* n0 = *nIt[0];
8902 const SMDS_MeshNode* n1 = *nIt[1];
8903 nodeGroupsToMerge.back().push_back( n1 );
8904 nodeGroupsToMerge.back().push_back( n0 );
8905 // position of node of the border changes due to merge
8906 param[ 0 ][ i[0] ] += du;
8907 // move n1 for the sake of elem shape evaluation during insertion.
8908 // n1 will be removed by MergeNodes() anyway
8909 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8910 next[0] = next[1] = true;
8915 int intoBord = ( du < 0 ) ? 0 : 1;
8916 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8917 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8918 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8919 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8920 if ( intoBord == 1 ) {
8921 // move node of the border to be on a link of elem of the side
8922 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8923 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8924 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8925 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8926 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8928 insertMapIt = insertMap.find( elem );
8929 bool notFound = ( insertMapIt == insertMap.end() );
8930 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8932 // insert into another link of the same element:
8933 // 1. perform insertion into the other link of the elem
8934 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8935 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8936 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8937 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8938 // 2. perform insertion into the link of adjacent faces
8940 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8942 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8946 if (toCreatePolyedrs) {
8947 // perform insertion into the links of adjacent volumes
8948 UpdateVolumes(n12, n22, nodeList);
8950 // 3. find an element appeared on n1 and n2 after the insertion
8951 insertMap.erase( elem );
8952 elem = findAdjacentFace( n1, n2, 0 );
8954 if ( notFound || otherLink ) {
8955 // add element and nodes of the side into the insertMap
8956 insertMapIt = insertMap.insert
8957 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8958 (*insertMapIt).second.push_back( n1 );
8959 (*insertMapIt).second.push_back( n2 );
8961 // add node to be inserted into elem
8962 (*insertMapIt).second.push_back( nIns );
8963 next[ 1 - intoBord ] = true;
8966 // go to the next segment
8967 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8968 if ( next[ iBord ] ) {
8969 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8971 nPrev[ iBord ] = *nIt[ iBord ];
8972 nIt[ iBord ]++; i[ iBord ]++;
8976 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8978 // perform insertion of nodes into elements
8980 for (insertMapIt = insertMap.begin();
8981 insertMapIt != insertMap.end();
8984 const SMDS_MeshElement* elem = (*insertMapIt).first;
8985 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8986 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8987 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8989 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8991 if ( !theSideIsFreeBorder ) {
8992 // look for and insert nodes into the faces adjacent to elem
8994 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8996 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9001 if (toCreatePolyedrs) {
9002 // perform insertion into the links of adjacent volumes
9003 UpdateVolumes(n1, n2, nodeList);
9009 } // end: insert new nodes
9011 MergeNodes ( nodeGroupsToMerge );
9016 //=======================================================================
9017 //function : InsertNodesIntoLink
9018 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
9019 // and theBetweenNode2 and split theElement
9020 //=======================================================================
9022 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
9023 const SMDS_MeshNode* theBetweenNode1,
9024 const SMDS_MeshNode* theBetweenNode2,
9025 list<const SMDS_MeshNode*>& theNodesToInsert,
9026 const bool toCreatePoly)
9028 if ( theFace->GetType() != SMDSAbs_Face ) return;
9030 // find indices of 2 link nodes and of the rest nodes
9031 int iNode = 0, il1, il2, i3, i4;
9032 il1 = il2 = i3 = i4 = -1;
9033 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9034 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9036 if(theFace->IsQuadratic()) {
9037 const SMDS_VtkFace* F =
9038 dynamic_cast<const SMDS_VtkFace*>(theFace);
9039 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9040 // use special nodes iterator
9041 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9042 while( anIter->more() ) {
9043 const SMDS_MeshNode* n = cast2Node(anIter->next());
9044 if ( n == theBetweenNode1 )
9046 else if ( n == theBetweenNode2 )
9052 nodes[ iNode++ ] = n;
9056 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9057 while ( nodeIt->more() ) {
9058 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9059 if ( n == theBetweenNode1 )
9061 else if ( n == theBetweenNode2 )
9067 nodes[ iNode++ ] = n;
9070 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9073 // arrange link nodes to go one after another regarding the face orientation
9074 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9075 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9080 aNodesToInsert.reverse();
9082 // check that not link nodes of a quadrangles are in good order
9083 int nbFaceNodes = theFace->NbNodes();
9084 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9090 if (toCreatePoly || theFace->IsPoly()) {
9093 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9095 // add nodes of face up to first node of link
9098 if(theFace->IsQuadratic()) {
9099 const SMDS_VtkFace* F =
9100 dynamic_cast<const SMDS_VtkFace*>(theFace);
9101 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9102 // use special nodes iterator
9103 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9104 while( anIter->more() && !isFLN ) {
9105 const SMDS_MeshNode* n = cast2Node(anIter->next());
9106 poly_nodes[iNode++] = n;
9107 if (n == nodes[il1]) {
9111 // add nodes to insert
9112 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9113 for (; nIt != aNodesToInsert.end(); nIt++) {
9114 poly_nodes[iNode++] = *nIt;
9116 // add nodes of face starting from last node of link
9117 while ( anIter->more() ) {
9118 poly_nodes[iNode++] = cast2Node(anIter->next());
9122 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9123 while ( nodeIt->more() && !isFLN ) {
9124 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9125 poly_nodes[iNode++] = n;
9126 if (n == nodes[il1]) {
9130 // add nodes to insert
9131 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9132 for (; nIt != aNodesToInsert.end(); nIt++) {
9133 poly_nodes[iNode++] = *nIt;
9135 // add nodes of face starting from last node of link
9136 while ( nodeIt->more() ) {
9137 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9138 poly_nodes[iNode++] = n;
9142 // edit or replace the face
9143 SMESHDS_Mesh *aMesh = GetMeshDS();
9145 if (theFace->IsPoly()) {
9146 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9149 int aShapeId = FindShape( theFace );
9151 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9152 myLastCreatedElems.Append(newElem);
9153 if ( aShapeId && newElem )
9154 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9156 aMesh->RemoveElement(theFace);
9161 SMESHDS_Mesh *aMesh = GetMeshDS();
9162 if( !theFace->IsQuadratic() ) {
9164 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9165 int nbLinkNodes = 2 + aNodesToInsert.size();
9166 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9167 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9168 linkNodes[ 0 ] = nodes[ il1 ];
9169 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9170 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9171 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9172 linkNodes[ iNode++ ] = *nIt;
9174 // decide how to split a quadrangle: compare possible variants
9175 // and choose which of splits to be a quadrangle
9176 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9177 if ( nbFaceNodes == 3 ) {
9178 iBestQuad = nbSplits;
9181 else if ( nbFaceNodes == 4 ) {
9182 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9183 double aBestRate = DBL_MAX;
9184 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9186 double aBadRate = 0;
9187 // evaluate elements quality
9188 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9189 if ( iSplit == iQuad ) {
9190 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9194 aBadRate += getBadRate( &quad, aCrit );
9197 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9199 nodes[ iSplit < iQuad ? i4 : i3 ]);
9200 aBadRate += getBadRate( &tria, aCrit );
9204 if ( aBadRate < aBestRate ) {
9206 aBestRate = aBadRate;
9211 // create new elements
9212 int aShapeId = FindShape( theFace );
9215 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9216 SMDS_MeshElement* newElem = 0;
9217 if ( iSplit == iBestQuad )
9218 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9223 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9225 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9226 myLastCreatedElems.Append(newElem);
9227 if ( aShapeId && newElem )
9228 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9231 // change nodes of theFace
9232 const SMDS_MeshNode* newNodes[ 4 ];
9233 newNodes[ 0 ] = linkNodes[ i1 ];
9234 newNodes[ 1 ] = linkNodes[ i2 ];
9235 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9236 newNodes[ 3 ] = nodes[ i4 ];
9237 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9238 const SMDS_MeshElement* newElem = 0;
9239 if (iSplit == iBestQuad)
9240 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9242 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9243 myLastCreatedElems.Append(newElem);
9244 if ( aShapeId && newElem )
9245 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9246 } // end if(!theFace->IsQuadratic())
9247 else { // theFace is quadratic
9248 // we have to split theFace on simple triangles and one simple quadrangle
9250 int nbshift = tmp*2;
9251 // shift nodes in nodes[] by nbshift
9253 for(i=0; i<nbshift; i++) {
9254 const SMDS_MeshNode* n = nodes[0];
9255 for(j=0; j<nbFaceNodes-1; j++) {
9256 nodes[j] = nodes[j+1];
9258 nodes[nbFaceNodes-1] = n;
9260 il1 = il1 - nbshift;
9261 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9262 // n0 n1 n2 n0 n1 n2
9263 // +-----+-----+ +-----+-----+
9272 // create new elements
9273 int aShapeId = FindShape( theFace );
9276 if(nbFaceNodes==6) { // quadratic triangle
9277 SMDS_MeshElement* newElem =
9278 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9279 myLastCreatedElems.Append(newElem);
9280 if ( aShapeId && newElem )
9281 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9282 if(theFace->IsMediumNode(nodes[il1])) {
9283 // create quadrangle
9284 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9285 myLastCreatedElems.Append(newElem);
9286 if ( aShapeId && newElem )
9287 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9293 // create quadrangle
9294 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9295 myLastCreatedElems.Append(newElem);
9296 if ( aShapeId && newElem )
9297 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9303 else { // nbFaceNodes==8 - quadratic quadrangle
9304 SMDS_MeshElement* newElem =
9305 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9306 myLastCreatedElems.Append(newElem);
9307 if ( aShapeId && newElem )
9308 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9309 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9310 myLastCreatedElems.Append(newElem);
9311 if ( aShapeId && newElem )
9312 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9313 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9314 myLastCreatedElems.Append(newElem);
9315 if ( aShapeId && newElem )
9316 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9317 if(theFace->IsMediumNode(nodes[il1])) {
9318 // create quadrangle
9319 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9320 myLastCreatedElems.Append(newElem);
9321 if ( aShapeId && newElem )
9322 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9328 // create quadrangle
9329 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9330 myLastCreatedElems.Append(newElem);
9331 if ( aShapeId && newElem )
9332 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9338 // create needed triangles using n1,n2,n3 and inserted nodes
9339 int nbn = 2 + aNodesToInsert.size();
9340 //const SMDS_MeshNode* aNodes[nbn];
9341 vector<const SMDS_MeshNode*> aNodes(nbn);
9342 aNodes[0] = nodes[n1];
9343 aNodes[nbn-1] = nodes[n2];
9344 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9345 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9346 aNodes[iNode++] = *nIt;
9348 for(i=1; i<nbn; i++) {
9349 SMDS_MeshElement* newElem =
9350 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9351 myLastCreatedElems.Append(newElem);
9352 if ( aShapeId && newElem )
9353 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9357 aMesh->RemoveElement(theFace);
9360 //=======================================================================
9361 //function : UpdateVolumes
9363 //=======================================================================
9364 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9365 const SMDS_MeshNode* theBetweenNode2,
9366 list<const SMDS_MeshNode*>& theNodesToInsert)
9368 myLastCreatedElems.Clear();
9369 myLastCreatedNodes.Clear();
9371 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9372 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9373 const SMDS_MeshElement* elem = invElemIt->next();
9375 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9376 SMDS_VolumeTool aVolume (elem);
9377 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9380 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9381 int iface, nbFaces = aVolume.NbFaces();
9382 vector<const SMDS_MeshNode *> poly_nodes;
9383 vector<int> quantities (nbFaces);
9385 for (iface = 0; iface < nbFaces; iface++) {
9386 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9387 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9388 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9390 for (int inode = 0; inode < nbFaceNodes; inode++) {
9391 poly_nodes.push_back(faceNodes[inode]);
9393 if (nbInserted == 0) {
9394 if (faceNodes[inode] == theBetweenNode1) {
9395 if (faceNodes[inode + 1] == theBetweenNode2) {
9396 nbInserted = theNodesToInsert.size();
9398 // add nodes to insert
9399 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9400 for (; nIt != theNodesToInsert.end(); nIt++) {
9401 poly_nodes.push_back(*nIt);
9405 else if (faceNodes[inode] == theBetweenNode2) {
9406 if (faceNodes[inode + 1] == theBetweenNode1) {
9407 nbInserted = theNodesToInsert.size();
9409 // add nodes to insert in reversed order
9410 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9412 for (; nIt != theNodesToInsert.begin(); nIt--) {
9413 poly_nodes.push_back(*nIt);
9415 poly_nodes.push_back(*nIt);
9422 quantities[iface] = nbFaceNodes + nbInserted;
9425 // Replace or update the volume
9426 SMESHDS_Mesh *aMesh = GetMeshDS();
9428 if (elem->IsPoly()) {
9429 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9433 int aShapeId = FindShape( elem );
9435 SMDS_MeshElement* newElem =
9436 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9437 myLastCreatedElems.Append(newElem);
9438 if (aShapeId && newElem)
9439 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9441 aMesh->RemoveElement(elem);
9448 //================================================================================
9450 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9452 //================================================================================
9454 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9455 vector<const SMDS_MeshNode *> & nodes,
9456 vector<int> & nbNodeInFaces )
9459 nbNodeInFaces.clear();
9460 SMDS_VolumeTool vTool ( elem );
9461 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9463 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9464 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9465 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9470 //=======================================================================
9472 * \brief Convert elements contained in a submesh to quadratic
9473 * \return int - nb of checked elements
9475 //=======================================================================
9477 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9478 SMESH_MesherHelper& theHelper,
9479 const bool theForce3d)
9482 if( !theSm ) return nbElem;
9484 vector<int> nbNodeInFaces;
9485 vector<const SMDS_MeshNode *> nodes;
9486 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9487 while(ElemItr->more())
9490 const SMDS_MeshElement* elem = ElemItr->next();
9491 if( !elem || elem->IsQuadratic() ) continue;
9493 // get elem data needed to re-create it
9495 const int id = elem->GetID();
9496 const int nbNodes = elem->NbNodes();
9497 const SMDSAbs_ElementType aType = elem->GetType();
9498 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9499 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9500 if ( aGeomType == SMDSEntity_Polyhedra )
9501 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9502 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9503 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9505 // remove a linear element
9506 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9508 const SMDS_MeshElement* NewElem = 0;
9514 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9522 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9525 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9528 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9533 case SMDSAbs_Volume :
9537 case SMDSEntity_Tetra:
9538 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9540 case SMDSEntity_Pyramid:
9541 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9543 case SMDSEntity_Penta:
9544 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9546 case SMDSEntity_Hexa:
9547 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9548 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9550 case SMDSEntity_Hexagonal_Prism:
9552 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9559 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9561 theSm->AddElement( NewElem );
9566 //=======================================================================
9567 //function : ConvertToQuadratic
9569 //=======================================================================
9571 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9573 SMESHDS_Mesh* meshDS = GetMeshDS();
9575 SMESH_MesherHelper aHelper(*myMesh);
9576 aHelper.SetIsQuadratic( true );
9578 int nbCheckedElems = 0;
9579 if ( myMesh->HasShapeToMesh() )
9581 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9583 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9584 while ( smIt->more() ) {
9585 SMESH_subMesh* sm = smIt->next();
9586 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9587 aHelper.SetSubShape( sm->GetSubShape() );
9588 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9593 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9594 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9596 SMESHDS_SubMesh *smDS = 0;
9597 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9598 while(aEdgeItr->more())
9600 const SMDS_MeshEdge* edge = aEdgeItr->next();
9601 if(edge && !edge->IsQuadratic())
9603 int id = edge->GetID();
9604 //MESSAGE("edge->GetID() " << id);
9605 const SMDS_MeshNode* n1 = edge->GetNode(0);
9606 const SMDS_MeshNode* n2 = edge->GetNode(1);
9608 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9610 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9611 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9614 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9615 while(aFaceItr->more())
9617 const SMDS_MeshFace* face = aFaceItr->next();
9618 if(!face || face->IsQuadratic() ) continue;
9620 const int id = face->GetID();
9621 const SMDSAbs_EntityType type = face->GetEntityType();
9622 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9624 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9626 SMDS_MeshFace * NewFace = 0;
9629 case SMDSEntity_Triangle:
9630 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9632 case SMDSEntity_Quadrangle:
9633 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9636 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9638 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9640 vector<int> nbNodeInFaces;
9641 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9642 while(aVolumeItr->more())
9644 const SMDS_MeshVolume* volume = aVolumeItr->next();
9645 if(!volume || volume->IsQuadratic() ) continue;
9647 const int id = volume->GetID();
9648 const SMDSAbs_EntityType type = volume->GetEntityType();
9649 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9650 if ( type == SMDSEntity_Polyhedra )
9651 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9652 else if ( type == SMDSEntity_Hexagonal_Prism )
9653 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9655 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9657 SMDS_MeshVolume * NewVolume = 0;
9660 case SMDSEntity_Tetra:
9661 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9663 case SMDSEntity_Hexa:
9664 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9665 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9667 case SMDSEntity_Pyramid:
9668 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9669 nodes[3], nodes[4], id, theForce3d);
9671 case SMDSEntity_Penta:
9672 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9673 nodes[3], nodes[4], nodes[5], id, theForce3d);
9675 case SMDSEntity_Hexagonal_Prism:
9677 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9679 ReplaceElemInGroups(volume, NewVolume, meshDS);
9684 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9685 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9686 aHelper.FixQuadraticElements(myError);
9690 //================================================================================
9692 * \brief Makes given elements quadratic
9693 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9694 * \param theElements - elements to make quadratic
9696 //================================================================================
9698 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9699 TIDSortedElemSet& theElements)
9701 if ( theElements.empty() ) return;
9703 // we believe that all theElements are of the same type
9704 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9706 // get all nodes shared by theElements
9707 TIDSortedNodeSet allNodes;
9708 TIDSortedElemSet::iterator eIt = theElements.begin();
9709 for ( ; eIt != theElements.end(); ++eIt )
9710 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9712 // complete theElements with elements of lower dim whose all nodes are in allNodes
9714 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9715 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9716 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9717 for ( ; nIt != allNodes.end(); ++nIt )
9719 const SMDS_MeshNode* n = *nIt;
9720 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9721 while ( invIt->more() )
9723 const SMDS_MeshElement* e = invIt->next();
9724 if ( e->IsQuadratic() )
9726 quadAdjacentElems[ e->GetType() ].insert( e );
9729 if ( e->GetType() >= elemType )
9731 continue; // same type of more complex linear element
9734 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9735 continue; // e is already checked
9739 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9740 while ( nodeIt->more() && allIn )
9741 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9743 theElements.insert(e );
9747 SMESH_MesherHelper helper(*myMesh);
9748 helper.SetIsQuadratic( true );
9750 // add links of quadratic adjacent elements to the helper
9752 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9753 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9754 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9756 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9758 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9759 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9760 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9762 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9764 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9765 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9766 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9768 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9771 // make quadratic elements instead of linear ones
9773 SMESHDS_Mesh* meshDS = GetMeshDS();
9774 SMESHDS_SubMesh* smDS = 0;
9775 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9777 const SMDS_MeshElement* elem = *eIt;
9778 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9781 const int id = elem->GetID();
9782 const SMDSAbs_ElementType type = elem->GetType();
9783 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9785 if ( !smDS || !smDS->Contains( elem ))
9786 smDS = meshDS->MeshElements( elem->getshapeId() );
9787 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9789 SMDS_MeshElement * newElem = 0;
9790 switch( nodes.size() )
9792 case 4: // cases for most frequently used element types go first (for optimization)
9793 if ( type == SMDSAbs_Volume )
9794 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9796 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9799 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9800 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9803 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9806 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9809 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9810 nodes[4], id, theForce3d);
9813 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9814 nodes[4], nodes[5], id, theForce3d);
9818 ReplaceElemInGroups( elem, newElem, meshDS);
9819 if( newElem && smDS )
9820 smDS->AddElement( newElem );
9823 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9824 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9825 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9826 helper.FixQuadraticElements( myError );
9830 //=======================================================================
9832 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9833 * \return int - nb of checked elements
9835 //=======================================================================
9837 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9838 SMDS_ElemIteratorPtr theItr,
9839 const int theShapeID)
9842 SMESHDS_Mesh* meshDS = GetMeshDS();
9844 while( theItr->more() )
9846 const SMDS_MeshElement* elem = theItr->next();
9848 if( elem && elem->IsQuadratic())
9850 int id = elem->GetID();
9851 int nbCornerNodes = elem->NbCornerNodes();
9852 SMDSAbs_ElementType aType = elem->GetType();
9854 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9856 //remove a quadratic element
9857 if ( !theSm || !theSm->Contains( elem ))
9858 theSm = meshDS->MeshElements( elem->getshapeId() );
9859 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9861 // remove medium nodes
9862 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9863 if ( nodes[i]->NbInverseElements() == 0 )
9864 meshDS->RemoveFreeNode( nodes[i], theSm );
9866 // add a linear element
9867 nodes.resize( nbCornerNodes );
9868 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9869 ReplaceElemInGroups(elem, newElem, meshDS);
9870 if( theSm && newElem )
9871 theSm->AddElement( newElem );
9877 //=======================================================================
9878 //function : ConvertFromQuadratic
9880 //=======================================================================
9882 bool SMESH_MeshEditor::ConvertFromQuadratic()
9884 int nbCheckedElems = 0;
9885 if ( myMesh->HasShapeToMesh() )
9887 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9889 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9890 while ( smIt->more() ) {
9891 SMESH_subMesh* sm = smIt->next();
9892 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9893 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9899 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9900 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9902 SMESHDS_SubMesh *aSM = 0;
9903 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9911 //================================================================================
9913 * \brief Return true if all medium nodes of the element are in the node set
9915 //================================================================================
9917 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9919 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9920 if ( !nodeSet.count( elem->GetNode(i) ))
9926 //================================================================================
9928 * \brief Makes given elements linear
9930 //================================================================================
9932 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9934 if ( theElements.empty() ) return;
9936 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9937 set<int> mediumNodeIDs;
9938 TIDSortedElemSet::iterator eIt = theElements.begin();
9939 for ( ; eIt != theElements.end(); ++eIt )
9941 const SMDS_MeshElement* e = *eIt;
9942 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9943 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9946 // replace given elements by linear ones
9947 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9948 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9949 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9951 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9952 // except those elements sharing medium nodes of quadratic element whose medium nodes
9953 // are not all in mediumNodeIDs
9955 // get remaining medium nodes
9956 TIDSortedNodeSet mediumNodes;
9957 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9958 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9959 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9960 mediumNodes.insert( mediumNodes.end(), n );
9962 // find more quadratic elements to convert
9963 TIDSortedElemSet moreElemsToConvert;
9964 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9965 for ( ; nIt != mediumNodes.end(); ++nIt )
9967 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9968 while ( invIt->more() )
9970 const SMDS_MeshElement* e = invIt->next();
9971 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9973 // find a more complex element including e and
9974 // whose medium nodes are not in mediumNodes
9975 bool complexFound = false;
9976 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9978 SMDS_ElemIteratorPtr invIt2 =
9979 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9980 while ( invIt2->more() )
9982 const SMDS_MeshElement* eComplex = invIt2->next();
9983 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9985 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9986 if ( nbCommonNodes == e->NbNodes())
9988 complexFound = true;
9989 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9995 if ( !complexFound )
9996 moreElemsToConvert.insert( e );
10000 elemIt = SMDS_ElemIteratorPtr
10001 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10002 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10005 //=======================================================================
10006 //function : SewSideElements
10008 //=======================================================================
10010 SMESH_MeshEditor::Sew_Error
10011 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
10012 TIDSortedElemSet& theSide2,
10013 const SMDS_MeshNode* theFirstNode1,
10014 const SMDS_MeshNode* theFirstNode2,
10015 const SMDS_MeshNode* theSecondNode1,
10016 const SMDS_MeshNode* theSecondNode2)
10018 myLastCreatedElems.Clear();
10019 myLastCreatedNodes.Clear();
10021 MESSAGE ("::::SewSideElements()");
10022 if ( theSide1.size() != theSide2.size() )
10023 return SEW_DIFF_NB_OF_ELEMENTS;
10025 Sew_Error aResult = SEW_OK;
10027 // 1. Build set of faces representing each side
10028 // 2. Find which nodes of the side 1 to merge with ones on the side 2
10029 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10031 // =======================================================================
10032 // 1. Build set of faces representing each side:
10033 // =======================================================================
10034 // a. build set of nodes belonging to faces
10035 // b. complete set of faces: find missing faces whose nodes are in set of nodes
10036 // c. create temporary faces representing side of volumes if correspondent
10037 // face does not exist
10039 SMESHDS_Mesh* aMesh = GetMeshDS();
10040 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10041 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10042 TIDSortedElemSet faceSet1, faceSet2;
10043 set<const SMDS_MeshElement*> volSet1, volSet2;
10044 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
10045 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
10046 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
10047 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10048 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10049 int iSide, iFace, iNode;
10051 list<const SMDS_MeshElement* > tempFaceList;
10052 for ( iSide = 0; iSide < 2; iSide++ ) {
10053 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10054 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10055 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10056 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10057 set<const SMDS_MeshElement*>::iterator vIt;
10058 TIDSortedElemSet::iterator eIt;
10059 set<const SMDS_MeshNode*>::iterator nIt;
10061 // check that given nodes belong to given elements
10062 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10063 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10064 int firstIndex = -1, secondIndex = -1;
10065 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10066 const SMDS_MeshElement* elem = *eIt;
10067 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10068 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10069 if ( firstIndex > -1 && secondIndex > -1 ) break;
10071 if ( firstIndex < 0 || secondIndex < 0 ) {
10072 // we can simply return until temporary faces created
10073 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10076 // -----------------------------------------------------------
10077 // 1a. Collect nodes of existing faces
10078 // and build set of face nodes in order to detect missing
10079 // faces corresponding to sides of volumes
10080 // -----------------------------------------------------------
10082 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10084 // loop on the given element of a side
10085 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10086 //const SMDS_MeshElement* elem = *eIt;
10087 const SMDS_MeshElement* elem = *eIt;
10088 if ( elem->GetType() == SMDSAbs_Face ) {
10089 faceSet->insert( elem );
10090 set <const SMDS_MeshNode*> faceNodeSet;
10091 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10092 while ( nodeIt->more() ) {
10093 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10094 nodeSet->insert( n );
10095 faceNodeSet.insert( n );
10097 setOfFaceNodeSet.insert( faceNodeSet );
10099 else if ( elem->GetType() == SMDSAbs_Volume )
10100 volSet->insert( elem );
10102 // ------------------------------------------------------------------------------
10103 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10104 // ------------------------------------------------------------------------------
10106 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10107 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10108 while ( fIt->more() ) { // loop on faces sharing a node
10109 const SMDS_MeshElement* f = fIt->next();
10110 if ( faceSet->find( f ) == faceSet->end() ) {
10111 // check if all nodes are in nodeSet and
10112 // complete setOfFaceNodeSet if they are
10113 set <const SMDS_MeshNode*> faceNodeSet;
10114 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10115 bool allInSet = true;
10116 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10117 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10118 if ( nodeSet->find( n ) == nodeSet->end() )
10121 faceNodeSet.insert( n );
10124 faceSet->insert( f );
10125 setOfFaceNodeSet.insert( faceNodeSet );
10131 // -------------------------------------------------------------------------
10132 // 1c. Create temporary faces representing sides of volumes if correspondent
10133 // face does not exist
10134 // -------------------------------------------------------------------------
10136 if ( !volSet->empty() ) {
10137 //int nodeSetSize = nodeSet->size();
10139 // loop on given volumes
10140 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10141 SMDS_VolumeTool vol (*vIt);
10142 // loop on volume faces: find free faces
10143 // --------------------------------------
10144 list<const SMDS_MeshElement* > freeFaceList;
10145 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10146 if ( !vol.IsFreeFace( iFace ))
10148 // check if there is already a face with same nodes in a face set
10149 const SMDS_MeshElement* aFreeFace = 0;
10150 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10151 int nbNodes = vol.NbFaceNodes( iFace );
10152 set <const SMDS_MeshNode*> faceNodeSet;
10153 vol.GetFaceNodes( iFace, faceNodeSet );
10154 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10156 // no such a face is given but it still can exist, check it
10157 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10158 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10160 if ( !aFreeFace ) {
10161 // create a temporary face
10162 if ( nbNodes == 3 ) {
10163 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10164 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10166 else if ( nbNodes == 4 ) {
10167 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10168 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10171 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10172 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10173 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10176 tempFaceList.push_back( aFreeFace );
10180 freeFaceList.push_back( aFreeFace );
10182 } // loop on faces of a volume
10184 // choose one of several free faces of a volume
10185 // --------------------------------------------
10186 if ( freeFaceList.size() > 1 ) {
10187 // choose a face having max nb of nodes shared by other elems of a side
10188 int maxNbNodes = -1;
10189 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10190 while ( fIt != freeFaceList.end() ) { // loop on free faces
10191 int nbSharedNodes = 0;
10192 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10193 while ( nodeIt->more() ) { // loop on free face nodes
10194 const SMDS_MeshNode* n =
10195 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10196 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10197 while ( invElemIt->more() ) {
10198 const SMDS_MeshElement* e = invElemIt->next();
10199 nbSharedNodes += faceSet->count( e );
10200 nbSharedNodes += elemSet->count( e );
10203 if ( nbSharedNodes > maxNbNodes ) {
10204 maxNbNodes = nbSharedNodes;
10205 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10207 else if ( nbSharedNodes == maxNbNodes ) {
10211 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10214 if ( freeFaceList.size() > 1 )
10216 // could not choose one face, use another way
10217 // choose a face most close to the bary center of the opposite side
10218 gp_XYZ aBC( 0., 0., 0. );
10219 set <const SMDS_MeshNode*> addedNodes;
10220 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10221 eIt = elemSet2->begin();
10222 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10223 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10224 while ( nodeIt->more() ) { // loop on free face nodes
10225 const SMDS_MeshNode* n =
10226 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10227 if ( addedNodes.insert( n ).second )
10228 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10231 aBC /= addedNodes.size();
10232 double minDist = DBL_MAX;
10233 fIt = freeFaceList.begin();
10234 while ( fIt != freeFaceList.end() ) { // loop on free faces
10236 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10237 while ( nodeIt->more() ) { // loop on free face nodes
10238 const SMDS_MeshNode* n =
10239 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10240 gp_XYZ p( n->X(),n->Y(),n->Z() );
10241 dist += ( aBC - p ).SquareModulus();
10243 if ( dist < minDist ) {
10245 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10248 fIt = freeFaceList.erase( fIt++ );
10251 } // choose one of several free faces of a volume
10253 if ( freeFaceList.size() == 1 ) {
10254 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10255 faceSet->insert( aFreeFace );
10256 // complete a node set with nodes of a found free face
10257 // for ( iNode = 0; iNode < ; iNode++ )
10258 // nodeSet->insert( fNodes[ iNode ] );
10261 } // loop on volumes of a side
10263 // // complete a set of faces if new nodes in a nodeSet appeared
10264 // // ----------------------------------------------------------
10265 // if ( nodeSetSize != nodeSet->size() ) {
10266 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10267 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10268 // while ( fIt->more() ) { // loop on faces sharing a node
10269 // const SMDS_MeshElement* f = fIt->next();
10270 // if ( faceSet->find( f ) == faceSet->end() ) {
10271 // // check if all nodes are in nodeSet and
10272 // // complete setOfFaceNodeSet if they are
10273 // set <const SMDS_MeshNode*> faceNodeSet;
10274 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10275 // bool allInSet = true;
10276 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10277 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10278 // if ( nodeSet->find( n ) == nodeSet->end() )
10279 // allInSet = false;
10281 // faceNodeSet.insert( n );
10283 // if ( allInSet ) {
10284 // faceSet->insert( f );
10285 // setOfFaceNodeSet.insert( faceNodeSet );
10291 } // Create temporary faces, if there are volumes given
10294 if ( faceSet1.size() != faceSet2.size() ) {
10295 // delete temporary faces: they are in reverseElements of actual nodes
10296 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10297 // while ( tmpFaceIt->more() )
10298 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10299 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10300 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10301 // aMesh->RemoveElement(*tmpFaceIt);
10302 MESSAGE("Diff nb of faces");
10303 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10306 // ============================================================
10307 // 2. Find nodes to merge:
10308 // bind a node to remove to a node to put instead
10309 // ============================================================
10311 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10312 if ( theFirstNode1 != theFirstNode2 )
10313 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10314 if ( theSecondNode1 != theSecondNode2 )
10315 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10317 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10318 set< long > linkIdSet; // links to process
10319 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10321 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10322 list< NLink > linkList[2];
10323 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10324 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10325 // loop on links in linkList; find faces by links and append links
10326 // of the found faces to linkList
10327 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10328 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10330 NLink link[] = { *linkIt[0], *linkIt[1] };
10331 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10332 if ( !linkIdSet.count( linkID ) )
10335 // by links, find faces in the face sets,
10336 // and find indices of link nodes in the found faces;
10337 // in a face set, there is only one or no face sharing a link
10338 // ---------------------------------------------------------------
10340 const SMDS_MeshElement* face[] = { 0, 0 };
10341 vector<const SMDS_MeshNode*> fnodes[2];
10342 int iLinkNode[2][2];
10343 TIDSortedElemSet avoidSet;
10344 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10345 const SMDS_MeshNode* n1 = link[iSide].first;
10346 const SMDS_MeshNode* n2 = link[iSide].second;
10347 //cout << "Side " << iSide << " ";
10348 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10349 // find a face by two link nodes
10350 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10351 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10352 if ( face[ iSide ])
10354 //cout << " F " << face[ iSide]->GetID() <<endl;
10355 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10356 // put face nodes to fnodes
10357 if ( face[ iSide ]->IsQuadratic() )
10359 // use interlaced nodes iterator
10360 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10361 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10362 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10363 while ( nIter->more() )
10364 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10368 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10369 face[ iSide ]->end_nodes() );
10371 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10375 // check similarity of elements of the sides
10376 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10377 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10378 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10379 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10382 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10384 break; // do not return because it's necessary to remove tmp faces
10387 // set nodes to merge
10388 // -------------------
10390 if ( face[0] && face[1] ) {
10391 const int nbNodes = face[0]->NbNodes();
10392 if ( nbNodes != face[1]->NbNodes() ) {
10393 MESSAGE("Diff nb of face nodes");
10394 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10395 break; // do not return because it s necessary to remove tmp faces
10397 bool reverse[] = { false, false }; // order of nodes in the link
10398 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10399 // analyse link orientation in faces
10400 int i1 = iLinkNode[ iSide ][ 0 ];
10401 int i2 = iLinkNode[ iSide ][ 1 ];
10402 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10404 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10405 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10406 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10408 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10409 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10412 // add other links of the faces to linkList
10413 // -----------------------------------------
10415 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10416 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10417 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10418 if ( !iter_isnew.second ) { // already in a set: no need to process
10419 linkIdSet.erase( iter_isnew.first );
10421 else // new in set == encountered for the first time: add
10423 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10424 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10425 linkList[0].push_back ( NLink( n1, n2 ));
10426 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10431 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10434 } // loop on link lists
10436 if ( aResult == SEW_OK &&
10437 ( //linkIt[0] != linkList[0].end() ||
10438 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10439 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10440 " " << (faceSetPtr[1]->empty()));
10441 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10444 // ====================================================================
10445 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10446 // ====================================================================
10448 // delete temporary faces
10449 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10450 // while ( tmpFaceIt->more() )
10451 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10452 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10453 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10454 aMesh->RemoveElement(*tmpFaceIt);
10456 if ( aResult != SEW_OK)
10459 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10460 // loop on nodes replacement map
10461 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10462 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10463 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10464 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10465 nodeIDsToRemove.push_back( nToRemove->GetID() );
10466 // loop on elements sharing nToRemove
10467 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10468 while ( invElemIt->more() ) {
10469 const SMDS_MeshElement* e = invElemIt->next();
10470 // get a new suite of nodes: make replacement
10471 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10472 vector< const SMDS_MeshNode*> nodes( nbNodes );
10473 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10474 while ( nIt->more() ) {
10475 const SMDS_MeshNode* n =
10476 static_cast<const SMDS_MeshNode*>( nIt->next() );
10477 nnIt = nReplaceMap.find( n );
10478 if ( nnIt != nReplaceMap.end() ) {
10480 n = (*nnIt).second;
10484 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10485 // elemIDsToRemove.push_back( e->GetID() );
10489 SMDSAbs_ElementType etyp = e->GetType();
10490 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10493 myLastCreatedElems.Append(newElem);
10494 AddToSameGroups(newElem, e, aMesh);
10495 int aShapeId = e->getshapeId();
10498 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10501 aMesh->RemoveElement(e);
10506 Remove( nodeIDsToRemove, true );
10511 //================================================================================
10513 * \brief Find corresponding nodes in two sets of faces
10514 * \param theSide1 - first face set
10515 * \param theSide2 - second first face
10516 * \param theFirstNode1 - a boundary node of set 1
10517 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10518 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10519 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10520 * \param nReplaceMap - output map of corresponding nodes
10521 * \return bool - is a success or not
10523 //================================================================================
10526 //#define DEBUG_MATCHING_NODES
10529 SMESH_MeshEditor::Sew_Error
10530 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10531 set<const SMDS_MeshElement*>& theSide2,
10532 const SMDS_MeshNode* theFirstNode1,
10533 const SMDS_MeshNode* theFirstNode2,
10534 const SMDS_MeshNode* theSecondNode1,
10535 const SMDS_MeshNode* theSecondNode2,
10536 TNodeNodeMap & nReplaceMap)
10538 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10540 nReplaceMap.clear();
10541 if ( theFirstNode1 != theFirstNode2 )
10542 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10543 if ( theSecondNode1 != theSecondNode2 )
10544 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10546 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10547 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10549 list< NLink > linkList[2];
10550 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10551 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10553 // loop on links in linkList; find faces by links and append links
10554 // of the found faces to linkList
10555 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10556 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10557 NLink link[] = { *linkIt[0], *linkIt[1] };
10558 if ( linkSet.find( link[0] ) == linkSet.end() )
10561 // by links, find faces in the face sets,
10562 // and find indices of link nodes in the found faces;
10563 // in a face set, there is only one or no face sharing a link
10564 // ---------------------------------------------------------------
10566 const SMDS_MeshElement* face[] = { 0, 0 };
10567 list<const SMDS_MeshNode*> notLinkNodes[2];
10568 //bool reverse[] = { false, false }; // order of notLinkNodes
10570 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10572 const SMDS_MeshNode* n1 = link[iSide].first;
10573 const SMDS_MeshNode* n2 = link[iSide].second;
10574 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10575 set< const SMDS_MeshElement* > facesOfNode1;
10576 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10578 // during a loop of the first node, we find all faces around n1,
10579 // during a loop of the second node, we find one face sharing both n1 and n2
10580 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10581 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10582 while ( fIt->more() ) { // loop on faces sharing a node
10583 const SMDS_MeshElement* f = fIt->next();
10584 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10585 ! facesOfNode1.insert( f ).second ) // f encounters twice
10587 if ( face[ iSide ] ) {
10588 MESSAGE( "2 faces per link " );
10589 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10592 faceSet->erase( f );
10594 // get not link nodes
10595 int nbN = f->NbNodes();
10596 if ( f->IsQuadratic() )
10598 nbNodes[ iSide ] = nbN;
10599 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10600 int i1 = f->GetNodeIndex( n1 );
10601 int i2 = f->GetNodeIndex( n2 );
10602 int iEnd = nbN, iBeg = -1, iDelta = 1;
10603 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10605 std::swap( iEnd, iBeg ); iDelta = -1;
10610 if ( i == iEnd ) i = iBeg + iDelta;
10611 if ( i == i1 ) break;
10612 nodes.push_back ( f->GetNode( i ) );
10618 // check similarity of elements of the sides
10619 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10620 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10621 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10622 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10625 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10629 // set nodes to merge
10630 // -------------------
10632 if ( face[0] && face[1] ) {
10633 if ( nbNodes[0] != nbNodes[1] ) {
10634 MESSAGE("Diff nb of face nodes");
10635 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10637 #ifdef DEBUG_MATCHING_NODES
10638 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10639 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10640 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10642 int nbN = nbNodes[0];
10644 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10645 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10646 for ( int i = 0 ; i < nbN - 2; ++i ) {
10647 #ifdef DEBUG_MATCHING_NODES
10648 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10650 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10654 // add other links of the face 1 to linkList
10655 // -----------------------------------------
10657 const SMDS_MeshElement* f0 = face[0];
10658 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10659 for ( int i = 0; i < nbN; i++ )
10661 const SMDS_MeshNode* n2 = f0->GetNode( i );
10662 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10663 linkSet.insert( SMESH_TLink( n1, n2 ));
10664 if ( !iter_isnew.second ) { // already in a set: no need to process
10665 linkSet.erase( iter_isnew.first );
10667 else // new in set == encountered for the first time: add
10669 #ifdef DEBUG_MATCHING_NODES
10670 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10671 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10673 linkList[0].push_back ( NLink( n1, n2 ));
10674 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10679 } // loop on link lists
10684 //================================================================================
10686 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10687 \param theElems - the list of elements (edges or faces) to be replicated
10688 The nodes for duplication could be found from these elements
10689 \param theNodesNot - list of nodes to NOT replicate
10690 \param theAffectedElems - the list of elements (cells and edges) to which the
10691 replicated nodes should be associated to.
10692 \return TRUE if operation has been completed successfully, FALSE otherwise
10694 //================================================================================
10696 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10697 const TIDSortedElemSet& theNodesNot,
10698 const TIDSortedElemSet& theAffectedElems )
10700 myLastCreatedElems.Clear();
10701 myLastCreatedNodes.Clear();
10703 if ( theElems.size() == 0 )
10706 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10711 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10712 // duplicate elements and nodes
10713 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10714 // replce nodes by duplications
10715 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10719 //================================================================================
10721 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10722 \param theMeshDS - mesh instance
10723 \param theElems - the elements replicated or modified (nodes should be changed)
10724 \param theNodesNot - nodes to NOT replicate
10725 \param theNodeNodeMap - relation of old node to new created node
10726 \param theIsDoubleElem - flag os to replicate element or modify
10727 \return TRUE if operation has been completed successfully, FALSE otherwise
10729 //================================================================================
10731 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10732 const TIDSortedElemSet& theElems,
10733 const TIDSortedElemSet& theNodesNot,
10734 std::map< const SMDS_MeshNode*,
10735 const SMDS_MeshNode* >& theNodeNodeMap,
10736 const bool theIsDoubleElem )
10738 MESSAGE("doubleNodes");
10739 // iterate on through element and duplicate them (by nodes duplication)
10741 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10742 for ( ; elemItr != theElems.end(); ++elemItr )
10744 const SMDS_MeshElement* anElem = *elemItr;
10748 bool isDuplicate = false;
10749 // duplicate nodes to duplicate element
10750 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10751 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10753 while ( anIter->more() )
10756 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10757 SMDS_MeshNode* aNewNode = aCurrNode;
10758 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10759 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10760 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10763 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10764 theNodeNodeMap[ aCurrNode ] = aNewNode;
10765 myLastCreatedNodes.Append( aNewNode );
10767 isDuplicate |= (aCurrNode != aNewNode);
10768 newNodes[ ind++ ] = aNewNode;
10770 if ( !isDuplicate )
10773 if ( theIsDoubleElem )
10774 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10777 MESSAGE("ChangeElementNodes");
10778 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10785 //================================================================================
10787 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10788 \param theNodes - identifiers of nodes to be doubled
10789 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10790 nodes. If list of element identifiers is empty then nodes are doubled but
10791 they not assigned to elements
10792 \return TRUE if operation has been completed successfully, FALSE otherwise
10794 //================================================================================
10796 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10797 const std::list< int >& theListOfModifiedElems )
10799 MESSAGE("DoubleNodes");
10800 myLastCreatedElems.Clear();
10801 myLastCreatedNodes.Clear();
10803 if ( theListOfNodes.size() == 0 )
10806 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10810 // iterate through nodes and duplicate them
10812 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10814 std::list< int >::const_iterator aNodeIter;
10815 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10817 int aCurr = *aNodeIter;
10818 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10824 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10827 anOldNodeToNewNode[ aNode ] = aNewNode;
10828 myLastCreatedNodes.Append( aNewNode );
10832 // Create map of new nodes for modified elements
10834 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10836 std::list< int >::const_iterator anElemIter;
10837 for ( anElemIter = theListOfModifiedElems.begin();
10838 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10840 int aCurr = *anElemIter;
10841 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10845 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10847 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10849 while ( anIter->more() )
10851 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10852 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10854 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10855 aNodeArr[ ind++ ] = aNewNode;
10858 aNodeArr[ ind++ ] = aCurrNode;
10860 anElemToNodes[ anElem ] = aNodeArr;
10863 // Change nodes of elements
10865 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10866 anElemToNodesIter = anElemToNodes.begin();
10867 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10869 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10870 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10873 MESSAGE("ChangeElementNodes");
10874 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10883 //================================================================================
10885 \brief Check if element located inside shape
10886 \return TRUE if IN or ON shape, FALSE otherwise
10888 //================================================================================
10890 template<class Classifier>
10891 bool isInside(const SMDS_MeshElement* theElem,
10892 Classifier& theClassifier,
10893 const double theTol)
10895 gp_XYZ centerXYZ (0, 0, 0);
10896 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10897 while (aNodeItr->more())
10898 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10900 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10901 theClassifier.Perform(aPnt, theTol);
10902 TopAbs_State aState = theClassifier.State();
10903 return (aState == TopAbs_IN || aState == TopAbs_ON );
10906 //================================================================================
10908 * \brief Classifier of the 3D point on the TopoDS_Face
10909 * with interaface suitable for isInside()
10911 //================================================================================
10913 struct _FaceClassifier
10915 Extrema_ExtPS _extremum;
10916 BRepAdaptor_Surface _surface;
10917 TopAbs_State _state;
10919 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10921 _extremum.Initialize( _surface,
10922 _surface.FirstUParameter(), _surface.LastUParameter(),
10923 _surface.FirstVParameter(), _surface.LastVParameter(),
10924 _surface.Tolerance(), _surface.Tolerance() );
10926 void Perform(const gp_Pnt& aPnt, double theTol)
10928 _state = TopAbs_OUT;
10929 _extremum.Perform(aPnt);
10930 if ( _extremum.IsDone() )
10931 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10932 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10933 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10935 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10938 TopAbs_State State() const
10945 //================================================================================
10947 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10948 This method is the first step of DoubleNodeElemGroupsInRegion.
10949 \param theElems - list of groups of elements (edges or faces) to be replicated
10950 \param theNodesNot - list of groups of nodes not to replicated
10951 \param theShape - shape to detect affected elements (element which geometric center
10952 located on or inside shape).
10953 The replicated nodes should be associated to affected elements.
10954 \return groups of affected elements
10955 \sa DoubleNodeElemGroupsInRegion()
10957 //================================================================================
10959 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10960 const TIDSortedElemSet& theNodesNot,
10961 const TopoDS_Shape& theShape,
10962 TIDSortedElemSet& theAffectedElems)
10964 if ( theShape.IsNull() )
10967 const double aTol = Precision::Confusion();
10968 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10969 auto_ptr<_FaceClassifier> aFaceClassifier;
10970 if ( theShape.ShapeType() == TopAbs_SOLID )
10972 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10973 bsc3d->PerformInfinitePoint(aTol);
10975 else if (theShape.ShapeType() == TopAbs_FACE )
10977 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10980 // iterates on indicated elements and get elements by back references from their nodes
10981 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10982 for ( ; elemItr != theElems.end(); ++elemItr )
10984 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10988 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10989 while ( nodeItr->more() )
10991 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10992 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10994 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10995 while ( backElemItr->more() )
10997 const SMDS_MeshElement* curElem = backElemItr->next();
10998 if ( curElem && theElems.find(curElem) == theElems.end() &&
11000 isInside( curElem, *bsc3d, aTol ) :
11001 isInside( curElem, *aFaceClassifier, aTol )))
11002 theAffectedElems.insert( curElem );
11009 //================================================================================
11011 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11012 \param theElems - group of of elements (edges or faces) to be replicated
11013 \param theNodesNot - group of nodes not to replicate
11014 \param theShape - shape to detect affected elements (element which geometric center
11015 located on or inside shape).
11016 The replicated nodes should be associated to affected elements.
11017 \return TRUE if operation has been completed successfully, FALSE otherwise
11019 //================================================================================
11021 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11022 const TIDSortedElemSet& theNodesNot,
11023 const TopoDS_Shape& theShape )
11025 if ( theShape.IsNull() )
11028 const double aTol = Precision::Confusion();
11029 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11030 auto_ptr<_FaceClassifier> aFaceClassifier;
11031 if ( theShape.ShapeType() == TopAbs_SOLID )
11033 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11034 bsc3d->PerformInfinitePoint(aTol);
11036 else if (theShape.ShapeType() == TopAbs_FACE )
11038 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11041 // iterates on indicated elements and get elements by back references from their nodes
11042 TIDSortedElemSet anAffected;
11043 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11044 for ( ; elemItr != theElems.end(); ++elemItr )
11046 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11050 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11051 while ( nodeItr->more() )
11053 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11054 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11056 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11057 while ( backElemItr->more() )
11059 const SMDS_MeshElement* curElem = backElemItr->next();
11060 if ( curElem && theElems.find(curElem) == theElems.end() &&
11062 isInside( curElem, *bsc3d, aTol ) :
11063 isInside( curElem, *aFaceClassifier, aTol )))
11064 anAffected.insert( curElem );
11068 return DoubleNodes( theElems, theNodesNot, anAffected );
11072 * \brief compute an oriented angle between two planes defined by four points.
11073 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11074 * @param p0 base of the rotation axe
11075 * @param p1 extremity of the rotation axe
11076 * @param g1 belongs to the first plane
11077 * @param g2 belongs to the second plane
11079 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11081 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11082 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11083 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11084 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11085 gp_Vec vref(p0, p1);
11088 gp_Vec n1 = vref.Crossed(v1);
11089 gp_Vec n2 = vref.Crossed(v2);
11090 return n2.AngleWithRef(n1, vref);
11094 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11095 * The list of groups must describe a partition of the mesh volumes.
11096 * The nodes of the internal faces at the boundaries of the groups are doubled.
11097 * In option, the internal faces are replaced by flat elements.
11098 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11099 * The flat elements are stored in groups of volumes.
11100 * @param theElems - list of groups of volumes, where a group of volume is a set of
11101 * SMDS_MeshElements sorted by Id.
11102 * @param createJointElems - if TRUE, create the elements
11103 * @return TRUE if operation has been completed successfully, FALSE otherwise
11105 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11106 bool createJointElems)
11108 MESSAGE("----------------------------------------------");
11109 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11110 MESSAGE("----------------------------------------------");
11112 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11113 meshDS->BuildDownWardConnectivity(true);
11115 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11117 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11118 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11119 // build the list of nodes shared by 2 or more domains, with their domain indexes
11121 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11122 std::map<int,int>celldom; // cell vtkId --> domain
11123 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11124 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11125 faceDomains.clear();
11127 cellDomains.clear();
11128 nodeDomains.clear();
11129 std::map<int,int> emptyMap;
11130 std::set<int> emptySet;
11133 for (int idom = 0; idom < theElems.size(); idom++)
11136 // --- build a map (face to duplicate --> volume to modify)
11137 // with all the faces shared by 2 domains (group of elements)
11138 // and corresponding volume of this domain, for each shared face.
11139 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11141 //MESSAGE("Domain " << idom);
11142 const TIDSortedElemSet& domain = theElems[idom];
11143 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11144 for (; elemItr != domain.end(); ++elemItr)
11146 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11149 int vtkId = anElem->getVtkId();
11150 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11151 int neighborsVtkIds[NBMAXNEIGHBORS];
11152 int downIds[NBMAXNEIGHBORS];
11153 unsigned char downTypes[NBMAXNEIGHBORS];
11154 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11155 for (int n = 0; n < nbNeighbors; n++)
11157 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11158 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11159 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11161 DownIdType face(downIds[n], downTypes[n]);
11162 if (!faceDomains.count(face))
11163 faceDomains[face] = emptyMap; // create an empty entry for face
11164 if (!faceDomains[face].count(idom))
11166 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11167 celldom[vtkId] = idom;
11168 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11175 //MESSAGE("Number of shared faces " << faceDomains.size());
11176 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11178 // --- explore the shared faces domain by domain,
11179 // explore the nodes of the face and see if they belong to a cell in the domain,
11180 // which has only a node or an edge on the border (not a shared face)
11182 for (int idomain = 0; idomain < theElems.size(); idomain++)
11184 //MESSAGE("Domain " << idomain);
11185 const TIDSortedElemSet& domain = theElems[idomain];
11186 itface = faceDomains.begin();
11187 for (; itface != faceDomains.end(); ++itface)
11189 std::map<int, int> domvol = itface->second;
11190 if (!domvol.count(idomain))
11192 DownIdType face = itface->first;
11193 //MESSAGE(" --- face " << face.cellId);
11194 std::set<int> oldNodes;
11196 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11197 std::set<int>::iterator itn = oldNodes.begin();
11198 for (; itn != oldNodes.end(); ++itn)
11201 //MESSAGE(" node " << oldId);
11202 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11203 for (int i=0; i<l.ncells; i++)
11205 int vtkId = l.cells[i];
11206 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11207 if (!domain.count(anElem))
11209 int vtkType = grid->GetCellType(vtkId);
11210 int downId = grid->CellIdToDownId(vtkId);
11213 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11214 continue; // not OK at this stage of the algorithm:
11215 //no cells created after BuildDownWardConnectivity
11217 DownIdType aCell(downId, vtkType);
11218 if (!cellDomains.count(aCell))
11219 cellDomains[aCell] = emptyMap; // create an empty entry for cell
11220 cellDomains[aCell][idomain] = vtkId;
11221 celldom[vtkId] = idomain;
11222 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11228 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11229 // for each shared face, get the nodes
11230 // for each node, for each domain of the face, create a clone of the node
11232 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11233 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11234 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11236 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11237 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11238 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11240 for (int idomain = 0; idomain < theElems.size(); idomain++)
11242 itface = faceDomains.begin();
11243 for (; itface != faceDomains.end(); ++itface)
11245 std::map<int, int> domvol = itface->second;
11246 if (!domvol.count(idomain))
11248 DownIdType face = itface->first;
11249 //MESSAGE(" --- face " << face.cellId);
11250 std::set<int> oldNodes;
11252 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11253 std::set<int>::iterator itn = oldNodes.begin();
11254 for (; itn != oldNodes.end(); ++itn)
11257 //MESSAGE("-+-+-a node " << oldId);
11258 if (!nodeDomains.count(oldId))
11259 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11260 if (nodeDomains[oldId].empty())
11262 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11263 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11265 std::map<int, int>::iterator itdom = domvol.begin();
11266 for (; itdom != domvol.end(); ++itdom)
11268 int idom = itdom->first;
11269 //MESSAGE(" domain " << idom);
11270 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11272 if (nodeDomains[oldId].size() >= 2) // a multiple node
11274 vector<int> orderedDoms;
11275 //MESSAGE("multiple node " << oldId);
11276 if (mutipleNodes.count(oldId))
11277 orderedDoms = mutipleNodes[oldId];
11280 map<int,int>::iterator it = nodeDomains[oldId].begin();
11281 for (; it != nodeDomains[oldId].end(); ++it)
11282 orderedDoms.push_back(it->first);
11284 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11285 //stringstream txt;
11286 //for (int i=0; i<orderedDoms.size(); i++)
11287 // txt << orderedDoms[i] << " ";
11288 //MESSAGE("orderedDoms " << txt.str());
11289 mutipleNodes[oldId] = orderedDoms;
11291 double *coords = grid->GetPoint(oldId);
11292 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11293 int newId = newNode->getVtkId();
11294 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11295 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11302 for (int idomain = 0; idomain < theElems.size(); idomain++)
11304 itface = faceDomains.begin();
11305 for (; itface != faceDomains.end(); ++itface)
11307 std::map<int, int> domvol = itface->second;
11308 if (!domvol.count(idomain))
11310 DownIdType face = itface->first;
11311 //MESSAGE(" --- face " << face.cellId);
11312 std::set<int> oldNodes;
11314 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11315 int nbMultipleNodes = 0;
11316 std::set<int>::iterator itn = oldNodes.begin();
11317 for (; itn != oldNodes.end(); ++itn)
11320 if (mutipleNodes.count(oldId))
11323 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11325 //MESSAGE("multiple Nodes detected on a shared face");
11326 int downId = itface->first.cellId;
11327 unsigned char cellType = itface->first.cellType;
11328 // --- shared edge or shared face ?
11329 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11332 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11333 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11334 if (mutipleNodes.count(nodes[i]))
11335 if (!mutipleNodesToFace.count(nodes[i]))
11336 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11338 else // shared face (between two volumes)
11340 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11341 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11342 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11343 for (int ie =0; ie < nbEdges; ie++)
11346 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11347 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11349 vector<int> vn0 = mutipleNodes[nodes[0]];
11350 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11352 for (int i0 = 0; i0 < vn0.size(); i0++)
11353 for (int i1 = 0; i1 < vn1.size(); i1++)
11354 if (vn0[i0] == vn1[i1])
11355 doms.push_back(vn0[i0]);
11356 if (doms.size() >2)
11358 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11359 double *coords = grid->GetPoint(nodes[0]);
11360 gp_Pnt p0(coords[0], coords[1], coords[2]);
11361 coords = grid->GetPoint(nodes[nbNodes - 1]);
11362 gp_Pnt p1(coords[0], coords[1], coords[2]);
11364 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11365 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11366 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11367 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11368 for (int id=0; id < doms.size(); id++)
11370 int idom = doms[id];
11371 for (int ivol=0; ivol<nbvol; ivol++)
11373 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11374 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11375 if (theElems[idom].count(elem))
11377 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11378 domvol[idom] = svol;
11379 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11381 vtkIdType npts = 0;
11382 vtkIdType* pts = 0;
11383 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11384 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11387 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11388 angleDom[idom] = 0;
11392 gp_Pnt g(values[0], values[1], values[2]);
11393 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11394 //MESSAGE(" angle=" << angleDom[idom]);
11400 map<double, int> sortedDom; // sort domains by angle
11401 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11402 sortedDom[ia->second] = ia->first;
11403 vector<int> vnodes;
11405 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11407 vdom.push_back(ib->second);
11408 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11410 for (int ino = 0; ino < nbNodes; ino++)
11411 vnodes.push_back(nodes[ino]);
11412 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11421 // --- iterate on shared faces (volumes to modify, face to extrude)
11422 // get node id's of the face (id SMDS = id VTK)
11423 // create flat element with old and new nodes if requested
11425 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11426 // (domain1 X domain2) = domain1 + MAXINT*domain2
11428 std::map<int, std::map<long,int> > nodeQuadDomains;
11429 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11431 if (createJointElems)
11434 string joints2DName = "joints2D";
11435 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11436 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11437 string joints3DName = "joints3D";
11438 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11439 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11441 itface = faceDomains.begin();
11442 for (; itface != faceDomains.end(); ++itface)
11444 DownIdType face = itface->first;
11445 std::set<int> oldNodes;
11446 std::set<int>::iterator itn;
11448 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11450 std::map<int, int> domvol = itface->second;
11451 std::map<int, int>::iterator itdom = domvol.begin();
11452 int dom1 = itdom->first;
11453 int vtkVolId = itdom->second;
11455 int dom2 = itdom->first;
11456 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11458 stringstream grpname;
11461 grpname << dom1 << "_" << dom2;
11463 grpname << dom2 << "_" << dom1;
11464 string namegrp = grpname.str();
11465 if (!mapOfJunctionGroups.count(namegrp))
11466 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11467 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11469 sgrp->Add(vol->GetID());
11470 if (vol->GetType() == SMDSAbs_Volume)
11471 joints3DGrp->Add(vol->GetID());
11472 else if (vol->GetType() == SMDSAbs_Face)
11473 joints2DGrp->Add(vol->GetID());
11477 // --- create volumes on multiple domain intersection if requested
11478 // iterate on mutipleNodesToFace
11479 // iterate on edgesMultiDomains
11481 if (createJointElems)
11483 // --- iterate on mutipleNodesToFace
11485 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11486 for (; itn != mutipleNodesToFace.end(); ++itn)
11488 int node = itn->first;
11489 vector<int> orderDom = itn->second;
11490 vector<vtkIdType> orderedNodes;
11491 for (int idom = 0; idom <orderDom.size(); idom++)
11492 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11493 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11495 stringstream grpname;
11497 grpname << 0 << "_" << 0;
11499 string namegrp = grpname.str();
11500 if (!mapOfJunctionGroups.count(namegrp))
11501 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11502 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11504 sgrp->Add(face->GetID());
11507 // --- iterate on edgesMultiDomains
11509 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11510 for (; ite != edgesMultiDomains.end(); ++ite)
11512 vector<int> nodes = ite->first;
11513 vector<int> orderDom = ite->second;
11514 vector<vtkIdType> orderedNodes;
11515 if (nodes.size() == 2)
11517 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11518 for (int ino=0; ino < nodes.size(); ino++)
11519 if (orderDom.size() == 3)
11520 for (int idom = 0; idom <orderDom.size(); idom++)
11521 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11523 for (int idom = orderDom.size()-1; idom >=0; idom--)
11524 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11525 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11528 string namegrp = "jointsMultiples";
11529 if (!mapOfJunctionGroups.count(namegrp))
11530 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11531 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11533 sgrp->Add(vol->GetID());
11537 INFOS("Quadratic multiple joints not implemented");
11538 // TODO quadratic nodes
11543 // --- list the explicit faces and edges of the mesh that need to be modified,
11544 // i.e. faces and edges built with one or more duplicated nodes.
11545 // associate these faces or edges to their corresponding domain.
11546 // only the first domain found is kept when a face or edge is shared
11548 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11549 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11550 faceOrEdgeDom.clear();
11553 for (int idomain = 0; idomain < theElems.size(); idomain++)
11555 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11556 for (; itnod != nodeDomains.end(); ++itnod)
11558 int oldId = itnod->first;
11559 //MESSAGE(" node " << oldId);
11560 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11561 for (int i = 0; i < l.ncells; i++)
11563 int vtkId = l.cells[i];
11564 int vtkType = grid->GetCellType(vtkId);
11565 int downId = grid->CellIdToDownId(vtkId);
11567 continue; // new cells: not to be modified
11568 DownIdType aCell(downId, vtkType);
11569 int volParents[1000];
11570 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11571 for (int j = 0; j < nbvol; j++)
11572 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11573 if (!feDom.count(vtkId))
11575 feDom[vtkId] = idomain;
11576 faceOrEdgeDom[aCell] = emptyMap;
11577 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11578 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11579 // << " type " << vtkType << " downId " << downId);
11585 // --- iterate on shared faces (volumes to modify, face to extrude)
11586 // get node id's of the face
11587 // replace old nodes by new nodes in volumes, and update inverse connectivity
11589 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11590 for (int m=0; m<3; m++)
11592 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11593 itface = (*amap).begin();
11594 for (; itface != (*amap).end(); ++itface)
11596 DownIdType face = itface->first;
11597 std::set<int> oldNodes;
11598 std::set<int>::iterator itn;
11600 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11601 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11602 std::map<int, int> localClonedNodeIds;
11604 std::map<int, int> domvol = itface->second;
11605 std::map<int, int>::iterator itdom = domvol.begin();
11606 for (; itdom != domvol.end(); ++itdom)
11608 int idom = itdom->first;
11609 int vtkVolId = itdom->second;
11610 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11611 localClonedNodeIds.clear();
11612 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11615 if (nodeDomains[oldId].count(idom))
11617 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11618 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11621 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11626 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11627 grid->BuildLinks();
11635 * \brief Double nodes on some external faces and create flat elements.
11636 * Flat elements are mainly used by some types of mechanic calculations.
11638 * Each group of the list must be constituted of faces.
11639 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11640 * @param theElems - list of groups of faces, where a group of faces is a set of
11641 * SMDS_MeshElements sorted by Id.
11642 * @return TRUE if operation has been completed successfully, FALSE otherwise
11644 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11646 MESSAGE("-------------------------------------------------");
11647 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11648 MESSAGE("-------------------------------------------------");
11650 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11652 // --- For each group of faces
11653 // duplicate the nodes, create a flat element based on the face
11654 // replace the nodes of the faces by their clones
11656 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11657 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11658 clonedNodes.clear();
11659 intermediateNodes.clear();
11660 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11661 mapOfJunctionGroups.clear();
11663 for (int idom = 0; idom < theElems.size(); idom++)
11665 const TIDSortedElemSet& domain = theElems[idom];
11666 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11667 for (; elemItr != domain.end(); ++elemItr)
11669 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11670 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11673 // MESSAGE("aFace=" << aFace->GetID());
11674 bool isQuad = aFace->IsQuadratic();
11675 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11677 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11679 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11680 while (nodeIt->more())
11682 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11683 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11685 ln2.push_back(node);
11687 ln0.push_back(node);
11689 const SMDS_MeshNode* clone = 0;
11690 if (!clonedNodes.count(node))
11692 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11693 clonedNodes[node] = clone;
11696 clone = clonedNodes[node];
11699 ln3.push_back(clone);
11701 ln1.push_back(clone);
11703 const SMDS_MeshNode* inter = 0;
11704 if (isQuad && (!isMedium))
11706 if (!intermediateNodes.count(node))
11708 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11709 intermediateNodes[node] = inter;
11712 inter = intermediateNodes[node];
11713 ln4.push_back(inter);
11717 // --- extrude the face
11719 vector<const SMDS_MeshNode*> ln;
11720 SMDS_MeshVolume* vol = 0;
11721 vtkIdType aType = aFace->GetVtkType();
11725 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11726 // MESSAGE("vol prism " << vol->GetID());
11727 ln.push_back(ln1[0]);
11728 ln.push_back(ln1[1]);
11729 ln.push_back(ln1[2]);
11732 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11733 // MESSAGE("vol hexa " << vol->GetID());
11734 ln.push_back(ln1[0]);
11735 ln.push_back(ln1[1]);
11736 ln.push_back(ln1[2]);
11737 ln.push_back(ln1[3]);
11739 case VTK_QUADRATIC_TRIANGLE:
11740 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11741 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11742 // MESSAGE("vol quad prism " << vol->GetID());
11743 ln.push_back(ln1[0]);
11744 ln.push_back(ln1[1]);
11745 ln.push_back(ln1[2]);
11746 ln.push_back(ln3[0]);
11747 ln.push_back(ln3[1]);
11748 ln.push_back(ln3[2]);
11750 case VTK_QUADRATIC_QUAD:
11751 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11752 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11753 // ln4[0], ln4[1], ln4[2], ln4[3]);
11754 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11755 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11756 ln4[0], ln4[1], ln4[2], ln4[3]);
11757 // MESSAGE("vol quad hexa " << vol->GetID());
11758 ln.push_back(ln1[0]);
11759 ln.push_back(ln1[1]);
11760 ln.push_back(ln1[2]);
11761 ln.push_back(ln1[3]);
11762 ln.push_back(ln3[0]);
11763 ln.push_back(ln3[1]);
11764 ln.push_back(ln3[2]);
11765 ln.push_back(ln3[3]);
11775 stringstream grpname;
11779 string namegrp = grpname.str();
11780 if (!mapOfJunctionGroups.count(namegrp))
11781 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11782 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11784 sgrp->Add(vol->GetID());
11787 // --- modify the face
11789 aFace->ChangeNodes(&ln[0], ln.size());
11796 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11797 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11798 * groups of faces to remove inside the object, (idem edges).
11799 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11801 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11802 const TopoDS_Shape& theShape,
11803 SMESH_NodeSearcher* theNodeSearcher,
11804 const char* groupName,
11805 std::vector<double>& nodesCoords,
11806 std::vector<std::vector<int> >& listOfListOfNodes)
11808 MESSAGE("--------------------------------");
11809 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11810 MESSAGE("--------------------------------");
11812 // --- zone of volumes to remove is given :
11813 // 1 either by a geom shape (one or more vertices) and a radius,
11814 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11815 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11816 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11817 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11818 // defined by it's name.
11820 SMESHDS_GroupBase* groupDS = 0;
11821 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11822 while ( groupIt->more() )
11825 SMESH_Group * group = groupIt->next();
11826 if ( !group ) continue;
11827 groupDS = group->GetGroupDS();
11828 if ( !groupDS || groupDS->IsEmpty() ) continue;
11829 std::string grpName = group->GetName();
11830 //MESSAGE("grpName=" << grpName);
11831 if (grpName == groupName)
11837 bool isNodeGroup = false;
11838 bool isNodeCoords = false;
11841 if (groupDS->GetType() != SMDSAbs_Node)
11843 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11846 if (nodesCoords.size() > 0)
11847 isNodeCoords = true; // a list o nodes given by their coordinates
11848 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11850 // --- define groups to build
11852 int idg; // --- group of SMDS volumes
11853 string grpvName = groupName;
11854 grpvName += "_vol";
11855 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11858 MESSAGE("group not created " << grpvName);
11861 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11863 int idgs; // --- group of SMDS faces on the skin
11864 string grpsName = groupName;
11865 grpsName += "_skin";
11866 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11869 MESSAGE("group not created " << grpsName);
11872 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11874 int idgi; // --- group of SMDS faces internal (several shapes)
11875 string grpiName = groupName;
11876 grpiName += "_internalFaces";
11877 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11880 MESSAGE("group not created " << grpiName);
11883 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11885 int idgei; // --- group of SMDS faces internal (several shapes)
11886 string grpeiName = groupName;
11887 grpeiName += "_internalEdges";
11888 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11891 MESSAGE("group not created " << grpeiName);
11894 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11896 // --- build downward connectivity
11898 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11899 meshDS->BuildDownWardConnectivity(true);
11900 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11902 // --- set of volumes detected inside
11904 std::set<int> setOfInsideVol;
11905 std::set<int> setOfVolToCheck;
11907 std::vector<gp_Pnt> gpnts;
11910 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11912 MESSAGE("group of nodes provided");
11913 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11914 while ( elemIt->more() )
11916 const SMDS_MeshElement* elem = elemIt->next();
11919 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11922 SMDS_MeshElement* vol = 0;
11923 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11924 while (volItr->more())
11926 vol = (SMDS_MeshElement*)volItr->next();
11927 setOfInsideVol.insert(vol->getVtkId());
11928 sgrp->Add(vol->GetID());
11932 else if (isNodeCoords)
11934 MESSAGE("list of nodes coordinates provided");
11937 while (i < nodesCoords.size()-2)
11939 double x = nodesCoords[i++];
11940 double y = nodesCoords[i++];
11941 double z = nodesCoords[i++];
11942 gp_Pnt p = gp_Pnt(x, y ,z);
11943 gpnts.push_back(p);
11944 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11947 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11949 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11950 TopTools_IndexedMapOfShape vertexMap;
11951 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11952 gp_Pnt p = gp_Pnt(0,0,0);
11953 if (vertexMap.Extent() < 1)
11956 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11958 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11959 p = BRep_Tool::Pnt(vertex);
11960 gpnts.push_back(p);
11961 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11965 if (gpnts.size() > 0)
11968 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11970 nodeId = startNode->GetID();
11971 MESSAGE("nodeId " << nodeId);
11973 double radius2 = radius*radius;
11974 MESSAGE("radius2 " << radius2);
11976 // --- volumes on start node
11978 setOfVolToCheck.clear();
11979 SMDS_MeshElement* startVol = 0;
11980 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11981 while (volItr->more())
11983 startVol = (SMDS_MeshElement*)volItr->next();
11984 setOfVolToCheck.insert(startVol->getVtkId());
11986 if (setOfVolToCheck.empty())
11988 MESSAGE("No volumes found");
11992 // --- starting with central volumes then their neighbors, check if they are inside
11993 // or outside the domain, until no more new neighbor volume is inside.
11994 // Fill the group of inside volumes
11996 std::map<int, double> mapOfNodeDistance2;
11997 mapOfNodeDistance2.clear();
11998 std::set<int> setOfOutsideVol;
11999 while (!setOfVolToCheck.empty())
12001 std::set<int>::iterator it = setOfVolToCheck.begin();
12003 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12004 bool volInside = false;
12005 vtkIdType npts = 0;
12006 vtkIdType* pts = 0;
12007 grid->GetCellPoints(vtkId, npts, pts);
12008 for (int i=0; i<npts; i++)
12010 double distance2 = 0;
12011 if (mapOfNodeDistance2.count(pts[i]))
12013 distance2 = mapOfNodeDistance2[pts[i]];
12014 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12018 double *coords = grid->GetPoint(pts[i]);
12019 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12021 for (int j=0; j<gpnts.size(); j++)
12023 double d2 = aPoint.SquareDistance(gpnts[j]);
12024 if (d2 < distance2)
12027 if (distance2 < radius2)
12031 mapOfNodeDistance2[pts[i]] = distance2;
12032 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12034 if (distance2 < radius2)
12036 volInside = true; // one or more nodes inside the domain
12037 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12043 setOfInsideVol.insert(vtkId);
12044 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12045 int neighborsVtkIds[NBMAXNEIGHBORS];
12046 int downIds[NBMAXNEIGHBORS];
12047 unsigned char downTypes[NBMAXNEIGHBORS];
12048 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12049 for (int n = 0; n < nbNeighbors; n++)
12050 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12051 setOfVolToCheck.insert(neighborsVtkIds[n]);
12055 setOfOutsideVol.insert(vtkId);
12056 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12058 setOfVolToCheck.erase(vtkId);
12062 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12063 // If yes, add the volume to the inside set
12065 bool addedInside = true;
12066 std::set<int> setOfVolToReCheck;
12067 while (addedInside)
12069 MESSAGE(" --------------------------- re check");
12070 addedInside = false;
12071 std::set<int>::iterator itv = setOfInsideVol.begin();
12072 for (; itv != setOfInsideVol.end(); ++itv)
12075 int neighborsVtkIds[NBMAXNEIGHBORS];
12076 int downIds[NBMAXNEIGHBORS];
12077 unsigned char downTypes[NBMAXNEIGHBORS];
12078 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12079 for (int n = 0; n < nbNeighbors; n++)
12080 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12081 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12083 setOfVolToCheck = setOfVolToReCheck;
12084 setOfVolToReCheck.clear();
12085 while (!setOfVolToCheck.empty())
12087 std::set<int>::iterator it = setOfVolToCheck.begin();
12089 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12091 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12092 int countInside = 0;
12093 int neighborsVtkIds[NBMAXNEIGHBORS];
12094 int downIds[NBMAXNEIGHBORS];
12095 unsigned char downTypes[NBMAXNEIGHBORS];
12096 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12097 for (int n = 0; n < nbNeighbors; n++)
12098 if (setOfInsideVol.count(neighborsVtkIds[n]))
12100 MESSAGE("countInside " << countInside);
12101 if (countInside > 1)
12103 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12104 setOfInsideVol.insert(vtkId);
12105 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12106 addedInside = true;
12109 setOfVolToReCheck.insert(vtkId);
12111 setOfVolToCheck.erase(vtkId);
12115 // --- map of Downward faces at the boundary, inside the global volume
12116 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12117 // fill group of SMDS faces inside the volume (when several volume shapes)
12118 // fill group of SMDS faces on the skin of the global volume (if skin)
12120 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12121 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12122 std::set<int>::iterator it = setOfInsideVol.begin();
12123 for (; it != setOfInsideVol.end(); ++it)
12126 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12127 int neighborsVtkIds[NBMAXNEIGHBORS];
12128 int downIds[NBMAXNEIGHBORS];
12129 unsigned char downTypes[NBMAXNEIGHBORS];
12130 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12131 for (int n = 0; n < nbNeighbors; n++)
12133 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12134 if (neighborDim == 3)
12136 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12138 DownIdType face(downIds[n], downTypes[n]);
12139 boundaryFaces[face] = vtkId;
12141 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12142 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12143 if (vtkFaceId >= 0)
12145 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12146 // find also the smds edges on this face
12147 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12148 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12149 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12150 for (int i = 0; i < nbEdges; i++)
12152 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12153 if (vtkEdgeId >= 0)
12154 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12158 else if (neighborDim == 2) // skin of the volume
12160 DownIdType face(downIds[n], downTypes[n]);
12161 skinFaces[face] = vtkId;
12162 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12163 if (vtkFaceId >= 0)
12164 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12169 // --- identify the edges constituting the wire of each subshape on the skin
12170 // define polylines with the nodes of edges, equivalent to wires
12171 // project polylines on subshapes, and partition, to get geom faces
12173 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12174 std::set<int> emptySet;
12176 std::set<int> shapeIds;
12178 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12179 while (itelem->more())
12181 const SMDS_MeshElement *elem = itelem->next();
12182 int shapeId = elem->getshapeId();
12183 int vtkId = elem->getVtkId();
12184 if (!shapeIdToVtkIdSet.count(shapeId))
12186 shapeIdToVtkIdSet[shapeId] = emptySet;
12187 shapeIds.insert(shapeId);
12189 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12192 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12193 std::set<DownIdType, DownIdCompare> emptyEdges;
12194 emptyEdges.clear();
12196 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12197 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12199 int shapeId = itShape->first;
12200 MESSAGE(" --- Shape ID --- "<< shapeId);
12201 shapeIdToEdges[shapeId] = emptyEdges;
12203 std::vector<int> nodesEdges;
12205 std::set<int>::iterator its = itShape->second.begin();
12206 for (; its != itShape->second.end(); ++its)
12209 MESSAGE(" " << vtkId);
12210 int neighborsVtkIds[NBMAXNEIGHBORS];
12211 int downIds[NBMAXNEIGHBORS];
12212 unsigned char downTypes[NBMAXNEIGHBORS];
12213 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12214 for (int n = 0; n < nbNeighbors; n++)
12216 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12218 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12219 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12220 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12222 DownIdType edge(downIds[n], downTypes[n]);
12223 if (!shapeIdToEdges[shapeId].count(edge))
12225 shapeIdToEdges[shapeId].insert(edge);
12227 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12228 nodesEdges.push_back(vtkNodeId[0]);
12229 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12230 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12236 std::list<int> order;
12238 if (nodesEdges.size() > 0)
12240 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12241 nodesEdges[0] = -1;
12242 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12243 nodesEdges[1] = -1; // do not reuse this edge
12247 int nodeTofind = order.back(); // try first to push back
12249 for (i = 0; i<nodesEdges.size(); i++)
12250 if (nodesEdges[i] == nodeTofind)
12252 if (i == nodesEdges.size())
12253 found = false; // no follower found on back
12256 if (i%2) // odd ==> use the previous one
12257 if (nodesEdges[i-1] < 0)
12261 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12262 nodesEdges[i-1] = -1;
12264 else // even ==> use the next one
12265 if (nodesEdges[i+1] < 0)
12269 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12270 nodesEdges[i+1] = -1;
12275 // try to push front
12277 nodeTofind = order.front(); // try to push front
12278 for (i = 0; i<nodesEdges.size(); i++)
12279 if (nodesEdges[i] == nodeTofind)
12281 if (i == nodesEdges.size())
12283 found = false; // no predecessor found on front
12286 if (i%2) // odd ==> use the previous one
12287 if (nodesEdges[i-1] < 0)
12291 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12292 nodesEdges[i-1] = -1;
12294 else // even ==> use the next one
12295 if (nodesEdges[i+1] < 0)
12299 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12300 nodesEdges[i+1] = -1;
12306 std::vector<int> nodes;
12307 nodes.push_back(shapeId);
12308 std::list<int>::iterator itl = order.begin();
12309 for (; itl != order.end(); itl++)
12311 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12312 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12314 listOfListOfNodes.push_back(nodes);
12317 // partition geom faces with blocFissure
12318 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12319 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12325 //================================================================================
12327 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12328 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12329 * \return TRUE if operation has been completed successfully, FALSE otherwise
12331 //================================================================================
12333 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12335 // iterates on volume elements and detect all free faces on them
12336 SMESHDS_Mesh* aMesh = GetMeshDS();
12339 //bool res = false;
12340 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12341 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12344 const SMDS_MeshVolume* volume = vIt->next();
12345 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12346 vTool.SetExternalNormal();
12347 //const bool isPoly = volume->IsPoly();
12348 const int iQuad = volume->IsQuadratic();
12349 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12351 if (!vTool.IsFreeFace(iface))
12354 vector<const SMDS_MeshNode *> nodes;
12355 int nbFaceNodes = vTool.NbFaceNodes(iface);
12356 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12358 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12359 nodes.push_back(faceNodes[inode]);
12360 if (iQuad) { // add medium nodes
12361 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12362 nodes.push_back(faceNodes[inode]);
12363 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12364 nodes.push_back(faceNodes[8]);
12366 // add new face based on volume nodes
12367 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12369 continue; // face already exsist
12371 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12375 return ( nbFree==(nbExisted+nbCreated) );
12380 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12382 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12384 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12387 //================================================================================
12389 * \brief Creates missing boundary elements
12390 * \param elements - elements whose boundary is to be checked
12391 * \param dimension - defines type of boundary elements to create
12392 * \param group - a group to store created boundary elements in
12393 * \param targetMesh - a mesh to store created boundary elements in
12394 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12395 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12396 * boundary elements will be copied into the targetMesh
12397 * \param toAddExistingBondary - if true, not only new but also pre-existing
12398 * boundary elements will be added into the new group
12399 * \param aroundElements - if true, elements will be created on boundary of given
12400 * elements else, on boundary of the whole mesh.
12401 * \return nb of added boundary elements
12403 //================================================================================
12405 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12406 Bnd_Dimension dimension,
12407 SMESH_Group* group/*=0*/,
12408 SMESH_Mesh* targetMesh/*=0*/,
12409 bool toCopyElements/*=false*/,
12410 bool toCopyExistingBoundary/*=false*/,
12411 bool toAddExistingBondary/*= false*/,
12412 bool aroundElements/*= false*/)
12414 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12415 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12416 // hope that all elements are of the same type, do not check them all
12417 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12418 throw SALOME_Exception(LOCALIZED("wrong element type"));
12421 toCopyElements = toCopyExistingBoundary = false;
12423 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12424 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12425 int nbAddedBnd = 0;
12427 // editor adding present bnd elements and optionally holding elements to add to the group
12428 SMESH_MeshEditor* presentEditor;
12429 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12430 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12432 SMESH_MesherHelper helper( *myMesh );
12433 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12434 SMDS_VolumeTool vTool;
12435 TIDSortedElemSet avoidSet;
12436 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12439 typedef vector<const SMDS_MeshNode*> TConnectivity;
12441 SMDS_ElemIteratorPtr eIt;
12442 if (elements.empty())
12443 eIt = aMesh->elementsIterator(elemType);
12445 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12447 while (eIt->more())
12449 const SMDS_MeshElement* elem = eIt->next();
12450 const int iQuad = elem->IsQuadratic();
12452 // ------------------------------------------------------------------------------------
12453 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12454 // ------------------------------------------------------------------------------------
12455 vector<const SMDS_MeshElement*> presentBndElems;
12456 vector<TConnectivity> missingBndElems;
12457 TConnectivity nodes;
12458 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12460 vTool.SetExternalNormal();
12461 const SMDS_MeshElement* otherVol = 0;
12462 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12464 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12465 ( !aroundElements || elements.count( otherVol )))
12467 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12468 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12469 if ( missType == SMDSAbs_Edge ) // boundary edges
12471 nodes.resize( 2+iQuad );
12472 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12474 for ( int j = 0; j < nodes.size(); ++j )
12476 if ( const SMDS_MeshElement* edge =
12477 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12478 presentBndElems.push_back( edge );
12480 missingBndElems.push_back( nodes );
12483 else // boundary face
12486 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12487 nodes.push_back( nn[inode] );
12488 if (iQuad) // add medium nodes
12489 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12490 nodes.push_back( nn[inode] );
12491 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12493 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12495 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12496 SMDSAbs_Face, /*noMedium=*/false ))
12497 presentBndElems.push_back( f );
12499 missingBndElems.push_back( nodes );
12501 if ( targetMesh != myMesh )
12503 // add 1D elements on face boundary to be added to a new mesh
12504 const SMDS_MeshElement* edge;
12505 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12508 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12510 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12511 if ( edge && avoidSet.insert( edge ).second )
12512 presentBndElems.push_back( edge );
12518 else // elem is a face ------------------------------------------
12520 avoidSet.clear(), avoidSet.insert( elem );
12521 int nbNodes = elem->NbCornerNodes();
12522 nodes.resize( 2 /*+ iQuad*/);
12523 for ( int i = 0; i < nbNodes; i++ )
12525 nodes[0] = elem->GetNode(i);
12526 nodes[1] = elem->GetNode((i+1)%nbNodes);
12527 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12528 continue; // not free link
12531 //nodes[2] = elem->GetNode( i + nbNodes );
12532 if ( const SMDS_MeshElement* edge =
12533 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12534 presentBndElems.push_back( edge );
12536 missingBndElems.push_back( nodes );
12540 // ---------------------------------
12541 // 2. Add missing boundary elements
12542 // ---------------------------------
12543 if ( targetMesh != myMesh )
12544 // instead of making a map of nodes in this mesh and targetMesh,
12545 // we create nodes with same IDs.
12546 for ( int i = 0; i < missingBndElems.size(); ++i )
12548 TConnectivity& srcNodes = missingBndElems[i];
12549 TConnectivity nodes( srcNodes.size() );
12550 for ( inode = 0; inode < nodes.size(); ++inode )
12551 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12552 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12554 /*noMedium=*/false))
12556 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12560 for ( int i = 0; i < missingBndElems.size(); ++i )
12562 TConnectivity& nodes = missingBndElems[i];
12563 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12565 /*noMedium=*/false))
12567 SMDS_MeshElement* elem =
12568 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12571 // try to set a new element to a shape
12572 if ( myMesh->HasShapeToMesh() )
12575 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12576 const int nbN = nodes.size() / (iQuad+1 );
12577 for ( inode = 0; inode < nbN && ok; ++inode )
12579 pair<int, TopAbs_ShapeEnum> i_stype =
12580 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12581 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12582 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12584 if ( ok && mediumShapes.size() > 1 )
12586 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12587 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12588 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12590 if (( ok = ( stype_i->first != stype_i_0.first )))
12591 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12592 aMesh->IndexToShape( stype_i_0.second ));
12595 if ( ok && mediumShapes.begin()->first == missShapeType )
12596 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12600 // ----------------------------------
12601 // 3. Copy present boundary elements
12602 // ----------------------------------
12603 if ( toCopyExistingBoundary )
12604 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12606 const SMDS_MeshElement* e = presentBndElems[i];
12607 TConnectivity nodes( e->NbNodes() );
12608 for ( inode = 0; inode < nodes.size(); ++inode )
12609 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12610 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12612 else // store present elements to add them to a group
12613 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12615 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12618 } // loop on given elements
12620 // ---------------------------------------------
12621 // 4. Fill group with boundary elements
12622 // ---------------------------------------------
12625 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12626 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12627 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12629 tgtEditor.myLastCreatedElems.Clear();
12630 tgtEditor2.myLastCreatedElems.Clear();
12632 // -----------------------
12633 // 5. Copy given elements
12634 // -----------------------
12635 if ( toCopyElements && targetMesh != myMesh )
12637 if (elements.empty())
12638 eIt = aMesh->elementsIterator(elemType);
12640 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12641 while (eIt->more())
12643 const SMDS_MeshElement* elem = eIt->next();
12644 TConnectivity nodes( elem->NbNodes() );
12645 for ( inode = 0; inode < nodes.size(); ++inode )
12646 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12647 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12649 tgtEditor.myLastCreatedElems.Clear();