// File:	Geom_SurfaceOfRevolution.cxx
// Created:	Wed Mar 10 10:51:26 1993
// Author:	JCV
//		<fid@phylox>
// Copyright:	Matra Datavision 1993

//File Geom_SurfaceOfRevolution.cxx, JCV 4/03/91


#include <Geom_SurfaceOfRevolution.ixx>
#include <BSplSLib.hxx>
#include <BSplCLib.hxx>
#include <Geom_Circle.hxx>
#include <gp.hxx>
#include <gp_Ax2d.hxx>
#include <gp_XYZ.hxx>
#include <gp_Lin.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt.hxx>
#include <Standard_ConstructionError.hxx>
#include <Standard_NotImplemented.hxx>
#include <Standard_RangeError.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Precision.hxx>
#define  POLES    (poles->Array2())
#define  WEIGHTS  (weights->Array2())
#define  UKNOTS   (uknots->Array1())
#define  VKNOTS   (vknots->Array1())
#define  UFKNOTS  (ufknots->Array1())
#define  VFKNOTS  (vfknots->Array1())
#define  FMULTS   (BSplCLib::NoMults())

typedef Geom_SurfaceOfRevolution         SurfaceOfRevolution;
typedef Handle(Geom_SurfaceOfRevolution) Handle(SurfaceOfRevolution);
typedef Handle(Geom_Geometry)            Handle(Geometry);
typedef Geom_Curve                       Curve;
typedef Handle(Geom_Curve)               Handle(Curve);
typedef gp_Ax1  Ax1;
typedef gp_Ax2  Ax2;
typedef gp_Dir  Dir;
typedef gp_Lin  Lin;
typedef gp_Pnt  Pnt;
typedef gp_Trsf Trsf;
typedef gp_Vec  Vec;
typedef gp_XYZ  XYZ;
//=======================================================================
//function : LocateSide
//purpose  : This  method locates U parameter on basis BSpline curve 
//  	       and calls LocalDi  methods corresponding an  order of  
//    	       derivative and  position of the surface side contained      
//    	       the point relatively the curve knots.
//=======================================================================
static void LocateSide(const Standard_Real U,
		       const Standard_Integer Side,
		       const Handle(Geom_BSplineCurve)& BSplC,
		       const Standard_Integer NDir,
		       gp_Pnt& P,
		       gp_Vec& D1U,
		       gp_Vec& D2U,
		       gp_Vec& D3U) 
{ 
  Standard_Integer Ideb, Ifin;
  Standard_Real ParTol=Precision::PConfusion()/2;
  BSplC->Geom_BSplineCurve::LocateU(U,ParTol,Ideb,Ifin,Standard_False);   
  if(Side == 1)
    {
      if(Ideb<1) Ideb=1;
      if ((Ideb>=Ifin))  Ifin = Ideb+1;
    }else
      if(Side ==-1)
	{ 
	  if(Ifin > BSplC -> NbKnots()) Ifin=BSplC->NbKnots();
	  if ((Ideb>=Ifin))  Ideb = Ifin-1;
	}
   
  switch(NDir) {
  case 0 :  BSplC->Geom_BSplineCurve::LocalD0(U,Ideb,Ifin,P); break;
  case 1 :  BSplC->Geom_BSplineCurve::LocalD1(U,Ideb,Ifin,P,D1U);  break;
  case 2 :  BSplC->Geom_BSplineCurve::LocalD2(U,Ideb,Ifin,P,D1U,D2U); break;
  case 3 :  BSplC->Geom_BSplineCurve::LocalD3(U,Ideb,Ifin,P,D1U,D2U,D3U); break;
  }
}

//=======================================================================
//function : LocateSideN
//purpose  : This  method locates U parameter on basis BSpline curve 
//  	       and calls LocalDN  method corresponding  position of  surface side 
//    	       contained the point relatively the curve knots.  
//=======================================================================
static gp_Vec LocateSideN(const Standard_Real V,
			  const Standard_Integer Side,
			  const Handle(Geom_BSplineCurve)& BSplC,
			  const	Standard_Integer  Nv ) 
{ 
  Standard_Integer Ideb, Ifin;
  Standard_Real ParTol=Precision::PConfusion()/2;
  BSplC->Geom_BSplineCurve::LocateU(V,ParTol,Ideb,Ifin,Standard_False);   
  if(Side == 1)
    {
      if(Ideb<1) Ideb=1;
      if ((Ideb>=Ifin))  Ifin = Ideb+1;
    }else
      if(Side ==-1)
	{ 
	  if(Ifin > BSplC -> NbKnots()) Ifin=BSplC->NbKnots();
	  if ((Ideb>=Ifin))  Ideb = Ifin-1;
	}
  return BSplC->Geom_BSplineCurve::LocalDN(V,Ideb,Ifin,Nv);
}




//=======================================================================
//function : Copy
//purpose  : 
//=======================================================================

Handle(Geom_Geometry) Geom_SurfaceOfRevolution::Copy () const {

  return new Geom_SurfaceOfRevolution (basisCurve, Axis());
}


//=======================================================================
//function : Geom_SurfaceOfRevolution
//purpose  : 
//=======================================================================

Geom_SurfaceOfRevolution::Geom_SurfaceOfRevolution 
  (const Handle(Curve)& C , 
   const Ax1&           A1 ) : loc (A1.Location()) {

  basisCurve = Handle(Curve)::DownCast(C->Copy());
  direction  = A1.Direction();
  smooth     = C->Continuity();
}


//=======================================================================
//function : UReverse
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::UReverse () { 

  direction.Reverse(); 
}


//=======================================================================
//function : UReversedParameter
//purpose  : 
//=======================================================================

Standard_Real Geom_SurfaceOfRevolution::UReversedParameter (const Standard_Real U) const {

  return ( 2.*PI - U);
}


//=======================================================================
//function : VReverse
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::VReverse () { 

  basisCurve->Reverse(); 
}


