// Created on: 1998-12-14
// Created by: Joelle CHAUVET
// Copyright (c) 1998-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.

// Modified:    Fri Jan  8 15:47:20 1999
//              enfin un calcul exact pour D1 et D2
//              le calcul par differ. finies est garde dans verifD1 et verifD2
// Modified:    Mon Jan 18 11:06:46 1999
//              mise au point de D1, D2 et IsConstant

#include <GeomFill_NSections.hxx>

#include <GCPnts_AbscissaPoint.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Geometry.hxx>
#include <Geom_Surface.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <GeomConvert.hxx>
#include <GeomFill_AppSurf.hxx>
#include <GeomFill_Line.hxx>
#include <GeomFill_SectionGenerator.hxx>
#include <gp_Circ.hxx>
#include <gp_Lin.hxx>
#include <gp_Pnt.hxx>
#include <Precision.hxx>
#include <TColGeom_Array1OfCurve.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <TColStd_Array1OfReal.hxx>

#include <stdio.h>
IMPLEMENT_STANDARD_RTTIEXT(GeomFill_NSections,GeomFill_SectionLaw)

#ifdef OCCT_DEBUG
# ifdef DRAW
#  include <DrawTrSurf.hxx>
#include <Geom_Curve.hxx>
# endif
static Standard_Boolean Affich = 0;
static Standard_Integer NbSurf = 0;
#endif

#ifdef OCCT_DEBUG
// verification des fonctions de derivation D1 et D2 par differences finies
static Standard_Boolean verifD1(const TColgp_Array1OfPnt& P1,
			 const TColStd_Array1OfReal& W1,
			 const TColgp_Array1OfPnt& P2,
			 const TColStd_Array1OfReal& W2,
			 const TColgp_Array1OfVec& DPoles,
			 const TColStd_Array1OfReal& DWeights,
			 const Standard_Real pTol, 
			 const Standard_Real wTol,
			 const Standard_Real pas)
{
  Standard_Boolean ok = Standard_True;
  Standard_Integer ii, L =  P1.Length();
  Standard_Real dw;
  gp_Vec dP;
  for (ii=1; ii<=L; ii++) {
    dw = (W2(ii)-W1(ii)) / pas;
    if (Abs(dw-DWeights(ii))>wTol) {
      if (Affich) {
        std::cout<<"erreur dans la derivee 1ere du poids pour l'indice "<<ii<<std::endl;
        std::cout<<"par diff finies : "<<dw<<std::endl;
        std::cout<<"resultat obtenu : "<<DWeights(ii)<<std::endl;
      }
      ok = Standard_False;
    }
    dP.SetXYZ( (P2(ii).XYZ()- P1(ii).XYZ()) /pas );
    gp_Vec diff = dP - DPoles(ii);
    if (diff.Magnitude()>pTol) {
      if (Affich) {
        std::cout<<"erreur dans la derivee 1ere du pole pour l'indice "<<ii<<std::endl;
        std::cout<<"par diff finies : ("<<dP.X()
                              <<" "<<dP.Y()
                              <<" "<<dP.Z()<<")"<<std::endl;
        std::cout<<"resultat obtenu : ("<<DPoles(ii).X()
                              <<" "<<DPoles(ii).Y()
                              <<" "<<DPoles(ii).Z()<<")"<<std::endl;
      }
      ok = Standard_False;
    }
  }
  return ok;
}

static Standard_Boolean verifD2(const TColgp_Array1OfVec& DP1,
                         const TColStd_Array1OfReal& DW1,
                         const TColgp_Array1OfVec& DP2,
                         const TColStd_Array1OfReal& DW2,
                         const TColgp_Array1OfVec& D2Poles,
                         const TColStd_Array1OfReal& D2Weights,
                         const Standard_Real pTol, 
                         const Standard_Real wTol,
                         const Standard_Real pas)
{
  Standard_Boolean ok = Standard_True;
  Standard_Integer ii, L =  DP1.Length();
  Standard_Real d2w;
  gp_Vec d2P;
  for (ii=1; ii<=L; ii++) {
    Standard_Real dw1 = DW1(ii), dw2 = DW2(ii);
    d2w = (dw2-dw1) / pas;
    if (Abs(d2w-D2Weights(ii))>wTol) {
      if (Affich) {
        std::cout<<"erreur dans la derivee 2nde du poids pour l'indice "<<ii<<std::endl;
        std::cout<<"par diff finies : "<<d2w<<std::endl;
        std::cout<<"resultat obtenu : "<<D2Weights(ii)<<std::endl;
      }
      ok = Standard_False;
    }
    d2P.SetXYZ( (DP2(ii).XYZ()- DP1(ii).XYZ()) /pas );
    gp_Vec diff = d2P - D2Poles(ii);
    if (diff.Magnitude()>pTol) {
      if (Affich) {
        std::cout<<"erreur dans la derivee 2nde du pole pour l'indice "<<ii<<std::endl;
        std::cout<<"par diff finies : ("<<d2P.X()
                              <<" "<<d2P.Y()
                              <<" "<<d2P.Z()<<")"<<std::endl;
        std::cout<<"resultat obtenu : ("<<D2Poles(ii).X()
                              <<" "<<D2Poles(ii).Y()
                              <<" "<<D2Poles(ii).Z()<<")"<<std::endl;
      }
      ok = Standard_False;
    }
  }
  return ok;
}
#endif

