// Copyright (c) 1995-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <GeomConvert.hxx>

#include <Convert_ConeToBSplineSurface.hxx>
#include <Convert_CylinderToBSplineSurface.hxx>
#include <Convert_ElementarySurfaceToBSplineSurface.hxx>
#include <Convert_SphereToBSplineSurface.hxx>
#include <Convert_TorusToBSplineSurface.hxx>
#include <Geom_BezierSurface.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_ConicalSurface.hxx>
#include <Geom_Curve.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <Geom_Geometry.hxx>
#include <Geom_OffsetSurface.hxx>
#include <Geom_Plane.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_SphericalSurface.hxx>
#include <Geom_Surface.hxx>
#include <Geom_SurfaceOfLinearExtrusion.hxx>
#include <Geom_SurfaceOfRevolution.hxx>
#include <Geom_ToroidalSurface.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <GeomConvert_ApproxSurface.hxx>
#include <gp_Cone.hxx>
#include <gp_Cylinder.hxx>
#include <gp_GTrsf.hxx>
#include <gp_Sphere.hxx>
#include <gp_Torus.hxx>
#include <gp_Trsf.hxx>
#include <gp_Vec.hxx>
#include <Precision.hxx>
#include <Standard_DomainError.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColgp_Array2OfPnt.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_Array2OfInteger.hxx>
#include <TColStd_Array2OfReal.hxx>

typedef Geom_Surface                          Surface;
typedef Geom_BSplineSurface                   BSplineSurface;
typedef TColStd_Array1OfReal                 Array1OfReal;
typedef TColStd_Array2OfReal                 Array2OfReal;
typedef TColStd_Array1OfInteger              Array1OfInteger;
typedef TColStd_Array2OfInteger              Array2OfInteger;
typedef TColgp_Array2OfPnt                          Array2OfPnt;
typedef TColgp_Array1OfPnt                          Array1OfPnt;
typedef gp_Pnt Pnt;

//=======================================================================
//function : BSplineSurfaceBuilder
//purpose  : 
//=======================================================================

static Handle(Geom_BSplineSurface) BSplineSurfaceBuilder (const Convert_ElementarySurfaceToBSplineSurface& Convert) 
{
  Handle(Geom_BSplineSurface) TheSurface;
  Standard_Integer UDegree  = Convert.UDegree ();
  Standard_Integer VDegree  = Convert.VDegree ();
  Standard_Integer NbUPoles = Convert.NbUPoles();
  Standard_Integer NbVPoles = Convert.NbVPoles();
  Standard_Integer NbUKnots = Convert.NbUKnots();
  Standard_Integer NbVKnots = Convert.NbVKnots();
  Array2OfPnt     Poles   (1, NbUPoles, 1, NbVPoles);
  Array2OfReal    Weights (1, NbUPoles, 1, NbVPoles);
  Array1OfReal    UKnots  (1, NbUKnots);
  Array1OfReal    VKnots  (1, NbVKnots);
  Array1OfInteger UMults  (1, NbUKnots);
  Array1OfInteger VMults  (1, NbVKnots);
  Standard_Integer i, j;
  for (j = 1; j <= NbVPoles; j++) {
    for (i = 1; i <= NbUPoles; i++) {
      Poles   (i, j) = Convert.Pole   (i, j);
      Weights (i, j) = Convert.Weight (i, j);         
    }
  }
  for (i = 1; i <= NbUKnots; i++) {
    UKnots (i) = Convert.UKnot (i);
    UMults (i) = Convert.UMultiplicity (i);
  }
  for (i = 1; i <= NbVKnots; i++) {
    VKnots (i) = Convert.VKnot (i);
    VMults (i) = Convert.VMultiplicity (i);
  }
  TheSurface = new BSplineSurface (Poles, Weights, UKnots, VKnots, 
    UMults, VMults, UDegree, VDegree,
    Convert.IsUPeriodic(),
    Convert.IsVPeriodic());
  return TheSurface;
}

//=======================================================================
//function : SplitBSplineSurface
//purpose  : 
//=======================================================================