//=======================================================================
//function : VReversedParameter
//purpose  : 
//=======================================================================

Standard_Real Geom_SurfaceOfRevolution::VReversedParameter (const Standard_Real V) const {

  return basisCurve->ReversedParameter(V);
}


//=======================================================================
//function : Location
//purpose  : 
//=======================================================================

const gp_Pnt& Geom_SurfaceOfRevolution::Location () const { 

  return loc; 
}

//=======================================================================
//function : IsUPeriodic
//purpose  : 
//=======================================================================

Standard_Boolean Geom_SurfaceOfRevolution::IsUPeriodic () const {

  return Standard_True; 
}

//=======================================================================
//function : IsCNu
//purpose  : 
//=======================================================================

Standard_Boolean Geom_SurfaceOfRevolution::IsCNu (const Standard_Integer ) const  {

  return Standard_True;
}

//=======================================================================
//function : Axis
//purpose  : 
//=======================================================================

Ax1 Geom_SurfaceOfRevolution::Axis () const  { 

  return Ax1 (loc, direction); 
}

//=======================================================================
//function : IsCNv
//purpose  : 
//=======================================================================

Standard_Boolean Geom_SurfaceOfRevolution::IsCNv (const Standard_Integer N) const {

  Standard_RangeError_Raise_if (N < 0, " ");
  return basisCurve->IsCN(N);
}


//=======================================================================
//function : IsUClosed
//purpose  : 
//=======================================================================

Standard_Boolean Geom_SurfaceOfRevolution::IsUClosed () const { 

  return Standard_True; 
}

//=======================================================================
//function : IsVClosed
//purpose  : 
//=======================================================================

Standard_Boolean Geom_SurfaceOfRevolution::IsVClosed () const 
{ 
  return basisCurve->IsClosed();
}


//=======================================================================
//function : IsVPeriodic
//purpose  : 
//=======================================================================

Standard_Boolean Geom_SurfaceOfRevolution::IsVPeriodic () const { 

  return basisCurve->IsPeriodic(); 
}


//=======================================================================
//function : SetAxis
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::SetAxis (const Ax1& A1) {

   direction = A1.Direction();
   loc = A1.Location();
}


//=======================================================================
//function : SetDirection
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::SetDirection (const Dir& V) {

   direction = V;
}


//=======================================================================
//function : SetBasisCurve
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::SetBasisCurve (const Handle(Curve)& C) {

   basisCurve = Handle(Curve)::DownCast(C->Copy());
   smooth     = C->Continuity();
}


//=======================================================================
//function : SetLocation
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::SetLocation (const Pnt& P) {

   loc = P;
}


//=======================================================================
//function : Bounds
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::Bounds ( Standard_Real& U1, 
				        Standard_Real& U2, 
				        Standard_Real& V1, 
				        Standard_Real& V2 ) const {

  U1 = 0.0; 
  U2 = 2.0 * PI; 
  V1 = basisCurve->FirstParameter();  
  V2 = basisCurve->LastParameter();
}


//=======================================================================
//function : D0
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::D0 
  (const Standard_Real U, const Standard_Real V, Pnt& P) const {

   // C origine sur l'axe de revolution
   // Vdir vecteur unitaire definissant la direction de l'axe de revolution
   // Q(v) point de parametre V sur la courbe de revolution
   // OM (u,v) = OC + CQ * Cos(U) + (CQ.Vdir)(1-Cos(U)) * Vdir +
   //            (Vdir^CQ)* Sin(U)


   Pnt Pc = basisCurve->Value (V);                  //Q(v)
   XYZ Q  = Pc.XYZ();                               //Q
   XYZ C  = loc.XYZ();                              //C
   Q.Subtract(C);                                   //CQ
   XYZ Vdir     = direction.XYZ();                  //Vdir
   XYZ VcrossCQ = Vdir.Crossed (Q);                 //Vdir^CQ
   VcrossCQ.Multiply (Sin(U));                      //(Vdir^CQ)*Sin(U)
   XYZ VdotCQ =
     Vdir.Multiplied ((Vdir.Dot(Q))*(1.0 - Cos(U)));//(CQ.Vdir)(1-Cos(U))Vdir
   VdotCQ.Add (VcrossCQ);                           //addition des composantes
   Q.Multiply (Cos(U));
   Q.Add (VdotCQ);
   Q.Add (C);
   P.SetXYZ(Q);
}


//=======================================================================
//function : D1
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::D1 
  (const Standard_Real U, const Standard_Real V, 
         Pnt& P, 
         Vec& D1U, Vec& D1V   ) const {

   // C origine sur l'axe de revolution
   // Vdir vecteur unitaire definissant la direction de l'axe de revolution
   // Q(v) point de parametre V sur la courbe de revolution
   // Q'(v) = DQ/DV
   // OM (u,v) = OC + CQ * Cos(U) + (CQ.Vdir)(1-Cos(U)) * Vdir + 
   //            (Vdir^CQ) * Sin(U)
   // D1U_M(u,v) = - CQ * Sin(U) + (CQ.Vdir)(Sin(U)) * Vdir +
   //              (Vdir^CQ) * Cos(U)
   // D1V_M(u,v) = Q' * Cos(U) + (Q'.Vdir)(1-Cos(U)) * Vdir +
   //              (Vdir^Q') * Sin(U)
	   
      Pnt Pc;
      Vec V1;
      basisCurve->D1 (V, Pc, V1);
      XYZ Q    = Pc.XYZ();                               //Q
      XYZ DQv  = V1.XYZ();                               //Q'
      XYZ C    = loc.XYZ();                              //C
      XYZ Vdir = direction.XYZ();                        //Vdir
      Q.Subtract(C);                                     //CQ
      XYZ VcrossCQ  = Vdir.Crossed (Q);                  //Vdir^CQ
      XYZ VcrossDQv = Vdir.Crossed (DQv);                //(Vdir^Q')
      XYZ VdotCQ    = Vdir.Multiplied (Vdir.Dot(Q));     //(Vdir.CQ)Vdir
      XYZ VdotDQv   = Vdir.Multiplied (Vdir.Dot(DQv));   //(Vdir.Q')Vdir

      VcrossDQv.Multiply (Sin(U));
      VdotDQv.Multiply   (1.0 - Cos(U));
      VdotDQv.Add        (VcrossDQv);
      DQv.Multiply       (Cos(U));
      DQv.Add            (VdotDQv);
      D1V.SetXYZ         (DQv);

      XYZ DQu = Q.Multiplied       (-Sin(U));
      DQu.Add (VcrossCQ.Multiplied (Cos(U)));
      DQu.Add (VdotCQ.Multiplied (Sin(U)));
      D1U.SetXYZ (DQu);

      Q.Multiply (Cos(U));
      Q.Add      (C);
      VcrossCQ.Multiply (Sin(U));
      Q.Add             (VcrossCQ);   
      VdotCQ.Multiply (1.0-Cos(U));
      Q.Add           (VdotCQ);   
      P.SetXYZ (Q);      
    }

