// File:	GeomAPI_PointsToBSplineSurface.cxx
// Created:	Tue Jan 17 17:02:05 1995
// Author:	Remi LEQUETTE
//		<rle@bravox>


#include <GeomAPI_PointsToBSplineSurface.ixx>

#include <Geom_BSplineCurve.hxx>
#include <GeomFill_SectionGenerator.hxx>
#include <GeomFill_Line.hxx>
#include <GeomFill_AppSurf.hxx>
#include <GeomAPI_PointsToBSpline.hxx>
#include <AppDef_BSplineCompute.hxx>
#include <AppDef_MultiLine.hxx>
#include <AppDef_MultiPointConstraint.hxx>
#include <BSplCLib.hxx>
#include <Precision.hxx>
#include <gp_Pnt.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <math_Vector.hxx>


//=======================================================================
//function : GeomAPI_PointsToBSplineSurface
//purpose  : 
//=======================================================================

GeomAPI_PointsToBSplineSurface::GeomAPI_PointsToBSplineSurface()
: myIsDone ( Standard_False)
{
}


//=======================================================================
//function : GeomAPI_PointsToBSplineSurface
//purpose  : 
//=======================================================================

GeomAPI_PointsToBSplineSurface::GeomAPI_PointsToBSplineSurface
(const TColgp_Array2OfPnt& Points, 
 const Standard_Integer DegMin,
 const Standard_Integer DegMax,
 const GeomAbs_Shape Continuity, 
 const Standard_Real Tol3D)
{
  Init(Points,DegMin,DegMax,Continuity,Tol3D);
}



//=======================================================================
//function : GeomAPI_PointsToBSplineSurface
//purpose  : 
//=======================================================================

GeomAPI_PointsToBSplineSurface::GeomAPI_PointsToBSplineSurface
(const TColStd_Array2OfReal& ZPoints, 
 const Standard_Real X0, 
 const Standard_Real dX, 
 const Standard_Real Y0,
 const Standard_Real dY, 
 const Standard_Integer DegMin, 
 const Standard_Integer DegMax, 
 const GeomAbs_Shape Continuity, 
 const Standard_Real Tol3D)
{
  Init(ZPoints,X0,dX,Y0,dY,DegMin,DegMax,Continuity,Tol3D);
}



//=======================================================================
//function : Init
//purpose  : 
//=======================================================================

void GeomAPI_PointsToBSplineSurface::Interpolate(const TColgp_Array2OfPnt& Points)
{
  Standard_Integer DegMin, DegMax;
  DegMin = DegMax = 3;
  GeomAbs_Shape CC  = GeomAbs_C2;
  Standard_Real Tol3d = -1.0;
  Init(Points, DegMin, DegMax, CC, Tol3d);
}



//=======================================================================
//function : Init
//purpose  : 
//=======================================================================

void GeomAPI_PointsToBSplineSurface::Init(const TColgp_Array2OfPnt& Points,
					  const Standard_Integer DegMin, 
					  const Standard_Integer DegMax, 
					  const GeomAbs_Shape Continuity,
					  const Standard_Real Tol3D)
{
  Standard_Integer Imin = Points.LowerRow();
  Standard_Integer Imax = Points.UpperRow();
  Standard_Integer Jmin = Points.LowerCol();
  Standard_Integer Jmax = Points.UpperCol();

  Standard_Real Tol2D = Tol3D;

  // first approximate the V isos:
  AppDef_MultiLine Line(Jmax-Jmin+1);
  Standard_Integer i, j;
//  Standard_Real X, Y;

  for (j = Jmin; j <= Jmax; j++) {
    AppDef_MultiPointConstraint MP(Imax-Imin+1, 0);
    for (i = Imin; i <= Imax; i++) {
      MP.SetPoint(i, Points(i,j));
    }
    Line.SetValue(j, MP);
  }

  AppDef_BSplineCompute TheComputer
    (DegMin,DegMax,Tol3D,Tol2D,0, Standard_True);

  switch( Continuity) {
  case GeomAbs_C0:
    TheComputer.SetContinuity(0); break;

  case GeomAbs_G1: 
  case GeomAbs_C1: 
    TheComputer.SetContinuity(1); break;

  case GeomAbs_G2:
  case GeomAbs_C2:
    TheComputer.SetContinuity(2); break;

  default: 
    TheComputer.SetContinuity(3);
  }
  
  if (Tol3D <= 0.0) {
    TheComputer.Interpol(Line);
  }
  else {
    TheComputer.Perform(Line);
  }

  const AppParCurves_MultiBSpCurve& TheCurve = TheComputer.Value();
  
  Standard_Integer VDegree = TheCurve.Degree();
  TColgp_Array1OfPnt Poles(1,TheCurve.NbPoles());
  const TColStd_Array1OfReal& VKnots = TheCurve.Knots();
  const TColStd_Array1OfInteger& VMults = TheCurve.Multiplicities();
  

  Standard_Integer nbisosu = Imax-Imin+1;
  AppDef_MultiLine Line2(nbisosu);

  for (i = 1; i <= nbisosu; i++) {
    TheCurve.Curve(i, Poles);
    AppDef_MultiPointConstraint MP(Poles.Upper(),0);
    for (j = 1; j <= Poles.Upper(); j++) {
      MP.SetPoint(j, Poles(j));
    }
    Line2.SetValue(i, MP);
  }


  // approximate the resulting poles:
  
  AppDef_BSplineCompute TheComputer2
    (DegMin,DegMax,Tol3D,Tol2D,0, Standard_True);
  if (Tol3D <= 0.0) {
    TheComputer2.Interpol(Line2);
  }
  else {
    TheComputer2.Perform(Line2);
  }
  
  const AppParCurves_MultiBSpCurve& TheCurve2 = TheComputer2.Value();
  
  Standard_Integer UDegree = TheCurve2.Degree();
  TColgp_Array1OfPnt Poles2(1,TheCurve2.NbPoles());
  const TColStd_Array1OfReal& UKnots = TheCurve2.Knots();
  const TColStd_Array1OfInteger& UMults = TheCurve2.Multiplicities();
  

  // computing the surface
  TColgp_Array2OfPnt ThePoles(1, Poles2.Upper(), 1, Poles.Upper());
  for ( j = 1; j <= Poles.Upper(); j++) {
    TheCurve2.Curve(j, Poles2);
    for (i = 1; i<= Poles2.Upper(); i++) {
      ThePoles(i, j) = Poles2(i);
    }
  }


  mySurface = new Geom_BSplineSurface(ThePoles, UKnots, VKnots, UMults, VMults,
				      UDegree, VDegree);

  myIsDone = Standard_True;
}