Handle(Geom_BSplineSurface) GeomConvert::SplitBSplineSurface 
  (const Handle(Geom_BSplineSurface)& S,
  const Standard_Integer FromUK1, 
  const Standard_Integer ToUK2,
  const Standard_Integer FromVK1, 
  const Standard_Integer ToVK2,
  const Standard_Boolean SameUOrientation, 
  const Standard_Boolean SameVOrientation ) 
{
  Standard_Integer FirstU = S->FirstUKnotIndex ();
  Standard_Integer FirstV = S->FirstVKnotIndex ();
  Standard_Integer LastU  = S->LastUKnotIndex  ();
  Standard_Integer LastV  = S->LastVKnotIndex  ();
  if (FromUK1 == ToUK2 || FromVK1 == ToVK2)   throw Standard_DomainError();
  Standard_Integer FirstUK = Min (FromUK1, ToUK2);
  Standard_Integer LastUK  = Max (FromUK1, ToUK2);
  Standard_Integer FirstVK = Min (FromVK1, ToVK2);
  Standard_Integer LastVK  = Max (FromVK1, ToVK2);
  if (FirstUK < FirstU || LastUK > LastU || 
    FirstVK < FirstV || LastVK > LastV) { throw Standard_DomainError(); }

  Handle(Geom_BSplineSurface) S1= Handle(Geom_BSplineSurface)::DownCast(S->Copy());

  S1->Segment(S1->UKnot(FirstUK),S1->UKnot(LastUK),
    S1->VKnot(FirstVK),S1->VKnot(LastVK));

  if (S->IsUPeriodic()) {
    if (!SameUOrientation) S1->UReverse();
  }
  else {
    if (FromUK1 > ToUK2)   S1->UReverse();
  }
  if (S->IsVPeriodic()) {
    if (!SameVOrientation) S1->VReverse();
  }
  else {
    if (FromVK1 > ToVK2)   S1->VReverse();
  }
  return S1;
}


//=======================================================================
//function : SplitBSplineSurface
//purpose  : 
//=======================================================================

Handle(Geom_BSplineSurface) GeomConvert::SplitBSplineSurface 
  (const Handle(Geom_BSplineSurface)& S,
  const Standard_Integer FromK1,
  const Standard_Integer ToK2,
  const Standard_Boolean USplit, 
  const Standard_Boolean SameOrientation )
{
  if (FromK1 == ToK2)   throw Standard_DomainError();


  Handle(Geom_BSplineSurface) S1 = Handle(Geom_BSplineSurface)::DownCast(S->Copy());

  if (USplit) {

    Standard_Integer FirstU = S->FirstUKnotIndex ();
    Standard_Integer LastU  = S->LastUKnotIndex  ();
    Standard_Integer FirstUK = Min (FromK1, ToK2);
    Standard_Integer LastUK  = Max (FromK1, ToK2);
    if (FirstUK < FirstU || LastUK > LastU)  throw Standard_DomainError();

    S1->Segment( S1->UKnot(FirstUK), 
      S1->UKnot(LastUK),
      S1->VKnot(S1->FirstVKnotIndex()),
      S1->VKnot(S1->LastVKnotIndex()));

    if (S->IsUPeriodic()) {
      if (!SameOrientation) S1->UReverse();
    }
    else {
      if (FromK1 > ToK2)    S1->UReverse();
    }

  }
  else {

    Standard_Integer FirstV = S->FirstVKnotIndex ();
    Standard_Integer LastV  = S->LastVKnotIndex  ();
    Standard_Integer FirstVK = Min (FromK1, ToK2);
    Standard_Integer LastVK  = Max (FromK1, ToK2);
    if (FirstVK < FirstV || LastVK > LastV)  throw Standard_DomainError();

    S1->Segment( S1->UKnot(S1->FirstUKnotIndex()), 
      S1->UKnot(S1->LastUKnotIndex()),
      S1->VKnot(FirstVK),
      S1->VKnot(LastVK));


    if (S->IsVPeriodic()) {
      if (!SameOrientation) S1->VReverse();
    }
    else {
      if (FromK1 > ToK2)    S1->VReverse();
    }
  }
  return S1;
}


//=======================================================================
//function : SplitBSplineSurface
//purpose  : 
//=======================================================================

Handle(Geom_BSplineSurface) GeomConvert::SplitBSplineSurface 
  (const Handle(Geom_BSplineSurface)& S, 
  const Standard_Real FromU1, 
  const Standard_Real ToU2,
  const Standard_Real FromV1, 
  const Standard_Real ToV2,
  //   const Standard_Real ParametricTolerance,
  const Standard_Real ,
  const Standard_Boolean SameUOrientation, 
  const Standard_Boolean SameVOrientation )
{
  Standard_Real FirstU = Min( FromU1, ToU2);
  Standard_Real LastU  = Max( FromU1, ToU2);
  Standard_Real FirstV = Min( FromV1, ToV2);
  Standard_Real LastV  = Max( FromV1, ToV2);

  Handle (Geom_BSplineSurface) NewSurface 
    = Handle(Geom_BSplineSurface)::DownCast(S->Copy());

  NewSurface->Segment(FirstU, LastU, FirstV, LastV);

  if (S->IsUPeriodic()) { 
    if (!SameUOrientation)  NewSurface->UReverse(); 
  }
  else { 
    if (FromU1 > ToU2)    NewSurface->UReverse(); 
  }
  if (S->IsVPeriodic()) {
    if (!SameVOrientation)  NewSurface->VReverse();
  }
  else {
    if (FromV1 > ToV2)     NewSurface->VReverse();
  }
  return NewSurface;
}


