+ // fill maps
+ for ( i = 0; i < 3; i++ ) {
+ SMESH_TLink link( aNodes[i], aNodes[i+1] );
+ // check if elements sharing a link can be fused
+ itLE = mapLi_listEl.find( link );
+ if ( itLE != mapLi_listEl.end() ) {
+ if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
+ continue;
+ const SMDS_MeshElement* elem2 = (*itLE).second.front();
+ //if ( FindShape( elem ) != FindShape( elem2 ))
+ // continue; // do not fuse triangles laying on different shapes
+ if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
+ continue; // avoid making badly shaped quads
+ (*itLE).second.push_back( elem );
+ }
+ else {
+ mapLi_listEl[ link ].push_back( elem );
+ }
+ mapEl_setLi [ elem ].insert( link );
+ }
+ }
+ // Clean the maps from the links shared by a sole element, ie
+ // links to which only one element is bound in mapLi_listEl
+
+ for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
+ int nbElems = (*itLE).second.size();
+ if ( nbElems < 2 ) {
+ const SMDS_MeshElement* elem = (*itLE).second.front();
+ SMESH_TLink link = (*itLE).first;
+ mapEl_setLi[ elem ].erase( link );
+ if ( mapEl_setLi[ elem ].empty() )
+ mapEl_setLi.erase( elem );
+ }
+ }
+
+ // Algo: fuse triangles into quadrangles
+
+ while ( ! mapEl_setLi.empty() ) {
+ // Look for the start element:
+ // the element having the least nb of shared links
+ const SMDS_MeshElement* startElem = 0;
+ int minNbLinks = 4;
+ for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
+ int nbLinks = (*itEL).second.size();
+ if ( nbLinks < minNbLinks ) {
+ startElem = (*itEL).first;
+ minNbLinks = nbLinks;
+ if ( minNbLinks == 1 )
+ break;
+ }
+ }
+
+ // search elements to fuse starting from startElem or links of elements
+ // fused earlyer - startLinks
+ list< SMESH_TLink > startLinks;
+ while ( startElem || !startLinks.empty() ) {
+ while ( !startElem && !startLinks.empty() ) {
+ // Get an element to start, by a link
+ SMESH_TLink linkId = startLinks.front();
+ startLinks.pop_front();
+ itLE = mapLi_listEl.find( linkId );
+ if ( itLE != mapLi_listEl.end() ) {
+ list< const SMDS_MeshElement* > & listElem = (*itLE).second;
+ list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
+ for ( ; itE != listElem.end() ; itE++ )
+ if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
+ startElem = (*itE);
+ mapLi_listEl.erase( itLE );
+ }
+ }
+
+ if ( startElem ) {
+ // Get candidates to be fused
+ const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
+ const SMESH_TLink *link12, *link13;
+ startElem = 0;
+ ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
+ set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
+ ASSERT( !setLi.empty() );
+ set< SMESH_TLink >::iterator itLi;
+ for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
+ {
+ const SMESH_TLink & link = (*itLi);
+ itLE = mapLi_listEl.find( link );
+ if ( itLE == mapLi_listEl.end() )
+ continue;
+
+ const SMDS_MeshElement* elem = (*itLE).second.front();
+ if ( elem == tr1 )
+ elem = (*itLE).second.back();
+ mapLi_listEl.erase( itLE );
+ if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
+ continue;
+ if ( tr2 ) {
+ tr3 = elem;
+ link13 = &link;
+ }
+ else {
+ tr2 = elem;
+ link12 = &link;
+ }
+
+ // add other links of elem to list of links to re-start from
+ set< SMESH_TLink >& links = mapEl_setLi[ elem ];
+ set< SMESH_TLink >::iterator it;
+ for ( it = links.begin(); it != links.end(); it++ ) {
+ const SMESH_TLink& link2 = (*it);
+ if ( link2 != link )
+ startLinks.push_back( link2 );
+ }
+ }
+
+ // Get nodes of possible quadrangles
+ const SMDS_MeshNode *n12 [4], *n13 [4];
+ bool Ok12 = false, Ok13 = false;
+ const SMDS_MeshNode *linkNode1, *linkNode2;
+ if(tr2) {
+ linkNode1 = link12->first;
+ linkNode2 = link12->second;
+ if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
+ Ok12 = true;
+ }
+ if(tr3) {
+ linkNode1 = link13->first;
+ linkNode2 = link13->second;
+ if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
+ Ok13 = true;
+ }
+
+ // Choose a pair to fuse
+ if ( Ok12 && Ok13 ) {
+ SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
+ SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
+ double aBadRate12 = getBadRate( &quad12, theCrit );
+ double aBadRate13 = getBadRate( &quad13, theCrit );
+ if ( aBadRate13 < aBadRate12 )
+ Ok12 = false;
+ else
+ Ok13 = false;
+ }
+
+ // Make quadrangles
+ // and remove fused elems and removed links from the maps
+ mapEl_setLi.erase( tr1 );
+ if ( Ok12 ) {
+ mapEl_setLi.erase( tr2 );
+ mapLi_listEl.erase( *link12 );
+ if(tr1->NbNodes()==3) {
+ const SMDS_MeshElement* newElem = 0;
+ newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
+ myLastCreatedElems.Append(newElem);
+ AddToSameGroups( newElem, tr1, aMesh );
+ int aShapeId = tr1->getshapeId();
+ if ( aShapeId )
+ {
+ aMesh->SetMeshElementOnShape( newElem, aShapeId );
+ }
+ aMesh->RemoveElement( tr1 );
+ aMesh->RemoveElement( tr2 );
+ }
+ else {
+ const SMDS_MeshNode* N1 [6];
+ const SMDS_MeshNode* N2 [6];
+ GetNodesFromTwoTria(tr1,tr2,N1,N2);
+ // now we receive following N1 and N2 (using numeration as above image)
+ // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
+ // i.e. first nodes from both arrays determ new diagonal
+ const SMDS_MeshNode* aNodes[8];
+ aNodes[0] = N1[0];
+ aNodes[1] = N1[1];
+ aNodes[2] = N2[0];
+ aNodes[3] = N2[1];
+ aNodes[4] = N1[3];
+ aNodes[5] = N2[5];
+ aNodes[6] = N2[3];
+ aNodes[7] = N1[5];
+ const SMDS_MeshElement* newElem = 0;
+ newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
+ aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
+ myLastCreatedElems.Append(newElem);
+ AddToSameGroups( newElem, tr1, aMesh );
+ int aShapeId = tr1->getshapeId();
+ if ( aShapeId )
+ {
+ aMesh->SetMeshElementOnShape( newElem, aShapeId );
+ }
+ aMesh->RemoveElement( tr1 );
+ aMesh->RemoveElement( tr2 );
+ // remove middle node (9)
+ GetMeshDS()->RemoveNode( N1[4] );
+ }
+ }
+ else if ( Ok13 ) {
+ mapEl_setLi.erase( tr3 );
+ mapLi_listEl.erase( *link13 );
+ if(tr1->NbNodes()==3) {
+ const SMDS_MeshElement* newElem = 0;
+ newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
+ myLastCreatedElems.Append(newElem);
+ AddToSameGroups( newElem, tr1, aMesh );
+ int aShapeId = tr1->getshapeId();
+ if ( aShapeId )
+ {
+ aMesh->SetMeshElementOnShape( newElem, aShapeId );
+ }
+ aMesh->RemoveElement( tr1 );
+ aMesh->RemoveElement( tr3 );
+ }
+ else {
+ const SMDS_MeshNode* N1 [6];
+ const SMDS_MeshNode* N2 [6];
+ GetNodesFromTwoTria(tr1,tr3,N1,N2);
+ // now we receive following N1 and N2 (using numeration as above image)
+ // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
+ // i.e. first nodes from both arrays determ new diagonal
+ const SMDS_MeshNode* aNodes[8];
+ aNodes[0] = N1[0];
+ aNodes[1] = N1[1];
+ aNodes[2] = N2[0];
+ aNodes[3] = N2[1];
+ aNodes[4] = N1[3];
+ aNodes[5] = N2[5];
+ aNodes[6] = N2[3];
+ aNodes[7] = N1[5];
+ const SMDS_MeshElement* newElem = 0;
+ newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
+ aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
+ myLastCreatedElems.Append(newElem);
+ AddToSameGroups( newElem, tr1, aMesh );
+ int aShapeId = tr1->getshapeId();
+ if ( aShapeId )
+ {
+ aMesh->SetMeshElementOnShape( newElem, aShapeId );
+ }
+ aMesh->RemoveElement( tr1 );
+ aMesh->RemoveElement( tr3 );
+ // remove middle node (9)
+ GetMeshDS()->RemoveNode( N1[4] );
+ }
+ }
+
+ // Next element to fuse: the rejected one
+ if ( tr3 )
+ startElem = Ok12 ? tr3 : tr2;
+
+ } // if ( startElem )
+ } // while ( startElem || !startLinks.empty() )
+ } // while ( ! mapEl_setLi.empty() )
+
+ return true;
+}
+
+
+/*#define DUMPSO(txt) \
+// cout << txt << endl;
+//=============================================================================
+//
+//
+//
+//=============================================================================
+static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
+{
+if ( i1 == i2 )
+return;
+int tmp = idNodes[ i1 ];
+idNodes[ i1 ] = idNodes[ i2 ];
+idNodes[ i2 ] = tmp;
+gp_Pnt Ptmp = P[ i1 ];
+P[ i1 ] = P[ i2 ];
+P[ i2 ] = Ptmp;
+DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
+}
+
+//=======================================================================
+//function : SortQuadNodes
+//purpose : Set 4 nodes of a quadrangle face in a good order.
+// Swap 1<->2 or 2<->3 nodes and correspondingly return
+// 1 or 2 else 0.
+//=======================================================================
+
+int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
+int idNodes[] )
+{
+ gp_Pnt P[4];
+ int i;
+ for ( i = 0; i < 4; i++ ) {
+ const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
+ if ( !n ) return 0;
+ P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
+ }
+
+ gp_Vec V1(P[0], P[1]);
+ gp_Vec V2(P[0], P[2]);
+ gp_Vec V3(P[0], P[3]);
+
+ gp_Vec Cross1 = V1 ^ V2;
+ gp_Vec Cross2 = V2 ^ V3;
+
+ i = 0;
+ if (Cross1.Dot(Cross2) < 0)
+ {
+ Cross1 = V2 ^ V1;
+ Cross2 = V1 ^ V3;
+
+ if (Cross1.Dot(Cross2) < 0)
+ i = 2;
+ else
+ i = 1;
+ swap ( i, i + 1, idNodes, P );
+
+ // for ( int ii = 0; ii < 4; ii++ ) {
+ // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
+ // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
+ // }
+ }
+ return i;
+}
+
+//=======================================================================
+//function : SortHexaNodes
+//purpose : Set 8 nodes of a hexahedron in a good order.
+// Return success status
+//=======================================================================
+
+bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
+ int idNodes[] )
+{
+ gp_Pnt P[8];
+ int i;
+ DUMPSO( "INPUT: ========================================");
+ for ( i = 0; i < 8; i++ ) {
+ const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
+ if ( !n ) return false;
+ P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
+ DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
+ }
+ DUMPSO( "========================================");
+
+
+ set<int> faceNodes; // ids of bottom face nodes, to be found
+ set<int> checkedId1; // ids of tried 2-nd nodes
+ Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
+ const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
+ int iMin, iLoop1 = 0;
+
+ // Loop to try the 2-nd nodes
+
+ while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
+ {
+ // Find not checked 2-nd node
+ for ( i = 1; i < 8; i++ )
+ if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
+ int id1 = idNodes[i];
+ swap ( 1, i, idNodes, P );
+ checkedId1.insert ( id1 );
+ break;
+ }
+
+ // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
+ // ie that all but meybe one (id3 which is on the same face) nodes
+ // lay on the same side from the triangle plane.
+
+ bool manyInPlane = false; // more than 4 nodes lay in plane
+ int iLoop2 = 0;
+ while ( ++iLoop2 < 6 ) {
+
+ // get 1-2-3 plane coeffs
+ Standard_Real A, B, C, D;
+ gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
+ if ( N.SquareMagnitude() > gp::Resolution() )
+ {
+ gp_Pln pln ( P[0], N );
+ pln.Coefficients( A, B, C, D );
+
+ // find the node (iMin) closest to pln
+ Standard_Real dist[ 8 ], minDist = DBL_MAX;
+ set<int> idInPln;
+ for ( i = 3; i < 8; i++ ) {
+ dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
+ if ( fabs( dist[i] ) < minDist ) {
+ minDist = fabs( dist[i] );
+ iMin = i;
+ }
+ if ( fabs( dist[i] ) <= tol )
+ idInPln.insert( idNodes[i] );
+ }
+
+ // there should not be more than 4 nodes in bottom plane
+ if ( idInPln.size() > 1 )
+ {
+ DUMPSO( "### idInPln.size() = " << idInPln.size());
+ // idInPlane does not contain the first 3 nodes
+ if ( manyInPlane || idInPln.size() == 5)
+ return false; // all nodes in one plane
+ manyInPlane = true;
+
+ // set the 1-st node to be not in plane
+ for ( i = 3; i < 8; i++ ) {
+ if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
+ DUMPSO( "### Reset 0-th node");
+ swap( 0, i, idNodes, P );
+ break;
+ }
+ }
+
+ // reset to re-check second nodes
+ leastDist = DBL_MAX;
+ faceNodes.clear();
+ checkedId1.clear();
+ iLoop1 = 0;
+ break; // from iLoop2;
+ }
+
+ // check that the other 4 nodes are on the same side
+ bool sameSide = true;
+ bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
+ for ( i = 3; sameSide && i < 8; i++ ) {
+ if ( i != iMin )
+ sameSide = ( isNeg == dist[i] <= 0.);
+ }
+
+ // keep best solution
+ if ( sameSide && minDist < leastDist ) {
+ leastDist = minDist;
+ faceNodes.clear();
+ faceNodes.insert( idNodes[ 1 ] );
+ faceNodes.insert( idNodes[ 2 ] );
+ faceNodes.insert( idNodes[ iMin ] );
+ DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
+ << " leastDist = " << leastDist);
+ if ( leastDist <= DBL_MIN )
+ break;
+ }
+ }
+
+ // set next 3-d node to check
+ int iNext = 2 + iLoop2;
+ if ( iNext < 8 ) {
+ DUMPSO( "Try 2-nd");
+ swap ( 2, iNext, idNodes, P );
+ }
+ } // while ( iLoop2 < 6 )
+ } // iLoop1
+
+ if ( faceNodes.empty() ) return false;
+
+ // Put the faceNodes in proper places
+ for ( i = 4; i < 8; i++ ) {
+ if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
+ // find a place to put
+ int iTo = 1;
+ while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
+ iTo++;
+ DUMPSO( "Set faceNodes");
+ swap ( iTo, i, idNodes, P );
+ }
+ }
+
+
+ // Set nodes of the found bottom face in good order
+ DUMPSO( " Found bottom face: ");
+ i = SortQuadNodes( theMesh, idNodes );
+ if ( i ) {
+ gp_Pnt Ptmp = P[ i ];
+ P[ i ] = P[ i+1 ];
+ P[ i+1 ] = Ptmp;
+ }
+ // else
+ // for ( int ii = 0; ii < 4; ii++ ) {
+ // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
+ // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
+ // }
+
+ // Gravity center of the top and bottom faces
+ gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
+ gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
+
+ // Get direction from the bottom to the top face
+ gp_Vec upDir ( aGCb, aGCt );
+ Standard_Real upDirSize = upDir.Magnitude();
+ if ( upDirSize <= gp::Resolution() ) return false;
+ upDir / upDirSize;
+
+ // Assure that the bottom face normal points up
+ gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
+ Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
+ if ( Nb.Dot( upDir ) < 0 ) {
+ DUMPSO( "Reverse bottom face");
+ swap( 1, 3, idNodes, P );
+ }
+
+ // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
+ Standard_Real minDist = DBL_MAX;
+ for ( i = 4; i < 8; i++ ) {
+ // projection of P[i] to the plane defined by P[0] and upDir
+ gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
+ Standard_Real sqDist = P[0].SquareDistance( Pp );
+ if ( sqDist < minDist ) {
+ minDist = sqDist;
+ iMin = i;
+ }
+ }
+ DUMPSO( "Set 4-th");
+ swap ( 4, iMin, idNodes, P );
+
+ // Set nodes of the top face in good order
+ DUMPSO( "Sort top face");
+ i = SortQuadNodes( theMesh, &idNodes[4] );
+ if ( i ) {
+ i += 4;
+ gp_Pnt Ptmp = P[ i ];
+ P[ i ] = P[ i+1 ];
+ P[ i+1 ] = Ptmp;
+ }
+
+ // Assure that direction of the top face normal is from the bottom face
+ gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
+ Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
+ if ( Nt.Dot( upDir ) < 0 ) {
+ DUMPSO( "Reverse top face");
+ swap( 5, 7, idNodes, P );
+ }
+
+ // DUMPSO( "OUTPUT: ========================================");
+ // for ( i = 0; i < 8; i++ ) {
+ // float *p = ugrid->GetPoint(idNodes[i]);
+ // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
+ // }
+
+ return true;
+}*/
+
+//================================================================================
+/*!
+ * \brief Return nodes linked to the given one
+ * \param theNode - the node
+ * \param linkedNodes - the found nodes
+ * \param type - the type of elements to check
+ *
+ * Medium nodes are ignored
+ */
+//================================================================================
+
+void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
+ TIDSortedElemSet & linkedNodes,
+ SMDSAbs_ElementType type )
+{
+ SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
+ while ( elemIt->more() )
+ {
+ const SMDS_MeshElement* elem = elemIt->next();
+ if(elem->GetType() == SMDSAbs_0DElement)
+ continue;
+
+ SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
+ if ( elem->GetType() == SMDSAbs_Volume )
+ {
+ SMDS_VolumeTool vol( elem );
+ while ( nodeIt->more() ) {
+ const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
+ if ( theNode != n && vol.IsLinked( theNode, n ))
+ linkedNodes.insert( n );
+ }
+ }
+ else
+ {
+ for ( int i = 0; nodeIt->more(); ++i ) {
+ const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
+ if ( n == theNode ) {
+ int iBefore = i - 1;
+ int iAfter = i + 1;
+ if ( elem->IsQuadratic() ) {
+ int nb = elem->NbNodes() / 2;
+ iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
+ iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
+ }
+ linkedNodes.insert( elem->GetNodeWrap( iAfter ));
+ linkedNodes.insert( elem->GetNodeWrap( iBefore ));
+ }
+ }
+ }
+ }
+}
+
+//=======================================================================
+//function : laplacianSmooth
+//purpose : pulls theNode toward the center of surrounding nodes directly
+// connected to that node along an element edge
+//=======================================================================
+
+void laplacianSmooth(const SMDS_MeshNode* theNode,
+ const Handle(Geom_Surface)& theSurface,
+ map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
+{
+ // find surrounding nodes
+
+ TIDSortedElemSet nodeSet;
+ SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
+
+ // compute new coodrs
+
+ double coord[] = { 0., 0., 0. };
+ TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
+ for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
+ const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
+ if ( theSurface.IsNull() ) { // smooth in 3D
+ coord[0] += node->X();
+ coord[1] += node->Y();
+ coord[2] += node->Z();
+ }
+ else { // smooth in 2D
+ ASSERT( theUVMap.find( node ) != theUVMap.end() );
+ gp_XY* uv = theUVMap[ node ];
+ coord[0] += uv->X();
+ coord[1] += uv->Y();
+ }
+ }
+ int nbNodes = nodeSet.size();
+ if ( !nbNodes )
+ return;
+ coord[0] /= nbNodes;
+ coord[1] /= nbNodes;
+
+ if ( !theSurface.IsNull() ) {
+ ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
+ theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
+ gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
+ coord[0] = p3d.X();
+ coord[1] = p3d.Y();
+ coord[2] = p3d.Z();
+ }
+ else
+ coord[2] /= nbNodes;
+
+ // move node
+
+ const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
+}
+
+//=======================================================================
+//function : centroidalSmooth
+//purpose : pulls theNode toward the element-area-weighted centroid of the
+// surrounding elements
+//=======================================================================
+
+void centroidalSmooth(const SMDS_MeshNode* theNode,
+ const Handle(Geom_Surface)& theSurface,
+ map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
+{
+ gp_XYZ aNewXYZ(0.,0.,0.);
+ SMESH::Controls::Area anAreaFunc;
+ double totalArea = 0.;
+ int nbElems = 0;
+
+ // compute new XYZ
+
+ SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
+ while ( elemIt->more() )
+ {
+ const SMDS_MeshElement* elem = elemIt->next();
+ nbElems++;
+
+ gp_XYZ elemCenter(0.,0.,0.);
+ SMESH::Controls::TSequenceOfXYZ aNodePoints;
+ SMDS_ElemIteratorPtr itN = elem->nodesIterator();
+ int nn = elem->NbNodes();
+ if(elem->IsQuadratic()) nn = nn/2;
+ int i=0;
+ //while ( itN->more() ) {
+ while ( i<nn ) {
+ const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
+ i++;
+ gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
+ aNodePoints.push_back( aP );
+ if ( !theSurface.IsNull() ) { // smooth in 2D
+ ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
+ gp_XY* uv = theUVMap[ aNode ];
+ aP.SetCoord( uv->X(), uv->Y(), 0. );
+ }
+ elemCenter += aP;
+ }
+ double elemArea = anAreaFunc.GetValue( aNodePoints );
+ totalArea += elemArea;
+ elemCenter /= nn;
+ aNewXYZ += elemCenter * elemArea;
+ }
+ aNewXYZ /= totalArea;
+ if ( !theSurface.IsNull() ) {
+ theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
+ aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
+ }
+
+ // move node
+
+ const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
+}
+
+//=======================================================================
+//function : getClosestUV
+//purpose : return UV of closest projection
+//=======================================================================
+
+static bool getClosestUV (Extrema_GenExtPS& projector,
+ const gp_Pnt& point,
+ gp_XY & result)
+{
+ projector.Perform( point );
+ if ( projector.IsDone() ) {
+ double u, v, minVal = DBL_MAX;
+ for ( int i = projector.NbExt(); i > 0; i-- )
+#if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
+ if ( projector.SquareDistance( i ) < minVal ) {
+ minVal = projector.SquareDistance( i );
+#else
+ if ( projector.Value( i ) < minVal ) {
+ minVal = projector.Value( i );
+#endif
+ projector.Point( i ).Parameter( u, v );
+ }
+ result.SetCoord( u, v );
+ return true;
+ }
+ return false;
+}
+
+//=======================================================================
+//function : Smooth
+//purpose : Smooth theElements during theNbIterations or until a worst
+// element has aspect ratio <= theTgtAspectRatio.
+// Aspect Ratio varies in range [1.0, inf].
+// If theElements is empty, the whole mesh is smoothed.
+// theFixedNodes contains additionally fixed nodes. Nodes built
+// on edges and boundary nodes are always fixed.
+//=======================================================================
+
+void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
+ set<const SMDS_MeshNode*> & theFixedNodes,
+ const SmoothMethod theSmoothMethod,
+ const int theNbIterations,
+ double theTgtAspectRatio,
+ const bool the2D)
+{
+ myLastCreatedElems.Clear();
+ myLastCreatedNodes.Clear();
+
+ MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
+
+ if ( theTgtAspectRatio < 1.0 )
+ theTgtAspectRatio = 1.0;
+
+ const double disttol = 1.e-16;
+
+ SMESH::Controls::AspectRatio aQualityFunc;
+
+ SMESHDS_Mesh* aMesh = GetMeshDS();
+
+ if ( theElems.empty() ) {
+ // add all faces to theElems
+ SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
+ while ( fIt->more() ) {
+ const SMDS_MeshElement* face = fIt->next();
+ theElems.insert( face );
+ }
+ }
+ // get all face ids theElems are on
+ set< int > faceIdSet;
+ TIDSortedElemSet::iterator itElem;
+ if ( the2D )
+ for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
+ int fId = FindShape( *itElem );
+ // check that corresponding submesh exists and a shape is face
+ if (fId &&
+ faceIdSet.find( fId ) == faceIdSet.end() &&
+ aMesh->MeshElements( fId )) {
+ TopoDS_Shape F = aMesh->IndexToShape( fId );
+ if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
+ faceIdSet.insert( fId );
+ }
+ }
+ faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
+
+ // ===============================================
+ // smooth elements on each TopoDS_Face separately
+ // ===============================================
+
+ set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
+ for ( ; fId != faceIdSet.rend(); ++fId ) {
+ // get face surface and submesh
+ Handle(Geom_Surface) surface;
+ SMESHDS_SubMesh* faceSubMesh = 0;
+ TopoDS_Face face;
+ double fToler2 = 0, f,l;
+ double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
+ bool isUPeriodic = false, isVPeriodic = false;
+ if ( *fId ) {
+ face = TopoDS::Face( aMesh->IndexToShape( *fId ));
+ surface = BRep_Tool::Surface( face );
+ faceSubMesh = aMesh->MeshElements( *fId );
+ fToler2 = BRep_Tool::Tolerance( face );
+ fToler2 *= fToler2 * 10.;
+ isUPeriodic = surface->IsUPeriodic();
+ if ( isUPeriodic )
+ surface->UPeriod();
+ isVPeriodic = surface->IsVPeriodic();
+ if ( isVPeriodic )
+ surface->VPeriod();
+ surface->Bounds( u1, u2, v1, v2 );
+ }
+ // ---------------------------------------------------------
+ // for elements on a face, find movable and fixed nodes and
+ // compute UV for them
+ // ---------------------------------------------------------
+ bool checkBoundaryNodes = false;
+ bool isQuadratic = false;
+ set<const SMDS_MeshNode*> setMovableNodes;
+ map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
+ list< gp_XY > listUV; // uvs the 2 uvMaps refer to
+ list< const SMDS_MeshElement* > elemsOnFace;
+
+ Extrema_GenExtPS projector;
+ GeomAdaptor_Surface surfAdaptor;
+ if ( !surface.IsNull() ) {
+ surfAdaptor.Load( surface );
+ projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
+ }
+ int nbElemOnFace = 0;
+ itElem = theElems.begin();
+ // loop on not yet smoothed elements: look for elems on a face
+ while ( itElem != theElems.end() ) {
+ if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
+ break; // all elements found
+
+ const SMDS_MeshElement* elem = *itElem;
+ if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
+ ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
+ ++itElem;
+ continue;
+ }
+ elemsOnFace.push_back( elem );
+ theElems.erase( itElem++ );
+ nbElemOnFace++;
+
+ if ( !isQuadratic )
+ isQuadratic = elem->IsQuadratic();
+
+ // get movable nodes of elem
+ const SMDS_MeshNode* node;
+ SMDS_TypeOfPosition posType;
+ SMDS_ElemIteratorPtr itN = elem->nodesIterator();
+ int nn = 0, nbn = elem->NbNodes();
+ if(elem->IsQuadratic())
+ nbn = nbn/2;
+ while ( nn++ < nbn ) {
+ node = static_cast<const SMDS_MeshNode*>( itN->next() );
+ const SMDS_PositionPtr& pos = node->GetPosition();
+ posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
+ if (posType != SMDS_TOP_EDGE &&
+ posType != SMDS_TOP_VERTEX &&
+ theFixedNodes.find( node ) == theFixedNodes.end())
+ {
+ // check if all faces around the node are on faceSubMesh
+ // because a node on edge may be bound to face
+ SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
+ bool all = true;
+ if ( faceSubMesh ) {
+ while ( eIt->more() && all ) {
+ const SMDS_MeshElement* e = eIt->next();
+ all = faceSubMesh->Contains( e );
+ }
+ }
+ if ( all )
+ setMovableNodes.insert( node );
+ else
+ checkBoundaryNodes = true;
+ }
+ if ( posType == SMDS_TOP_3DSPACE )
+ checkBoundaryNodes = true;
+ }
+
+ if ( surface.IsNull() )
+ continue;
+
+ // get nodes to check UV
+ list< const SMDS_MeshNode* > uvCheckNodes;
+ itN = elem->nodesIterator();
+ nn = 0; nbn = elem->NbNodes();
+ if(elem->IsQuadratic())
+ nbn = nbn/2;
+ while ( nn++ < nbn ) {
+ node = static_cast<const SMDS_MeshNode*>( itN->next() );
+ if ( uvMap.find( node ) == uvMap.end() )
+ uvCheckNodes.push_back( node );
+ // add nodes of elems sharing node
+ // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
+ // while ( eIt->more() ) {
+ // const SMDS_MeshElement* e = eIt->next();
+ // if ( e != elem ) {
+ // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
+ // while ( nIt->more() ) {
+ // const SMDS_MeshNode* n =
+ // static_cast<const SMDS_MeshNode*>( nIt->next() );
+ // if ( uvMap.find( n ) == uvMap.end() )
+ // uvCheckNodes.push_back( n );
+ // }
+ // }
+ // }
+ }
+ // check UV on face
+ list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
+ for ( ; n != uvCheckNodes.end(); ++n ) {
+ node = *n;
+ gp_XY uv( 0, 0 );
+ const SMDS_PositionPtr& pos = node->GetPosition();
+ posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
+ // get existing UV
+ switch ( posType ) {
+ case SMDS_TOP_FACE: {
+ SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
+ uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
+ break;
+ }
+ case SMDS_TOP_EDGE: {
+ TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
+ Handle(Geom2d_Curve) pcurve;
+ if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
+ pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
+ if ( !pcurve.IsNull() ) {
+ double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
+ uv = pcurve->Value( u ).XY();
+ }
+ break;
+ }
+ case SMDS_TOP_VERTEX: {
+ TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
+ if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
+ uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
+ break;
+ }
+ default:;
+ }
+ // check existing UV
+ bool project = true;
+ gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
+ double dist1 = DBL_MAX, dist2 = 0;
+ if ( posType != SMDS_TOP_3DSPACE ) {
+ dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
+ project = dist1 > fToler2;
+ }
+ if ( project ) { // compute new UV
+ gp_XY newUV;
+ if ( !getClosestUV( projector, pNode, newUV )) {
+ MESSAGE("Node Projection Failed " << node);
+ }
+ else {
+ if ( isUPeriodic )
+ newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
+ if ( isVPeriodic )
+ newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
+ // check new UV
+ if ( posType != SMDS_TOP_3DSPACE )
+ dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
+ if ( dist2 < dist1 )
+ uv = newUV;
+ }
+ }
+ // store UV in the map
+ listUV.push_back( uv );
+ uvMap.insert( make_pair( node, &listUV.back() ));
+ }
+ } // loop on not yet smoothed elements
+
+ if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
+ checkBoundaryNodes = true;
+
+ // fix nodes on mesh boundary
+
+ if ( checkBoundaryNodes ) {
+ map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
+ map< SMESH_TLink, int >::iterator link_nb;
+ // put all elements links to linkNbMap
+ list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
+ for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
+ const SMDS_MeshElement* elem = (*elemIt);
+ int nbn = elem->NbCornerNodes();
+ // loop on elem links: insert them in linkNbMap
+ for ( int iN = 0; iN < nbn; ++iN ) {
+ const SMDS_MeshNode* n1 = elem->GetNode( iN );
+ const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
+ SMESH_TLink link( n1, n2 );
+ link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
+ link_nb->second++;
+ }
+ }
+ // remove nodes that are in links encountered only once from setMovableNodes
+ for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
+ if ( link_nb->second == 1 ) {
+ setMovableNodes.erase( link_nb->first.node1() );
+ setMovableNodes.erase( link_nb->first.node2() );
+ }
+ }
+ }
+
+ // -----------------------------------------------------
+ // for nodes on seam edge, compute one more UV ( uvMap2 );
+ // find movable nodes linked to nodes on seam and which
+ // are to be smoothed using the second UV ( uvMap2 )
+ // -----------------------------------------------------
+
+ set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
+ if ( !surface.IsNull() ) {
+ TopExp_Explorer eExp( face, TopAbs_EDGE );
+ for ( ; eExp.More(); eExp.Next() ) {
+ TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
+ if ( !BRep_Tool::IsClosed( edge, face ))
+ continue;
+ SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
+ if ( !sm ) continue;
+ // find out which parameter varies for a node on seam
+ double f,l;
+ gp_Pnt2d uv1, uv2;
+ Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
+ if ( pcurve.IsNull() ) continue;
+ uv1 = pcurve->Value( f );
+ edge.Reverse();
+ pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
+ if ( pcurve.IsNull() ) continue;
+ uv2 = pcurve->Value( f );
+ int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
+ // assure uv1 < uv2
+ if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
+ gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
+ }
+ // get nodes on seam and its vertices
+ list< const SMDS_MeshNode* > seamNodes;
+ SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
+ while ( nSeamIt->more() ) {
+ const SMDS_MeshNode* node = nSeamIt->next();
+ if ( !isQuadratic || !IsMedium( node ))
+ seamNodes.push_back( node );
+ }
+ TopExp_Explorer vExp( edge, TopAbs_VERTEX );
+ for ( ; vExp.More(); vExp.Next() ) {
+ sm = aMesh->MeshElements( vExp.Current() );
+ if ( sm ) {
+ nSeamIt = sm->GetNodes();
+ while ( nSeamIt->more() )
+ seamNodes.push_back( nSeamIt->next() );
+ }
+ }
+ // loop on nodes on seam
+ list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
+ for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
+ const SMDS_MeshNode* nSeam = *noSeIt;
+ map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
+ if ( n_uv == uvMap.end() )
+ continue;
+ // set the first UV
+ n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
+ // set the second UV
+ listUV.push_back( *n_uv->second );
+ listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
+ if ( uvMap2.empty() )
+ uvMap2 = uvMap; // copy the uvMap contents
+ uvMap2[ nSeam ] = &listUV.back();
+
+ // collect movable nodes linked to ones on seam in nodesNearSeam
+ SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
+ while ( eIt->more() ) {
+ const SMDS_MeshElement* e = eIt->next();
+ int nbUseMap1 = 0, nbUseMap2 = 0;
+ SMDS_ElemIteratorPtr nIt = e->nodesIterator();
+ int nn = 0, nbn = e->NbNodes();
+ if(e->IsQuadratic()) nbn = nbn/2;
+ while ( nn++ < nbn )
+ {
+ const SMDS_MeshNode* n =
+ static_cast<const SMDS_MeshNode*>( nIt->next() );
+ if (n == nSeam ||
+ setMovableNodes.find( n ) == setMovableNodes.end() )
+ continue;
+ // add only nodes being closer to uv2 than to uv1
+ gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
+ 0.5 * ( n->Y() + nSeam->Y() ),
+ 0.5 * ( n->Z() + nSeam->Z() ));
+ gp_XY uv;
+ getClosestUV( projector, pMid, uv );
+ if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
+ nodesNearSeam.insert( n );
+ nbUseMap2++;
+ }
+ else
+ nbUseMap1++;
+ }
+ // for centroidalSmooth all element nodes must
+ // be on one side of a seam
+ if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
+ SMDS_ElemIteratorPtr nIt = e->nodesIterator();
+ nn = 0;
+ while ( nn++ < nbn ) {
+ const SMDS_MeshNode* n =
+ static_cast<const SMDS_MeshNode*>( nIt->next() );
+ setMovableNodes.erase( n );
+ }
+ }
+ }
+ } // loop on nodes on seam
+ } // loop on edge of a face
+ } // if ( !face.IsNull() )
+
+ if ( setMovableNodes.empty() ) {
+ MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
+ continue; // goto next face
+ }
+
+ // -------------
+ // SMOOTHING //
+ // -------------
+
+ int it = -1;
+ double maxRatio = -1., maxDisplacement = -1.;
+ set<const SMDS_MeshNode*>::iterator nodeToMove;
+ for ( it = 0; it < theNbIterations; it++ ) {
+ maxDisplacement = 0.;
+ nodeToMove = setMovableNodes.begin();
+ for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
+ const SMDS_MeshNode* node = (*nodeToMove);
+ gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
+
+ // smooth
+ bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
+ if ( theSmoothMethod == LAPLACIAN )
+ laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
+ else
+ centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
+
+ // node displacement
+ gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
+ Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
+ if ( aDispl > maxDisplacement )
+ maxDisplacement = aDispl;
+ }
+ // no node movement => exit
+ //if ( maxDisplacement < 1.e-16 ) {
+ if ( maxDisplacement < disttol ) {
+ MESSAGE("-- no node movement --");
+ break;
+ }