// fonction d'evaluation des poles et des poids de mySurface pour D1 et D2
static void ResultEval(const Handle(Geom_BSplineSurface)& surf,
                       const Standard_Real V,
                       const Standard_Integer deriv,
                       TColStd_Array1OfReal& Result)
{
  Standard_Boolean rational = surf->IsVRational() ;
  Standard_Integer gap = 3;
  if ( rational ) gap++;
  Standard_Integer Cdeg = surf->VDegree(), 
  Cdim = surf->NbUPoles() * gap, 
  NbP = surf->NbVPoles();
  
  //  les noeuds plats
  Standard_Integer Ksize = NbP + Cdeg + 1;
  TColStd_Array1OfReal FKnots(1,Ksize);
  surf->VKnotSequence(FKnots);
  
  //  les poles
  Standard_Integer Psize = Cdim * NbP;
  TColStd_Array1OfReal SPoles(1,Psize);
  Standard_Integer ii, jj, ipole=1;
  for (jj=1;jj<=NbP;jj++) {
    for (ii=1;ii<=surf->NbUPoles();ii++) {
      SPoles(ipole) = surf->Pole(ii,jj).X();
      SPoles(ipole+1) = surf->Pole(ii,jj).Y();
      SPoles(ipole+2) = surf->Pole(ii,jj).Z();
      if (rational) {
	SPoles(ipole+3) = surf->Weight(ii,jj);
	SPoles(ipole) *= SPoles(ipole+3);
	SPoles(ipole+1) *= SPoles(ipole+3);
	SPoles(ipole+2) *= SPoles(ipole+3);
      }
      ipole+=gap;
    }
  }
  Standard_Real * Padr = (Standard_Real *) &SPoles(1);
  
  Standard_Boolean periodic_flag = Standard_False ;
  Standard_Integer extrap_mode[2];
  extrap_mode[0] = extrap_mode[1] = Cdeg;
  TColStd_Array1OfReal  EvalBS(1, Cdim * (deriv+1)) ; 
  Standard_Real * Eadr = (Standard_Real *) &EvalBS(1) ;
  BSplCLib::Eval(V,periodic_flag,deriv,extrap_mode[0],
		 Cdeg,FKnots,Cdim,*Padr,*Eadr);

  for (ii=1;ii<=Cdim;ii++) {
    Result(ii) = EvalBS(ii+deriv*Cdim);
  }
}


//=======================================================================
//function : GeomFill_NSections
//purpose  : 
//=======================================================================

GeomFill_NSections::GeomFill_NSections(const TColGeom_SequenceOfCurve& NC)
{
  mySections = NC;
  UFirst = 0.;
  ULast = 1.;
  VFirst = 0.;
  VLast = 1.;
  myRefSurf.Nullify();
  ComputeSurface();
}

//=======================================================================
//function : GeomFill_NSections
//purpose  : 
//=======================================================================

GeomFill_NSections::GeomFill_NSections(const TColGeom_SequenceOfCurve& NC,
				       const TColStd_SequenceOfReal& NP)
{
  mySections = NC;
  myParams = NP;
  UFirst = 0.;
  ULast = 1.;
  VFirst = 0.;
  VLast = 1.;
  myRefSurf.Nullify();
  ComputeSurface();
}

//=======================================================================
//function : GeomFill_NSections
//purpose  :
//=======================================================================
GeomFill_NSections::GeomFill_NSections (const TColGeom_SequenceOfCurve& theNC,
                                        const TColStd_SequenceOfReal& theNP,
                                        const Standard_Real theUF,
                                        const Standard_Real theUL)
{
  mySections = theNC;
  myParams = theNP;
  UFirst = theUF;
  ULast = theUL;
  VFirst = 0.0;
  VLast = 1.0;
  myRefSurf.Nullify();
  ComputeSurface();
}

//=======================================================================
//function : GeomFill_NSections
//purpose  : 
//=======================================================================

GeomFill_NSections::GeomFill_NSections(const TColGeom_SequenceOfCurve& NC,
				       const TColStd_SequenceOfReal& NP,
				       const Standard_Real UF,
				       const Standard_Real UL,
				       const Standard_Real VF,
				       const Standard_Real VL)
{
  mySections = NC;
  myParams = NP;
  UFirst = UF;
  ULast = UL;
  VFirst = VF;
  VLast = VL;
  myRefSurf.Nullify();
  ComputeSurface();
}

