X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSMESH%2FSMESH_MesherHelper.cxx;h=698446b5c53268c5c592ee118fce14bbd3a95dee;hb=5958bdcce798556f2ef6866cb0066b0351f9b053;hp=bb96f035229472f6a3ae3f2dcf88043e988ffb45;hpb=d8f644ca3d4ce62f2ef41d4aacb52f5bb1221df3;p=modules%2Fsmesh.git diff --git a/src/SMESH/SMESH_MesherHelper.cxx b/src/SMESH/SMESH_MesherHelper.cxx index bb96f0352..698446b5c 100644 --- a/src/SMESH/SMESH_MesherHelper.cxx +++ b/src/SMESH/SMESH_MesherHelper.cxx @@ -1,23 +1,23 @@ -// Copyright (C) 2007-2010 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 @@ -30,6 +30,7 @@ #include "SMDS_EdgePosition.hxx" #include "SMDS_VolumeTool.hxx" #include "SMESH_subMesh.hxx" +#include "SMESH_ProxyMesh.hxx" #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -122,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; + } + } } } } @@ -209,9 +226,15 @@ void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh) 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 @@ -223,13 +246,13 @@ void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh) if ( Abs( uv1.Coord(1) - uv2.Coord(1) ) < Abs( uv1.Coord(2) - uv2.Coord(2) )) { myParIndex |= U_periodic; - myPar1[0] = surface.FirstUParameter(); - myPar2[0] = surface.LastUParameter(); + myPar1[0] = surf.FirstUParameter(); + myPar2[0] = surf.LastUParameter(); } else { myParIndex |= V_periodic; - myPar1[1] = surface.FirstVParameter(); - myPar2[1] = surface.LastVParameter(); + myPar1[1] = surf.FirstVParameter(); + myPar2[1] = surf.LastVParameter(); } // store seam shape indices, negative if shape encounters twice int edgeID = meshDS->ShapeToIndex( edge ); @@ -291,9 +314,9 @@ bool SMESH_MesherHelper::IsMedium(const SMDS_MeshNode* 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 @@ -315,6 +338,100 @@ 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 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 @@ -360,7 +477,7 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, { // node has position on face const SMDS_FacePosition* fpos = - static_cast(n->GetPosition().get()); + static_cast(n->GetPosition()); uv.SetCoord(fpos->GetUParameter(),fpos->GetVParameter()); if ( check ) uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F )); @@ -371,8 +488,8 @@ 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); @@ -380,7 +497,7 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, if ( validU ) uv = C2d->Value( u ); else - uv.SetCoord(0.,0.); + uv.SetCoord( Precision::Infinite(),0.); if ( check || !validU ) uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F ),/*force=*/ !validU ); @@ -407,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 ); @@ -459,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; @@ -475,19 +596,28 @@ bool SMESH_MesherHelper::CheckNodeUV(const TopoDS_Face& F, const SMDS_MeshNode* n, gp_XY& uv, const double tol, - const bool force) const + const bool force, + double distXYZ[4]) const { - if ( force || !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 ( Precision::IsInfinite( uv.X() ) || - Precision::IsInfinite( uv.Y() ) || - 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 = GetProjector( F, loc, tol ); projector.Perform( nodePnt ); @@ -499,15 +629,26 @@ bool SMESH_MesherHelper::CheckNodeUV(const TopoDS_Face& F, Quantity_Parameter U,V; projector.LowerDistanceParameters(U,V); uv.SetCoord( U,V ); - if ( nodePnt.Distance( surface->Value( U, V )) > tol ) + 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; } + // 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; @@ -594,7 +735,14 @@ gp_XY SMESH_MesherHelper::GetMiddleUV(const Handle(Geom_Surface)& surface, const gp_XY& p1, const gp_XY& p2) { - return applyIn2D( surface, p1, p2, & AverageUV ); + // 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 ); } //======================================================================= @@ -611,7 +759,7 @@ double SMESH_MesherHelper::GetNodeU(const TopoDS_Edge& E, const SMDS_PositionPtr pos = n->GetPosition(); if ( pos->GetTypeOfPosition()==SMDS_TOP_EDGE ) { - const SMDS_EdgePosition* epos = static_cast( pos.get() ); + const SMDS_EdgePosition* epos = static_cast( pos ); param = epos->GetUParameter(); } else if( pos->GetTypeOfPosition() == SMDS_TOP_VERTEX ) @@ -626,7 +774,7 @@ double SMESH_MesherHelper::GetNodeU(const TopoDS_Edge& E, else { SMESHDS_Mesh * meshDS = GetMeshDS(); - int vertexID = pos->GetShapeId(); + int vertexID = n->getshapeId(); const TopoDS_Vertex& V = TopoDS::Vertex(meshDS->IndexToShape(vertexID)); param = BRep_Tool::Parameter( V, E ); } @@ -637,7 +785,7 @@ double SMESH_MesherHelper::GetNodeU(const TopoDS_Edge& 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 ) != pos->GetShapeId() ); + force = ( GetMeshDS()->ShapeToIndex( E ) != n->getshapeId() ); *check = CheckNodeU( E, n, param, 2*tol, force ); } @@ -655,11 +803,11 @@ bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge& E, double& u, const double tol, const bool force, - double* distance) const + double distXYZ[4]) const { - if ( force || !myOkNodePosShapes.count( n->GetPosition()->GetShapeId() )) + int shapeID = n->getshapeId(); + if ( force || toCheckPosOnShape( shapeID )) { - // check that u is correct TopLoc_Location loc; double f,l; Handle(Geom_Curve) curve = BRep_Tool::Curve( E,loc,f,l ); if ( curve.IsNull() ) // degenerated edge @@ -672,12 +820,18 @@ bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge& E, } else { - gp_Pnt nodePnt = SMESH_MeshEditor::TNodeXYZ( n ); + gp_Pnt nodePnt = SMESH_TNodeXYZ( n ); if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() ); - double dist = nodePnt.Distance( curve->Value( u )); - if ( distance ) *distance = dist; + 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 ); @@ -697,18 +851,27 @@ bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge& E, } Quantity_Parameter U = projector->LowerDistanceParameter(); u = double( U ); - dist = nodePnt.Distance( curve->Value( U )); - if ( distance ) *distance = dist; + 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; } - //u = double( U ); + // 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() ) { - ((SMESH_MesherHelper*) this)->myOkNodePosShapes.insert( n->GetPosition()->GetShapeId() ); + setPosOnShapeValidity( shapeID, true ); } if (( u < f-tol || u > l+tol ) && force ) { @@ -751,31 +914,36 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1, 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 - TopoDS_Edge E; double u [2]; - TopoDS_Face F; gp_XY uv[2]; - bool uvOK[2] = { false, false }; TopAbs_ShapeEnum shapeType = myShape.IsNull() ? TopAbs_SHAPE : myShape.ShapeType(); if ( faceID>0 || shapeType == TopAbs_FACE) { @@ -792,7 +960,7 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1, { if ( Pos1->GetTypeOfPosition()==SMDS_TOP_EDGE && Pos2->GetTypeOfPosition()==SMDS_TOP_EDGE && - Pos1->GetShapeId() != Pos2->GetShapeId() ) // issue 0021006 + n1->getshapeId() != n2->getshapeId() ) // issue 0021006 return getMediumNodeOnComposedWire(n1,n2,force3d); if( myShape.IsNull() ) @@ -812,12 +980,14 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1, { if ( uvOK[0] && uvOK[1] ) { - if ( IsDegenShape( Pos1->GetShapeId() )) + 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( Pos2->GetShapeId() )) + } + 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); @@ -854,11 +1024,13 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1, } } } + // 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 ( !F.IsNull() ) { gp_XY UV = ( uv[0] + uv[1] ) / 2.; @@ -875,6 +1047,7 @@ const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1, { meshDS->SetNodeInVolume(n12, myShapeID); } + myTLinkNodeMap.insert( make_pair( link, n12 )); return n12; } @@ -895,7 +1068,7 @@ const SMDS_MeshNode* SMESH_MesherHelper::getMediumNodeOnComposedWire(const SMDS_ // 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(); + double u = 0, distMiddleProj = Precision::Infinite(), distXYZ[4]; int iOkEdge = 0; TopoDS_Edge edges[2]; for ( int is2nd = 0; is2nd < 2; ++is2nd ) @@ -909,11 +1082,11 @@ const SMDS_MeshNode* SMESH_MesherHelper::getMediumNodeOnComposedWire(const SMDS_ // 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 ), foundDist = node2MiddleDist; - CheckNodeU( edge, n12, foundU, 2*BRep_Tool::Tolerance(edge), /*force=*/true, &foundDist ); - if ( foundDist < node2MiddleDist ) + double foundU = GetNodeU( edge, n ); + CheckNodeU( edge, n12, foundU, 2*BRep_Tool::Tolerance(edge), /*force=*/true, distXYZ ); + if ( distXYZ[0] < node2MiddleDist ) { - distMiddleProj = foundDist; + distMiddleProj = distXYZ[0]; u = foundU; iOkEdge = is2nd; } @@ -1399,9 +1572,24 @@ SMESH_MesherHelper::AddPolyhedralVolume (const std::vector bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, const TopoDS_Face& theFace, const TopoDS_Edge& theBaseEdge, - SMESHDS_Mesh* theMesh) + SMESHDS_Mesh* theMesh, + SMESH_ProxyMesh* theProxyMesh) { - SMESHDS_SubMesh* faceSubMesh = theMesh->MeshElements( theFace ); + 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; @@ -1422,12 +1610,21 @@ bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, nCol.resize( nbRows ); nCol[0] = u_n->second; } + 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 ); + } + } // fill theParam2ColumnMap column by column by passing from nodes on // theBaseEdge up via mesh faces on theFace - TParam2ColumnMap::iterator par_nVec_2 = theParam2ColumnMap.begin(); - TParam2ColumnMap::iterator par_nVec_1 = par_nVec_2++; + 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 ) { @@ -1458,7 +1655,7 @@ bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, if ( iRow + 1 < nbRows ) // compact if necessary nCol1.resize( iRow + 1 ), nCol2.resize( iRow + 1 ); } - return true; + return theParam2ColumnMap.size() > 1 && theParam2ColumnMap.begin()->second.size() > 1; } //======================================================================= @@ -1535,7 +1732,7 @@ bool SMESH_MesherHelper::IsSubShape( const TopoDS_Shape& shape, SMESH_Mesh* aMes return aMesh->GetMeshDS()->ShapeToIndex( shape ) || // PAL16202 - shape.ShapeType() == TopAbs_COMPOUND && aMesh->GetMeshDS()->IsGroupOfSubShapes( shape ); + (shape.ShapeType() == TopAbs_COMPOUND && aMesh->GetMeshDS()->IsGroupOfSubShapes( shape )); } //================================================================================ @@ -1558,6 +1755,43 @@ double SMESH_MesherHelper::MaxTolerance( const TopoDS_Shape& shape ) 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, @@ -1603,6 +1837,57 @@ double SMESH_MesherHelper::GetOtherParam(const double param) const 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 iterator on ancestors of the given type + */ +//======================================================================= + +PShapeIteratorPtr SMESH_MesherHelper::GetAncestors(const TopoDS_Shape& shape, + const SMESH_Mesh& mesh, + TopAbs_ShapeEnum ancestorType) +{ + return PShapeIteratorPtr( new TAncestorsIterator( mesh.GetAncestors(shape), ancestorType)); +} + //#include //======================================================================= @@ -1655,7 +1940,7 @@ 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 IsMoved() const { return (_nbMoves > 0 /*&& !IsStraight()*/); } bool IsStraight() const { return isStraightLink( (XYZ(node1())-XYZ(node2())).SquareModulus(), _nodeMove.SquareMagnitude()); @@ -1665,9 +1950,9 @@ namespace { // Structures used by FixQuadraticElements() 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; } +// }; }; // --------------------------------------------------------- /*! @@ -1701,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; @@ -1714,7 +2001,7 @@ 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; @@ -1753,6 +2040,8 @@ namespace { // Structures used by FixQuadraticElements() 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, @@ -1797,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) << " " @@ -1876,9 +2165,10 @@ namespace { // Structures used by FixQuadraticElements() // 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 ( face->_sides[i]->MediumPos() == pos ) if ( const QFace* contFace = face->_sides[i]->GetContinuesFace( face )) - faces.push_back( contFace ); + if ( contFace->_sides.size() == 3 ) + faces.push_back( contFace ); } } faces.pop_front(); @@ -1901,10 +2191,11 @@ namespace { // Structures used by FixQuadraticElements() // 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 @@ -2098,7 +2389,7 @@ namespace { // Structures used by FixQuadraticElements() 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 @@ -2109,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 @@ -2139,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 @@ -2157,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] @@ -2168,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; } - if ( iFaceCont > 0 ) // continues faces found, set one by the other + // 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] ); + } + } + else if ( iFaceCont > 0 ) // continues faces found { if ( iFaceCont != 1 ) std::swap( _faces[1], _faces[iFaceCont] ); @@ -2219,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 @@ -2237,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 @@ -2284,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 ); } } } @@ -2518,6 +2855,10 @@ namespace { // Structures used by FixQuadraticElements() void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) { + // 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() ) { @@ -2540,7 +2881,9 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) 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( s.Current() ); h.FixQuadraticElements(false); @@ -2548,7 +2891,7 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) } // fix nodes on geom faces #ifdef _DEBUG_ - int nbfaces = faces.Extent(); + 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())); @@ -2589,20 +2932,23 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) bool isCurved = false; //bool hasRectFaces = false; - set nbElemNodeSet; + //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 @@ -2632,6 +2978,9 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) 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 ) @@ -2644,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 @@ -2666,9 +3015,9 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) return; // no curved edges of faces // 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; @@ -2680,7 +3029,7 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) 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 @@ -2713,12 +3062,12 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) { TChain& chain = chains[iC]; if ( chain.empty() ) continue; - if ( chain.front()->IsStraight() && chain.back()->IsStraight() ) { + if ( chain.front().IsStraight() && chain.back().IsStraight() ) { MSG("3D straight - ignore"); continue; } if ( chain.front()->MediumPos() > bndPos || - chain.back()->MediumPos() > bndPos ) { + chain.back() ->MediumPos() > bndPos ) { MSG("Internal chain - ignore"); continue; } @@ -2748,9 +3097,11 @@ 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 ) { @@ -2769,14 +3120,24 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) ( 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(),uvMove.SquareModulus()); + isStraight[is1] = isStraightLink( (uv2-uv1).SquareModulus(), + 10 * uvMove.SquareModulus()); } -// if ( move0.SquareMagnitude() < straightTol2 && -// move1.SquareMagnitude() < straightTol2 ) { 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; @@ -2846,56 +3207,106 @@ void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) } // 4. Move nodes - // ----------- + // ------------- +// vector 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() ); } } -} -//======================================================================= -/*! - * \brief Iterator on ancestors of the given type - */ -//======================================================================= + //return; -struct TAncestorsIterator : public SMDS_Iterator -{ - TopTools_ListIteratorOfListOfShape _ancIter; - TopAbs_ShapeEnum _type; - TAncestorsIterator( const TopTools_ListOfShape& ancestors, TopAbs_ShapeEnum type) - : _ancIter( ancestors ), _type( type ) - { - if ( _ancIter.More() && _ancIter.Value().ShapeType() != _type ) next(); - } - virtual bool more() - { - return _ancIter.More(); - } - virtual const TopoDS_Shape* next() + // issue 0020982 + // Move the apex of pyramid together with the most curved link + + TIDSortedNodeSet::iterator apexIt = apexOfPyramid.begin(); + for ( ; apexIt != apexOfPyramid.end(); ++apexIt ) { - const TopoDS_Shape* s = _ancIter.More() ? & _ancIter.Value() : 0; - if ( _ancIter.More() ) - for ( _ancIter.Next(); _ancIter.More(); _ancIter.Next()) - if ( _ancIter.Value().ShapeType() == _type ) - break; - return s; - } -}; + SMESH_TNodeXYZ apex = *apexIt; -//======================================================================= -/*! - * \brief Return iterator on ancestors of the given type - */ -//======================================================================= + gp_Vec maxMove( 0,0,0 ); + double maxMoveSize2 = 0; -PShapeIteratorPtr SMESH_MesherHelper::GetAncestors(const TopoDS_Shape& shape, - const SMESH_Mesh& mesh, - TopAbs_ShapeEnum ancestorType) -{ - return PShapeIteratorPtr( new TAncestorsIterator( mesh.GetAncestors(shape), ancestorType)); + // 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()); + } + } + } } +