//=======================================================================
//function : SplitBSplineSurface
//purpose  : 
//=======================================================================

Handle(Geom_BSplineSurface) GeomConvert::SplitBSplineSurface 
  (const Handle(Geom_BSplineSurface)& S, 
  const Standard_Real                    FromParam1, 
  const Standard_Real                    ToParam2,
  const Standard_Boolean                 USplit, 
  const Standard_Real                    ParametricTolerance, 
  const Standard_Boolean                 SameOrientation  )
{
  if (Abs (FromParam1 - ToParam2) <= Abs(ParametricTolerance)) {
    throw Standard_DomainError();
  }
  Handle(Geom_BSplineSurface) NewSurface
    = Handle(Geom_BSplineSurface)::DownCast(S->Copy());

  if (USplit) {
    Standard_Real FirstU = Min( FromParam1, ToParam2);
    Standard_Real LastU  = Max( FromParam1, ToParam2);
    Standard_Real FirstV = S->VKnot(S->FirstVKnotIndex());
    Standard_Real LastV  = S->VKnot(S->LastVKnotIndex());

    NewSurface->Segment(FirstU, LastU, FirstV, LastV);

    if (S->IsUPeriodic()) { 
      if (!SameOrientation)  NewSurface->UReverse(); 
    }
    else { 
      if (FromParam1 > ToParam2)    NewSurface->UReverse(); 
    }

  }
  else {
    Standard_Real FirstU = S->UKnot(S->FirstUKnotIndex());
    Standard_Real LastU  = S->UKnot(S->LastUKnotIndex());
    Standard_Real FirstV = Min( FromParam1, ToParam2);
    Standard_Real LastV  = Max( FromParam1, ToParam2);

    NewSurface->Segment(FirstU, LastU, FirstV, LastV);

    if (S->IsUPeriodic()) { 
      if (!SameOrientation)  NewSurface->UReverse(); 
    }
    else { 
      if (FromParam1 > ToParam2)    NewSurface->UReverse(); 
    }

  }
  return NewSurface;
}


//=======================================================================
//function : SurfaceToBSplineSurface
//purpose  : 
//=======================================================================