//=======================================================================
//function : GeomFill_NSections
//purpose  : 
//=======================================================================

GeomFill_NSections::GeomFill_NSections(const TColGeom_SequenceOfCurve& NC,
				       const GeomFill_SequenceOfTrsf& Trsfs,
				       const TColStd_SequenceOfReal& NP,
				       const Standard_Real UF,
				       const Standard_Real UL,
				       const Standard_Real VF,
				       const Standard_Real VL,
				       const Handle(Geom_BSplineSurface)& Surf)
{
  mySections = NC;
  myTrsfs = Trsfs;
  myParams = NP;
  UFirst = UF;
  ULast = UL;
  VFirst = VF;
  VLast = VL;
  myRefSurf = Surf;
  ComputeSurface();
}

//=======================================================
// Purpose :D0
//=======================================================
 Standard_Boolean GeomFill_NSections::D0(const Standard_Real V,
					      TColgp_Array1OfPnt& Poles,
					      TColStd_Array1OfReal& Weights) 
{
  if (mySurface.IsNull()) {
    return Standard_False;
  }
  else {
    Handle(Geom_BSplineCurve) Curve 
      = Handle(Geom_BSplineCurve)::DownCast(mySurface->VIso( V, Standard_False ));
    TColgp_Array1OfPnt poles(1,mySurface->NbUPoles());
    TColStd_Array1OfReal weights(1,mySurface->NbUPoles());
    Curve->Poles(poles);
    Curve->Weights(weights);
    Standard_Integer ii, L =  Poles.Length();
    for (ii=1; ii<=L; ii++) {
      Poles(ii).SetXYZ(poles(ii).XYZ());
      Weights(ii) = weights(ii);
    }
  }
  return Standard_True;
}

//=======================================================
// Purpose :D1
//=======================================================
 Standard_Boolean GeomFill_NSections::D1(const Standard_Real V,
					      TColgp_Array1OfPnt& Poles,
					      TColgp_Array1OfVec& DPoles,
					      TColStd_Array1OfReal& Weights,
					      TColStd_Array1OfReal& DWeights) 
{
  if (mySurface.IsNull() ) return Standard_False;

  Standard_Boolean ok = D0(V,Poles,Weights);
  if (!ok) return Standard_False;

  Standard_Integer L =  Poles.Length(), derivative_request = 1;
  Standard_Boolean rational = mySurface->IsVRational() ;
  Standard_Integer gap = 3;
  if (rational) gap++;

  Standard_Integer dimResult = mySurface->NbUPoles() * gap;
  Handle(Geom_BSplineSurface) surf_deper;
  if (mySurface->IsVPeriodic()) {
    surf_deper = Handle(Geom_BSplineSurface)::DownCast(mySurface->Copy());
    surf_deper->SetVNotPeriodic();
    dimResult = surf_deper->NbUPoles() * gap;
  }
  TColStd_Array1OfReal Result(1,dimResult);
  if (mySurface->IsVPeriodic()) {
    ResultEval(surf_deper,V,derivative_request,Result);
  }
  else {
    ResultEval(mySurface,V,derivative_request,Result);
  }


  Standard_Real ww, EpsW = 10*Precision::PConfusion();
  Standard_Boolean NullWeight = Standard_False;
  if (!rational) DWeights.Init(0.);
  Standard_Integer indice = 1, ii;

  //  recopie des poles du resultat sous forme de points 3D et de poids
  for (ii=1; ii<=L && (!NullWeight) ; ii++) {
    DPoles(ii).SetX( Result(indice) );
    DPoles(ii).SetY( Result(indice+1) );
    DPoles(ii).SetZ( Result(indice+2) );
    if (rational) {
      ww = Weights(ii);
      if (ww < EpsW) {
        NullWeight = Standard_True;
      }
      else {
        DWeights(ii) = Result(indice+3);
        DPoles(ii)
          .SetXYZ( ( DPoles(ii).XYZ()-DWeights(ii)*Poles(ii).Coord() ) / ww );
      }
    }
    indice += gap;
  }
  if (NullWeight) return Standard_False;

  // verif par diff finies sous debug sauf pour les surfaces periodiques
#ifdef OCCT_DEBUG
  if (!mySurface->IsVPeriodic()) {
    Standard_Real pas = 1.e-6, wTol = 1.e-4, pTol = 1.e-3;
    Standard_Real V1,V2;
    Standard_Boolean ok1,ok2;
    TColStd_Array1OfReal W1(1,L),W2(1,L);
    TColgp_Array1OfPnt P1(1,L),P2(1,L);
    gp_Pnt nul(0.,0.,0.);
    W1.Init(0.);
    W2.Init(0.);
    P1.Init(nul);
    P2.Init(nul);
    
    V1 = V;
    V2 = V+pas;
    ok1 = D0(V1,P1,W1);
    ok2 = D0(V2,P2,W2);
    if (!ok1 || !ok2) std::cout<<"probleme en D0"<<std::endl;
    Standard_Boolean check = verifD1(P1,W1,P2,W2,DPoles,DWeights,pTol,wTol,pas);
    if (!check) std::cout<<"D1 incorrecte en V = "<<V<<std::endl;
  }
#endif
  
  return Standard_True;
}