//=======================================================================
//function : D2
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::D2 
  (const Standard_Real   U, const Standard_Real V,
         Pnt&   P, 
         Vec& D1U, Vec& D1V, 
         Vec& D2U, Vec& D2V, Vec& D2UV ) const {


   // C origine sur l'axe de revolution
   // V vecteur unitaire definissant la direction de l'axe de revolution
   // Q(v) point de parametre V sur la courbe de revolution
   // Q'(v) = D1Q/DV
   // Q"(v) = D2Q/DV
   // OM (u,v) = OC + CQ * Cos(U) + (CQ.Vdir)(1-Cos(U)) * Vdir +
   //            (Vdir^CQ) * Sin(U)
   // D1U_M(u,v) = - CQ * Sin(U) + (CQ.Vdir)(Sin(U)) * Vdir +
   //              (Vdir^CQ) * Cos(U)
   // D1V_M(u,v) = Q' * Cos(U) + (Q'.Vdir)(1-Cos(U)) * Vdir +
   //              (Vdir^Q') * Sin(U)
   // D2U_M(u,v) = -CQ * Cos(U) + (CQ.Vdir)(Cos(U)) * Vdir + 
   //              (Vdir^CQ) * -(Sin(U))
   // D2V_M(u,v) = Q" * Cos(U) + (Q".Vdir)(1-Cos(U)) * Vdir +
   //              (Vdir^Q") * Sin(U)
   // D2UV_M(u,v)= -Q' * Sin(U) + (Q'.Vdir)(Sin(U)) * Vdir +
   //              (Vdir^Q') * Cos(U)


      Pnt Pc;
      Vec V1 , V2;
      basisCurve->D2 (V, Pc, V1, V2);
      XYZ Q     = Pc.XYZ();                                 //Q
      XYZ D1Qv  = V1.XYZ();                                 //Q'
      XYZ D2Qv  = V2.XYZ();                                 //Q"
      XYZ C     = loc.XYZ();                                //C
      XYZ Vdir  = direction.XYZ();                          //Vdir
      Q.Subtract(C);                                        //CQ
      XYZ VcrossCQ   = Vdir.Crossed (Q);                    //Vdir^CQ
      XYZ VcrossD1Qv = Vdir.Crossed (D1Qv);                 //(Vdir^Q')
      XYZ VcrossD2Qv = Vdir.Crossed (D2Qv);                 //(Vdir^Q")
      XYZ VdotCQ     = Vdir.Multiplied (Vdir.Dot(Q));       //(Vdir.CQ)Vdir
      XYZ VdotD1Qv   = Vdir.Multiplied (Vdir.Dot(D1Qv));    //(Vdir.Q')Vdir
      XYZ VdotD2Qv   = Vdir.Multiplied (Vdir.Dot(D2Qv));    //(Vdir.Q")Vdir

      
      XYZ D2Quv = D1Qv.Multiplied(-Sin(U));
      D2Quv.Add (VcrossD1Qv.Multiplied (Cos(U)));      
      D2Quv.Add (VdotD1Qv.Multiplied (Sin(U)));
      D2UV.SetXYZ (D2Quv);

      D1Qv.Multiply       (Cos(U));
      VcrossD1Qv.Multiply (Sin(U));
      VdotD1Qv.Multiply   (1.0 - Cos(U));
      D1Qv.Add            (VcrossD1Qv);
      D1Qv.Add            (VdotD1Qv);
      D1V.SetXYZ          (D1Qv);

      VcrossD2Qv.Multiply (Sin(U));
      VdotD2Qv.Multiply (1.0 - Cos(U));
      VdotD2Qv.Add (VcrossD2Qv);
      D2Qv.Multiply (Cos(U));
      D2Qv.Add (VdotD2Qv);
      D2V.SetXYZ (D2Qv);

      XYZ D1Qu = Q.Multiplied (-Sin(U));
      D1Qu.Add (VcrossCQ.Multiplied (Cos(U)));
      D1Qu.Add (VdotCQ.Multiplied (Sin(U)));
      D1U.SetXYZ (D1Qu);

      Q.Multiply (Cos(U));
      VcrossCQ.Multiply (Sin(U));
      Q.Add (VcrossCQ);   
      XYZ D2Qu = Q.Multiplied(-1.0);
      D2Qu.Add (VdotCQ.Multiplied (Cos(U)));
      D2U.SetXYZ (D2Qu);
      VdotCQ.Multiply (1.0-Cos(U));
      Q.Add (VdotCQ);   
      Q.Add (C);
      P.SetXYZ (Q);      
}