//=======================================================================
//function : Init
//purpose  : 
//=======================================================================

void GeomAPI_PointsToBSplineSurface::Interpolate(const TColStd_Array2OfReal& ZPoints,
						 const Standard_Real X0, 
						 const Standard_Real dX, 
						 const Standard_Real Y0, 
						 const Standard_Real dY)
{
  Standard_Integer DegMin, DegMax;
  DegMin = DegMax = 3;
  Standard_Real Tol3D  = -1.0;
  GeomAbs_Shape CC = GeomAbs_C2;
  Init(ZPoints, X0, dX, Y0, dY, DegMin, DegMax, CC, Tol3D);
}

//=======================================================================
//function : Init
//purpose  : 
//=======================================================================

void GeomAPI_PointsToBSplineSurface::Init(const TColStd_Array2OfReal& ZPoints,
					  const Standard_Real X0, 
					  const Standard_Real dX, 
					  const Standard_Real Y0, 
					  const Standard_Real dY, 
					  const Standard_Integer DegMin,
					  const Standard_Integer DegMax,
					  const GeomAbs_Shape Continuity,
					  const Standard_Real Tol3D)
{
  Standard_Integer Imin = ZPoints.LowerRow();
  Standard_Integer Imax = ZPoints.UpperRow();
  Standard_Integer Jmin = ZPoints.LowerCol();
  Standard_Integer Jmax = ZPoints.UpperCol();
  Standard_Real length;

  Standard_Real Tol2D = Tol3D;

  // first approximate the V isos:
  AppDef_MultiLine Line(Jmax-Jmin+1);
  math_Vector Param(Jmin, Jmax);
  Standard_Integer i, j;
//  Standard_Real X, Y;
  length = dY * (Jmax-Jmin);

  for (j = Jmin; j <= Jmax; j++) {
    AppDef_MultiPointConstraint MP(0, Imax-Imin+1);
    for (i = Imin; i <= Imax; i++) {
      MP.SetPoint2d(i, gp_Pnt2d(0.0, ZPoints(i, j)));
    }
    Param(j) = (Standard_Real)(j-Jmin)/(Standard_Real)(Jmax-Jmin);
    Line.SetValue(j, MP);
  }

  AppDef_BSplineCompute TheComputer
    (Param, DegMin,DegMax,Tol3D,Tol2D,0, Standard_True, Standard_True);

  switch( Continuity) {
  case GeomAbs_C0:
    TheComputer.SetContinuity(0); break;

  case GeomAbs_G1: 
  case GeomAbs_C1: 
    TheComputer.SetContinuity(1); break;

  case GeomAbs_G2:
  case GeomAbs_C2:
    TheComputer.SetContinuity(2); break;

  default: 
    TheComputer.SetContinuity(3);
  }
  
  if (Tol3D <= 0.0) {
    TheComputer.Interpol(Line);
  }
  else {
    TheComputer.Perform(Line);
  }


  const AppParCurves_MultiBSpCurve& TheCurve = TheComputer.Value();
  
  Standard_Integer VDegree = TheCurve.Degree();
  TColgp_Array1OfPnt2d Poles(1,TheCurve.NbPoles());
  Standard_Integer nk = TheCurve.Knots().Length();
  TColStd_Array1OfReal VKnots(1,nk);
  TColStd_Array1OfInteger VMults(1,nk);
  

  // compute Y values for the poles
  TColStd_Array1OfReal YPoles(1,Poles.Upper());
  
  // start with a line
  TColStd_Array1OfReal    TempPoles(1,2);
  TColStd_Array1OfReal    TempKnots(1,2);
  TColStd_Array1OfInteger TempMults(1,2);
  TempMults.Init(2);
  TempPoles(1) = Y0;
  TempPoles(2) = Y0 + length;
  TempKnots(1) = 0.;
  TempKnots(2) = 1.;
  
  // increase the Degree
  TColStd_Array1OfReal    NewTempPoles(1,VDegree+1);
  TColStd_Array1OfReal    NewTempKnots(1,2);
  TColStd_Array1OfInteger NewTempMults(1,2);
  BSplCLib::IncreaseDegree(1,VDegree,Standard_False,1,
			   TempPoles,TempKnots,TempMults,
			   NewTempPoles,NewTempKnots,NewTempMults);
  
  
  // insert the Knots
  BSplCLib::InsertKnots(VDegree,Standard_False,1,
			NewTempPoles,NewTempKnots,NewTempMults,
			TheCurve.Knots(),TheCurve.Multiplicities(),
			YPoles,VKnots,VMults,
			Epsilon(1));
  
  // scale the knots
  for (i = 1; i <= nk; i++) {
    VKnots(i) = Y0 + length * VKnots(i);
  }
  

  Standard_Integer nbisosu = Imax-Imin+1;
  AppDef_MultiLine Line2(nbisosu);
  math_Vector Param2(1, nbisosu);
  length = dX*(Imax-Imin);

  for (i = 1; i <= nbisosu; i++) {
    TheCurve.Curve(i, Poles);
    AppDef_MultiPointConstraint MP(0, Poles.Upper());
    for (j = 1; j <= Poles.Upper(); j++) {
      MP.SetPoint2d(j, gp_Pnt2d(0.0, Poles(j).Y()));
    }
    Param2(i) = (Standard_Real)(i-1)/(Standard_Real)(nbisosu-1);
    Line2.SetValue(i, MP);
  }


  // approximate the resulting poles:
  
  AppDef_BSplineCompute TheComputer2
    (Param2, DegMin,DegMax,Tol3D,Tol2D,0, Standard_True, Standard_True);
  if (Tol3D <= 0.0) {
    TheComputer2.Interpol(Line2);
  }
  else {
    TheComputer2.Perform(Line2);
  }
  
  const AppParCurves_MultiBSpCurve& TheCurve2 = TheComputer2.Value();
  
  Standard_Integer UDegree = TheCurve2.Degree();
  TColgp_Array1OfPnt2d Poles2(1,TheCurve2.NbPoles());
  Standard_Integer nk2 = TheCurve2.Knots().Length();
  TColStd_Array1OfReal UKnots(1,nk2);
  TColStd_Array1OfInteger UMults(1,nk2);
  

  // compute X values for the poles
  TColStd_Array1OfReal XPoles(1,Poles2.Upper());
  
  // start with a line
  TempPoles(1) = X0;
  TempPoles(2) = X0 + length;
  TempKnots(1) = 0.;
  TempKnots(2) = 1.;
  TempMults(1) = TempMults(2) = 2;
  
  // increase the Degree
  TColStd_Array1OfReal    NewTempPoles2(1,UDegree+1);
  BSplCLib::IncreaseDegree(1,UDegree,Standard_False,1,
			   TempPoles,TempKnots,TempMults,
			   NewTempPoles2,NewTempKnots,NewTempMults);
  
  
  // insert the Knots
  BSplCLib::InsertKnots(UDegree,Standard_False,1,
			NewTempPoles2,NewTempKnots,NewTempMults,
			TheCurve2.Knots(),TheCurve2.Multiplicities(),
			XPoles,UKnots,UMults,
			Epsilon(1));
  
  // scale the knots
  for (i = 1; i <= nk2; i++) {
    UKnots(i) = X0 + length * UKnots(i);
  }
  
  // creating the surface
  TColgp_Array2OfPnt ThePoles(1, Poles2.Upper(), 1, Poles.Upper());

  for ( j = 1; j <= Poles.Upper(); j++) {
    TheCurve2.Curve(j, Poles2);
    for (i = 1; i<= Poles2.Upper(); i++) {
      ThePoles(i, j).SetCoord(XPoles(i), YPoles(j), Poles2(i).Y());
    }
  }


  mySurface = new Geom_BSplineSurface(ThePoles, UKnots, VKnots, UMults, VMults,
				      UDegree, VDegree);

  myIsDone = Standard_True;
  
}


//=======================================================================
//function : Handle_Geom_BSplineSurface&
//purpose  : 
//=======================================================================

const Handle(Geom_BSplineSurface)& GeomAPI_PointsToBSplineSurface::Surface()
const 
{
  StdFail_NotDone_Raise_if
    ( !myIsDone,
     "GeomAPI_PointsToBSplineSurface: Surface not done");

  return mySurface;
}



//=======================================================================
//function : Handle
//purpose  : 
//=======================================================================

GeomAPI_PointsToBSplineSurface::operator Handle(Geom_BSplineSurface)() const
{
  return Surface();
}