//=======================================================
// Purpose :D2
//=======================================================
 Standard_Boolean GeomFill_NSections::D2(const Standard_Real V,
                                              TColgp_Array1OfPnt& Poles,
                                              TColgp_Array1OfVec& DPoles,
                                              TColgp_Array1OfVec& D2Poles,
                                              TColStd_Array1OfReal& Weights,
                                              TColStd_Array1OfReal& DWeights,
                                              TColStd_Array1OfReal& D2Weights) 
{ 
  if (mySurface.IsNull() ) return Standard_False;

  // pb dans BSplCLib::Eval() pour les surfaces rationnelles de degre 1
  // si l'ordre de derivation est egal a 2.
  if (mySurface->VDegree()<2) return Standard_False;

  Standard_Boolean ok = D1(V,Poles,DPoles,Weights,DWeights);
  if (!ok) return Standard_False;

  Standard_Integer L =  Poles.Length(), derivative_request = 2;
  Standard_Boolean rational = mySurface->IsVRational() ;
  Standard_Integer gap = 3;
  if (rational) gap++;
  
  Standard_Integer dimResult = mySurface->NbUPoles() * gap;
  Handle(Geom_BSplineSurface) surf_deper;
  if (mySurface->IsVPeriodic()) {
    surf_deper = Handle(Geom_BSplineSurface)::DownCast(mySurface->Copy());
    surf_deper->SetVNotPeriodic();
    dimResult = surf_deper->NbUPoles() * gap;
  }
  TColStd_Array1OfReal Result(1,dimResult);
  if (mySurface->IsVPeriodic()) {
    ResultEval(surf_deper,V,derivative_request,Result);
  }
  else {
    ResultEval(mySurface,V,derivative_request,Result);
  }


  Standard_Real ww, EpsW = 10*Precision::PConfusion();
  Standard_Boolean NullWeight = Standard_False;
  if (!rational) D2Weights.Init(0.);
  Standard_Integer indice = 1, ii;

  //  recopie des poles du resultat sous forme de points 3D et de poids
  for (ii=1; ii<=L && (!NullWeight) ; ii++) {
    D2Poles(ii).SetX( Result(indice) );
    D2Poles(ii).SetY( Result(indice+1) );
    D2Poles(ii).SetZ( Result(indice+2) );
    if (rational) {
      ww = Weights(ii);
      if (ww < EpsW) {
        NullWeight = Standard_True;
      }
      else {
        D2Weights(ii) = Result(indice+3);
        D2Poles(ii)
          .SetXYZ( ( D2Poles(ii).XYZ() - D2Weights(ii)*Poles(ii).Coord()
                    - 2*DWeights(ii)*DPoles(ii).XYZ() ) / ww );
      }
    }
    indice += gap;
  }
  if (NullWeight) return Standard_False;

  // verif par diff finies sous debug sauf pour les surfaces periodiques
#ifdef OCCT_DEBUG
  if (!mySurface->IsVPeriodic()) {
    Standard_Real V1,V2;
    Standard_Boolean ok1,ok2;
    Standard_Real pas = 1.e-6, wTol = 1.e-4, pTol = 1.e-3;
    TColStd_Array1OfReal W1(1,L),W2(1,L),DW1(1,L),DW2(1,L);
    TColgp_Array1OfPnt P1(1,L),P2(1,L);
    TColgp_Array1OfVec DP1(1,L),DP2(1,L);
    gp_Pnt nul(0.,0.,0.);
    gp_Vec Vnul(0.,0.,0.);
    W1.Init(0.);
    W2.Init(0.);
    DW1.Init(0.);
    DW2.Init(0.);
    P1.Init(nul);
    P2.Init(nul);
    DP1.Init(Vnul);
    DP2.Init(Vnul);
    
    V1 = V;
    V2 = V+pas;
    ok1 = D1(V1,P1,DP1,W1,DW1);
    ok2 = D1(V2,P2,DP2,W2,DW2);
    if (!ok1 || !ok2) std::cout<<"probleme en D0 ou en D1"<<std::endl;
    Standard_Boolean check = verifD2(DP1,DW1,DP2,DW2,D2Poles,D2Weights,pTol,wTol,pas);
    if (!check) std::cout<<"D2 incorrecte en V = "<<V<<std::endl;
  }
#endif
  
  return Standard_True;
}