//=======================================================================
//function : D3
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::D3 
  (const Standard_Real U, const Standard_Real V,
         Pnt& P,
         Vec& D1U, Vec& D1V, 
         Vec& D2U, Vec& D2V, Vec& D2UV,
         Vec& D3U, Vec& D3V, Vec& D3UUV, Vec& D3UVV ) const {

   // C origine sur l'axe de revolution
   // Vdir vecteur unitaire definissant la direction de l'axe de revolution
   // Q(v) point de parametre V sur la courbe de revolution
   // Q'(v) = D1Q/DV
   // Q"(v) = D2Q/DV
   // OM (u,v) = OC + CQ * Cos(u) + (CQ.Vdir)(1-Cos(u)) * Vdir +
   //            (Vdir^CQ) * Sin(u)
   // D1U_M(u,v) = - CQ * Sin(u) + (CQ.Vdir)(Sin(u)) * Vdir +
   //              (Vdir^CQ) * Cos(u)
   // D2U_M(u,v) = -CQ * Cos(u) + (CQ.Vdir)(Cos(u)) * Vdir +
   //              (Vdir^CQ) * -Sin(u)
   // D2UV_M(u,v)= -Q' * Sin(u) + (Q'.Vdir)(Sin(u)) * Vdir +
   //              (Vdir^Q') * Cos(u)
   // D3UUV_M(u,v) = -Q' * Cos(u) + (Q'.Vdir)(Cos(u)) * Vdir +
   //                (Vdir^Q') * -Sin(u)
   // D3U_M(u,v) = CQ * Sin(u) + (CQ.Vdir)(-Sin(u)) * Vdir +
   //              (Vdir^CQ) * -Cos(u)
   // D1V_M(u,v) = Q' * Cos(u) + (Q'.Vdir)(1-Cos(u)) * Vdir + 
   //              (Vdir^Q') * Sin(u)
   // D2V_M(u,v) = Q" * Cos(u) + (Q".Vdir)(1-Cos(u)) * Vdir +
   //              (Vdir^Q") * Sin(u)
   // D3UVV_M(u,v) = -Q" * Sin(u) + (Q".Vdir)(Sin(u)) * Vdir +
   //                (Vdir^Q") * Cos(u)
   // D3V_M(u,v) = Q'''* Cos(u) + (Q'''.Vdir)(1-Cos(u)) * Vdir +
   //             (Vdir^Q''') * Sin(u)
   

      Pnt Pc;
      Vec V1 , V2, V3;
      basisCurve->D3 (V, Pc, V1, V2, V3);
      XYZ Q     = Pc.XYZ();                                 //Q
      XYZ D1Qv  = V1.XYZ();                                 //Q'
      XYZ D2Qv  = V2.XYZ();                                 //Q"
      XYZ D3Qv  = V3.XYZ();                                 //Q'''
      XYZ C     = loc.XYZ();                                //C
      XYZ Vdir  = direction.XYZ();                          //Vdir
      Q.Subtract(C);                                        //CQ
      XYZ VcrossCQ   = Vdir.Crossed (Q);                    //Vdir^CQ
      XYZ VcrossD1Qv = Vdir.Crossed (D1Qv);                 //(Vdir^Q')
      XYZ VcrossD2Qv = Vdir.Crossed (D2Qv);                 //(Vdir^Q")
      XYZ VcrossD3Qv = Vdir.Crossed (D3Qv);                 //(Vdir^Q''')
      XYZ VdotCQ     = Vdir.Multiplied (Vdir.Dot(Q));       //(Vdir.CQ)Vdir
      XYZ VdotD1Qv   = Vdir.Multiplied (Vdir.Dot(D1Qv));    //(Vdir.Q')Vdir
      XYZ VdotD2Qv   = Vdir.Multiplied (Vdir.Dot(D2Qv));    //(Vdir.Q")Vdir
      XYZ VdotD3Qv   = Vdir.Multiplied (Vdir.Dot(D3Qv));    //(Vdir.Q''')Vdir

      XYZ D3Quuv = D1Qv.Multiplied (-Cos(U));
      D3Quuv.Add (VcrossD1Qv.Multiplied (-Sin(U)));      
      D3Quuv.Add (VdotD1Qv.Multiplied (Cos(U)));
      D3UUV.SetXYZ (D3Quuv);

      XYZ D2Quv = D1Qv.Multiplied (-Sin(U));
      D2Quv.Add (VcrossD1Qv.Multiplied (Cos(U)));      
      D2Quv.Add (VdotD1Qv.Multiplied (Sin(U)));
      D2UV.SetXYZ (D2Quv);

      D1Qv.Multiply (Cos(U));
      VcrossD1Qv.Multiply (Sin(U));
      VdotD1Qv.Multiply (1.0 - Cos(U));
      D1Qv.Add (VcrossD1Qv);
      D1Qv.Add (VdotD1Qv);
      D1V.SetXYZ (D1Qv);

      XYZ D3Qvvu = D2Qv.Multiplied (-Sin(U));
      D3Qvvu.Add (VcrossD2Qv.Multiplied (Cos(U)));
      D3Qvvu.Add (VdotD2Qv.Multiplied (Sin(U)));
      D3UVV.SetXYZ (D3Qvvu);
      
      VcrossD2Qv.Multiply (Sin(U));
      VdotD2Qv.Multiply (1.0 - Cos(U));
      VdotD2Qv.Add (VcrossD2Qv);
      D2Qv.Multiply (Cos(U));
      D2Qv.Add (VdotD2Qv);
      D2V.SetXYZ (D2Qv);

      VcrossD3Qv.Multiply (Sin(U));
      VdotD3Qv.Multiply (1.0 - Cos(U));
      VdotD3Qv.Add (VcrossD2Qv);
      D3Qv.Multiply (Cos(U));
      D3Qv.Add (VdotD3Qv);
      D3V.SetXYZ (D3Qv);

      XYZ D1Qu = Q.Multiplied (- Sin(U));
      D1Qu.Add (VcrossCQ.Multiplied (Cos(U)));
      XYZ D3Qu = D1Qu.Multiplied (-1.0);
      D1Qu.Add (VdotCQ.Multiplied (Sin(U)));
      D3Qu.Add (VdotCQ.Multiplied (-Sin(U)));
      D1U.SetXYZ (D1Qu);
      D3U.SetXYZ (D3Qu);

      Q.Multiply (Cos(U));
      VcrossCQ.Multiply (Sin(U));
      Q.Add (VcrossCQ);   
      XYZ D2Qu = Q.Multiplied(-1.0);
      D2Qu.Add (VdotCQ.Multiplied (Cos(U)));
      D2U.SetXYZ (D2Qu);
      VdotCQ.Multiply (1.0-Cos(U));
      Q.Add (VdotCQ);   
      Q.Add (C);
      P.SetXYZ (Q);      
}