Handle(Geom_BSplineSurface) GeomConvert::SurfaceToBSplineSurface
  (const Handle(Geom_Surface)& Sr) 
{
 
  Standard_Real U1, U2, V1, V2;
  Sr->Bounds (U1, U2, V1, V2);
  Standard_Real UFirst = Min (U1, U2);
  Standard_Real ULast  = Max (U1, U2);
  Standard_Real VFirst = Min (V1, V2);
  Standard_Real VLast  = Max (V1, V2);

  //If the surface Sr is infinite stop the computation
  if (Precision::IsNegativeInfinite(UFirst) || 
    Precision::IsPositiveInfinite(ULast)  || 
    Precision::IsNegativeInfinite(VFirst) || 
    Precision::IsPositiveInfinite(VLast) )   {
      throw Standard_DomainError ("GeomConvert::SurfaceToBSplineSurface() - infinite surface");
  }

  Handle(Geom_BSplineSurface) TheSurface;
  Handle(Geom_Surface) S; 
  Handle(Geom_OffsetSurface) OffsetSur;
  if (Sr->IsKind(STANDARD_TYPE(Geom_OffsetSurface))) {
    OffsetSur = Handle(Geom_OffsetSurface)::DownCast (Sr);
    S = OffsetSur->Surface();
    if (!S.IsNull()) { // Convert the equivalent surface.
      return SurfaceToBSplineSurface(S);
    }
  }
  S = Sr;

  if (S->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface))) {
    Handle(Geom_RectangularTrimmedSurface) Strim = 
      Handle(Geom_RectangularTrimmedSurface)::DownCast(S);

    Handle(Geom_Surface) Surf = Strim->BasisSurface();
    UFirst = U1;  ULast = U2; VFirst = V1; VLast = V2;
    if (Surf->IsKind(STANDARD_TYPE(Geom_OffsetSurface))) {
      Handle(Geom_OffsetSurface) OffsetSurBasis =
        Handle(Geom_OffsetSurface)::DownCast(Surf);

      S = OffsetSurBasis->Surface();
      if (!S.IsNull()) {
        Surf = S;
      } 
      else S = Surf;
    }

    if  (Surf->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface))) {
      Handle(Geom_RectangularTrimmedSurface) aStrim = new 
        (Geom_RectangularTrimmedSurface) (Surf, 
        UFirst, ULast, 
        VFirst, VLast);
      return SurfaceToBSplineSurface(aStrim);
    }
    //
    //For cylinders, cones, spheres, toruses
    const Standard_Boolean isUClosed = Abs((ULast - UFirst) - 2. * M_PI) <= Precision::PConfusion();
    const Standard_Real eps = 100. * Epsilon(2. * M_PI);
    //
    if (Surf->IsKind(STANDARD_TYPE(Geom_Plane))) {
      TColgp_Array2OfPnt Poles (1, 2, 1, 2);
      Poles (1, 1) = Strim->Value (U1, V1);
      Poles (1, 2) = Strim->Value (U1, V2);
      Poles (2, 1) = Strim->Value (U2, V1);
      Poles (2, 2) = Strim->Value (U2, V2);
      TColStd_Array1OfReal UKnots (1, 2);
      TColStd_Array1OfReal VKnots (1, 2);
      TColStd_Array1OfInteger UMults (1, 2);
      TColStd_Array1OfInteger VMults (1, 2);
      UKnots (1) = U1;
      UKnots (2) = U2;
      VKnots (1) = V1;
      VKnots (2) = V2;
      UMults (1) = 2;
      UMults (2) = 2;
      VMults (1) = 2;
      VMults (2) = 2;
      Standard_Integer UDegree = 1;
      Standard_Integer VDegree = 1;
      TheSurface = new Geom_BSplineSurface (Poles, UKnots, VKnots, UMults,
        VMults, UDegree, VDegree);
    }
    else if (Surf->IsKind(STANDARD_TYPE(Geom_CylindricalSurface))) {
      Handle(Geom_CylindricalSurface) TheElSurf= 
        Handle(Geom_CylindricalSurface)::DownCast(Surf);

      gp_Cylinder Cyl = TheElSurf->Cylinder();
      if (isUClosed) {
        Convert_CylinderToBSplineSurface Convert (Cyl, VFirst, VLast);
        TheSurface = BSplineSurfaceBuilder (Convert);
        Standard_Integer aNbK = TheSurface->NbUKnots();
        if (Abs(TheSurface->UKnot(1) - UFirst) > eps || Abs(TheSurface->UKnot(aNbK) - ULast) > eps)
        {
          TheSurface->CheckAndSegment(UFirst, ULast, VFirst, VLast);
        }
      }
      else {
        Convert_CylinderToBSplineSurface 
          Conv (Cyl, UFirst, ULast, VFirst, VLast);
        TheSurface = BSplineSurfaceBuilder (Conv);
      }
    }


    else if (Surf->IsKind(STANDARD_TYPE(Geom_ConicalSurface))) {
      Handle(Geom_ConicalSurface) TheElSurf = 
        Handle(Geom_ConicalSurface)::DownCast(Surf);
      gp_Cone Co = TheElSurf->Cone();
      if (isUClosed) {
        Convert_ConeToBSplineSurface Convert (Co, VFirst, VLast);
        TheSurface = BSplineSurfaceBuilder (Convert);
        Standard_Integer aNbK = TheSurface->NbUKnots();
        if (Abs(TheSurface->UKnot(1) - UFirst) > eps || Abs(TheSurface->UKnot(aNbK) - ULast) > eps)
        {
          TheSurface->CheckAndSegment(UFirst, ULast, VFirst, VLast);
        }
      }
      else {
        Convert_ConeToBSplineSurface 
          Convert (Co, UFirst, ULast, VFirst, VLast);
        TheSurface = BSplineSurfaceBuilder (Convert);
      }
    }


    else if (Surf->IsKind(STANDARD_TYPE(Geom_SphericalSurface))) {
      Handle(Geom_SphericalSurface) TheElSurf = 
        Handle(Geom_SphericalSurface)::DownCast(Surf);
      gp_Sphere Sph = TheElSurf->Sphere();
      //OCC217
      if (isUClosed) {
        //if (Strim->IsVClosed()) {
        //Convert_SphereToBSplineSurface Convert (Sph, UFirst, ULast);
        Convert_SphereToBSplineSurface Convert (Sph, VFirst, VLast, Standard_False);
        TheSurface = BSplineSurfaceBuilder (Convert);
        Standard_Integer aNbK = TheSurface->NbUKnots();
        if (Abs(TheSurface->UKnot(1) - UFirst) > eps || Abs(TheSurface->UKnot(aNbK) - ULast) > eps)
        {
          TheSurface->CheckAndSegment(UFirst, ULast, VFirst, VLast);
        }
      }
      else {
        Convert_SphereToBSplineSurface 
          Convert (Sph, UFirst, ULast, VFirst, VLast);
        TheSurface = BSplineSurfaceBuilder (Convert);
      }
    }


    else if (Surf->IsKind(STANDARD_TYPE(Geom_ToroidalSurface))) {
      Handle(Geom_ToroidalSurface) TheElSurf = 
        Handle(Geom_ToroidalSurface)::DownCast(Surf);

      gp_Torus Tr = TheElSurf->Torus();
      //
      // if isUClosed = true and U trim does not coinside with first period of torus, 
      // method CheckAndSegment shifts position of U seam boundary of surface.
      // probably bug? So, for this case we must build not periodic surface. 
      Standard_Boolean isUFirstPeriod = !(UFirst < 0. || ULast > 2.*M_PI);
      Standard_Boolean isVFirstPeriod = !(VFirst < 0. || VLast > 2.*M_PI);
      if (isUClosed && isUFirstPeriod) {
        Convert_TorusToBSplineSurface Convert (Tr, VFirst, VLast, 
          Standard_False);
        TheSurface = BSplineSurfaceBuilder (Convert);
        Standard_Integer aNbK = TheSurface->NbUKnots();
        if (Abs(TheSurface->UKnot(1) - UFirst) > eps || Abs(TheSurface->UKnot(aNbK) - ULast) > eps)
        {
          TheSurface->CheckAndSegment(UFirst, ULast, VFirst, VLast);
        }
      }
      else if (Strim->IsVClosed() && isVFirstPeriod) {
        Convert_TorusToBSplineSurface Convert (Tr, UFirst, ULast);
        TheSurface = BSplineSurfaceBuilder (Convert);
        Standard_Integer aNbK = TheSurface->NbVKnots();
        if (Abs(TheSurface->VKnot(1) - VFirst) > eps || Abs(TheSurface->VKnot(aNbK) - VLast) > eps)
        {
          TheSurface->CheckAndSegment(UFirst, ULast, VFirst, VLast);
        }
      }
      else {
        Convert_TorusToBSplineSurface 
          Convert (Tr, UFirst, ULast, VFirst, VLast);
        TheSurface = BSplineSurfaceBuilder (Convert);
      }
    }


    else if (Surf->IsKind(STANDARD_TYPE(Geom_SurfaceOfRevolution))) {
      Handle(Geom_SurfaceOfRevolution) Revol = 
        Handle(Geom_SurfaceOfRevolution)::DownCast(Surf);

      Handle(Geom_Curve) Meridian = Revol->BasisCurve();
      Handle(Geom_BSplineCurve) C; 
      if (Strim->IsVClosed()) {
        C =  GeomConvert::CurveToBSplineCurve (Meridian);
      }
      else {
        Handle(Geom_TrimmedCurve) CT = 
          new Geom_TrimmedCurve( Meridian, VFirst, VLast);
        C =  GeomConvert::CurveToBSplineCurve (CT);
      }
      Standard_Integer NbUPoles, NbUKnots;
      Standard_Integer NbVPoles, NbVKnots;
      Standard_Boolean periodic = Standard_False;

      // Poles of meridian = Vpoles
      NbVPoles = C->NbPoles();
      TColgp_Array1OfPnt Poles(1, NbVPoles);
      C->Poles(Poles);
      TColStd_Array1OfReal Weights( 1, NbVPoles);
      Weights.Init(1.);
      if ( C->IsRational()) C->Weights(Weights);

      Standard_Integer nbUSpans;
      Standard_Real AlfaU;
      if (Strim->IsUPeriodic()) {
        NbUKnots = 4;
        nbUSpans = 3;
        AlfaU    = M_PI / 3.;
        NbUPoles = 6;
        periodic = Standard_True;
      }
      else {
        // Nombre de spans : ouverture maximale = 150 degres ( = PI / 1.2 rds)
        nbUSpans = 
          (Standard_Integer)IntegerPart( 1.2 * (ULast - UFirst) / M_PI) + 1;
        AlfaU = (ULast - UFirst) / ( nbUSpans * 2);
        NbUPoles = 2 * nbUSpans + 1;
        NbUKnots = nbUSpans + 1;
      }
      // Compute Knots and Mults
      TColStd_Array1OfReal    UKnots(1, NbUKnots);
      TColStd_Array1OfInteger UMults( 1, NbUKnots);
      Standard_Integer i,j;
      for ( i = 1; i <= NbUKnots; i++) {
        UKnots(i) = UFirst + (i-1) * 2 * AlfaU;
        UMults(i) = 2;
      }
      if (!periodic) {
        UMults(1)++; UMults(NbUKnots)++;
      }
      NbVKnots = C->NbKnots();
      TColStd_Array1OfReal    VKnots(1, NbVKnots);
      TColStd_Array1OfInteger VMults(1, NbVKnots);
      C->Knots(VKnots);
      C->Multiplicities(VMults);

      // Compute the poles.
      TColgp_Array2OfPnt   NewPoles  ( 1, NbUPoles, 1, NbVPoles);
      TColStd_Array2OfReal NewWeights( 1, NbUPoles, 1, NbVPoles);
      gp_Trsf Trsf;

      for ( i = 1; i<= NbUPoles; i+=2) {
        Trsf.SetRotation( Revol->Axis(), UFirst + (i-1)*AlfaU);
        for ( j = 1; j <= NbVPoles; j++) {
          NewPoles(i,j) = Poles(j).Transformed(Trsf);
          NewWeights(i,j) = Weights(j);
        }
      }
      gp_GTrsf Aff;
      Aff.SetAffinity( Revol->Axis(), 1/Cos(AlfaU));
      gp_XYZ coord;
      for ( j= 1; j<= NbVPoles; j++) {
        coord = Poles(j).XYZ();
        Aff.Transforms(coord);
        Poles(j).SetXYZ(coord);
      }
      for ( i = 2; i<= NbUPoles; i+=2) {
        Trsf.SetRotation( Revol->Axis(), UFirst + (i-1)*AlfaU);
        for ( j = 1; j <= NbVPoles; j++) {
          NewPoles(i,j) = Poles(j).Transformed(Trsf);
          NewWeights(i,j) = Weights(j) * Cos(AlfaU);
        }
      }

      TheSurface = new Geom_BSplineSurface(NewPoles, NewWeights, 
        UKnots, VKnots, 
        UMults, VMults,
        2 , C->Degree(),
        periodic, C->IsPeriodic());

    }


    else if (Surf->IsKind(STANDARD_TYPE(Geom_SurfaceOfLinearExtrusion))) {
      Handle(Geom_SurfaceOfLinearExtrusion) Extru = 
        Handle(Geom_SurfaceOfLinearExtrusion)::DownCast(Surf);

      Handle(Geom_Curve) Meridian = Extru->BasisCurve();
      Handle(Geom_BSplineCurve) C; 
      if (Strim->IsUClosed()) {
        C =  GeomConvert::CurveToBSplineCurve (Meridian);
      }
      else {
        Handle(Geom_TrimmedCurve) CT = 
          new Geom_TrimmedCurve( Meridian, UFirst, ULast);
        C =  GeomConvert::CurveToBSplineCurve (CT);
      }
      TColgp_Array2OfPnt      Poles  ( 1, C->NbPoles(), 1, 2);
      TColStd_Array2OfReal    Weights( 1, C->NbPoles(), 1, 2);
      TColStd_Array1OfReal    UKnots ( 1, C->NbKnots());
      C->Knots(UKnots);
      TColStd_Array1OfInteger UMults ( 1, C->NbKnots());
      C->Multiplicities(UMults);
      TColStd_Array1OfReal    VKnots ( 1, 2);
      VKnots(1) = VFirst; 
      VKnots(2) = VLast;
      TColStd_Array1OfInteger VMults ( 1, 2);
      VMults.Init(2);

      gp_Vec D( Extru->Direction());
      gp_Vec DV1 = VFirst * D;
      gp_Vec DV2 = VLast  * D;
      for (Standard_Integer i = 1; i <= C->NbPoles(); i++) {
        Poles(i,1) = C->Pole(i).Translated(DV1);
        Poles(i,2) = C->Pole(i).Translated(DV2);
        Weights(i,1) = Weights(i,2) = C->Weight(i);
      } 
      TheSurface = new Geom_BSplineSurface(Poles, Weights, UKnots, VKnots, 
        UMults, VMults,
        C->Degree(), 1,
        C->IsPeriodic(), Standard_False);
    }


    else if (Surf->IsKind(STANDARD_TYPE(Geom_BezierSurface))) {

      Handle(Geom_BezierSurface) SBez = 
        Handle(Geom_BezierSurface)::DownCast(Surf->Copy());

      SBez->Segment (U1, U2, V1, V2);
      Standard_Integer NbUPoles = SBez->NbUPoles();
      Standard_Integer NbVPoles = SBez->NbVPoles();
      Standard_Integer UDegree  = SBez->UDegree();
      Standard_Integer VDegree  = SBez->VDegree();
      TColgp_Array2OfPnt      Poles    (1, NbUPoles, 1, NbVPoles);
      TColStd_Array1OfReal    UKnots   (1, 2);
      TColStd_Array1OfInteger UMults   (1, 2);
      TColStd_Array1OfReal    VKnots   (1, 2);
      TColStd_Array1OfInteger VMults   (1, 2);
      UKnots (1) = 0.0;
      UKnots (2) = 1.0;
      UMults (1) = UDegree + 1;
      UMults (2) = UDegree + 1;
      VKnots (1) = 0.0;
      VKnots (2) = 1.0;
      VMults (1) = VDegree + 1;
      VMults (2) = VDegree + 1;
      SBez->Poles (Poles);
      if (SBez->IsURational() || SBez->IsVRational()) {    
        TColStd_Array2OfReal Weights (1, NbUPoles, 1, NbVPoles);
        SBez->Weights (Weights);
        TheSurface = new Geom_BSplineSurface (Poles, Weights, UKnots, VKnots,
          UMults, VMults,
          UDegree, VDegree);
      }
      else {
        TheSurface = new Geom_BSplineSurface (Poles, UKnots, VKnots, 
          UMults, VMults,
          UDegree, VDegree);
      }
    }

    else if (Surf->IsKind(STANDARD_TYPE(Geom_BSplineSurface))) {
      Handle(Geom_BSplineSurface) BS = 
        Handle(Geom_BSplineSurface)::DownCast(Surf->Copy());
      Standard_Real umin, umax, vmin, vmax;
      BS->Bounds(umin, umax, vmin, vmax);
      if (!BS->IsUPeriodic()) {
        if (U1 < umin)
          U1 = umin;
        if (U2 > umax) 
          U2 = umax;
      }

      if (!BS->IsVPeriodic()) {
        if (V1 < vmin)
          V1 = vmin;
        if (V2 > vmax)
          V2 = vmax;
      }
      if (BS->IsUPeriodic() || BS->IsVPeriodic())
        BS->CheckAndSegment (U1, U2, V1, V2);
      else
        BS->Segment (U1, U2, V1, V2);
      TheSurface = BS;
    }

    else {
      Standard_Real Tol3d=1.e-4;
      Standard_Integer MaxDegree =14, MaxSeg;
      GeomAbs_Shape cont;
      GeomAdaptor_Surface AS(Sr);
      if (AS.NbUIntervals(GeomAbs_C2) > 1 || AS.NbVIntervals(GeomAbs_C2) > 1 ) 
        cont=GeomAbs_C1;
      else
        cont=GeomAbs_C2;
      MaxSeg = 4*(AS.NbUIntervals(GeomAbs_CN)+1)*(AS.NbVIntervals(GeomAbs_CN)+1); 
      GeomConvert_ApproxSurface BSpS(Sr, Tol3d, cont, cont,
        MaxDegree, MaxDegree, MaxSeg, 1);
      TheSurface = BSpS.Surface();
    }
  } // Fin du cas Rectangular::TrimmedSurface

  else { 

    if (S->IsKind(STANDARD_TYPE(Geom_SphericalSurface))) {
      Handle(Geom_SphericalSurface) TheElSurf = 
        Handle(Geom_SphericalSurface)::DownCast(S);

      gp_Sphere Sph = TheElSurf->Sphere();
      Convert_SphereToBSplineSurface Convert(Sph);
      TheSurface = BSplineSurfaceBuilder(Convert);
    }


    else if (S->IsKind(STANDARD_TYPE(Geom_ToroidalSurface))) {
      Handle(Geom_ToroidalSurface) TheElSurf = 
        Handle(Geom_ToroidalSurface)::DownCast(S);

      gp_Torus Tr = TheElSurf->Torus();
      Convert_TorusToBSplineSurface Convert(Tr);
      TheSurface = BSplineSurfaceBuilder(Convert);
    }


    else if (S->IsKind(STANDARD_TYPE(Geom_SurfaceOfRevolution))) {

      Handle(Geom_SurfaceOfRevolution) Revol = 
        Handle(Geom_SurfaceOfRevolution)::DownCast(S);

      Handle(Geom_Curve) Meridian = Revol->BasisCurve();
      Handle(Geom_BSplineCurve) C 
        = GeomConvert::CurveToBSplineCurve (Meridian);

      Standard_Integer NbUPoles, NbUKnots;
      Standard_Integer NbVPoles, NbVKnots;
      Standard_Boolean periodic = Standard_True;

      // Poles of meridian = Vpoles
      NbVPoles = C->NbPoles();
      TColgp_Array1OfPnt Poles(1, NbVPoles);
      C->Poles(Poles);
      TColStd_Array1OfReal Weights( 1, NbVPoles);
      Weights.Init(1.);
      if ( C->IsRational()) C->Weights(Weights);

      Standard_Real AlfaU;
      NbUKnots = 4;
      AlfaU    = M_PI / 3.;
      NbUPoles = 6;

      // Compute Knots and Mults
      TColStd_Array1OfReal    UKnots(1, NbUKnots);
      TColStd_Array1OfInteger UMults( 1, NbUKnots);
      Standard_Integer i,j;
      for ( i = 1; i <= NbUKnots; i++) {
        UKnots(i) = UFirst + (i-1) * 2 * AlfaU;
        UMults(i) = 2;
      }
      NbVKnots = C->NbKnots();
      TColStd_Array1OfReal    VKnots(1, NbVKnots);
      TColStd_Array1OfInteger VMults(1, NbVKnots);
      C->Knots(VKnots);
      C->Multiplicities(VMults);

      // Compute the poles.
      TColgp_Array2OfPnt   NewPoles  ( 1, NbUPoles, 1, NbVPoles);
      TColStd_Array2OfReal NewWeights( 1, NbUPoles, 1, NbVPoles);
      gp_Trsf Trsf;

      for ( i = 1; i<= NbUPoles; i+=2) {
        Trsf.SetRotation( Revol->Axis(), UFirst + (i-1)*AlfaU);
        for ( j = 1; j <= NbVPoles; j++) {
          NewPoles(i,j) = Poles(j).Transformed(Trsf);
          NewWeights(i,j) = Weights(j);
        }
      }
      gp_GTrsf Aff;
      Aff.SetAffinity( Revol->Axis(), 1/Cos(AlfaU));
      gp_XYZ coord;
      for ( j= 1; j<= NbVPoles; j++) {
        coord = Poles(j).XYZ();
        Aff.Transforms(coord);
        Poles(j).SetXYZ(coord);
      }
      for ( i = 2; i<= NbUPoles; i+=2) {
        Trsf.SetRotation( Revol->Axis(), UFirst + (i-1)*AlfaU);
        for ( j = 1; j <= NbVPoles; j++) {
          NewPoles(i,j) = Poles(j).Transformed(Trsf);
          NewWeights(i,j) = Weights(j) * Cos(AlfaU);
        }
      }

      TheSurface = new Geom_BSplineSurface(NewPoles, NewWeights, 
        UKnots, VKnots, 
        UMults, VMults,
        2 , C->Degree(),
        periodic, C->IsPeriodic());
    }


    else if (S->IsKind(STANDARD_TYPE(Geom_BezierSurface))) {

      Handle(Geom_BezierSurface) SBez = 
        Handle(Geom_BezierSurface)::DownCast(S);

      Standard_Integer NbUPoles = SBez->NbUPoles();
      Standard_Integer NbVPoles = SBez->NbVPoles();
      Standard_Integer UDegree  = SBez->UDegree();
      Standard_Integer VDegree  = SBez->VDegree();
      TColgp_Array2OfPnt      Poles (1, NbUPoles, 1, NbVPoles);
      TColStd_Array1OfReal    UKnots(1, 2);
      TColStd_Array1OfInteger UMults(1, 2);
      TColStd_Array1OfReal    VKnots(1, 2);
      TColStd_Array1OfInteger VMults(1, 2);
      UKnots (1) = 0.0;
      UKnots (2) = 1.0;
      UMults (1) = UDegree + 1;
      UMults (2) = UDegree + 1;
      VKnots (1) = 0.0;
      VKnots (2) = 1.0;
      VMults (1) = VDegree + 1;
      VMults (2) = VDegree + 1;
      SBez->Poles (Poles);
      if (SBez->IsURational() || SBez->IsVRational()) {    
        TColStd_Array2OfReal    Weights (1, NbUPoles, 1, NbVPoles);
        SBez->Weights (Weights);
        TheSurface = new Geom_BSplineSurface (Poles, Weights, UKnots, VKnots,
          UMults, VMults,
          UDegree, VDegree);
      }
      else {
        TheSurface = new Geom_BSplineSurface (Poles, UKnots, VKnots, 
          UMults, VMults,
          UDegree, VDegree);
      }
    }

    else if (S->IsKind(STANDARD_TYPE(Geom_BSplineSurface))) {
      TheSurface = Handle(Geom_BSplineSurface)::DownCast(S->Copy()); //Just a copy
    }

    else { // In other cases => Approx
      Standard_Real Tol3d=1.e-4;
      Standard_Integer MaxDegree = 14, MaxSeg;
      GeomAbs_Shape ucont = GeomAbs_C0, vcont = GeomAbs_C0;
      GeomAdaptor_Surface AS(Sr);
      //
      if (Sr->IsCNu(2)) 
      {
        ucont=GeomAbs_C2;
      }
      else if(Sr->IsCNu(1)) 
      {
        ucont=GeomAbs_C1;
      }
      //
      if (Sr->IsCNv(2)) 
      {
        vcont=GeomAbs_C2;
      }
      else if(Sr->IsCNv(1)) 
      {
        vcont=GeomAbs_C1;
      }
      //
      MaxSeg = 4*(AS.NbUIntervals(GeomAbs_CN)+1)*(AS.NbVIntervals(GeomAbs_CN)+1);      
      GeomConvert_ApproxSurface BSpS(Sr, Tol3d, ucont, vcont,
                                     MaxDegree, MaxDegree, MaxSeg, 1);
      TheSurface = BSpS.Surface();
    }
  } // Fin du cas direct
  return TheSurface;
}