//=======================================================
// Purpose :BSplineSurface()
//=======================================================
 Handle(Geom_BSplineSurface) 
     GeomFill_NSections::BSplineSurface() const
{
  return mySurface;
}


//=======================================================
// Purpose :SetSurface()
//=======================================================
 void GeomFill_NSections::SetSurface(const Handle(Geom_BSplineSurface)& RefSurf)
{
  myRefSurf = RefSurf;
}

//=======================================================
// Purpose :ComputeSurface()
//=======================================================
 void GeomFill_NSections::ComputeSurface()
{

  Handle(Geom_BSplineSurface) BS;
  if (myRefSurf.IsNull()) {

    Standard_Real myPres3d = 1.e-06;
    Standard_Integer i,j,jdeb=1,jfin=mySections.Length();
    
    if (jfin <= jdeb)
    {
      //We will not be able to create surface from single curve.
      return;
    }

    GeomFill_SectionGenerator section;
    Handle(Geom_BSplineSurface) surface;

    for (j=jdeb; j<=jfin; j++) {

        // read the j-th curve
        Handle(Geom_Curve) curv = mySections(j);
        
        // transformation to BSpline reparametrized to [UFirst,ULast]
        Handle(Geom_BSplineCurve) curvBS = Handle(Geom_BSplineCurve)::DownCast (curv);
        if (curvBS.IsNull())
        {
          curvBS = GeomConvert::CurveToBSplineCurve (curv, Convert_QuasiAngular);
        }

        TColStd_Array1OfReal BSK(1,curvBS->NbKnots());
        curvBS->Knots(BSK);
        BSplCLib::Reparametrize(UFirst,ULast,BSK);
        curvBS->SetKnots(BSK);
        
        section.AddCurve(curvBS);        
    }
    
    /*
    if (s2Point) {
      curv =  mySections(jfin+1);
      first =  curv->FirstParameter();
      last = curv->LastParameter();
      TColgp_Array1OfPnt Extremities(1,2);
      Extremities(1) = curv->Value(first);
      Extremities(2) = curv->Value(last);
      TColStd_Array1OfReal Bounds(1,2);
      Bounds(1) = UFirst;
      Bounds(2) = ULast;
      Standard_Real Deg = 1;
      TColStd_Array1OfInteger Mult(1,2);
      Mult(1) = (Standard_Integer ) Deg+1;
      Mult(2) = (Standard_Integer ) Deg+1;
      Handle(Geom_BSplineCurve) BSPoint
        = new Geom_BSplineCurve(Extremities,Bounds,Mult,(Standard_Integer ) Deg);
      section.AddCurve(BSPoint);
    }*/

    Standard_Integer Nbcurves = mySections.Length();
    Standard_Integer Nbpar = myParams.Length();
    if (Nbpar > 0)
    {
      Handle(TColStd_HArray1OfReal) HPar
        = new TColStd_HArray1OfReal(1, Nbpar);
      for (i = 1; i <= Nbpar; i++) {
        HPar->SetValue(i, myParams(i));
      }
      section.SetParam(HPar);
    }
    section.Perform(Precision::PConfusion());
    
    Handle(GeomFill_Line) line = new GeomFill_Line(Nbcurves);
    Standard_Integer nbIt = 0, degmin = 2, degmax = 6;
    Standard_Boolean knownP = Nbpar > 0;
    GeomFill_AppSurf anApprox(degmin, degmax, myPres3d, myPres3d, nbIt, knownP);
    anApprox.SetContinuity(GeomAbs_C1);
    Standard_Boolean SpApprox = Standard_True;
    anApprox.Perform(line, section, SpApprox);

    BS = 
      new Geom_BSplineSurface(anApprox.SurfPoles(), anApprox.SurfWeights(),
                              anApprox.SurfUKnots(), anApprox.SurfVKnots(),
                              anApprox.SurfUMults(), anApprox.SurfVMults(),
                              anApprox.UDegree(), anApprox.VDegree(), 
                              section.IsPeriodic());
  }

  else {
  
    // segmentation de myRefSurf
    Standard_Real Ui1, Ui2, V0, V1;
    BS = Handle(Geom_BSplineSurface)::DownCast(myRefSurf->Copy());
    Ui1 = UFirst;
    Ui2 = ULast;
    Standard_Integer i1, i2;
    myRefSurf->LocateU( Ui1, Precision::PConfusion(), i1, i2 );
    if (Abs(Ui1 - myRefSurf->UKnot(i1)) <= Precision::PConfusion())
      Ui1 = myRefSurf->UKnot(i1);
    if (Abs(Ui1 - myRefSurf->UKnot(i2)) <= Precision::PConfusion())
      Ui1 = myRefSurf->UKnot(i2);
    myRefSurf->LocateU( Ui2, Precision::PConfusion(), i1, i2 );
    if (Abs(Ui2 - myRefSurf->UKnot(i1)) <= Precision::PConfusion())
      Ui2 = myRefSurf->UKnot(i1);
    if (Abs(Ui2 - myRefSurf->UKnot(i2)) <= Precision::PConfusion())
      Ui2 = myRefSurf->UKnot(i2);
    V0  = myRefSurf->VKnot(myRefSurf->FirstVKnotIndex());
    V1  = myRefSurf->VKnot(myRefSurf->LastVKnotIndex());
    BS->CheckAndSegment(Ui1,Ui2,V0,V1);
  }
  mySurface = BS;
  // On augmente le degre pour que le positionnement D2 soit correct 
  if (mySurface->VDegree()<2) {
    mySurface->IncreaseDegree(mySurface->UDegree(),2);
  }
#ifdef OCCT_DEBUG
  NbSurf++;
  if (Affich) {
#ifdef DRAW
    char name[256];
    sprintf(name,"NS_Surf_%d",NbSurf);
    DrawTrSurf::Set(name,BS);
    std::cout<<std::endl<<"RESULTAT de ComputeSurface : NS_Surf_"<<NbSurf<<std::endl<<std::endl;
#endif
  }
#endif
}