//=======================================================================
//function : DN
//purpose  : 
//=======================================================================

Vec Geom_SurfaceOfRevolution::DN (const Standard_Real    U , const Standard_Real    V, 
                                  const Standard_Integer Nu, const Standard_Integer Nv) const {

   Standard_RangeError_Raise_if (Nu + Nv < 1 || Nu < 0 || Nv < 0, " ");
   if (Nu == 0) {
     XYZ Vn = (basisCurve->DN (V, Nv)).XYZ();
     XYZ Vdir = direction.XYZ();
     XYZ VDot = Vdir.Multiplied (Vn.Dot (Vdir));
     VDot.Multiply (1-Cos(U));
     XYZ VCross = Vdir.Crossed (Vn);
     VCross.Multiply (Sin(U));
     Vn.Multiply (Cos(U));
     Vn.Add (VDot);
     Vn.Add (VCross);     
     return Vec (Vn);
   }
   else if (Nv == 0) {
     XYZ CQ = (basisCurve->Value (V)).XYZ() - loc.XYZ();
     XYZ Vdir = direction.XYZ();
     XYZ VDot = Vdir.Multiplied (CQ.Dot (Vdir));
     XYZ VCross = Vdir.Crossed (CQ);
     if ((Nu + 6) % 4 == 0) {
       CQ.Multiply (-Cos (U));
       VDot.Multiply (Cos(U));
       VCross.Multiply (-Sin(U));
     }
     else if ((Nu + 5) % 4 == 0) {
       CQ.Multiply (Sin (U));
       VDot.Multiply (-Sin(U));
       VCross.Multiply (-Cos(U));
     }
     else if ((Nu+3) % 4 == 0) {
       CQ.Multiply (-Sin (U));
       VDot.Multiply (+Sin(U));
       VCross.Multiply (Cos(U));
     }
     else if (Nu+4 % 4 == 0) {
       CQ.Multiply (Cos (U));
       VDot.Multiply (-Cos(U));
       VCross.Multiply (Sin(U));
     }
     CQ.Add (VDot);
     CQ.Add (VCross);
     return Vec (CQ);
   }
   else {
     XYZ Vn = (basisCurve->DN (V, Nv)).XYZ();
     XYZ Vdir = direction.XYZ();
     XYZ VDot = Vdir.Multiplied (Vn.Dot (Vdir));
     XYZ VCross = Vdir.Crossed (Vn);
     if ((Nu + 6) % 4 == 0) {
       Vn.Multiply (-Cos (U));
       VDot.Multiply (Cos(U));
       VCross.Multiply (-Sin(U));
     }
     else if ((Nu + 5) % 4 == 0) {
       Vn.Multiply (Sin (U));
       VDot.Multiply (-Sin(U));
       VCross.Multiply (-Cos(U));
     }
     else if ((Nu+3) % 4 == 0) {
       Vn.Multiply (-Sin (U));
       VDot.Multiply (+Sin(U));
       VCross.Multiply (Cos(U));
     }
     else if (Nu+4 % 4 == 0) {
       Vn.Multiply (Cos (U));
       VDot.Multiply (-Cos(U));
       VCross.Multiply (Sin(U));
     }
     Vn.Add (VDot);
     Vn.Add (VCross);     
     return Vec (Vn);
   }
}

//=======================================================================
//function : LocalD0
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::LocalD0 (const Standard_Real    U,
				   const Standard_Real    V, 
				   const Standard_Integer VSide,
				         gp_Pnt&          P     )  const
{
  if((VSide !=0 ) && basisCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) 
    { 
      gp_Vec D1V,D2V,D3V;
      Handle( Geom_BSplineCurve) BSplC;
      BSplC= Handle(Geom_BSplineCurve)::DownCast(basisCurve);

	  LocateSide(V,VSide,BSplC,0,P,D1V,D2V,D3V);
	  XYZ Q  = P.XYZ();                               //Q
	  XYZ C  = loc.XYZ();                              //C
	  Q.Subtract(C);                                   //CQ
	  XYZ Vdir     = direction.XYZ();                  //Vdir
	  XYZ VcrossCQ = Vdir.Crossed (Q);                 //Vdir^CQ
	  VcrossCQ.Multiply (Sin(U));                      //(Vdir^CQ)*Sin(U)
	  XYZ VdotCQ =
	    Vdir.Multiplied ((Vdir.Dot(Q))*(1.0 - Cos(U)));//(CQ.Vdir)(1-Cos(U))Vdir
	  VdotCQ.Add (VcrossCQ);                           //addition des composantes
	  Q.Multiply (Cos(U));
	  Q.Add (VdotCQ);
	  Q.Add (C);
	  P.SetXYZ(Q);
    }
  else  D0(U,V,P);
}

