+//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 bool force,
+ double distXYZ[4]) const
+{
+ 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 ), surfPnt(0,0,0);
+ double dist = 0;
+ if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() );
+ 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 );
+ if ( !projector.IsDone() || projector.NbPoints() < 1 )
+ {
+ MESSAGE( "SMESH_MesherHelper::CheckNodeUV() failed to project" );
+ return false;
+ }
+ Quantity_Parameter U,V;
+ projector.LowerDistanceParameters(U,V);
+ 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;
+ }
+ // store the fixed UV on the face
+ if ( myShape.IsSame(F) && shapeID == myShapeID )
+ const_cast<SMDS_MeshNode*>(n)->SetPosition
+ ( SMDS_PositionPtr( new SMDS_FacePosition( U, V )));
+ }
+ else if ( uv.Modulus() > numeric_limits<double>::min() )
+ {
+ setPosOnShapeValidity( shapeID, true );
+ }
+ }
+ return true;
+}
+
+//=======================================================================
+//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