//=======================================================
// Purpose :SectionShape
//=======================================================
 void GeomFill_NSections::SectionShape(Standard_Integer& NbPoles,
                                            Standard_Integer& NbKnots,
                                            Standard_Integer& Degree) const
{
  if (mySurface.IsNull())
    return;
  
  NbPoles = mySurface->NbUPoles();
  NbKnots = mySurface->NbUKnots();
  Degree  = mySurface->UDegree();
}

//=======================================================
// Purpose :Knots
//=======================================================
 void GeomFill_NSections::Knots(TColStd_Array1OfReal& TKnots) const
{
  if (!mySurface.IsNull())
    mySurface->UKnots(TKnots);
}

//=======================================================
// Purpose :Mults
//=======================================================
 void GeomFill_NSections::Mults(TColStd_Array1OfInteger& TMults) const
{
  if (!mySurface.IsNull())
    mySurface->UMultiplicities(TMults);
}


//=======================================================
// Purpose :IsRational
//=======================================================
 Standard_Boolean GeomFill_NSections::IsRational() const
{
  if (!mySurface.IsNull())
    return mySurface->IsURational();

  return Standard_False;
}

//=======================================================
// Purpose :IsUPeriodic
//=======================================================
 Standard_Boolean GeomFill_NSections::IsUPeriodic() const
{
  if (!mySurface.IsNull())
    return  mySurface->IsUPeriodic();

  return Standard_False;
}

//=======================================================
// Purpose :IsVPeriodic
//=======================================================
 Standard_Boolean GeomFill_NSections::IsVPeriodic() const
{
  if (!mySurface.IsNull())
    return  mySurface->IsVPeriodic();

  return Standard_False;
}

//=======================================================
// Purpose :NbIntervals
//=======================================================
 Standard_Integer GeomFill_NSections::NbIntervals(const GeomAbs_Shape S) const
{
  if (mySurface.IsNull())
    return 0;

  GeomAdaptor_Surface AdS(mySurface);
  return AdS.NbVIntervals(S);
}


//=======================================================
// Purpose :Intervals
//=======================================================
 void GeomFill_NSections::Intervals(TColStd_Array1OfReal& T,
                                         const GeomAbs_Shape S) const
{
  if (mySurface.IsNull())
    return;

  GeomAdaptor_Surface AdS(mySurface);
  AdS.VIntervals(T,S);
}


//=======================================================
// Purpose : SetInterval
//=======================================================
// void GeomFill_NSections::SetInterval(const Standard_Real F,
 void GeomFill_NSections::SetInterval(const Standard_Real ,
//                                           const Standard_Real L) 
                                           const Standard_Real ) 
{
  // rien a faire : mySurface est supposee Cn en V
}

//=======================================================
// Purpose : GetInterval
//=======================================================
 void GeomFill_NSections::GetInterval(Standard_Real& F,
                                           Standard_Real& L) const
{
  F = VFirst;
  L = VLast;
}

//=======================================================
// Purpose : GetDomain
//=======================================================
 void GeomFill_NSections::GetDomain(Standard_Real& F,
                                         Standard_Real& L) const
{
  F = VFirst;
  L = VLast;
}