//=======================================================================
//function : LocalD1
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::LocalD1 (const Standard_Real    U, 
				   const Standard_Real    V,
				   const Standard_Integer VSide, 
				         gp_Pnt&          P,
				         gp_Vec&          D1U, 
				         gp_Vec&          D1V)     const
{
 if((VSide !=0 ) && basisCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) 
   {
     Handle( Geom_BSplineCurve) BSplC;
     BSplC= Handle(Geom_BSplineCurve)::DownCast(basisCurve);
         Vec D2V,D3V; 
	 Vec V1; 
	 LocateSide(V,VSide,BSplC,1,P,V1,D2V,D3V);
	 XYZ Q    = P.XYZ();                               //Q
	 XYZ DQv  = V1.XYZ();                               //Q'
	 XYZ C    = loc.XYZ();                              //C
	 XYZ Vdir = direction.XYZ();                        //Vdir
	 Q.Subtract(C);                                     //CQ
	 XYZ VcrossCQ  = Vdir.Crossed (Q);                  //Vdir^CQ
	 XYZ VcrossDQv = Vdir.Crossed (DQv);                //(Vdir^Q')
	 XYZ VdotCQ    = Vdir.Multiplied (Vdir.Dot(Q));     //(Vdir.CQ)Vdir
	 XYZ VdotDQv   = Vdir.Multiplied (Vdir.Dot(DQv));   //(Vdir.Q')Vdir
	 
	 VcrossDQv.Multiply (Sin(U));
	 VdotDQv.Multiply   (1.0 - Cos(U));
	 VdotDQv.Add        (VcrossDQv);
	 DQv.Multiply       (Cos(U));
	 DQv.Add            (VdotDQv);
	 D1V.SetXYZ         (DQv);
	 
	 XYZ DQu = Q.Multiplied       (-Sin(U));
	 DQu.Add (VcrossCQ.Multiplied (Cos(U)));
	 DQu.Add (VdotCQ.Multiplied (Sin(U)));
	 D1U.SetXYZ (DQu);
	 
	 Q.Multiply (Cos(U));
	 Q.Add      (C);
	 VcrossCQ.Multiply (Sin(U));
	 Q.Add             (VcrossCQ);   
	 VdotCQ.Multiply (1.0-Cos(U));
	 Q.Add           (VdotCQ);   
	 P.SetXYZ (Q);  
   }else 
     D1(U,V,P,D1U,D1V);
}

