X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2FSMESH%2FSMESH_MesherHelper.cxx;h=dd79438606bbf89049efc3740d35e5a8456aa865;hb=0ae2084641f29671e14b18271bed64a7457deebe;hp=0ca91a4d34df1f06ae08723e1962fa872b99a56b;hpb=2daa321efae3d0c6ce41199fa5a2482630d505d9;p=modules%2Fsmesh.git diff --git a/src/SMESH/SMESH_MesherHelper.cxx b/src/SMESH/SMESH_MesherHelper.cxx index 0ca91a4d3..dd7943860 100644 --- a/src/SMESH/SMESH_MesherHelper.cxx +++ b/src/SMESH/SMESH_MesherHelper.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE // -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS // -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License. +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. // -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. // -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // -// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File: SMESH_MesherHelper.cxx // Created: 15.02.06 15:22:41 // Author: Sergey KUUL @@ -29,14 +30,17 @@ #include "SMDS_EdgePosition.hxx" #include "SMDS_VolumeTool.hxx" #include "SMESH_subMesh.hxx" +#include "SMESH_ProxyMesh.hxx" #include #include #include #include #include +#include #include #include +#include #include #include #include @@ -56,12 +60,15 @@ #include +using namespace std; + #define RETURN_BAD_RESULT(msg) { MESSAGE(msg); return false; } namespace { gp_XYZ XYZ(const SMDS_MeshNode* n) { return gp_XYZ(n->X(), n->Y(), n->Z()); } + enum { U_periodic = 1, V_periodic = 2 }; } //================================================================================ @@ -71,16 +78,38 @@ namespace { //================================================================================ SMESH_MesherHelper::SMESH_MesherHelper(SMESH_Mesh& theMesh) - : myMesh(&theMesh), myShapeID(0), myCreateQuadratic(false) + : myParIndex(0), myMesh(&theMesh), myShapeID(0), myCreateQuadratic(false) { + myPar1[0] = myPar2[0] = myPar1[1] = myPar2[1] = 0; mySetElemOnShape = ( ! myMesh->HasShapeToMesh() ); } //======================================================================= -//function : CheckShape +//function : ~SMESH_MesherHelper //purpose : //======================================================================= +SMESH_MesherHelper::~SMESH_MesherHelper() +{ + { + TID2ProjectorOnSurf::iterator i_proj = myFace2Projector.begin(); + for ( ; i_proj != myFace2Projector.end(); ++i_proj ) + delete i_proj->second; + } + { + TID2ProjectorOnCurve::iterator i_proj = myEdge2Projector.begin(); + for ( ; i_proj != myEdge2Projector.end(); ++i_proj ) + delete i_proj->second; + } +} + +//======================================================================= +//function : IsQuadraticSubMesh +//purpose : Check submesh for given shape: if all elements on this shape +// are quadratic, quadratic elements will be created. +// Also fill myTLinkNodeMap +//======================================================================= + bool SMESH_MesherHelper::IsQuadraticSubMesh(const TopoDS_Shape& aSh) { SMESHDS_Mesh* meshDS = GetMeshDS(); @@ -95,35 +124,50 @@ bool SMESH_MesherHelper::IsQuadraticSubMesh(const TopoDS_Shape& aSh) int nbOldLinks = myTLinkNodeMap.size(); - TopExp_Explorer exp( aSh, subType ); - for (; exp.More() && myCreateQuadratic; exp.Next()) { - if ( SMESHDS_SubMesh * subMesh = meshDS->MeshElements( exp.Current() )) { - if ( SMDS_ElemIteratorPtr it = subMesh->GetElements() ) { - while(it->more()) { - const SMDS_MeshElement* e = it->next(); - if ( e->GetType() != elemType || !e->IsQuadratic() ) { - myCreateQuadratic = false; - break; - } - else { - // fill TLinkNodeMap - switch ( e->NbNodes() ) { - case 3: - AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(2)); break; - case 6: - AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(3)); - AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(4)); - AddTLinkNode(e->GetNode(2),e->GetNode(0),e->GetNode(5)); break; - case 8: - AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(4)); - AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(5)); - AddTLinkNode(e->GetNode(2),e->GetNode(3),e->GetNode(6)); - AddTLinkNode(e->GetNode(3),e->GetNode(0),e->GetNode(7)); - break; - default: + if ( !myMesh->HasShapeToMesh() ) + { + if (( myCreateQuadratic = myMesh->NbFaces( ORDER_QUADRATIC ))) + { + SMDS_FaceIteratorPtr fIt = meshDS->facesIterator(); + while ( fIt->more() ) + AddTLinks( static_cast< const SMDS_MeshFace* >( fIt->next() )); + } + } + else + { + TopExp_Explorer exp( aSh, subType ); + TopTools_MapOfShape checkedSubShapes; + for (; exp.More() && myCreateQuadratic; exp.Next()) { + if ( !checkedSubShapes.Add( exp.Current() )) + continue; // needed if aSh is compound of solids + if ( SMESHDS_SubMesh * subMesh = meshDS->MeshElements( exp.Current() )) { + if ( SMDS_ElemIteratorPtr it = subMesh->GetElements() ) { + while(it->more()) { + const SMDS_MeshElement* e = it->next(); + if ( e->GetType() != elemType || !e->IsQuadratic() ) { myCreateQuadratic = false; break; } + else { + // fill TLinkNodeMap + switch ( e->NbNodes() ) { + case 3: + AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(2)); break; + case 6: + AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(3)); + AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(4)); + AddTLinkNode(e->GetNode(2),e->GetNode(0),e->GetNode(5)); break; + case 8: + AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(4)); + AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(5)); + AddTLinkNode(e->GetNode(2),e->GetNode(3),e->GetNode(6)); + AddTLinkNode(e->GetNode(3),e->GetNode(0),e->GetNode(7)); + break; + default: + myCreateQuadratic = false; + break; + } + } } } } @@ -141,29 +185,25 @@ bool SMESH_MesherHelper::IsQuadraticSubMesh(const TopoDS_Shape& aSh) return myCreateQuadratic; } -//================================================================================ -/*! - * \brief Set geomerty to make elements on - * \param aSh - geomertic shape - */ -//================================================================================ +//======================================================================= +//function : SetSubShape +//purpose : Set geomerty to make elements on +//======================================================================= void SMESH_MesherHelper::SetSubShape(const int aShID) { if ( aShID == myShapeID ) return; - if ( aShID > 1 ) + if ( aShID > 0 ) SetSubShape( GetMeshDS()->IndexToShape( aShID )); else SetSubShape( TopoDS_Shape() ); } -//================================================================================ -/*! - * \brief Set geomerty to make elements on - * \param aSh - geomertic shape - */ -//================================================================================ +//======================================================================= +//function : SetSubShape +//purpose : Set geomerty to create elements on +//======================================================================= void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh) { @@ -180,34 +220,39 @@ void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh) } SMESHDS_Mesh* meshDS = GetMeshDS(); myShapeID = meshDS->ShapeToIndex(aSh); + myParIndex = 0; // treatment of periodic faces for ( TopExp_Explorer eF( aSh, TopAbs_FACE ); eF.More(); eF.Next() ) { const TopoDS_Face& face = TopoDS::Face( eF.Current() ); - BRepAdaptor_Surface surface( face ); - if ( surface.IsUPeriodic() || surface.IsVPeriodic() ) + TopLoc_Location loc; + Handle(Geom_Surface) surface = BRep_Tool::Surface( face, loc ); + + if ( surface->IsUPeriodic() || surface->IsVPeriodic() ) { + //while ( surface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface ))) + //surface = Handle(Geom_RectangularTrimmedSurface)::DownCast( surface )->BasisSurface(); + GeomAdaptor_Surface surf( surface ); + for (TopExp_Explorer exp( face, TopAbs_EDGE ); exp.More(); exp.Next()) { // look for a seam edge const TopoDS_Edge& edge = TopoDS::Edge( exp.Current() ); if ( BRep_Tool::IsClosed( edge, face )) { // initialize myPar1, myPar2 and myParIndex - if ( mySeamShapeIds.empty() ) { - gp_Pnt2d uv1, uv2; - BRep_Tool::UVPoints( edge, face, uv1, uv2 ); - if ( Abs( uv1.Coord(1) - uv2.Coord(1) ) < Abs( uv1.Coord(2) - uv2.Coord(2) )) - { - myParIndex = 1; // U periodic - myPar1 = surface.FirstUParameter(); - myPar2 = surface.LastUParameter(); - } - else { - myParIndex = 2; // V periodic - myPar1 = surface.FirstVParameter(); - myPar2 = surface.LastVParameter(); - } + gp_Pnt2d uv1, uv2; + BRep_Tool::UVPoints( edge, face, uv1, uv2 ); + if ( Abs( uv1.Coord(1) - uv2.Coord(1) ) < Abs( uv1.Coord(2) - uv2.Coord(2) )) + { + myParIndex |= U_periodic; + myPar1[0] = surf.FirstUParameter(); + myPar2[0] = surf.LastUParameter(); + } + else { + myParIndex |= V_periodic; + myPar1[1] = surf.FirstVParameter(); + myPar2[1] = surf.LastVParameter(); } // store seam shape indices, negative if shape encounters twice int edgeID = meshDS->ShapeToIndex( edge ); @@ -229,13 +274,13 @@ void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh) } } -//================================================================================ - /*! - * \brief Check if inFaceNode argument is necessary for call GetNodeUV(F,..) - * \param F - the face - * \retval bool - return true if the face is periodic - */ -//================================================================================ +//======================================================================= +//function : GetNodeUVneedInFaceNode +//purpose : Check if inFaceNode argument is necessary for call GetNodeUV(F,..) +// Return true if the face is periodic. +// If F is Null, answer about subshape set through IsQuadraticSubMesh() or +// * SetSubShape() +//======================================================================= bool SMESH_MesherHelper::GetNodeUVneedInFaceNode(const TopoDS_Face& F) const { @@ -264,18 +309,14 @@ bool SMESH_MesherHelper::IsMedium(const SMDS_MeshNode* node, } //======================================================================= -/*! - * \brief Return support shape of a node - * \param node - the node - * \param meshDS - mesh DS - * \retval TopoDS_Shape - found support shape - */ +//function : GetSubShapeByNode +//purpose : Return support shape of a node //======================================================================= TopoDS_Shape SMESH_MesherHelper::GetSubShapeByNode(const SMDS_MeshNode* node, - SMESHDS_Mesh* meshDS) + const SMESHDS_Mesh* meshDS) { - int shapeID = node->GetPosition()->GetShapeId(); + int shapeID = node->getshapeId(); if ( 0 < shapeID && shapeID <= meshDS->MaxShapeIndex() ) return meshDS->IndexToShape( shapeID ); else @@ -285,11 +326,9 @@ TopoDS_Shape SMESH_MesherHelper::GetSubShapeByNode(const SMDS_MeshNode* node, //======================================================================= //function : AddTLinkNode -//purpose : +//purpose : add a link in my data structure //======================================================================= -/*! - * Auxilary function for filling myTLinkNodeMap - */ + void SMESH_MesherHelper::AddTLinkNode(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshNode* n12) @@ -299,36 +338,131 @@ void SMESH_MesherHelper::AddTLinkNode(const SMDS_MeshNode* n1, myTLinkNodeMap.insert( make_pair(link,n12)); } -//======================================================================= +//================================================================================ +/*! + * \brief Add quadratic links of edge to own data structure + */ +//================================================================================ + +void SMESH_MesherHelper::AddTLinks(const SMDS_MeshEdge* edge) +{ + if ( edge->IsQuadratic() ) + AddTLinkNode(edge->GetNode(0), edge->GetNode(1), edge->GetNode(2)); +} + +//================================================================================ +/*! + * \brief Add quadratic links of face to own data structure + */ +//================================================================================ + +void SMESH_MesherHelper::AddTLinks(const SMDS_MeshFace* f) +{ + if ( !f->IsPoly() ) + switch ( f->NbNodes() ) { + case 6: + AddTLinkNode(f->GetNode(0),f->GetNode(1),f->GetNode(3)); + AddTLinkNode(f->GetNode(1),f->GetNode(2),f->GetNode(4)); + AddTLinkNode(f->GetNode(2),f->GetNode(0),f->GetNode(5)); break; + case 8: + AddTLinkNode(f->GetNode(0),f->GetNode(1),f->GetNode(4)); + AddTLinkNode(f->GetNode(1),f->GetNode(2),f->GetNode(5)); + AddTLinkNode(f->GetNode(2),f->GetNode(3),f->GetNode(6)); + AddTLinkNode(f->GetNode(3),f->GetNode(0),f->GetNode(7)); + default:; + } +} + +//================================================================================ +/*! + * \brief Add quadratic links of volume to own data structure + */ +//================================================================================ + +void SMESH_MesherHelper::AddTLinks(const SMDS_MeshVolume* volume) +{ + if ( volume->IsQuadratic() ) + { + SMDS_VolumeTool vTool( volume ); + const SMDS_MeshNode** nodes = vTool.GetNodes(); + set addedLinks; + for ( int iF = 1; iF < vTool.NbFaces(); ++iF ) + { + const int nbN = vTool.NbFaceNodes( iF ); + const int* iNodes = vTool.GetFaceNodesIndices( iF ); + for ( int i = 0; i < nbN; ) + { + int iN1 = iNodes[i++]; + int iN12 = iNodes[i++]; + int iN2 = iNodes[i++]; + if ( iN1 > iN2 ) std::swap( iN1, iN2 ); + int linkID = iN1 * vTool.NbNodes() + iN2; + pair< set::iterator, bool > it_isNew = addedLinks.insert( linkID ); + if ( it_isNew.second ) + AddTLinkNode( nodes[iN1], nodes[iN2], nodes[iN12] ); + else + addedLinks.erase( it_isNew.first ); // each link encounters only twice + } + } + } +} + +//================================================================================ /*! - * \brief Select UV on either of 2 pcurves of a seam edge, closest to the given UV - * \param uv1 - UV on the seam - * \param uv2 - UV within a face - * \retval gp_Pnt2d - selected UV + * \brief Return true if position of nodes on the shape hasn't yet been checked or + * the positions proved to be invalid */ +//================================================================================ + +bool SMESH_MesherHelper::toCheckPosOnShape(int shapeID ) const +{ + map< int,bool >::const_iterator id_ok = myNodePosShapesValidity.find( shapeID ); + return ( id_ok == myNodePosShapesValidity.end() || !id_ok->second ); +} + +//================================================================================ +/*! + * \brief Set validity of positions of nodes on the shape. + * Once set, validity is not changed + */ +//================================================================================ + +void SMESH_MesherHelper::setPosOnShapeValidity(int shapeID, bool ok ) const +{ + ((SMESH_MesherHelper*)this)->myNodePosShapesValidity.insert( make_pair( shapeID, ok)); +} + +//======================================================================= +//function : GetUVOnSeam +//purpose : Select UV on either of 2 pcurves of a seam edge, closest to the given UV //======================================================================= gp_Pnt2d SMESH_MesherHelper::GetUVOnSeam( const gp_Pnt2d& uv1, const gp_Pnt2d& uv2 ) const { - double p1 = uv1.Coord( myParIndex ); - double p2 = uv2.Coord( myParIndex ); - double p3 = ( Abs( p1 - myPar1 ) < Abs( p1 - myPar2 )) ? myPar2 : myPar1; - if ( Abs( p2 - p1 ) > Abs( p2 - p3 )) - p1 = p3; gp_Pnt2d result = uv1; - result.SetCoord( myParIndex, p1 ); + for ( int i = U_periodic; i <= V_periodic ; ++i ) + { + if ( myParIndex & i ) + { + double p1 = uv1.Coord( i ); + double dp1 = Abs( p1-myPar1[i-1]), dp2 = Abs( p1-myPar2[i-1]); + if ( myParIndex == i || + dp1 < ( myPar2[i-1] - myPar2[i-1] ) / 100. || + dp2 < ( myPar2[i-1] - myPar2[i-1] ) / 100. ) + { + double p2 = uv2.Coord( i ); + double p1Alt = ( dp1 < dp2 ) ? myPar2[i-1] : myPar1[i-1]; + if ( Abs( p2 - p1 ) > Abs( p2 - p1Alt )) + result.SetCoord( i, p1Alt ); + } + } + } return result; } //======================================================================= -/*! - * \brief Return node UV on face - * \param F - the face - * \param n - the node - * \param n2 - a node of element being created located inside a face - * \param check - optional flag returing false if found UV are invalid - * \retval gp_XY - resulting UV - */ +//function : GetNodeUV +//purpose : Return node UV on face //======================================================================= gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, @@ -336,16 +470,17 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, const SMDS_MeshNode* n2, bool* check) const { - gp_Pnt2d uv( 1e100, 1e100 ); + gp_Pnt2d uv( Precision::Infinite(), Precision::Infinite() ); const SMDS_PositionPtr Pos = n->GetPosition(); bool uvOK = false; if(Pos->GetTypeOfPosition()==SMDS_TOP_FACE) { // node has position on face const SMDS_FacePosition* fpos = - static_cast(n->GetPosition().get()); + static_cast(n->GetPosition()); uv.SetCoord(fpos->GetUParameter(),fpos->GetVParameter()); - uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), BRep_Tool::Tolerance( F )); + if ( check ) + uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F )); } else if(Pos->GetTypeOfPosition()==SMDS_TOP_EDGE) { @@ -353,21 +488,23 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, // corresponding edge from face, get pcurve for this // edge and retrieve value from this pcurve const SMDS_EdgePosition* epos = - static_cast(n->GetPosition().get()); - int edgeID = Pos->GetShapeId(); + static_cast(n->GetPosition()); + int edgeID = n->getshapeId(); TopoDS_Edge E = TopoDS::Edge(GetMeshDS()->IndexToShape(edgeID)); double f, l, u = epos->GetUParameter(); Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface(E, F, f, l); - if ( f < u && u < l ) + bool validU = ( f < u && u < l ); + if ( validU ) uv = C2d->Value( u ); else - uv.SetCoord(0.,0.); - uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), BRep_Tool::Tolerance( E )); + uv.SetCoord( Precision::Infinite(),0.); + if ( check || !validU ) + uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F ),/*force=*/ !validU ); // for a node on a seam edge select one of UVs on 2 pcurves if ( n2 && IsSeamShape( edgeID ) ) { - uv = GetUVOnSeam( uv, GetNodeUV( F, n2, 0 )); + uv = GetUVOnSeam( uv, GetNodeUV( F, n2, 0, check )); } else { // adjust uv to period @@ -387,7 +524,7 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, } else if(Pos->GetTypeOfPosition()==SMDS_TOP_VERTEX) { - if ( int vertexID = n->GetPosition()->GetShapeId() ) { + if ( int vertexID = n->getshapeId() ) { const TopoDS_Vertex& V = TopoDS::Vertex(GetMeshDS()->IndexToShape(vertexID)); try { uv = BRep_Tool::Parameters( V, F ); @@ -418,6 +555,7 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, } } else { + uvOK = false; TopTools_ListIteratorOfListOfShape it( myMesh->GetAncestors( V )); for ( ; it.More(); it.Next() ) { if ( it.Value().ShapeType() == TopAbs_EDGE ) { @@ -427,6 +565,7 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, if ( !C2d.IsNull() ) { double u = ( V == TopExp::FirstVertex( edge ) ) ? f : l; uv = C2d->Value( u ); + uvOK = true; break; } } @@ -437,6 +576,10 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, uv = GetUVOnSeam( uv, GetNodeUV( F, n2, 0 )); } } + else + { + uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F )); + } if ( check ) *check = uvOK; @@ -445,28 +588,39 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, } //======================================================================= -/*! - * \brief Check and fix node UV on a face - * \retval bool - false if UV is bad and could not be fixed - */ +//function : CheckNodeUV +//purpose : Check and fix node UV on a face //======================================================================= bool SMESH_MesherHelper::CheckNodeUV(const TopoDS_Face& F, const SMDS_MeshNode* n, gp_XY& uv, - const double tol) const + const double tol, + const bool force, + double distXYZ[4]) const { - if ( !myOkNodePosShapes.count( n->GetPosition()->GetShapeId() )) + int shapeID = n->getshapeId(); + bool infinit = ( Precision::IsInfinite( uv.X() ) || Precision::IsInfinite( uv.Y() )); + if ( force || toCheckPosOnShape( shapeID ) || infinit ) { // check that uv is correct TopLoc_Location loc; Handle(Geom_Surface) surface = BRep_Tool::Surface( F,loc ); - gp_Pnt nodePnt = XYZ( n ); + gp_Pnt nodePnt = XYZ( n ), surfPnt(0,0,0); + double dist = 0; if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() ); - if ( nodePnt.Distance( surface->Value( uv.X(), uv.Y() )) > tol ) + if ( infinit || + (dist = nodePnt.Distance( surfPnt = surface->Value( uv.X(), uv.Y() ))) > tol ) { + setPosOnShapeValidity( shapeID, false ); + if ( !infinit && distXYZ ) { + surfPnt.Transform( loc ); + distXYZ[0] = dist; + distXYZ[1] = surfPnt.X(); distXYZ[2] = surfPnt.Y(); distXYZ[3]=surfPnt.Z(); + } // uv incorrect, project the node to surface - GeomAPI_ProjectPointOnSurf projector( nodePnt, surface, tol ); + GeomAPI_ProjectPointOnSurf& projector = GetProjector( F, loc, tol ); + projector.Perform( nodePnt ); if ( !projector.IsDone() || projector.NbPoints() < 1 ) { MESSAGE( "SMESH_MesherHelper::CheckNodeUV() failed to project" ); @@ -474,223 +628,505 @@ bool SMESH_MesherHelper::CheckNodeUV(const TopoDS_Face& F, } Quantity_Parameter U,V; projector.LowerDistanceParameters(U,V); - if ( nodePnt.Distance( surface->Value( U, V )) > tol ) + uv.SetCoord( U,V ); + surfPnt = surface->Value( U, V ); + dist = nodePnt.Distance( surfPnt ); + if ( distXYZ ) { + surfPnt.Transform( loc ); + distXYZ[0] = dist; + distXYZ[1] = surfPnt.X(); distXYZ[2] = surfPnt.Y(); distXYZ[3]=surfPnt.Z(); + } + if ( dist > tol ) { MESSAGE( "SMESH_MesherHelper::CheckNodeUV(), invalid projection" ); return false; } - uv.SetCoord( U,V ); + // store the fixed UV on the face + if ( myShape.IsSame(F) && shapeID == myShapeID ) + const_cast(n)->SetPosition + ( SMDS_PositionPtr( new SMDS_FacePosition( U, V ))); } else if ( uv.Modulus() > numeric_limits::min() ) { - ((SMESH_MesherHelper*) this)->myOkNodePosShapes.insert( n->GetPosition()->GetShapeId() ); + setPosOnShapeValidity( shapeID, true ); } } return true; } //======================================================================= -/*! - * \brief Return middle UV taking in account surface period - */ +//function : GetProjector +//purpose : Return projector intitialized by given face without location, which is returned +//======================================================================= + +GeomAPI_ProjectPointOnSurf& SMESH_MesherHelper::GetProjector(const TopoDS_Face& F, + TopLoc_Location& loc, + double tol ) const +{ + Handle(Geom_Surface) surface = BRep_Tool::Surface( F,loc ); + int faceID = GetMeshDS()->ShapeToIndex( F ); + TID2ProjectorOnSurf& i2proj = const_cast< TID2ProjectorOnSurf&>( myFace2Projector ); + TID2ProjectorOnSurf::iterator i_proj = i2proj.find( faceID ); + if ( i_proj == i2proj.end() ) + { + if ( tol == 0 ) tol = BRep_Tool::Tolerance( F ); + double U1, U2, V1, V2; + surface->Bounds(U1, U2, V1, V2); + GeomAPI_ProjectPointOnSurf* proj = new GeomAPI_ProjectPointOnSurf(); + proj->Init( surface, U1, U2, V1, V2, tol ); + i_proj = i2proj.insert( make_pair( faceID, proj )).first; + } + return *( i_proj->second ); +} + +namespace +{ + gp_XY AverageUV(const gp_XY& uv1, const gp_XY& uv2) { return ( uv1 + uv2 ) / 2.; } + gp_XY_FunPtr(Added); // define gp_XY_Added pointer to function calling gp_XY::Added(gp_XY) + gp_XY_FunPtr(Subtracted); +} + +//======================================================================= +//function : applyIn2D +//purpose : Perform given operation on two 2d points in parameric space of given surface. +// It takes into account period of the surface. Use gp_XY_FunPtr macro +// to easily define pointer to function of gp_XY class. +//======================================================================= + +gp_XY SMESH_MesherHelper::applyIn2D(const Handle(Geom_Surface)& surface, + const gp_XY& uv1, + const gp_XY& uv2, + xyFunPtr fun, + const bool resultInPeriod) +{ + Standard_Boolean isUPeriodic = surface.IsNull() ? false : surface->IsUPeriodic(); + Standard_Boolean isVPeriodic = surface.IsNull() ? false : surface->IsVPeriodic(); + if ( !isUPeriodic && !isVPeriodic ) + return fun(uv1,uv2); + + // move uv2 not far than half-period from uv1 + double u2 = + uv2.X()+(isUPeriodic ? ShapeAnalysis::AdjustByPeriod(uv2.X(),uv1.X(),surface->UPeriod()) :0); + double v2 = + uv2.Y()+(isVPeriodic ? ShapeAnalysis::AdjustByPeriod(uv2.Y(),uv1.Y(),surface->VPeriod()) :0); + + // execute operation + gp_XY res = fun( uv1, gp_XY(u2,v2) ); + + // move result within period + if ( resultInPeriod ) + { + Standard_Real UF,UL,VF,VL; + surface->Bounds(UF,UL,VF,VL); + if ( isUPeriodic ) + res.SetX( res.X() + ShapeAnalysis::AdjustToPeriod(res.X(),UF,UL)); + if ( isVPeriodic ) + res.SetY( res.Y() + ShapeAnalysis::AdjustToPeriod(res.Y(),VF,VL)); + } + + return res; +} +//======================================================================= +//function : GetMiddleUV +//purpose : Return middle UV taking in account surface period //======================================================================= gp_XY SMESH_MesherHelper::GetMiddleUV(const Handle(Geom_Surface)& surface, const gp_XY& p1, const gp_XY& p2) { - if ( surface.IsNull() ) - return 0.5 * ( p1 + p2 ); - //checking if surface is periodic - Standard_Real UF,UL,VF,VL; - surface->Bounds(UF,UL,VF,VL); - - Standard_Real u,v; - Standard_Boolean isUPeriodic = surface->IsUPeriodic(); - if(isUPeriodic) { - Standard_Real UPeriod = surface->UPeriod(); - Standard_Real p2x = p2.X()+ShapeAnalysis::AdjustByPeriod(p2.X(),p1.X(),UPeriod); - Standard_Real pmid = (p1.X()+p2x)/2.; - u = pmid+ShapeAnalysis::AdjustToPeriod(pmid,UF,UL); - } - else { - u= (p1.X()+p2.X())/2.; - } - Standard_Boolean isVPeriodic = surface->IsVPeriodic(); - if(isVPeriodic) { - Standard_Real VPeriod = surface->VPeriod(); - Standard_Real p2y = p2.Y()+ShapeAnalysis::AdjustByPeriod(p2.Y(),p1.Y(),VPeriod); - Standard_Real pmid = (p1.Y()+p2y)/2.; - v = pmid+ShapeAnalysis::AdjustToPeriod(pmid,VF,VL); - } - else { - v = (p1.Y()+p2.Y())/2.; - } - return gp_XY( u,v ); + // NOTE: + // the proper place of getting basic surface seems to be in applyIn2D() + // but we put it here to decrease a risk of regressions just before releasing a version + Handle(Geom_Surface) surf = surface; + while ( !surf.IsNull() && surf->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface ))) + surf = Handle(Geom_RectangularTrimmedSurface)::DownCast( surf )->BasisSurface(); + + return applyIn2D( surf, p1, p2, & AverageUV ); } //======================================================================= -/*! - * \brief Return node U on edge - * \param E - the Edge - * \param n - the node - * \retval double - resulting U - * - * Auxilary function called form GetMediumNode() - */ +//function : GetNodeU +//purpose : Return node U on edge //======================================================================= double SMESH_MesherHelper::GetNodeU(const TopoDS_Edge& E, const SMDS_MeshNode* n, + const SMDS_MeshNode* inEdgeNode, bool* check) { double param = 0; - const SMDS_PositionPtr Pos = n->GetPosition(); - if(Pos->GetTypeOfPosition()==SMDS_TOP_EDGE) { - const SMDS_EdgePosition* epos = - static_cast(n->GetPosition().get()); + const SMDS_PositionPtr pos = n->GetPosition(); + if ( pos->GetTypeOfPosition()==SMDS_TOP_EDGE ) + { + const SMDS_EdgePosition* epos = static_cast( pos ); param = epos->GetUParameter(); } - else if(Pos->GetTypeOfPosition()==SMDS_TOP_VERTEX) { - SMESHDS_Mesh * meshDS = GetMeshDS(); - int vertexID = n->GetPosition()->GetShapeId(); - const TopoDS_Vertex& V = TopoDS::Vertex(meshDS->IndexToShape(vertexID)); - param = BRep_Tool::Parameter( V, E ); + else if( pos->GetTypeOfPosition() == SMDS_TOP_VERTEX ) + { + if ( inEdgeNode && TopExp::FirstVertex( E ).IsSame( TopExp::LastVertex( E ))) // issue 0020128 + { + Standard_Real f,l; + BRep_Tool::Range( E, f,l ); + double uInEdge = GetNodeU( E, inEdgeNode ); + param = ( fabs( uInEdge - f ) < fabs( l - uInEdge )) ? f : l; + } + else + { + SMESHDS_Mesh * meshDS = GetMeshDS(); + int vertexID = n->getshapeId(); + const TopoDS_Vertex& V = TopoDS::Vertex(meshDS->IndexToShape(vertexID)); + param = BRep_Tool::Parameter( V, E ); + } + } + if ( check ) + { + double tol = BRep_Tool::Tolerance( E ); + double f,l; BRep_Tool::Range( E, f,l ); + bool force = ( param < f-tol || param > l+tol ); + if ( !force && pos->GetTypeOfPosition()==SMDS_TOP_EDGE ) + force = ( GetMeshDS()->ShapeToIndex( E ) != n->getshapeId() ); + + *check = CheckNodeU( E, n, param, 2*tol, force ); } return param; } -//================================================================================ -/*! - * \brief Return existing or create new medium nodes between given ones - * \param force3d - if true, new node is the middle of n1 and n2, - * else is located on geom face or geom edge - */ -//================================================================================ +//======================================================================= +//function : CheckNodeU +//purpose : Check and fix node U on an edge +// Return false if U is bad and could not be fixed +//======================================================================= + +bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge& E, + const SMDS_MeshNode* n, + double& u, + const double tol, + const bool force, + double distXYZ[4]) const +{ + int shapeID = n->getshapeId(); + if ( force || toCheckPosOnShape( shapeID )) + { + TopLoc_Location loc; double f,l; + Handle(Geom_Curve) curve = BRep_Tool::Curve( E,loc,f,l ); + if ( curve.IsNull() ) // degenerated edge + { + if ( u+tol < f || u-tol > l ) + { + double r = Max( 0.5, 1 - tol*n->GetID()); // to get a unique u on edge + u = f*r + l*(1-r); + } + } + else + { + gp_Pnt nodePnt = SMESH_TNodeXYZ( n ); + if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() ); + gp_Pnt curvPnt = curve->Value( u ); + double dist = nodePnt.Distance( curvPnt ); + if ( distXYZ ) { + curvPnt.Transform( loc ); + distXYZ[0] = dist; + distXYZ[1] = curvPnt.X(); distXYZ[2] = curvPnt.Y(); distXYZ[3]=curvPnt.Z(); + } + if ( dist > tol ) + { + setPosOnShapeValidity( shapeID, false ); + // u incorrect, project the node to the curve + int edgeID = GetMeshDS()->ShapeToIndex( E ); + TID2ProjectorOnCurve& i2proj = const_cast< TID2ProjectorOnCurve&>( myEdge2Projector ); + TID2ProjectorOnCurve::iterator i_proj = + i2proj.insert( make_pair( edgeID, (GeomAPI_ProjectPointOnCurve*) 0 )).first; + if ( !i_proj->second ) + { + i_proj->second = new GeomAPI_ProjectPointOnCurve(); + i_proj->second->Init( curve, f, l ); + } + GeomAPI_ProjectPointOnCurve* projector = i_proj->second; + projector->Perform( nodePnt ); + if ( projector->NbPoints() < 1 ) + { + MESSAGE( "SMESH_MesherHelper::CheckNodeU() failed to project" ); + return false; + } + Quantity_Parameter U = projector->LowerDistanceParameter(); + u = double( U ); + curvPnt = curve->Value( u ); + dist = nodePnt.Distance( curvPnt ); + if ( distXYZ ) { + curvPnt.Transform( loc ); + distXYZ[0] = dist; + distXYZ[1] = curvPnt.X(); distXYZ[2] = curvPnt.Y(); distXYZ[3]=curvPnt.Z(); + } + if ( dist > tol ) + { + MESSAGE( "SMESH_MesherHelper::CheckNodeU(), invalid projection" ); + MESSAGE("distance " << dist << " " << tol ); + return false; + } + // store the fixed U on the edge + if ( myShape.IsSame(E) && shapeID == myShapeID ) + const_cast(n)->SetPosition + ( SMDS_PositionPtr( new SMDS_EdgePosition( U ))); + } + else if ( fabs( u ) > numeric_limits::min() ) + { + setPosOnShapeValidity( shapeID, true ); + } + if (( u < f-tol || u > l+tol ) && force ) + { + // node is on vertex but is set on periodic but trimmed edge (issue 0020890) + try + { + // do not use IsPeriodic() as Geom_TrimmedCurve::IsPeriodic () returns false + double period = curve->Period(); + u = ( u < f ) ? u + period : u - period; + } + catch (Standard_Failure& exc) + { + return false; + } + } + } + } + return true; +} + +//======================================================================= +//function : GetMediumNode +//purpose : Return existing or create new medium nodes between given ones +//======================================================================= const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, - bool force3d) + bool force3d) { + // Find existing node + SMESH_TLink link(n1,n2); ItTLinkNode itLN = myTLinkNodeMap.find( link ); if ( itLN != myTLinkNodeMap.end() ) { return (*itLN).second; } - // create medium node + + // Create medium node + SMDS_MeshNode* n12; SMESHDS_Mesh* meshDS = GetMeshDS(); + + if ( IsSeamShape( n1->getshapeId() )) + // to get a correct UV of a node on seam, the second node must have checked UV + std::swap( n1, n2 ); + + // get type of shape for the new medium node int faceID = -1, edgeID = -1; const SMDS_PositionPtr Pos1 = n1->GetPosition(); const SMDS_PositionPtr Pos2 = n2->GetPosition(); + TopoDS_Edge E; double u [2]; + TopoDS_Face F; gp_XY uv[2]; + bool uvOK[2] = { false, false }; + if( myShape.IsNull() ) { if( Pos1->GetTypeOfPosition()==SMDS_TOP_FACE ) { - faceID = Pos1->GetShapeId(); + faceID = n1->getshapeId(); } else if( Pos2->GetTypeOfPosition()==SMDS_TOP_FACE ) { - faceID = Pos2->GetShapeId(); + faceID = n2->getshapeId(); } if( Pos1->GetTypeOfPosition()==SMDS_TOP_EDGE ) { - edgeID = Pos1->GetShapeId(); + edgeID = n1->getshapeId(); } if( Pos2->GetTypeOfPosition()==SMDS_TOP_EDGE ) { - edgeID = Pos2->GetShapeId(); + edgeID = n2->getshapeId(); + } + } + // get positions of the given nodes on shapes + TopAbs_ShapeEnum shapeType = myShape.IsNull() ? TopAbs_SHAPE : myShape.ShapeType(); + if ( faceID>0 || shapeType == TopAbs_FACE) + { + if( myShape.IsNull() ) + F = TopoDS::Face(meshDS->IndexToShape(faceID)); + else { + F = TopoDS::Face(myShape); + faceID = myShapeID; } + uv[0] = GetNodeUV(F,n1,n2, force3d ? 0 : &uvOK[0]); + uv[1] = GetNodeUV(F,n2,n1, force3d ? 0 : &uvOK[1]); + } + else if (edgeID>0 || shapeType == TopAbs_EDGE) + { + if ( Pos1->GetTypeOfPosition()==SMDS_TOP_EDGE && + Pos2->GetTypeOfPosition()==SMDS_TOP_EDGE && + n1->getshapeId() != n2->getshapeId() ) // issue 0021006 + return getMediumNodeOnComposedWire(n1,n2,force3d); + + if( myShape.IsNull() ) + E = TopoDS::Edge(meshDS->IndexToShape(edgeID)); + else { + E = TopoDS::Edge(myShape); + edgeID = myShapeID; + } + u[0] = GetNodeU(E,n1,n2, force3d ? 0 : &uvOK[0]); + u[1] = GetNodeU(E,n2,n1, force3d ? 0 : &uvOK[1]); } if(!force3d) { // we try to create medium node using UV parameters of // nodes, else - medium between corresponding 3d points - - TopAbs_ShapeEnum shapeType = myShape.IsNull() ? TopAbs_SHAPE : myShape.ShapeType(); - if(faceID>0 || shapeType == TopAbs_FACE) { - // obtaining a face and 2d points for nodes - TopoDS_Face F; - if( myShape.IsNull() ) - F = TopoDS::Face(meshDS->IndexToShape(faceID)); - else { - F = TopoDS::Face(myShape); - faceID = myShapeID; - } - bool uvOK1, uvOK2; - gp_XY p1 = GetNodeUV(F,n1,n2, &uvOK1); - gp_XY p2 = GetNodeUV(F,n2,n1, &uvOK2); - - if ( uvOK1 && uvOK2 ) + if( ! F.IsNull() ) + { + if ( uvOK[0] && uvOK[1] ) { - if ( IsDegenShape( Pos1->GetShapeId() )) - p1.SetCoord( myParIndex, p2.Coord( myParIndex )); - else if ( IsDegenShape( Pos2->GetShapeId() )) - p2.SetCoord( myParIndex, p1.Coord( myParIndex )); + if ( IsDegenShape( n1->getshapeId() )) { + if ( myParIndex & U_periodic ) uv[0].SetCoord( 1, uv[1].Coord( 1 )); + else uv[0].SetCoord( 2, uv[1].Coord( 2 )); + } + else if ( IsDegenShape( n2->getshapeId() )) { + if ( myParIndex & U_periodic ) uv[1].SetCoord( 1, uv[0].Coord( 1 )); + else uv[1].SetCoord( 2, uv[0].Coord( 2 )); + } TopLoc_Location loc; Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc); - gp_XY uv = GetMiddleUV( S, p1, p2 ); - gp_Pnt P = S->Value( uv.X(), uv.Y() ).Transformed(loc); + gp_XY UV = GetMiddleUV( S, uv[0], uv[1] ); + gp_Pnt P = S->Value( UV.X(), UV.Y() ).Transformed(loc); n12 = meshDS->AddNode(P.X(), P.Y(), P.Z()); - meshDS->SetNodeOnFace(n12, faceID, uv.X(), uv.Y()); + meshDS->SetNodeOnFace(n12, faceID, UV.X(), UV.Y()); myTLinkNodeMap.insert(make_pair(link,n12)); return n12; } } - if (edgeID>0 || shapeType == TopAbs_EDGE) { - - TopoDS_Edge E; - if( myShape.IsNull() ) - E = TopoDS::Edge(meshDS->IndexToShape(edgeID)); - else { - E = TopoDS::Edge(myShape); - edgeID = myShapeID; - } - - double p1 = GetNodeU(E,n1); - double p2 = GetNodeU(E,n2); - + else if ( !E.IsNull() ) + { double f,l; Handle(Geom_Curve) C = BRep_Tool::Curve(E, f, l); - if(!C.IsNull()) { - + if(!C.IsNull()) + { Standard_Boolean isPeriodic = C->IsPeriodic(); - double u; + double U; if(isPeriodic) { Standard_Real Period = C->Period(); - Standard_Real p = p2+ShapeAnalysis::AdjustByPeriod(p2,p1,Period); - Standard_Real pmid = (p1+p)/2.; - u = pmid+ShapeAnalysis::AdjustToPeriod(pmid,C->FirstParameter(),C->LastParameter()); + Standard_Real p = u[1]+ShapeAnalysis::AdjustByPeriod(u[1],u[0],Period); + Standard_Real pmid = (u[0]+p)/2.; + U = pmid+ShapeAnalysis::AdjustToPeriod(pmid,C->FirstParameter(),C->LastParameter()); } else - u = (p1+p2)/2.; + U = (u[0]+u[1])/2.; - gp_Pnt P = C->Value( u ); + gp_Pnt P = C->Value( U ); n12 = meshDS->AddNode(P.X(), P.Y(), P.Z()); - meshDS->SetNodeOnEdge(n12, edgeID, u); + meshDS->SetNodeOnEdge(n12, edgeID, U); myTLinkNodeMap.insert(make_pair(link,n12)); return n12; } } } + // 3d variant double x = ( n1->X() + n2->X() )/2.; double y = ( n1->Y() + n2->Y() )/2.; double z = ( n1->Z() + n2->Z() )/2.; n12 = meshDS->AddNode(x,y,z); - if(edgeID>0) - meshDS->SetNodeOnEdge(n12, edgeID); - else if(faceID>0) - meshDS->SetNodeOnFace(n12, faceID); - else + + if ( !F.IsNull() ) + { + gp_XY UV = ( uv[0] + uv[1] ) / 2.; + CheckNodeUV( F, n12, UV, 2*BRep_Tool::Tolerance( F ), /*force=*/true); + meshDS->SetNodeOnFace(n12, faceID, UV.X(), UV.Y() ); + } + else if ( !E.IsNull() ) + { + double U = ( u[0] + u[1] ) / 2.; + CheckNodeU( E, n12, U, 2*BRep_Tool::Tolerance( E ), /*force=*/true); + meshDS->SetNodeOnEdge(n12, edgeID, U); + } + else if ( myShapeID > 0 ) + { meshDS->SetNodeInVolume(n12, myShapeID); + } + myTLinkNodeMap.insert( make_pair( link, n12 )); return n12; } -//======================================================================= +//================================================================================ /*! - * Creates a node + * \brief Makes a medium node if nodes reside different edges */ +//================================================================================ + +const SMDS_MeshNode* SMESH_MesherHelper::getMediumNodeOnComposedWire(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + bool force3d) +{ + gp_Pnt middle = 0.5 * XYZ(n1) + 0.5 * XYZ(n2); + SMDS_MeshNode* n12 = AddNode( middle.X(), middle.Y(), middle.Z() ); + + // To find position on edge and 3D position for n12, + // project to 2 edges and select projection most close to + + double u = 0, distMiddleProj = Precision::Infinite(), distXYZ[4]; + int iOkEdge = 0; + TopoDS_Edge edges[2]; + for ( int is2nd = 0; is2nd < 2; ++is2nd ) + { + // get an edge + const SMDS_MeshNode* n = is2nd ? n2 : n1; + TopoDS_Shape shape = GetSubShapeByNode( n, GetMeshDS() ); + if ( shape.IsNull() || shape.ShapeType() != TopAbs_EDGE ) + continue; + + // project to get U of projection and distance from middle to projection + TopoDS_Edge edge = edges[ is2nd ] = TopoDS::Edge( shape ); + double node2MiddleDist = middle.Distance( XYZ(n) ); + double foundU = GetNodeU( edge, n ); + CheckNodeU( edge, n12, foundU, 2*BRep_Tool::Tolerance(edge), /*force=*/true, distXYZ ); + if ( distXYZ[0] < node2MiddleDist ) + { + distMiddleProj = distXYZ[0]; + u = foundU; + iOkEdge = is2nd; + } + } + if ( Precision::IsInfinite( distMiddleProj )) + { + // both projections failed; set n12 on the edge of n1 with U of a common vertex + TopoDS_Vertex vCommon; + if ( TopExp::CommonVertex( edges[0], edges[1], vCommon )) + u = BRep_Tool::Parameter( vCommon, edges[0] ); + else + { + double f,l, u0 = GetNodeU( edges[0], n1 ); + BRep_Tool::Range( edges[0],f,l ); + u = ( fabs(u0-f) < fabs(u0-l) ) ? f : l; + } + iOkEdge = 0; + distMiddleProj = 0; + } + + // move n12 to position of a successfull projection + double tol = BRep_Tool::Tolerance(edges[ iOkEdge ]); + if ( !force3d && distMiddleProj > 2*tol ) + { + TopLoc_Location loc; double f,l; + Handle(Geom_Curve) curve = BRep_Tool::Curve( edges[iOkEdge],loc,f,l ); + gp_Pnt p = curve->Value( u ); + GetMeshDS()->MoveNode( n12, p.X(), p.Y(), p.Z() ); + } + + GetMeshDS()->SetNodeOnEdge(n12, edges[iOkEdge], u); + + myTLinkNodeMap.insert( make_pair( SMESH_TLink(n1,n2), n12 )); + + return n12; +} + +//======================================================================= +//function : AddNode +//purpose : Creates a node //======================================================================= SMDS_MeshNode* SMESH_MesherHelper::AddNode(double x, double y, double z, int ID) @@ -715,15 +1151,14 @@ SMDS_MeshNode* SMESH_MesherHelper::AddNode(double x, double y, double z, int ID) } //======================================================================= -/*! - * Creates quadratic or linear edge - */ +//function : AddEdge +//purpose : Creates quadratic or linear edge //======================================================================= SMDS_MeshEdge* SMESH_MesherHelper::AddEdge(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - const int id, - const bool force3d) + const SMDS_MeshNode* n2, + const int id, + const bool force3d) { SMESHDS_Mesh * meshDS = GetMeshDS(); @@ -749,9 +1184,8 @@ SMDS_MeshEdge* SMESH_MesherHelper::AddEdge(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear triangle - */ +//function : AddFace +//purpose : Creates quadratic or linear triangle //======================================================================= SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, @@ -789,17 +1223,16 @@ SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear quadrangle - */ +//function : AddFace +//purpose : Creates quadratic or linear quadrangle //======================================================================= SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshNode* n3, const SMDS_MeshNode* n4, - const int id, - const bool force3d) + const int id, + const bool force3d) { SMESHDS_Mesh * meshDS = GetMeshDS(); SMDS_MeshFace* elem = 0; @@ -836,9 +1269,48 @@ SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, const SMDS_MeshNode* n41 = GetMediumNode(n4,n1,force3d); if(id) - elem = meshDS->AddFaceWithID(n1, n2, n3, n4, n12, n23, n34, n41, id); + elem = meshDS->AddFaceWithID(n1, n2, n3, n4, n12, n23, n34, n41, id); + else + elem = meshDS->AddFace(n1, n2, n3, n4, n12, n23, n34, n41); + } + if ( mySetElemOnShape && myShapeID > 0 ) + meshDS->SetMeshElementOnShape( elem, myShapeID ); + + return elem; +} + +//======================================================================= +//function : AddPolygonalFace +//purpose : Creates polygon, with additional nodes in quadratic mesh +//======================================================================= + +SMDS_MeshFace* SMESH_MesherHelper::AddPolygonalFace (const vector& nodes, + const int id, + const bool force3d) +{ + SMESHDS_Mesh * meshDS = GetMeshDS(); + SMDS_MeshFace* elem = 0; + + if(!myCreateQuadratic) { + if(id) + elem = meshDS->AddPolygonalFaceWithID(nodes, id); + else + elem = meshDS->AddPolygonalFace(nodes); + } + else { + vector newNodes; + for ( int i = 0; i < nodes.size(); ++i ) + { + const SMDS_MeshNode* n1 = nodes[i]; + const SMDS_MeshNode* n2 = nodes[(i+1)/nodes.size()]; + const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); + newNodes.push_back( n1 ); + newNodes.push_back( n12 ); + } + if(id) + elem = meshDS->AddPolygonalFaceWithID(newNodes, id); else - elem = meshDS->AddFace(n1, n2, n3, n4, n12, n23, n34, n41); + elem = meshDS->AddPolygonalFace(newNodes); } if ( mySetElemOnShape && myShapeID > 0 ) meshDS->SetMeshElementOnShape( elem, myShapeID ); @@ -847,9 +1319,8 @@ SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear volume - */ +//function : AddVolume +//purpose : Creates quadratic or linear prism //======================================================================= SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, @@ -896,9 +1367,8 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear volume - */ +//function : AddVolume +//purpose : Creates quadratic or linear tetrahedron //======================================================================= SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, @@ -937,9 +1407,8 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear pyramid - */ +//function : AddVolume +//purpose : Creates quadratic or linear pyramid //======================================================================= SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, @@ -985,9 +1454,8 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear hexahedron - */ +//function : AddVolume +//purpose : Creates quadratic or linear hexahedron //======================================================================= SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, @@ -1041,271 +1509,158 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * \brief Load nodes bound to face into a map of node columns - * \param theParam2ColumnMap - map of node columns to fill - * \param theFace - the face on which nodes are searched for - * \param theBaseEdge - the edge nodes of which are columns' bases - * \param theMesh - the mesh containing nodes - * \retval bool - false if something is wrong - * - * The key of the map is a normalized parameter of each - * base node on theBaseEdge. - * This method works in supposition that nodes on the face - * forms a rectangular grid and elements can be quardrangles or triangles - */ +//function : AddPolyhedralVolume +//purpose : Creates polyhedron. In quadratic mesh, adds medium nodes //======================================================================= -bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, - const TopoDS_Face& theFace, - const TopoDS_Edge& theBaseEdge, - SMESHDS_Mesh* theMesh) +SMDS_MeshVolume* +SMESH_MesherHelper::AddPolyhedralVolume (const std::vector& nodes, + const std::vector& quantities, + const int id, + const bool force3d) { - // get vertices of theBaseEdge - TopoDS_Vertex vfb, vlb, vft; // first and last, bottom and top vertices - TopoDS_Edge eFrw = TopoDS::Edge( theBaseEdge.Oriented( TopAbs_FORWARD )); - TopExp::Vertices( eFrw, vfb, vlb ); - - // find the other edges of theFace and orientation of e1 - TopoDS_Edge e1, e2, eTop; - bool rev1, CumOri = false; - TopExp_Explorer exp( theFace, TopAbs_EDGE ); - int nbEdges = 0; - for ( ; exp.More(); exp.Next() ) { - if ( ++nbEdges > 4 ) { - return false; // more than 4 edges in theFace - } - TopoDS_Edge e = TopoDS::Edge( exp.Current() ); - if ( theBaseEdge.IsSame( e )) - continue; - TopoDS_Vertex vCommon; - if ( !TopExp::CommonVertex( theBaseEdge, e, vCommon )) - eTop = e; - else if ( vCommon.IsSame( vfb )) { - e1 = e; - vft = TopExp::LastVertex( e1, CumOri ); - rev1 = vfb.IsSame( vft ); - if ( rev1 ) - vft = TopExp::FirstVertex( e1, CumOri ); - } + SMESHDS_Mesh * meshDS = GetMeshDS(); + SMDS_MeshVolume* elem = 0; + if(!myCreateQuadratic) + { + if(id) + elem = meshDS->AddPolyhedralVolumeWithID(nodes, quantities, id); else - e2 = e; - } - if ( nbEdges < 4 ) { - return false; // less than 4 edges in theFace - } - if ( e2.IsNull() && vfb.IsSame( vlb )) - e2 = e1; - - // submeshes corresponding to shapes - SMESHDS_SubMesh* smFace = theMesh->MeshElements( theFace ); - SMESHDS_SubMesh* smb = theMesh->MeshElements( theBaseEdge ); - SMESHDS_SubMesh* smt = theMesh->MeshElements( eTop ); - SMESHDS_SubMesh* sm1 = theMesh->MeshElements( e1 ); - SMESHDS_SubMesh* sm2 = theMesh->MeshElements( e2 ); - SMESHDS_SubMesh* smVfb = theMesh->MeshElements( vfb ); - SMESHDS_SubMesh* smVlb = theMesh->MeshElements( vlb ); - SMESHDS_SubMesh* smVft = theMesh->MeshElements( vft ); - if (!smFace || !smb || !smt || !sm1 || !sm2 || !smVfb || !smVlb || !smVft ) { - RETURN_BAD_RESULT( "NULL submesh " <NbNodes() != smt->NbNodes() || sm1->NbNodes() != sm2->NbNodes() ) { - RETURN_BAD_RESULT(" Diff nb of nodes on opposite edges" ); - } - if (smVfb->NbNodes() != 1 || smVlb->NbNodes() != 1 || smVft->NbNodes() != 1) { - RETURN_BAD_RESULT("Empty submesh of vertex"); - } - // define whether mesh is quadratic - bool isQuadraticMesh = false; - SMDS_ElemIteratorPtr eIt = smFace->GetElements(); - if ( !eIt->more() ) { - RETURN_BAD_RESULT("No elements on the face"); - } - const SMDS_MeshElement* e = eIt->next(); - isQuadraticMesh = e->IsQuadratic(); - - if ( sm1->NbNodes() * smb->NbNodes() != smFace->NbNodes() ) { - // check quadratic case - if ( isQuadraticMesh ) { - // what if there are quadrangles and triangles mixed? -// int n1 = sm1->NbNodes()/2; -// int n2 = smb->NbNodes()/2; -// int n3 = sm1->NbNodes() - n1; -// int n4 = smb->NbNodes() - n2; -// int nf = sm1->NbNodes()*smb->NbNodes() - n3*n4; -// if( nf != smFace->NbNodes() ) { -// MESSAGE( "Wrong nb face nodes: " << -// sm1->NbNodes()<<" "<NbNodes()<<" "<NbNodes()); -// return false; -// } - } - else { - RETURN_BAD_RESULT( "Wrong nb face nodes: " << - sm1->NbNodes()<<" "<NbNodes()<<" "<NbNodes()); - } + elem = meshDS->AddPolyhedralVolume(nodes, quantities); } - // IJ size - int vsize = sm1->NbNodes() + 2; - int hsize = smb->NbNodes() + 2; - if(isQuadraticMesh) { - vsize = vsize - sm1->NbNodes()/2 -1; - hsize = hsize - smb->NbNodes()/2 -1; + else + { + vector newNodes; + vector newQuantities; + for ( int iFace=0, iN=0; iFace < quantities.size(); ++iFace) + { + int nbNodesInFace = quantities[iFace]; + newQuantities.push_back(0); + for ( int i = 0; i < nbNodesInFace; ++i ) + { + const SMDS_MeshNode* n1 = nodes[ iN + i ]; + newNodes.push_back( n1 ); + newQuantities.back()++; + + const SMDS_MeshNode* n2 = nodes[ iN + ( i+1==nbNodesInFace ? 0 : i+1 )]; +// if ( n1->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE && +// n2->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE ) + { + const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); + newNodes.push_back( n12 ); + newQuantities.back()++; + } + } + iN += nbNodesInFace; + } + if(id) + elem = meshDS->AddPolyhedralVolumeWithID( newNodes, newQuantities, id ); + else + elem = meshDS->AddPolyhedralVolume( newNodes, newQuantities ); } + if ( mySetElemOnShape && myShapeID > 0 ) + meshDS->SetMeshElementOnShape( elem, myShapeID ); + + return elem; +} - // load nodes from theBaseEdge +//======================================================================= +//function : LoadNodeColumns +//purpose : Load nodes bound to face into a map of node columns +//======================================================================= - std::set loadedNodes; - const SMDS_MeshNode* nullNode = 0; +bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, + const TopoDS_Face& theFace, + const TopoDS_Edge& theBaseEdge, + SMESHDS_Mesh* theMesh, + SMESH_ProxyMesh* theProxyMesh) +{ + const SMESHDS_SubMesh* faceSubMesh = 0; + if ( theProxyMesh ) + { + faceSubMesh = theProxyMesh->GetSubMesh( theFace ); + if ( !faceSubMesh || + faceSubMesh->NbElements() == 0 || + theProxyMesh->IsTemporary( faceSubMesh->GetElements()->next() )) + { + // can use a proxy sub-mesh with not temporary elements only + faceSubMesh = 0; + theProxyMesh = 0; + } + } + if ( !faceSubMesh ) + faceSubMesh = theMesh->MeshElements( theFace ); + if ( !faceSubMesh || faceSubMesh->NbElements() == 0 ) + return false; - std::vector & nVecf = theParam2ColumnMap[ 0.]; - nVecf.resize( vsize, nullNode ); - loadedNodes.insert( nVecf[ 0 ] = smVfb->GetNodes()->next() ); + // get nodes on theBaseEdge sorted by param on edge and initialize theParam2ColumnMap with them - std::vector & nVecl = theParam2ColumnMap[ 1.]; - nVecl.resize( vsize, nullNode ); - loadedNodes.insert( nVecl[ 0 ] = smVlb->GetNodes()->next() ); + map< double, const SMDS_MeshNode*> sortedBaseNodes; + if ( !SMESH_Algo::GetSortedNodesOnEdge( theMesh, theBaseEdge,/*noMedium=*/true, sortedBaseNodes) + || sortedBaseNodes.size() < 2 ) + return false; - double f, l; - BRep_Tool::Range( eFrw, f, l ); - double range = l - f; - SMDS_NodeIteratorPtr nIt = smb->GetNodes(); - const SMDS_MeshNode* node; - while ( nIt->more() ) { - node = nIt->next(); - if(IsMedium(node, SMDSAbs_Edge)) - continue; - const SMDS_EdgePosition* pos = - dynamic_cast( node->GetPosition().get() ); - if ( !pos ) { - return false; - } - double u = ( pos->GetUParameter() - f ) / range; - std::vector & nVec = theParam2ColumnMap[ u ]; - nVec.resize( vsize, nullNode ); - loadedNodes.insert( nVec[ 0 ] = node ); + int nbRows = faceSubMesh->NbElements() / ( sortedBaseNodes.size()-1 ) + 1; + map< double, const SMDS_MeshNode*>::iterator u_n = sortedBaseNodes.begin(); + double f = u_n->first, range = sortedBaseNodes.rbegin()->first - f; + for ( ; u_n != sortedBaseNodes.end(); u_n++ ) + { + double par = ( u_n->first - f ) / range; + vector& nCol = theParam2ColumnMap[ par ]; + nCol.resize( nbRows ); + nCol[0] = u_n->second; } - if ( theParam2ColumnMap.size() != hsize ) { - RETURN_BAD_RESULT( "Wrong node positions on theBaseEdge" ); + TParam2ColumnMap::iterator par_nVec_2, par_nVec_1 = theParam2ColumnMap.begin(); + if ( theProxyMesh ) + { + for ( ; par_nVec_1 != theParam2ColumnMap.end(); ++par_nVec_1 ) + { + const SMDS_MeshNode* & n = par_nVec_1->second[0]; + n = theProxyMesh->GetProxyNode( n ); + } } - // load nodes from e1 + // fill theParam2ColumnMap column by column by passing from nodes on + // theBaseEdge up via mesh faces on theFace - std::map< double, const SMDS_MeshNode*> sortedNodes; // sort by param on edge - nIt = sm1->GetNodes(); - while ( nIt->more() ) { - node = nIt->next(); - if(IsMedium(node)) - continue; - const SMDS_EdgePosition* pos = - dynamic_cast( node->GetPosition().get() ); - if ( !pos ) { - return false; - } - sortedNodes.insert( std::make_pair( pos->GetUParameter(), node )); - } - loadedNodes.insert( nVecf[ vsize - 1 ] = smVft->GetNodes()->next() ); - std::map< double, const SMDS_MeshNode*>::iterator u_n = sortedNodes.begin(); - int row = rev1 ? vsize - 1 : 0; - int dRow = rev1 ? -1 : +1; - for ( ; u_n != sortedNodes.end(); u_n++ ) { - row += dRow; - loadedNodes.insert( nVecf[ row ] = u_n->second ); - } - - // try to load the rest nodes - - // get all faces from theFace - TIDSortedElemSet allFaces, foundFaces; - eIt = smFace->GetElements(); - while ( eIt->more() ) { - const SMDS_MeshElement* e = eIt->next(); - if ( e->GetType() == SMDSAbs_Face ) - allFaces.insert( e ); - } - // Starting from 2 neighbour nodes on theBaseEdge, look for a face - // the nodes belong to, and between the nodes of the found face, - // look for a not loaded node considering this node to be the next - // in a column of the starting second node. Repeat, starting - // from nodes next to the previous starting nodes in their columns, - // and so on while a face can be found. Then go the the next pair - // of nodes on theBaseEdge. - TParam2ColumnMap::iterator par_nVec_1 = theParam2ColumnMap.begin(); - TParam2ColumnMap::iterator par_nVec_2 = par_nVec_1; - // loop on columns - int col = 0; - for ( par_nVec_2++; par_nVec_2 != theParam2ColumnMap.end(); par_nVec_1++, par_nVec_2++ ) { - col++; - row = 0; - const SMDS_MeshNode* n1 = par_nVec_1->second[ row ]; - const SMDS_MeshNode* n2 = par_nVec_2->second[ row ]; - const SMDS_MeshElement* face = 0; - bool lastColOnClosedFace = ( nVecf[ row ] == n2 ); - do { - // look for a face by 2 nodes - face = SMESH_MeshEditor::FindFaceInSet( n1, n2, allFaces, foundFaces ); - if ( face ) { - int nbFaceNodes = face->NbNodes(); - if ( face->IsQuadratic() ) - nbFaceNodes /= 2; - if ( nbFaceNodes>4 ) { - RETURN_BAD_RESULT(" Too many nodes in a face: " << nbFaceNodes ); - } - // look for a not loaded node of the - bool found = false; - const SMDS_MeshNode* n3 = 0; // a node defferent from n1 and n2 - for ( int i = 0; i < nbFaceNodes && !found; ++i ) { - node = face->GetNode( i ); - found = loadedNodes.insert( node ).second; - if ( !found && node != n1 && node != n2 ) - n3 = node; - } - if ( lastColOnClosedFace && row + 1 < vsize ) { - node = nVecf[ row + 1 ]; - found = ( face->GetNodeIndex( node ) >= 0 ); - } - if ( found ) { - if ( ++row > vsize - 1 ) { - RETURN_BAD_RESULT( "Too many nodes in column "<< col <<": "<< row+1); - } - par_nVec_2->second[ row ] = node; - foundFaces.insert( face ); - n2 = node; - if ( nbFaceNodes==4 ) { - n1 = par_nVec_1->second[ row ]; - } - } - else if ( nbFaceNodes==3 && n3 == par_nVec_1->second[ row + 1 ] ) { - n1 = n3; - } - else { - RETURN_BAD_RESULT( "Not quad mesh, column "<< col ); - } + par_nVec_2 = theParam2ColumnMap.begin(); + par_nVec_1 = par_nVec_2++; + TIDSortedElemSet emptySet, avoidSet; + for ( ; par_nVec_2 != theParam2ColumnMap.end(); ++par_nVec_1, ++par_nVec_2 ) + { + vector& nCol1 = par_nVec_1->second; + vector& nCol2 = par_nVec_2->second; + + int i1, i2, iRow = 0; + const SMDS_MeshNode *n1 = nCol1[0], *n2 = nCol2[0]; + // find face sharing node n1 and n2 and belonging to faceSubMesh + while ( const SMDS_MeshElement* face = + SMESH_MeshEditor::FindFaceInSet( n1, n2, emptySet, avoidSet, &i1, &i2)) + { + if ( faceSubMesh->Contains( face )) + { + int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes(); + if ( nbNodes != 4 ) + return false; + n1 = face->GetNode( (i2+2) % 4 ); // opposite corner of quadrangle face + n2 = face->GetNode( (i1+2) % 4 ); + if ( ++iRow >= nbRows ) + return false; + nCol1[ iRow ] = n1; + nCol2[ iRow ] = n2; + avoidSet.clear(); } + avoidSet.insert( face ); } - while ( face && n1 && n2 ); - - if ( row < vsize - 1 ) { - MESSAGE( "Too few nodes in column "<< col <<": "<< row+1); - MESSAGE( "Base node 1: "<< par_nVec_1->second[0]); - MESSAGE( "Base node 2: "<< par_nVec_2->second[0]); - if ( n1 ) { MESSAGE( "Current node 1: "<< n1); } - else { MESSAGE( "Current node 1: NULL"); } - if ( n2 ) { MESSAGE( "Current node 2: "<< n2); } - else { MESSAGE( "Current node 2: NULL"); } - MESSAGE( "first base node: "<< theParam2ColumnMap.begin()->second[0]); - MESSAGE( "last base node: "<< theParam2ColumnMap.rbegin()->second[0]); - return false; - } - } // loop on columns - - return true; + if ( iRow + 1 < nbRows ) // compact if necessary + nCol1.resize( iRow + 1 ), nCol2.resize( iRow + 1 ); + } + return theParam2ColumnMap.size() > 1 && theParam2ColumnMap.begin()->second.size() > 1; } //======================================================================= -/*! - * \brief Return number of unique ancestors of the shape - */ +//function : NbAncestors +//purpose : Return number of unique ancestors of the shape //======================================================================= int SMESH_MesherHelper::NbAncestors(const TopoDS_Shape& shape, @@ -1322,11 +1677,126 @@ int SMESH_MesherHelper::NbAncestors(const TopoDS_Shape& shape, } //======================================================================= -/** - * Check mesh without geometry for: if all elements on this shape are quadratic, - * quadratic elements will be created. - * Used then generated 3D mesh without geometry. +//function : GetSubShapeOri +//purpose : Return orientation of sub-shape in the main shape +//======================================================================= + +TopAbs_Orientation SMESH_MesherHelper::GetSubShapeOri(const TopoDS_Shape& shape, + const TopoDS_Shape& subShape) +{ + TopAbs_Orientation ori = TopAbs_Orientation(-1); + if ( !shape.IsNull() && !subShape.IsNull() ) + { + TopExp_Explorer e( shape, subShape.ShapeType() ); + if ( shape.Orientation() >= TopAbs_INTERNAL ) // TopAbs_INTERNAL or TopAbs_EXTERNAL + e.Init( shape.Oriented(TopAbs_FORWARD), subShape.ShapeType() ); + for ( ; e.More(); e.Next()) + if ( subShape.IsSame( e.Current() )) + break; + if ( e.More() ) + ori = e.Current().Orientation(); + } + return ori; +} + +//======================================================================= +//function : IsSubShape +//purpose : +//======================================================================= + +bool SMESH_MesherHelper::IsSubShape( const TopoDS_Shape& shape, + const TopoDS_Shape& mainShape ) +{ + if ( !shape.IsNull() && !mainShape.IsNull() ) + { + for ( TopExp_Explorer exp( mainShape, shape.ShapeType()); + exp.More(); + exp.Next() ) + if ( shape.IsSame( exp.Current() )) + return true; + } + SCRUTE((shape.IsNull())); + SCRUTE((mainShape.IsNull())); + return false; +} + +//======================================================================= +//function : IsSubShape +//purpose : +//======================================================================= + +bool SMESH_MesherHelper::IsSubShape( const TopoDS_Shape& shape, SMESH_Mesh* aMesh ) +{ + if ( shape.IsNull() || !aMesh ) + return false; + return + aMesh->GetMeshDS()->ShapeToIndex( shape ) || + // PAL16202 + (shape.ShapeType() == TopAbs_COMPOUND && aMesh->GetMeshDS()->IsGroupOfSubShapes( shape )); +} + +//================================================================================ +/*! + * \brief Return maximal tolerance of shape + */ +//================================================================================ + +double SMESH_MesherHelper::MaxTolerance( const TopoDS_Shape& shape ) +{ + double tol = Precision::Confusion(); + TopExp_Explorer exp; + for ( exp.Init( shape, TopAbs_FACE ); exp.More(); exp.Next() ) + tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Face( exp.Current()))); + for ( exp.Init( shape, TopAbs_EDGE ); exp.More(); exp.Next() ) + tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Edge( exp.Current()))); + for ( exp.Init( shape, TopAbs_VERTEX ); exp.More(); exp.Next() ) + tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Vertex( exp.Current()))); + + return tol; +} + +//================================================================================ +/*! + * \brief Check if the first and last vertices of an edge are the same + * \param anEdge - the edge to check + * \retval bool - true if same + */ +//================================================================================ + +bool SMESH_MesherHelper::IsClosedEdge( const TopoDS_Edge& anEdge ) +{ + if ( anEdge.Orientation() >= TopAbs_INTERNAL ) + return IsClosedEdge( TopoDS::Edge( anEdge.Oriented( TopAbs_FORWARD ))); + return TopExp::FirstVertex( anEdge ).IsSame( TopExp::LastVertex( anEdge )); +} + +//================================================================================ +/*! + * \brief Wrapper over TopExp::FirstVertex() and TopExp::LastVertex() fixing them + * in the case of INTERNAL edge */ +//================================================================================ + +TopoDS_Vertex SMESH_MesherHelper::IthVertex( const bool is2nd, + TopoDS_Edge anEdge, + const bool CumOri ) +{ + if ( anEdge.Orientation() >= TopAbs_INTERNAL ) + anEdge.Orientation( TopAbs_FORWARD ); + + const TopAbs_Orientation tgtOri = is2nd ? TopAbs_REVERSED : TopAbs_FORWARD; + TopoDS_Iterator vIt( anEdge, CumOri ); + while ( vIt.More() && vIt.Value().Orientation() != tgtOri ) + vIt.Next(); + + return ( vIt.More() ? TopoDS::Vertex(vIt.Value()) : TopoDS_Vertex() ); +} + +//======================================================================= +//function : IsQuadraticMesh +//purpose : Check mesh without geometry for: if all elements on this shape are quadratic, +// quadratic elements will be created. +// Used then generated 3D mesh without geometry. //======================================================================= SMESH_MesherHelper:: MType SMESH_MesherHelper::IsQuadraticMesh() @@ -1356,27 +1826,85 @@ SMESH_MesherHelper:: MType SMESH_MesherHelper::IsQuadraticMesh() return SMESH_MesherHelper::COMP; } +//======================================================================= +//function : GetOtherParam +//purpose : Return an alternative parameter for a node on seam +//======================================================================= + +double SMESH_MesherHelper::GetOtherParam(const double param) const +{ + int i = myParIndex & U_periodic ? 0 : 1; + return fabs(param-myPar1[i]) < fabs(param-myPar2[i]) ? myPar2[i] : myPar1[i]; +} + +namespace { + + //======================================================================= + /*! + * \brief Iterator on ancestors of the given type + */ + //======================================================================= + + struct TAncestorsIterator : public SMDS_Iterator + { + TopTools_ListIteratorOfListOfShape _ancIter; + TopAbs_ShapeEnum _type; + TopTools_MapOfShape _encountered; + TAncestorsIterator( const TopTools_ListOfShape& ancestors, TopAbs_ShapeEnum type) + : _ancIter( ancestors ), _type( type ) + { + if ( _ancIter.More() ) { + if ( _ancIter.Value().ShapeType() != _type ) next(); + else _encountered.Add( _ancIter.Value() ); + } + } + virtual bool more() + { + return _ancIter.More(); + } + virtual const TopoDS_Shape* next() + { + const TopoDS_Shape* s = _ancIter.More() ? & _ancIter.Value() : 0; + if ( _ancIter.More() ) + for ( _ancIter.Next(); _ancIter.More(); _ancIter.Next()) + if ( _ancIter.Value().ShapeType() == _type && _encountered.Add( _ancIter.Value() )) + break; + return s; + } + }; + +} // namespace + //======================================================================= /*! - * \brief Return an alternative parameter for a node on seam + * \brief Return iterator on ancestors of the given type */ //======================================================================= -double SMESH_MesherHelper::GetOtherParam(const double param) const +PShapeIteratorPtr SMESH_MesherHelper::GetAncestors(const TopoDS_Shape& shape, + const SMESH_Mesh& mesh, + TopAbs_ShapeEnum ancestorType) { - return fabs(param-myPar1) < fabs(param-myPar2) ? myPar2 : myPar1; + return PShapeIteratorPtr( new TAncestorsIterator( mesh.GetAncestors(shape), ancestorType)); } +//#include + //======================================================================= namespace { // Structures used by FixQuadraticElements() //======================================================================= #define __DMP__(txt) \ -//cout << txt + //cout << txt #define MSG(txt) __DMP__(txt< < 1/15 * + return middleNodeMove2 < 1/15./15. * linkLen2; + } struct QFace; // --------------------------------------- @@ -1412,17 +1940,19 @@ namespace { // Structures used by FixQuadraticElements() void Move(const gp_Vec& move, bool sum=false) const { _nodeMove += move; _nbMoves += sum ? (_nbMoves==0) : 1; } gp_XYZ Move() const { return _nodeMove.XYZ() / _nbMoves; } - bool IsMoved() const { return (_nbMoves > 0 && !IsStraight()); } - bool IsStraight() const { return _nodeMove.SquareMagnitude() <= straightTol2; } - + bool IsMoved() const { return (_nbMoves > 0 /*&& !IsStraight()*/); } + bool IsStraight() const + { return isStraightLink( (XYZ(node1())-XYZ(node2())).SquareModulus(), + _nodeMove.SquareMagnitude()); + } bool operator<(const QLink& other) const { return (node1()->GetID() == other.node1()->GetID() ? node2()->GetID() < other.node2()->GetID() : node1()->GetID() < other.node1()->GetID()); } - struct PtrComparator { - bool operator() (const QLink* l1, const QLink* l2 ) const { return *l1 < *l2; } - }; +// struct PtrComparator { +// bool operator() (const QLink* l1, const QLink* l2 ) const { return *l1 < *l2; } +// }; }; // --------------------------------------------------------- /*! @@ -1436,7 +1966,7 @@ namespace { // Structures used by FixQuadraticElements() TChainLink(const QLink* qlink=0):_qlink(qlink) { _qfaces[0] = _qfaces[1] = 0; } - void SetFace(const QFace* face) { int iF = _qfaces[0] ? 1 : 0; _qfaces[iF]=face; } + void SetFace(const QFace* face) const { int iF = _qfaces[0] ? 1 : 0; _qfaces[iF]=face; } bool IsBoundary() const { return !_qfaces[1]; } @@ -1456,6 +1986,8 @@ namespace { // Structures used by FixQuadraticElements() const QLink* operator->() const { return _qlink; } gp_Vec Normal() const; + + bool IsStraight() const; }; // -------------------------------------------------------------------- typedef list< TChainLink > TChain; @@ -1469,14 +2001,17 @@ namespace { // Structures used by FixQuadraticElements() /*! * \brief Face shared by two volumes and bound by QLinks */ - struct QFace: public TIDSortedElemSet + struct QFace: public TIDSortedNodeSet { mutable const SMDS_MeshElement* _volumes[2]; mutable vector< const QLink* > _sides; mutable bool _sideIsAdded[4]; // added in chain of links gp_Vec _normal; +#ifdef _DEBUG_ + mutable const SMDS_MeshElement* _face; +#endif - QFace( const vector< const QLink*>& links ); + QFace( const vector< const QLink*>& links, const SMDS_MeshElement* face=0 ); void SetVolume(const SMDS_MeshElement* v) const { _volumes[ _volumes[0] ? 1 : 0 ] = v; } @@ -1490,26 +2025,29 @@ namespace { // Structures used by FixQuadraticElements() for (int i=0; i<_sides.size(); ++i ) if ( _sides[i] == side ) return i; return -1; } - bool GetLinkChain( int iSide, TChain& chain, SMDS_TypeOfPosition pos, int& error) const; + bool GetLinkChain( int iSide, TChain& chain, SMDS_TypeOfPosition pos, int& err) const; - bool GetLinkChain( TChainLink& link, TChain& chain, SMDS_TypeOfPosition pos, int& error) const + bool GetLinkChain( TChainLink& link, TChain& chain, SMDS_TypeOfPosition pos, int& err) const { int i = LinkIndex( link._qlink ); if ( i < 0 ) return true; _sideIsAdded[i] = true; link.SetFace( this ); // continue from opposite link - return GetLinkChain( (i+2)%_sides.size(), chain, pos, error ); + return GetLinkChain( (i+2)%_sides.size(), chain, pos, err ); } bool IsBoundary() const { return !_volumes[1]; } bool Contains( const SMDS_MeshNode* node ) const { return count(node); } + bool IsSpoiled(const QLink* bentLink ) const; + TLinkInSet GetBoundaryLink( const TLinkSet& links, const TChainLink& avoidLink, TLinkInSet * notBoundaryLink = 0, const SMDS_MeshNode* nodeToContain = 0, - bool * isAdjacentUsed = 0) const; + bool * isAdjacentUsed = 0, + int nbRecursionsLeft = -1) const; TLinkInSet GetLinkByNode( const TLinkSet& links, const TChainLink& avoidLink, @@ -1548,7 +2086,7 @@ namespace { // Structures used by FixQuadraticElements() ostream& operator << (ostream& out, const QFace& f) { out <<"QFace nodes: "/*<< &f << " "*/; - for ( TIDSortedElemSet::const_iterator n = f.begin(); n != f.end(); ++n ) + for ( TIDSortedNodeSet::const_iterator n = f.begin(); n != f.end(); ++n ) out << (*n)->GetID() << " "; out << " \tvolumes: " << (f._volumes[0] ? f._volumes[0]->GetID() : 0) << " " @@ -1563,7 +2101,7 @@ namespace { // Structures used by FixQuadraticElements() */ //================================================================================ - QFace::QFace( const vector< const QLink*>& links ) + QFace::QFace( const vector< const QLink*>& links, const SMDS_MeshElement* face ) { _volumes[0] = _volumes[1] = 0; _sides = links; @@ -1584,10 +2122,14 @@ namespace { // Structures used by FixQuadraticElements() _normal /= sqrt( normSqSize ); else _normal.SetCoord(1e-33,0,0); + +#ifdef _DEBUG_ + _face = face; +#endif } //================================================================================ /*! - * \brief Make up chain of links + * \brief Make up a chain of links * \param iSide - link to add first * \param chain - chain to fill in * \param pos - postion of medium nodes the links should have @@ -1606,18 +2148,34 @@ namespace { // Structures used by FixQuadraticElements() if ( _sides.size() != 4 ) { // triangle - visit all my continous faces MSGBEG( *this ); - for ( int i = 0; i < _sides.size(); ++i ) { - if ( !_sideIsAdded[i] && _sides[i] ) { - _sideIsAdded[i]=true; - TChain::iterator chLink = chain.insert( chain.begin(), TChainLink(_sides[i])); - chLink->SetFace( this ); - if ( _sides[i]->MediumPos() >= pos ) - if ( const QFace* f = _sides[i]->GetContinuesFace( this )) - f->GetLinkChain( *chLink, chain, pos, error ); + TLinkSet links; + list< const QFace* > faces( 1, this ); + while ( !faces.empty() ) { + const QFace* face = faces.front(); + for ( int i = 0; i < face->_sides.size(); ++i ) { + if ( !face->_sideIsAdded[i] && face->_sides[i] ) { + face->_sideIsAdded[i] = true; + // find a face side in the chain + TLinkInSet chLink = links.insert( TChainLink(face->_sides[i])).first; +// TChain::iterator chLink = chain.begin(); +// for ( ; chLink != chain.end(); ++chLink ) +// if ( chLink->_qlink == face->_sides[i] ) +// break; +// if ( chLink == chain.end() ) +// chLink = chain.insert( chain.begin(), TChainLink(face->_sides[i])); + // add a face to a chained link and put a continues face in the queue + chLink->SetFace( face ); + if ( face->_sides[i]->MediumPos() == pos ) + if ( const QFace* contFace = face->_sides[i]->GetContinuesFace( face )) + if ( contFace->_sides.size() == 3 ) + faces.push_back( contFace ); + } } + faces.pop_front(); } if ( error < ERR_TRI ) error = ERR_TRI; + chain.insert( chain.end(), links.begin(),links.end() ); return false; } _sideIsAdded[iSide] = true; // not to add this link to chain again @@ -1630,13 +2188,14 @@ namespace { // Structures used by FixQuadraticElements() chLink->SetFace( this ); MSGBEG( *this ); - // propagate from rectangle to neighbour faces + // propagate from quadrangle to neighbour faces if ( link->MediumPos() >= pos ) { int nbLinkFaces = link->_faces.size(); - if ( nbLinkFaces == 4 || nbLinkFaces < 4 && link->OnBoundary()) { + if ( nbLinkFaces == 4 || (/*nbLinkFaces < 4 && */link->OnBoundary())) { // hexahedral mesh or boundary quadrangles - goto a continous face if ( const QFace* f = link->GetContinuesFace( this )) - return f->GetLinkChain( *chLink, chain, pos, error ); + if ( f->_sides.size() == 4 ) + return f->GetLinkChain( *chLink, chain, pos, error ); } else { TChainLink chLink(link); // side face of prismatic mesh - visit all faces of iSide @@ -1660,6 +2219,7 @@ namespace { // Structures used by FixQuadraticElements() * \param nodeToContain - node the returned link must contain; if provided, search * also performed on adjacent faces * \param isAdjacentUsed - returns true if link is found in adjacent faces + * \param nbRecursionsLeft - to limit recursion */ //================================================================================ @@ -1667,7 +2227,8 @@ namespace { // Structures used by FixQuadraticElements() const TChainLink& avoidLink, TLinkInSet * notBoundaryLink, const SMDS_MeshNode* nodeToContain, - bool * isAdjacentUsed) const + bool * isAdjacentUsed, + int nbRecursionsLeft) const { TLinkInSet linksEnd = links.end(), boundaryLink = linksEnd; @@ -1680,6 +2241,8 @@ namespace { // Structures used by FixQuadraticElements() continue; TLinkInSet link = links.find( _sides[iL] ); if ( link == linksEnd ) continue; + if ( (*link)->MediumPos() > SMDS_TOP_FACE ) + continue; // We work on faces here, don't go inside a solid // check link if ( link->IsBoundary() ) { @@ -1696,19 +2259,21 @@ namespace { // Structures used by FixQuadraticElements() if ( boundaryLink != linksEnd ) break; } - if ( boundaryLink == linksEnd && nodeToContain ) // cellect adjacent faces + if ( boundaryLink == linksEnd && nodeToContain ) // collect adjacent faces if ( const QFace* adj = link->NextFace( this )) if ( adj->Contains( nodeToContain )) adjacentFaces.push_back( make_pair( adj, link )); } if ( isAdjacentUsed ) *isAdjacentUsed = false; - if ( boundaryLink == linksEnd && nodeToContain ) // check adjacent faces + if ( boundaryLink == linksEnd && nodeToContain && nbRecursionsLeft) // check adjacent faces { + if ( nbRecursionsLeft < 0 ) + nbRecursionsLeft = nodeToContain->NbInverseElements(); TFaceLinkList::iterator adj = adjacentFaces.begin(); for ( ; boundaryLink == linksEnd && adj != adjacentFaces.end(); ++adj ) - boundaryLink = adj->first->GetBoundaryLink( links, *(adj->second), - 0, nodeToContain, isAdjacentUsed); + boundaryLink = adj->first->GetBoundaryLink( links, *(adj->second), 0, nodeToContain, + isAdjacentUsed, nbRecursionsLeft-1); if ( isAdjacentUsed ) *isAdjacentUsed = true; } return boundaryLink; @@ -1812,16 +2377,19 @@ namespace { // Structures used by FixQuadraticElements() int iL1 = (iL + 1) % 3, iL2 = (iL + 2) % 3; // indices of the two other links of triangle TLinkInSet link1 = theLinks.find( _sides[iL1] ); TLinkInSet link2 = theLinks.find( _sides[iL2] ); + if ( link1 == theLinks.end() || link2 == theLinks.end() ) + return thePrevLen; const QFace* f1 = link1->NextFace( this ); // adjacent faces const QFace* f2 = link2->NextFace( this ); // propagate to adjacent faces till limit step or boundary double len1 = thePrevLen + (theLink->MiddlePnt() - _sides[iL1]->MiddlePnt()).Modulus(); double len2 = thePrevLen + (theLink->MiddlePnt() - _sides[iL2]->MiddlePnt()).Modulus(); - gp_Vec linkDir1, linkDir2; + gp_Vec linkDir1(0,0,0); // initialize to avoid valgrind error ("Conditional jump...") + gp_Vec linkDir2(0,0,0); try { OCC_CATCH_SIGNALS; - if ( f1 ) + if ( f1 && theLink->MediumPos() <= (*link1)->MediumPos() ) len1 = f1->MoveByBoundary ( *link1, theRefVec, theLinks, theFaceHelper, len1, theStep-1, &linkDir1, theSign); else @@ -1832,7 +2400,7 @@ namespace { // Structures used by FixQuadraticElements() } try { OCC_CATCH_SIGNALS; - if ( f2 ) + if ( f2 && theLink->MediumPos() <= (*link2)->MediumPos() ) len2 = f2->MoveByBoundary ( *link2, theRefVec, theLinks, theFaceHelper, len2, theStep-1, &linkDir2, theSign); else @@ -1862,6 +2430,37 @@ namespace { // Structures used by FixQuadraticElements() return fullLen; } + //================================================================================ + /*! + * \brief Checks if the face is distorted due to bentLink + */ + //================================================================================ + + bool QFace::IsSpoiled(const QLink* bentLink ) const + { + // code is valid for convex faces only + gp_XYZ gc(0,0,0); + for ( TIDSortedNodeSet::const_iterator n = begin(); n!=end(); ++n) + gc += XYZ( *n ) / size(); + for (unsigned i = 0; i < _sides.size(); ++i ) + { + if ( _sides[i] == bentLink ) continue; + gp_Vec linkNorm = _normal ^ gp_Vec( XYZ(_sides[i]->node1()), XYZ(_sides[i]->node2())); + gp_Vec vecOut( gc, _sides[i]->MiddlePnt() ); + if ( linkNorm * vecOut < 0 ) + linkNorm.Reverse(); + double mag2 = linkNorm.SquareMagnitude(); + if ( mag2 > numeric_limits::min() ) + linkNorm /= sqrt( mag2 ); + gp_Vec vecBent ( _sides[i]->MiddlePnt(), bentLink->MediumPnt()); + gp_Vec vecStraight( _sides[i]->MiddlePnt(), bentLink->MiddlePnt()); + if ( vecBent * linkNorm > -0.1*vecStraight.Magnitude() ) + return true; + } + return false; + + } + //================================================================================ /*! * \brief Find pairs of continues faces @@ -1880,7 +2479,9 @@ namespace { // Structures used by FixQuadraticElements() if ( _faces.empty() ) return; - int iFaceCont = -1; + int iFaceCont = -1, nbBoundary = 0, iBoundary[2]={-1,-1}; + if ( _faces[0]->IsBoundary() ) + iBoundary[ nbBoundary++ ] = 0; for ( int iF = 1; iFaceCont < 0 && iF < _faces.size(); ++iF ) { // look for a face bounding none of volumes bound by _faces[0] @@ -1891,8 +2492,21 @@ namespace { // Structures used by FixQuadraticElements() _faces[iF]->_volumes[iV] == _faces[0]->_volumes[1]); if ( !sameVol ) iFaceCont = iF; + if ( _faces[iF]->IsBoundary() ) + iBoundary[ nbBoundary++ ] = iF; + } + // Set continues faces: arrange _faces to have + // _faces[0] continues to _faces[1] + // _faces[2] continues to _faces[3] + if ( nbBoundary == 2 ) // bnd faces are continues + { + if (( iBoundary[0] < 2 ) != ( iBoundary[1] < 2 )) + { + int iNear0 = iBoundary[0] < 2 ? 1-iBoundary[0] : 5-iBoundary[0]; + std::swap( _faces[ iBoundary[1] ], _faces[iNear0] ); + } } - if ( iFaceCont > 0 ) // continues faces found, set one by the other + else if ( iFaceCont > 0 ) // continues faces found { if ( iFaceCont != 1 ) std::swap( _faces[1], _faces[iFaceCont] ); @@ -1942,6 +2556,27 @@ namespace { // Structures used by FixQuadraticElements() if (_qfaces[1]) norm += _qfaces[1]->_normal; return norm; } + //================================================================================ + /*! + * \brief Test link curvature taking into account size of faces + */ + //================================================================================ + + bool TChainLink::IsStraight() const + { + bool isStraight = _qlink->IsStraight(); + if ( isStraight && _qfaces[0] && !_qfaces[1] ) + { + int i = _qfaces[0]->LinkIndex( _qlink ); + int iOpp = ( i + 2 ) % _qfaces[0]->_sides.size(); + gp_XYZ mid1 = _qlink->MiddlePnt(); + gp_XYZ mid2 = _qfaces[0]->_sides[ iOpp ]->MiddlePnt(); + double faceSize2 = (mid1-mid2).SquareModulus(); + isStraight = _qlink->_nodeMove.SquareMagnitude() < 1/3./3. * faceSize2; + } + return isStraight; + } + //================================================================================ /*! * \brief Move medium nodes of vertical links of pentahedrons adjacent by side faces @@ -1960,7 +2595,7 @@ namespace { // Structures used by FixQuadraticElements() bndLinks1.insert( lnk->_qlink ); else interLinks.insert( lnk->_qlink ); - isCurved = isCurved || !(*lnk)->IsStraight(); + isCurved = isCurved || !lnk->IsStraight(); } if ( !isCurved ) return; // no need to move @@ -2007,41 +2642,20 @@ namespace { // Structures used by FixQuadraticElements() TLinkSet linkSet( allLinks.begin(), allLinks.end()); TLinkInSet linkIt = linkSet.begin(), linksEnd = linkSet.end(); - // move in 2d if we are on geom face -// TopoDS_Face face; -// TopLoc_Location loc; -// SMESH_MesherHelper faceHelper( *helper.GetMesh()); -// while ( linkIt->IsBoundary()) ++linkIt; -// if ( linkIt == linksEnd ) return; -// if ( (*linkIt)->MediumPos() == SMDS_TOP_FACE ) { -// bool checkPos = true; -// TopoDS_Shape f = helper.GetSubShapeByNode( (*linkIt)->_mediumNode, helper.GetMeshDS() ); -// if ( !f.IsNull() && f.ShapeType() == TopAbs_FACE ) { -// face = TopoDS::Face( f ); -// helper.GetNodeUV( face, (*linkIt)->_mediumNode, 0, &checkPos); -// if (checkPos) -// face.Nullify(); -// else -// faceHelper.SetSubShape( face ); -// } -// } for ( linkIt = linkSet.begin(); linkIt != linksEnd; ++linkIt) { - if ( linkIt->IsBoundary() && !(*linkIt)->IsStraight() && linkIt->_qfaces[0]) + if ( linkIt->IsBoundary() && !linkIt->IsStraight() && linkIt->_qfaces[0]) { -// if ( !face.IsNull() ) { -// const SMDS_MeshNode* inFaceNode = -// faceHelper.GetNodeUVneedInFaceNode() ? linkIt->_qfaces[0]->GetNodeInFace() : 0; -// gp_XY uvm = helper.GetNodeUV( face, (*linkIt)->_mediumNode, inFaceNode ); -// gp_XY uv1 = helper.GetNodeUV( face, (*linkIt)->node1(), inFaceNode); -// gp_XY uv2 = helper.GetNodeUV( face, (*linkIt)->node2(), inFaceNode); -// gp_XY uvMove = uvm - helper.GetMiddleUV( BRep_Tool::Surface(face,loc), uv1, uv2); -// gp_Vec move( uvMove.X(), uvMove.Y(), 0 ); -// linkIt->_qfaces[0]->MoveByBoundary( *linkIt, move, linkSet, &faceHelper ); -// } -// else { - linkIt->_qfaces[0]->MoveByBoundary( *linkIt, (*linkIt)->_nodeMove, linkSet ); - //} + // move iff a boundary link is bent towards inside of a face (issue 0021084) + const QFace* face = linkIt->_qfaces[0]; + gp_XYZ pIn = ( face->_sides[0]->MiddlePnt() + + face->_sides[1]->MiddlePnt() + + face->_sides[2]->MiddlePnt() ) / 3.; + gp_XYZ insideDir( pIn - (*linkIt)->MiddlePnt()); + bool linkBentInside = ((*linkIt)->_nodeMove.Dot( insideDir ) > 0 ); + //if ( face->IsSpoiled( linkIt->_qlink )) + if ( linkBentInside ) + face->MoveByBoundary( *linkIt, (*linkIt)->_nodeMove, linkSet ); } } } @@ -2054,7 +2668,7 @@ namespace { // Structures used by FixQuadraticElements() enum TSplitTriaResult { _OK, _NO_CORNERS, _FEW_ROWS, _MANY_ROWS, _NO_SIDELINK, _BAD_MIDQUAD, _NOT_RECT, - _NO_MIDQUAD, _NO_UPTRIA, _BAD_SET_SIZE, _BAD_CORNER, _BAD_START, _NO_BOTLINK }; + _NO_MIDQUAD, _NO_UPTRIA, _BAD_SET_SIZE, _BAD_CORNER, _BAD_START, _NO_BOTLINK, _TWISTED_CHAIN }; TSplitTriaResult splitTrianglesIntoChains( TChain & allLinks, vector< TChain> & resultChains, @@ -2120,6 +2734,8 @@ namespace { // Structures used by FixQuadraticElements() const QFace* botTria = botLink->_qfaces[0]; // bottom triangle bound by botLink if ( !botTria ) { // the column ends + if ( botLink == startLink ) + return _TWISTED_CHAIN; // issue 0020951 linkSet.erase( botLink ); if ( iRow != rowChains.size() ) return _FEW_ROWS; // different nb of rows in columns @@ -2179,8 +2795,10 @@ namespace { // Structures used by FixQuadraticElements() // next bottom link ends at the new corner linkSet.erase( botLink ); botLink = upTria->GetLinkByNode( linkSet, (isCase2 ? *sideLink : *midQuadLink), corner ); - if ( botLink == linksEnd || botLink == (isCase2 ? midQuadLink : sideLink)) + if ( botLink == linksEnd || botLink == midQuadLink || botLink == sideLink) return _NO_BOTLINK; + if ( midQuadLink == startLink || sideLink == startLink ) + return _TWISTED_CHAIN; // issue 0020951 linkSet.erase( midQuadLink ); linkSet.erase( sideLink ); @@ -2224,7 +2842,7 @@ namespace { // Structures used by FixQuadraticElements() return _OK; } -} +} //namespace //======================================================================= /*! @@ -2237,37 +2855,55 @@ namespace { // Structures used by FixQuadraticElements() void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) { - // apply algorithm to solids or geom faces + // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion + if ( getenv("NO_FixQuadraticElements") ) + return; + + // 0. Apply algorithm to solids or geom faces // ---------------------------------------------- if ( myShape.IsNull() ) { if ( !myMesh->HasShapeToMesh() ) return; SetSubShape( myMesh->GetShapeToMesh() ); +#ifdef _DEBUG_ + int nbSolids = 0; + TopTools_IndexedMapOfShape solids; + TopExp::MapShapes(myShape,TopAbs_SOLID,solids); + nbSolids = solids.Extent(); +#endif TopTools_MapOfShape faces; // faces not in solid or in not meshed solid for ( TopExp_Explorer f(myShape,TopAbs_FACE,TopAbs_SOLID); f.More(); f.Next() ) { - faces.Add( f.Current() ); + faces.Add( f.Current() ); // not in solid } - for ( TopExp_Explorer v(myShape,TopAbs_SOLID); v.More(); v.Next() ) { - if ( myMesh->GetSubMesh( v.Current() )->IsEmpty() ) { // get faces of solid - for ( TopExp_Explorer f( v.Current(), TopAbs_FACE); f.More(); f.Next() ) - faces.Add( f.Current() ); + for ( TopExp_Explorer s(myShape,TopAbs_SOLID); s.More(); s.Next() ) { + if ( myMesh->GetSubMesh( s.Current() )->IsEmpty() ) { // get faces of solid + for ( TopExp_Explorer f( s.Current(), TopAbs_FACE); f.More(); f.Next() ) + faces.Add( f.Current() ); // in not meshed solid } else { // fix nodes in the solid and its faces +#ifdef _DEBUG_ + MSG("FIX SOLID " << nbSolids-- << " #" << GetMeshDS()->ShapeToIndex(s.Current())); +#endif SMESH_MesherHelper h(*myMesh); - h.SetSubShape( v.Current() ); + h.SetSubShape( s.Current() ); h.FixQuadraticElements(false); } } // fix nodes on geom faces +#ifdef _DEBUG_ + int nbfaces = faces.Extent(); /*avoid "unused varianbles": */ nbfaces++, nbfaces--; +#endif for ( TopTools_MapIteratorOfMapOfShape fIt( faces ); fIt.More(); fIt.Next() ) { + MSG("FIX FACE " << nbfaces-- << " #" << GetMeshDS()->ShapeToIndex(fIt.Key())); SMESH_MesherHelper h(*myMesh); h.SetSubShape( fIt.Key() ); - h.FixQuadraticElements(); + h.FixQuadraticElements(true); } + //perf_print_all_meters(1); return; } - // Find out type of elements and get iterator on them + // 1. Find out type of elements and get iterator on them // --------------------------------------------------- SMDS_ElemIteratorPtr elemIt; @@ -2286,7 +2922,7 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) if ( !elemIt || !elemIt->more() || elemType < SMDSAbs_Face ) return; - // Fill in auxiliary data structures + // 2. Fill in auxiliary data structures // ---------------------------------- set< QLink > links; @@ -2295,21 +2931,24 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) set< QFace >::iterator pFace; bool isCurved = false; - bool hasRectFaces = false; - set nbElemNodeSet; + //bool hasRectFaces = false; + //set nbElemNodeSet; + SMDS_VolumeTool volTool; + + TIDSortedNodeSet apexOfPyramid; + const int apexIndex = 4; if ( elemType == SMDSAbs_Volume ) { - SMDS_VolumeTool volTool; while ( elemIt->more() ) // loop on volumes { const SMDS_MeshElement* vol = elemIt->next(); if ( !vol->IsQuadratic() || !volTool.Set( vol )) - return; //continue; + return; for ( int iF = 0; iF < volTool.NbFaces(); ++iF ) // loop on faces of volume { int nbN = volTool.NbFaceNodes( iF ); - nbElemNodeSet.insert( nbN ); + //nbElemNodeSet.insert( nbN ); const SMDS_MeshNode** faceNodes = volTool.GetFaceNodes( iF ); vector< const QLink* > faceLinks( nbN/2 ); for ( int iN = 0; iN < nbN; iN += 2 ) // loop on links of a face @@ -2328,10 +2967,20 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) if ( pFace->NbVolumes() == 0 ) pFace->AddSelfToLinks(); pFace->SetVolume( vol ); - hasRectFaces = hasRectFaces || - ( volTool.GetVolumeType() == SMDS_VolumeTool::QUAD_HEXA || - volTool.GetVolumeType() == SMDS_VolumeTool::QUAD_PENTA ); +// hasRectFaces = hasRectFaces || +// ( volTool.GetVolumeType() == SMDS_VolumeTool::QUAD_HEXA || +// volTool.GetVolumeType() == SMDS_VolumeTool::QUAD_PENTA ); +#ifdef _DEBUG_ + if ( nbN == 6 ) + pFace->_face = GetMeshDS()->FindFace(faceNodes[0],faceNodes[2],faceNodes[4]); + else + pFace->_face = GetMeshDS()->FindFace(faceNodes[0],faceNodes[2], + faceNodes[4],faceNodes[6] ); +#endif } + // collect pyramid apexes for further correction + if ( vol->NbCornerNodes() == 5 ) + apexOfPyramid.insert( vol->GetNode( apexIndex )); } set< QLink >::iterator pLink = links.begin(); for ( ; pLink != links.end(); ++pLink ) @@ -2344,7 +2993,7 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) const SMDS_MeshElement* face = elemIt->next(); if ( !face->IsQuadratic() ) continue; - nbElemNodeSet.insert( face->NbNodes() ); + //nbElemNodeSet.insert( face->NbNodes() ); int nbN = face->NbNodes()/2; vector< const QLink* > faceLinks( nbN ); for ( int iN = 0; iN < nbN; ++iN ) // loop on links of a face @@ -2359,27 +3008,28 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) // store QFace pFace = faces.insert( QFace( faceLinks )).first; pFace->AddSelfToLinks(); - hasRectFaces = ( hasRectFaces || nbN == 4 ); + //hasRectFaces = ( hasRectFaces || nbN == 4 ); } } if ( !isCurved ) return; // no curved edges of faces - // Compute displacement of medium nodes - // ------------------------------------- + // 3. Compute displacement of medium nodes + // --------------------------------------- - // two loops on faces: the first is to treat boundary links, the second is for internal ones + // two loops on QFaces: the first is to treat boundary links, the second is for internal ones TopLoc_Location loc; // not treat boundary of volumic submesh int isInside = ( elemType == SMDSAbs_Volume && volumeOnly ) ? 1 : 0; for ( ; isInside < 2; ++isInside ) { - MSG( "--------------- LOOP " << isInside << " ------------------"); + MSG( "--------------- LOOP (inside=" << isInside << ") ------------------"); SMDS_TypeOfPosition pos = isInside ? SMDS_TOP_3DSPACE : SMDS_TOP_FACE; + SMDS_TypeOfPosition bndPos = isInside ? SMDS_TOP_FACE : SMDS_TOP_EDGE; for ( pFace = faces.begin(); pFace != faces.end(); ++pFace ) { if ( bool(isInside) == pFace->IsBoundary() ) continue; - for ( int dir = 0; dir < 2; ++dir ) // 2 directions of propagation from quadrangle + for ( int dir = 0; dir < 2; ++dir ) // 2 directions of propagation from the quadrangle { MSG( "CHAIN"); // make chain of links connected via continues faces @@ -2390,18 +3040,18 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) if ( !pFace->GetLinkChain( dir+2, rawChain, pos, error ) && error ==ERR_UNKNOWN ) continue; vector< TChain > chains; - if ( error == ERR_OK ) { // chains contains continues rectangles + if ( error == ERR_OK ) { // chain contains continues rectangles chains.resize(1); chains[0].splice( chains[0].begin(), rawChain ); } - else if ( error == ERR_TRI ) { // chains contains continues triangles + else if ( error == ERR_TRI ) { // chain contains continues triangles TSplitTriaResult res = splitTrianglesIntoChains( rawChain, chains, pos ); - if ( res != _OK ) { // not rectangles split into triangles + if ( res != _OK ) { // not quadrangles split into triangles fixTriaNearBoundary( rawChain, *this ); break; } } - else if ( error == ERR_PRISM ) { // side faces of prisms + else if ( error == ERR_PRISM ) { // quadrangle side faces of prisms fixPrism( rawChain ); break; } @@ -2412,8 +3062,13 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) { TChain& chain = chains[iC]; if ( chain.empty() ) continue; - if ( chain.front()->IsStraight() && chain.back()->IsStraight() ) { - MSG("3D straight"); + if ( chain.front().IsStraight() && chain.back().IsStraight() ) { + MSG("3D straight - ignore"); + continue; + } + if ( chain.front()->MediumPos() > bndPos || + chain.back() ->MediumPos() > bndPos ) { + MSG("Internal chain - ignore"); continue; } // mesure chain length and compute link position along the chain @@ -2442,26 +3097,47 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) TopoDS_Face face; bool checkUV = true; - if ( !isInside ) { - // compute node displacement of end links in parametric space of face - const SMDS_MeshNode* nodeOnFace = (*(++chain.begin()))->_mediumNode; + if ( !isInside ) + { + // compute node displacement of end links of chain in parametric space of face + TChainLink& linkOnFace = *(++chain.begin()); + const SMDS_MeshNode* nodeOnFace = linkOnFace->_mediumNode; TopoDS_Shape f = GetSubShapeByNode( nodeOnFace, GetMeshDS() ); - if ( !f.IsNull() && f.ShapeType() == TopAbs_FACE ) { + if ( !f.IsNull() && f.ShapeType() == TopAbs_FACE ) + { face = TopoDS::Face( f ); - for ( int is1 = 0; is1 < 2; ++is1 ) { // move0 or move1 + Handle(Geom_Surface) surf = BRep_Tool::Surface(face,loc); + bool isStraight[2]; + for ( int is1 = 0; is1 < 2; ++is1 ) // move0 or move1 + { TChainLink& link = is1 ? chain.back() : chain.front(); + gp_XY uvm = GetNodeUV( face, link->_mediumNode, nodeOnFace, &checkUV); gp_XY uv1 = GetNodeUV( face, link->node1(), nodeOnFace, &checkUV); gp_XY uv2 = GetNodeUV( face, link->node2(), nodeOnFace, &checkUV); - gp_XY uvm = GetNodeUV( face, link->_mediumNode, nodeOnFace, &checkUV); - gp_XY uvMove = uvm - GetMiddleUV( BRep_Tool::Surface(face,loc), uv1, uv2); - if ( is1 ) move1.SetCoord( uvMove.X(), uvMove.Y(), 0 ); - else move0.SetCoord( uvMove.X(), uvMove.Y(), 0 ); + gp_XY uv12 = GetMiddleUV( surf, uv1, uv2); + // uvMove = uvm - uv12 + gp_XY uvMove = applyIn2D(surf, uvm, uv12, gp_XY_Subtracted, /*inPeriod=*/false); + ( is1 ? move1 : move0 ).SetCoord( uvMove.X(), uvMove.Y(), 0 ); + if ( !is1 ) // correct nodeOnFace for move1 (issue 0020919) + nodeOnFace = (*(++chain.rbegin()))->_mediumNode; + isStraight[is1] = isStraightLink( (uv2-uv1).SquareModulus(), + 10 * uvMove.SquareModulus()); } - if ( move0.SquareMagnitude() < straightTol2 && - move1.SquareMagnitude() < straightTol2 ) { - MSG("2D straight"); + if ( isStraight[0] && isStraight[1] ) { + MSG("2D straight - ignore"); continue; // straight - no need to move nodes of internal links } + + // check if a chain is already fixed + gp_XY uvm = GetNodeUV( face, linkOnFace->_mediumNode, 0, &checkUV); + gp_XY uv1 = GetNodeUV( face, linkOnFace->node1(), nodeOnFace, &checkUV); + gp_XY uv2 = GetNodeUV( face, linkOnFace->node2(), nodeOnFace, &checkUV); + gp_XY uv12 = GetMiddleUV( surf, uv1, uv2); + if (( uvm - uv12 ).SquareModulus() > 1e-10 ) + { + MSG("Already fixed - ignore"); + continue; + } } } gp_Trsf trsf; @@ -2500,9 +3176,10 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) } else { // compute 3D displacement by 2D one + Handle(Geom_Surface) s = BRep_Tool::Surface(face,loc); gp_XY oldUV = GetNodeUV( face, (*link1)->_mediumNode, 0, &checkUV); - gp_XY newUV = oldUV + gp_XY( move.X(), move.Y() ); - gp_Pnt newPnt = BRep_Tool::Surface(face,loc)->Value( newUV.X(), newUV.Y()); + gp_XY newUV = applyIn2D( s, oldUV, gp_XY( move.X(),move.Y()), gp_XY_Added); + gp_Pnt newPnt = s->Value( newUV.X(), newUV.Y()); move = gp_Vec( XYZ((*link1)->_mediumNode), newPnt.Transformed(loc) ); #ifdef _DEBUG_ if ( (XYZ((*link1)->node1()) - XYZ((*link1)->node2())).SquareModulus() < @@ -2510,7 +3187,8 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) { gp_XY uv0 = GetNodeUV( face, (*link0)->_mediumNode, 0, &checkUV); gp_XY uv2 = GetNodeUV( face, (*link2)->_mediumNode, 0, &checkUV); - MSG( "uv0: "< vols( 100 ); +// vector volSize( 100 ); +// int nbVols; +// bool ok; for ( pLink = links.begin(); pLink != links.end(); ++pLink ) { if ( pLink->IsMoved() ) { - //gp_Pnt p = pLink->MediumPnt() + pLink->Move(); gp_Pnt p = pLink->MiddlePnt() + pLink->Move(); GetMeshDS()->MoveNode( pLink->_mediumNode, p.X(), p.Y(), p.Z()); + // +// gp_Pnt pNew = pLink->MiddlePnt() + pLink->Move(); +// if ( pLink->MediumPos() != SMDS_TOP_3DSPACE ) +// { +// // avoid making distorted volumes near boundary +// SMDS_ElemIteratorPtr volIt = +// (*pLink)._mediumNode->GetInverseElementIterator( SMDSAbs_Volume ); +// for ( nbVols = 0; volIt->more() && volTool.Set( volIt->next() ); ++nbVols ) +// { +// vols [ nbVols ] = volTool.Element(); +// volSize[ nbVols ] = volTool.GetSize(); +// } +// gp_Pnt pOld = pLink->MediumPnt(); +// const_cast( pLink->_mediumNode )->setXYZ( pNew.X(), pNew.Y(), pNew.Z() ); +// ok = true; +// while ( nbVols-- && ok ) +// { +// volTool.Set( vols[ nbVols ]); +// ok = ( volSize[ nbVols ] * volTool.GetSize() > 1e-20 ); +// } +// if ( !ok ) +// { +// const_cast( pLink->_mediumNode )->setXYZ( pOld.X(), pOld.Y(), pOld.Z() ); +// MSG( "Do NOT move \t" << pLink->_mediumNode->GetID() +// << " because of distortion of volume " << vols[ nbVols+1 ]->GetID()); +// continue; +// } +// } +// GetMeshDS()->MoveNode( pLink->_mediumNode, pNew.X(), pNew.Y(), pNew.Z() ); + } + } + + //return; + + // issue 0020982 + // Move the apex of pyramid together with the most curved link + + TIDSortedNodeSet::iterator apexIt = apexOfPyramid.begin(); + for ( ; apexIt != apexOfPyramid.end(); ++apexIt ) + { + SMESH_TNodeXYZ apex = *apexIt; + + gp_Vec maxMove( 0,0,0 ); + double maxMoveSize2 = 0; + + // shift of node index to get medium nodes between the base nodes + const int base2MediumShift = 5; + + // find maximal movement of medium node + SMDS_ElemIteratorPtr volIt = apex._node->GetInverseElementIterator( SMDSAbs_Volume ); + vector< const SMDS_MeshElement* > pyramids; + while ( volIt->more() ) + { + const SMDS_MeshElement* pyram = volIt->next(); + if ( pyram->GetEntityType() != SMDSEntity_Quad_Pyramid ) continue; + pyramids.push_back( pyram ); + + for ( int iBase = 0; iBase < apexIndex; ++iBase ) + { + SMESH_TNodeXYZ medium = pyram->GetNode( iBase + base2MediumShift ); + if ( medium._node->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE ) + { + SMESH_TNodeXYZ n1 = pyram->GetNode( iBase ); + SMESH_TNodeXYZ n2 = pyram->GetNode( ( iBase+1 ) % 4 ); + gp_Pnt middle = 0.5 * ( n1 + n2 ); + gp_Vec move( middle, medium ); + double moveSize2 = move.SquareMagnitude(); + if ( moveSize2 > maxMoveSize2 ) + maxMove = move, maxMoveSize2 = moveSize2; + } + } + } + + // move the apex + if ( maxMoveSize2 > 1e-20 ) + { + apex += maxMove.XYZ(); + GetMeshDS()->MoveNode( apex._node, apex.X(), apex.Y(), apex.Z()); + + // move medium nodes neighboring the apex to the middle + const int base2MediumShift_2 = 9; + for ( unsigned i = 0; i < pyramids.size(); ++i ) + for ( int iBase = 0; iBase < apexIndex; ++iBase ) + { + SMESH_TNodeXYZ base = pyramids[i]->GetNode( iBase ); + const SMDS_MeshNode* medium = pyramids[i]->GetNode( iBase + base2MediumShift_2 ); + gp_XYZ middle = 0.5 * ( apex + base ); + GetMeshDS()->MoveNode( medium, middle.X(), middle.Y(), middle.Z()); + } } } } +