//=======================================================
// Purpose : GetTolerance
//=======================================================
 void GeomFill_NSections::GetTolerance(const Standard_Real BoundTol,
                                            const Standard_Real SurfTol,
//                                            const Standard_Real AngleTol,
                                            const Standard_Real ,
                                            TColStd_Array1OfReal& Tol3d) const
{
  Tol3d.Init(SurfTol);
  if (BoundTol<SurfTol) {
    Tol3d(Tol3d.Lower()) = BoundTol;
    Tol3d(Tol3d.Upper()) = BoundTol;
  }
}

//=======================================================
// Purpose : BarycentreOfSurf
//=======================================================
 gp_Pnt GeomFill_NSections::BarycentreOfSurf() const
{
  gp_Pnt P, Bary;
  Bary.SetCoord(0., 0., 0.);

  if (mySurface.IsNull())
    return Bary;

  Standard_Integer ii,jj;
  Standard_Real U0, U1, V0, V1;
  mySurface->Bounds(U0,U1,V0,V1);
  Standard_Real V = V0, DeltaV = ( V1 - V0 ) / 20;
  Standard_Real U = U0, DeltaU = ( U1 - U0 ) / 20;
  for(jj=0;jj<=20;jj++,V+=DeltaV) {
    for (ii=0 ; ii <=20; ii++, U+=DeltaU) {
      P = mySurface->Value(U,V);
      Bary.ChangeCoord() += P.XYZ();
    } 
  }
    
  Bary.ChangeCoord() /= (21*21);
  return Bary;
}


//=======================================================
// Purpose : MaximalSection
//=======================================================
 Standard_Real GeomFill_NSections::MaximalSection() const
{
  Standard_Real L, Lmax=0.;
  Standard_Integer ii;
  for (ii=1; ii <=mySections.Length(); ii++) {
    GeomAdaptor_Curve AC (mySections(ii));
    L = GCPnts_AbscissaPoint::Length(AC);
    if (L>Lmax) Lmax = L;
  }
  return Lmax;
}


//=======================================================
// Purpose : GetMinimalWeight
//=======================================================
void GeomFill_NSections::GetMinimalWeight(TColStd_Array1OfReal& Weights) const
{
  if (mySurface.IsNull())
    return;

  if (mySurface->IsURational()) {
    Standard_Integer NbU = mySurface->NbUPoles(),
                     NbV = mySurface->NbVPoles();
    TColStd_Array2OfReal WSurf(1,NbU,1,NbV);
    mySurface->Weights(WSurf);
    Standard_Integer i,j;
    for (i=1;i<=NbU;i++) {
      Standard_Real min = WSurf(i,1);
      for (j=2;j<=NbV;j++) {
        if (min> WSurf(i,j)) min = WSurf(i,j);
      }
      Weights.SetValue(i,min);
    }
  }
  else {
    Weights.Init(1);
  }
  
}


//=======================================================
// Purpose : IsConstant
//=======================================================
 Standard_Boolean GeomFill_NSections::IsConstant(Standard_Real& Error) const
{
  // on se limite a 2 sections
  Standard_Boolean isconst = (mySections.Length()==2);
  Standard_Real Err = 0.;

  if (isconst) {
    GeomAdaptor_Curve AC1(mySections(1));
    GeomAbs_CurveType CType = AC1.GetType();
    GeomAdaptor_Curve AC2(mySections(2));
    // les sections doivent avoir le meme type
    isconst = ( AC2.GetType() == CType);

    if (isconst) {
      if (CType == GeomAbs_Circle) {
        gp_Circ C1 = AC1.Circle();
        gp_Circ C2 = AC2.Circle();
        Standard_Real Tol = 1.e-7;
        Standard_Boolean samedir, samerad, samepos;
        samedir = (C1.Axis().IsParallel(C2.Axis(),1.e-4));
        samerad = (Abs(C1.Radius()-C2.Radius())<Tol);
        samepos = (C1.Location().Distance(C2.Location())<Tol);
        if (!samepos) {
          gp_Ax1 D(C1.Location(),gp_Vec(C1.Location(),C2.Location()));
          samepos = (C1.Axis().IsParallel(D,1.e-4));
        }
        isconst = samedir && samerad && samepos;
      }
      else if (CType == GeomAbs_Line) {
        gp_Lin L1 = AC1.Line();
        gp_Lin L2 = AC2.Line(); 
        Standard_Real Tol = 1.e-7;
        Standard_Boolean samedir, samelength, samepos;
        samedir = (L1.Direction().IsParallel(L2.Direction(),1.e-4));
        gp_Pnt P11 = AC1.Value(AC1.FirstParameter()),
               P12 = AC1.Value(AC1.LastParameter()),
               P21 = AC2.Value(AC2.FirstParameter()),
               P22 = AC2.Value(AC2.LastParameter());
        samelength = (Abs(P11.Distance(P12)-P21.Distance(P22))<Tol);
        // l'ecart entre les 2 sections ne compte pas
        samepos = ( ( P11.Distance(P21)<Tol && P12.Distance(P22)<Tol )
                     || ( P12.Distance(P21)<Tol && P11.Distance(P22)<Tol ) );
        //samepos = Standard_True;
        isconst = samedir && samelength && samepos;
      }
      else {
        isconst = Standard_False;
      }
    }

  }

  Error = Err;
  return isconst;
}