//=======================================================================
//function : LocalD2
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::LocalD2 (const Standard_Real    U,
				   const Standard_Real    V,
				   const Standard_Integer VSide,
				         gp_Pnt&          P,
				         gp_Vec&          D1U,
				         gp_Vec&          D1V,
				         gp_Vec&          D2U,
				         gp_Vec&          D2V,
				         gp_Vec&          D2UV) const
{
  if((VSide !=0 ) && basisCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) 
    {
      Handle( Geom_BSplineCurve) BSplC;
      BSplC= Handle(Geom_BSplineCurve)::DownCast(basisCurve);
	  Vec d3v;
	  LocateSide(V,VSide,BSplC,2,P,D1V,D2V,d3v);
	  XYZ Q     = P.XYZ();                                   //Q
	  XYZ D1Qv  = D1V.XYZ();                                 //Q'
	  XYZ D2Qv  = D2V.XYZ();                                 //Q"
	  XYZ C     = loc.XYZ();                                 //C
	  XYZ Vdir  = direction.XYZ();                           //Vdir
	  Q.Subtract(C);                                         //CQ
	  XYZ VcrossCQ   = Vdir.Crossed (Q);                     //Vdir^CQ
	  XYZ VcrossD1Qv = Vdir.Crossed (D1Qv);                  //(Vdir^Q')
	  XYZ VcrossD2Qv = Vdir.Crossed (D2Qv);                  //(Vdir^Q")
	  XYZ VdotCQ     = Vdir.Multiplied (Vdir.Dot(Q));        //(Vdir.CQ)Vdir
	  XYZ VdotD1Qv   = Vdir.Multiplied (Vdir.Dot(D1Qv));     //(Vdir.Q')Vdir
	  XYZ VdotD2Qv   = Vdir.Multiplied (Vdir.Dot(D2Qv));     //(Vdir.Q")Vdir
	  
	  
	  XYZ D2Quv = D1Qv.Multiplied(-Sin(U));
	  D2Quv.Add (VcrossD1Qv.Multiplied (Cos(U)));      
	  D2Quv.Add (VdotD1Qv.Multiplied (Sin(U)));
	  D2UV.SetXYZ (D2Quv);
	  
	  D1Qv.Multiply       (Cos(U));
	  VcrossD1Qv.Multiply (Sin(U));
	  VdotD1Qv.Multiply   (1.0 - Cos(U));
	  D1Qv.Add            (VcrossD1Qv);
	  D1Qv.Add            (VdotD1Qv);
	  D1V.SetXYZ          (D1Qv);
	  
	  VcrossD2Qv.Multiply (Sin(U));
	  VdotD2Qv.Multiply (1.0 - Cos(U));
	  VdotD2Qv.Add (VcrossD2Qv);
	  D2Qv.Multiply (Cos(U));
	  D2Qv.Add (VdotD2Qv);
	  D2V.SetXYZ (D2Qv);

	  XYZ D1Qu = Q.Multiplied (-Sin(U));
	  D1Qu.Add (VcrossCQ.Multiplied (Cos(U)));
	  D1Qu.Add (VdotCQ.Multiplied (Sin(U)));
	  D1U.SetXYZ (D1Qu);
	  
	  Q.Multiply (Cos(U));
	  VcrossCQ.Multiply (Sin(U));
	  Q.Add (VcrossCQ);   
	  XYZ D2Qu = Q.Multiplied(-1.0);
	  D2Qu.Add (VdotCQ.Multiplied (Cos(U)));
	  D2U.SetXYZ (D2Qu);
	  VdotCQ.Multiply (1.0-Cos(U));
	  Q.Add (VdotCQ);   
	  Q.Add (C);
	  P.SetXYZ (Q);      
    } 
  else
    D2(U,V,P,D1U,D1V,D2U,D2V,D2UV);
}
//=======================================================================
//function : LocalD3
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::LocalD3 (const Standard_Real    U, 
				   const Standard_Real    V,
				   const Standard_Integer VSide, 
				         gp_Pnt&          P,
				         gp_Vec&          D1U,
				         gp_Vec&          D1V, 
				         gp_Vec&          D2U, 
				         gp_Vec&          D2V, 
				         gp_Vec&          D2UV, 
				         gp_Vec&          D3U,
				         gp_Vec&          D3V,
				         gp_Vec&          D3UUV,
				         gp_Vec&          D3UVV) const
{
  if((VSide !=0 ) && basisCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) 
    {
      Handle( Geom_BSplineCurve) BSplC;
      BSplC= Handle(Geom_BSplineCurve)::DownCast(basisCurve);

	  LocateSide(V,VSide,BSplC,3,P,D1V,D2V,D3V);
	  XYZ Q     = P.XYZ();                                   //Q
	  XYZ D1Qv  = D1V.XYZ();                                 //Q'
	  XYZ D2Qv  = D2V.XYZ();                                 //Q"
	  XYZ D3Qv  = D3V.XYZ();                                 //Q'''
	  XYZ C     = loc.XYZ();                                //C
	  XYZ Vdir  = direction.XYZ();                          //Vdir
	  Q.Subtract(C);                                        //CQ
	  XYZ VcrossCQ   = Vdir.Crossed (Q);                    //Vdir^CQ
	  XYZ VcrossD1Qv = Vdir.Crossed (D1Qv);                 //(Vdir^Q')
	  XYZ VcrossD2Qv = Vdir.Crossed (D2Qv);                 //(Vdir^Q")
	  XYZ VcrossD3Qv = Vdir.Crossed (D3Qv);                 //(Vdir^Q''')
	  XYZ VdotCQ     = Vdir.Multiplied (Vdir.Dot(Q));       //(Vdir.CQ)Vdir
	  XYZ VdotD1Qv   = Vdir.Multiplied (Vdir.Dot(D1Qv));    //(Vdir.Q')Vdir
	  XYZ VdotD2Qv   = Vdir.Multiplied (Vdir.Dot(D2Qv));    //(Vdir.Q")Vdir
	  XYZ VdotD3Qv   = Vdir.Multiplied (Vdir.Dot(D3Qv));    //(Vdir.Q''')Vdir

	  XYZ D3Quuv = D1Qv.Multiplied (-Cos(U));
	  D3Quuv.Add (VcrossD1Qv.Multiplied (-Sin(U)));      
	  D3Quuv.Add (VdotD1Qv.Multiplied (Cos(U)));
	  D3UUV.SetXYZ (D3Quuv);
	  
	  XYZ D2Quv = D1Qv.Multiplied (-Sin(U));
	  D2Quv.Add (VcrossD1Qv.Multiplied (Cos(U)));      
	  D2Quv.Add (VdotD1Qv.Multiplied (Sin(U)));
	  D2UV.SetXYZ (D2Quv);
	  
	  D1Qv.Multiply (Cos(U));
	  VcrossD1Qv.Multiply (Sin(U));
	  VdotD1Qv.Multiply (1.0 - Cos(U));
	  D1Qv.Add (VcrossD1Qv);
	  D1Qv.Add (VdotD1Qv);
	  D1V.SetXYZ (D1Qv);

	  XYZ D3Qvvu = D2Qv.Multiplied (-Sin(U));
	  D3Qvvu.Add (VcrossD2Qv.Multiplied (Cos(U)));
	  D3Qvvu.Add (VdotD2Qv.Multiplied (Sin(U)));
	  D3UVV.SetXYZ (D3Qvvu);
	  
	  VcrossD2Qv.Multiply (Sin(U));
	  VdotD2Qv.Multiply (1.0 - Cos(U));
	  VdotD2Qv.Add (VcrossD2Qv);
	  D2Qv.Multiply (Cos(U));
	  D2Qv.Add (VdotD2Qv);
	  D2V.SetXYZ (D2Qv);
	  
	  VcrossD3Qv.Multiply (Sin(U));
	  VdotD3Qv.Multiply (1.0 - Cos(U));
	  VdotD3Qv.Add (VcrossD2Qv);
	  D3Qv.Multiply (Cos(U));
	  D3Qv.Add (VdotD3Qv);
	  D3V.SetXYZ (D3Qv);
	  
	  XYZ D1Qu = Q.Multiplied (- Sin(U));
	  D1Qu.Add (VcrossCQ.Multiplied (Cos(U)));
	  XYZ D3Qu = D1Qu.Multiplied (-1.0);
	  D1Qu.Add (VdotCQ.Multiplied (Sin(U)));
	  D3Qu.Add (VdotCQ.Multiplied (-Sin(U)));
	  D1U.SetXYZ (D1Qu);
	  D3U.SetXYZ (D3Qu);
	  
	  Q.Multiply (Cos(U));
	  VcrossCQ.Multiply (Sin(U));
	  Q.Add (VcrossCQ);   
	  XYZ D2Qu = Q.Multiplied(-1.0);
	  D2Qu.Add (VdotCQ.Multiplied (Cos(U)));
	  D2U.SetXYZ (D2Qu);
	  VdotCQ.Multiply (1.0-Cos(U));
	  Q.Add (VdotCQ);   
	  Q.Add (C);
	  P.SetXYZ (Q); 
    }
  else
    D3(U,V,P,D1U,D1V,D2U,D2V,D2UV,D3U,D3V,D3UUV,D3UVV);
}
//=======================================================================
//function : LocalDN
//purpose  : 
//=======================================================================