//=======================================================
// Purpose : ConstantSection
//=======================================================
 Handle(Geom_Curve) GeomFill_NSections::ConstantSection() const
{
//  Standard_Real Err;
//  if (!IsConstant(Err)) throw StdFail_NotDone("The Law is not Constant!");
  Handle(Geom_Curve) C;
  C = Handle(Geom_Curve)::DownCast( mySections(1)->Copy());
  return C;
}


//=======================================================
// Purpose : IsConicalLaw
//=======================================================
 Standard_Boolean GeomFill_NSections::IsConicalLaw(Standard_Real& Error) const
{
  Standard_Boolean isconic = (mySections.Length()==2);
  Standard_Real Err = 0.;
  if (isconic) {
    GeomAdaptor_Curve AC1(mySections(1));
    GeomAdaptor_Curve AC2(mySections(2));
    isconic = ( AC1.GetType() == GeomAbs_Circle )
                     &&  ( AC2.GetType() == GeomAbs_Circle ) ;
    if (isconic) {
      gp_Circ C1 = AC1.Circle();
      if (!myTrsfs.IsEmpty())
        C1.Transform(myTrsfs(1).Inverted());
      gp_Circ C2 = AC2.Circle();
      if (!myTrsfs.IsEmpty())
        C2.Transform(myTrsfs(2).Inverted());
      Standard_Real Tol = 1.e-7;
      //Standard_Boolean samedir, linearrad, sameaxis;
      isconic = (C1.Axis().IsParallel(C2.Axis(),1.e-4));
      //  pour 2 sections, la variation du rayon est forcement lineaire
      //linearrad = Standard_True;
      //  formule plus generale pour 3 sections au moins
      //  Standard_Real param0 = C2.Radius()*myParams(1) - C1.Radius()*myParams(2);
      //  param0 = param0 / (C2.Radius()-C1.Radius()) ;
      //  linearrad = ( Abs( C3.Radius()*myParams(1)-C1.Radius()*myParams(3)
      //                          - param0*(C3.Radius()-C1.Radius()) ) < Tol);
      if (isconic)
      {
        gp_Lin Line1(C1.Axis());
        isconic = (Line1.Distance(C2.Location()) < Tol);
        /*
        sameaxis = (C1.Location().Distance(C2.Location())<Tol);
        if (!sameaxis) {
        gp_Ax1 D(C1.Location(),gp_Vec(C1.Location(),C2.Location()));
        sameaxis = (C1.Axis().IsParallel(D,1.e-4));
        }
        isconic = samedir && linearrad && sameaxis;
        */
        if (isconic)
        {
          //// Modified by jgv, 18.02.2009 for OCC20866 ////
          Standard_Real first1 = AC1.FirstParameter(), last1 = AC1.LastParameter();
          Standard_Real first2 = AC2.FirstParameter(), last2 = AC2.LastParameter();
          isconic = (Abs(first1-first2) <= Precision::PConfusion() &&
                     Abs(last1-last2)   <= Precision::PConfusion());
          //////////////////////////////////////////////////
        }
      }
    }
  }

  Error = Err;
  return isconic;
}


//=======================================================
// Purpose : CirclSection
//=======================================================
 Handle(Geom_Curve) GeomFill_NSections::CirclSection(const Standard_Real V) const
{
  Standard_Real Err;
  if (!IsConicalLaw(Err)) throw StdFail_NotDone("The Law is not Conical!");

  GeomAdaptor_Curve AC1(mySections(1));
  GeomAdaptor_Curve AC2(mySections(mySections.Length()));
  gp_Circ C1 = AC1.Circle();
  gp_Circ C2 = AC2.Circle();

  Standard_Real p1 = myParams(1), p2 = myParams(myParams.Length());
  Standard_Real radius = ( C2.Radius() - C1.Radius() ) * (V - p1) / (p2 - p1) 
                                  + C1.Radius();

  C1.SetRadius(radius);
  Handle(Geom_Curve) C = new (Geom_Circle) (C1);

  const Standard_Real aParF = AC1.FirstParameter();
  const Standard_Real aParL = AC1.LastParameter();
  const Standard_Real aPeriod = AC1.IsPeriodic() ? AC1.Period() : 0.0;

  if ((aPeriod == 0.0) || (Abs(aParL - aParF - aPeriod) > Precision::PConfusion()))
  {
    Handle(Geom_Curve) Cbis = new Geom_TrimmedCurve(C, aParF, aParL);
    C = Cbis;
  }
  return C;
}