gp_Vec Geom_SurfaceOfRevolution::LocalDN  (const Standard_Real    U, 
				      const Standard_Real    V,
				      const Standard_Integer VSide,
				      const Standard_Integer Nu,
				      const Standard_Integer Nv) const
{
  Standard_RangeError_Raise_if (Nu + Nv < 1 || Nu < 0 || Nv < 0, " "); 
  XYZ Vn, CQ;
  if (Nu == 0) {
    if((VSide !=0 ) && basisCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) 
      {
	Handle( Geom_BSplineCurve) BSplC;
	BSplC= Handle(Geom_BSplineCurve)::DownCast(basisCurve);  
	Vn = LocateSideN(V,VSide,BSplC,Nv).XYZ();
      }else
	return DN(U,V,Nu,Nv);
    XYZ Vdir = direction.XYZ();
    XYZ VDot = Vdir.Multiplied (Vn.Dot (Vdir));
    VDot.Multiply (1-Cos(U));
    XYZ VCross = Vdir.Crossed (Vn);
    VCross.Multiply (Sin(U));
    Vn.Multiply (Cos(U));
    Vn.Add (VDot);
    Vn.Add (VCross);     
    return Vec (Vn);
  }
  
  else if (Nv == 0) {
    if((VSide !=0 ) && basisCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) 
      {
	Handle( Geom_BSplineCurve) BSplC;
	BSplC= Handle(Geom_BSplineCurve)::DownCast(basisCurve);
	CQ = LocateSideN(V,VSide,BSplC,Nv).XYZ() - loc.XYZ();
      }else
	return DN(U,V,Nu,Nv);
    XYZ Vdir = direction.XYZ();
    XYZ VDot = Vdir.Multiplied (CQ.Dot (Vdir));
    XYZ VCross = Vdir.Crossed (CQ);
    if ((Nu + 6) % 4 == 0) {
      CQ.Multiply (-Cos (U));
      VDot.Multiply (Cos(U));
      VCross.Multiply (-Sin(U));
    }
    else if ((Nu + 5) % 4 == 0) {
      CQ.Multiply (Sin (U));
      VDot.Multiply (-Sin(U));
      VCross.Multiply (-Cos(U));
    }
    else if ((Nu+3) % 4 == 0) {
      CQ.Multiply (-Sin (U));
      VDot.Multiply (+Sin(U));
      VCross.Multiply (Cos(U));
    }
    else if (Nu+4 % 4 == 0) {
      CQ.Multiply (Cos (U));
      VDot.Multiply (-Cos(U));
      VCross.Multiply (Sin(U));
    }
    CQ.Add (VDot);
    CQ.Add (VCross);
    return Vec (CQ);
  }
  
  else {
    if((VSide !=0 ) && basisCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) 
      {
	Handle( Geom_BSplineCurve) BSplC;
	BSplC= Handle(Geom_BSplineCurve)::DownCast(basisCurve);
	Vn = LocateSideN(V,VSide,BSplC,Nv).XYZ();
      }else 
	return DN(U,V,Nu,Nv);
    XYZ Vdir = direction.XYZ();
    XYZ VDot = Vdir.Multiplied (Vn.Dot (Vdir));
    XYZ VCross = Vdir.Crossed (Vn);
    if ((Nu + 6) % 4 == 0) {
      Vn.Multiply (-Cos (U));
      VDot.Multiply (Cos(U));
      VCross.Multiply (-Sin(U));
    }
    else if ((Nu + 5) % 4 == 0) {
      Vn.Multiply (Sin (U));
      VDot.Multiply (-Sin(U));
      VCross.Multiply (-Cos(U));
    }
    else if ((Nu+3) % 4 == 0) {
      Vn.Multiply (-Sin (U));
      VDot.Multiply (+Sin(U));
      VCross.Multiply (Cos(U));
    }
    else if (Nu+4 % 4 == 0) {
      Vn.Multiply (Cos (U));
      VDot.Multiply (-Cos(U));
      VCross.Multiply (Sin(U));
    }
    Vn.Add (VDot);
    Vn.Add (VCross);     
    return Vec (Vn);
  }
}

//=======================================================================
//function : ReferencePlane
//purpose  : 
//=======================================================================

Ax2 Geom_SurfaceOfRevolution::ReferencePlane() const {
        
   Standard_NotImplemented::Raise ();
   return gp_Ax2();
}


//=======================================================================
//function : UIso
//purpose  : 
//=======================================================================

Handle(Curve) Geom_SurfaceOfRevolution::UIso (const Standard_Real U) const {

   Handle(Curve) C = Handle(Curve)::DownCast(basisCurve->Copy());
   Ax1 RotAxis = Ax1 (loc, direction);
   C->Rotate (RotAxis, U);
   return C;
}


//=======================================================================
//function : VIso
//purpose  : 
//=======================================================================

Handle(Geom_Curve) Geom_SurfaceOfRevolution::VIso (const Standard_Real V) const {

  Handle(Geom_Circle) Circ;
  Pnt Pc = basisCurve->Value (V);
  gp_Lin L1(loc,direction);
  Standard_Real Rad= L1.Distance(Pc);

  Ax2 Rep ;
  if ( Rad > gp::Resolution()) { 
    XYZ P  = Pc.XYZ(); 
    XYZ C;
    C.SetLinearForm((P-loc.XYZ()).Dot(direction.XYZ()), 
		    direction.XYZ(), loc.XYZ() );
    P = P-C;
    if(P.Modulus() > gp::Resolution()) {
      gp_Dir D = P.Normalized();
      Rep = gp_Ax2(C, direction, D);
    }
    else 
      Rep = gp_Ax2(C, direction);
  }
  else
    Rep = gp_Ax2(Pc, direction);

  Circ   = new Geom_Circle (Rep, Rad);
  return Circ;
}


//=======================================================================
//function : Transform
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::Transform (const Trsf& T) {

  loc.Transform (T);
  direction.Transform (T);
  basisCurve->Transform (T);
  if(T.ScaleFactor()*T.HVectorialPart().Determinant() < 0.) UReverse(); 
}

//=======================================================================
//function : TransformParameters
//purpose  : 
//=======================================================================

void Geom_SurfaceOfRevolution::TransformParameters(Standard_Real& ,
						   Standard_Real& V,
						   const gp_Trsf& T) 
const
{
  V = basisCurve->TransformedParameter(V,T);
}

//=======================================================================
//function : ParametricTransformation
//purpose  : 
//=======================================================================

gp_GTrsf2d Geom_SurfaceOfRevolution::ParametricTransformation
(const gp_Trsf& T) const
{
  gp_GTrsf2d T2;
  gp_Ax2d Axis(gp::Origin2d(),gp::DX2d());
  T2.SetAffinity(Axis, basisCurve->ParametricTransformation(T));
  return T2;
}

