// File:	HLRBRep_Curve.gxx
// Created:	Fri Mar 13 11:08:32 1992
// Author:	Christophe MARION
//		<cma@sdsun2>

#include <HLRBRep_Curve.ixx>
#include <gp.hxx>
#include <gp_Ax3.hxx>
#include <gp_Pln.hxx>
#include <Precision.hxx>
#include <ProjLib.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <HLRAlgo.hxx>
#include <HLRAlgo_Projector.hxx>
#include <HLRBRep_CLProps.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_BezierCurve.hxx>
#include <Handle_Geom_BSplineCurve.hxx>
#include <Handle_Geom_BezierCurve.hxx>

#define OCC155 // jfa 05.03.2002 // bad vectors projection

//=======================================================================
//function : HLRBRep_Curve
//purpose  : 
//=======================================================================

HLRBRep_Curve::HLRBRep_Curve ()
{}

//=======================================================================
//function : Curve
//purpose  : 
//=======================================================================

void HLRBRep_Curve::Curve (const TopoDS_Edge& E)
{ myCurve.Initialize(E); }

//=======================================================================
//function : Parameter2d
//purpose  : 
//=======================================================================

Standard_Real 
HLRBRep_Curve::Parameter2d (const Standard_Real P3d) const
{

  // Mathematical formula for lines

  //        myOF P3d (myOF myVX - myOZ myVX + myOX myVZ)
  // P2d -> --------------------------------------------
  //        (-myOF + myOZ) (-myOF + myOZ + P3d myVZ)

  Standard_Real P2d;
  if (myType == GeomAbs_Line) {
    if (((HLRAlgo_Projector*) myProj)->Perspective()) {
      Standard_Real FmOZ = myOF - myOZ;
      P2d = myOF * P3d * (myVX * FmOZ + myOX * myVZ) /
	(FmOZ * (FmOZ - P3d * myVZ));
    }
    else P2d = P3d * myVX;
  }

  else if (myType == GeomAbs_Ellipse) {
    P2d = P3d + myOX;
  }

  else P2d = P3d;
  return P2d;
}

//=======================================================================
//function : Parameter3d
//purpose  : 
//=======================================================================

Standard_Real
HLRBRep_Curve::Parameter3d (const Standard_Real P2d) const
{

  // Mathematical formula for lines

  //                                 2   
  //                   (-myOF + myOZ)  P2d
  // P3d -> -----------------------------------------------------
  //        (myOF - myOZ) (myOF myVX + P2d myVZ) + myOF myOX myVZ

  Standard_Real P3d;
  if (myType == GeomAbs_Line) {
    if (((HLRAlgo_Projector*) myProj)->Perspective()) {
      Standard_Real FmOZ = myOF - myOZ;
      P3d = P2d * FmOZ * FmOZ /
	(FmOZ * (myOF * myVX + P2d * myVZ) + myOF * myOX * myVZ);
    }
    else P3d = P2d / myVX;
  }

  else if (myType == GeomAbs_Ellipse) {
    P3d = P2d - myOX;
  }

  else P3d = P2d;
  return P3d;
}

//=======================================================================
//function : Update
//purpose  : 
//=======================================================================

Standard_Real  HLRBRep_Curve::Update (const Standard_Address TotMin,
				      const Standard_Address TotMax)
{
  GeomAbs_CurveType typ = HLRBRep_BCurveTool::GetType(myCurve);
  myType = GeomAbs_OtherCurve;

  switch (typ) {

  case GeomAbs_Line:
    myType = typ;
    break;

  case GeomAbs_Circle:
    if (!((HLRAlgo_Projector*) myProj)->Perspective()) {
      gp_Dir D1 = HLRBRep_BCurveTool::Circle(myCurve).Axis().Direction();
      D1.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
      if (D1.IsParallel(gp::DZ(),Precision::Angular()))
	myType = GeomAbs_Circle;
      else if (Abs(D1.Dot(gp::DZ())) < Precision::Angular())
	myType = GeomAbs_OtherCurve;
      else {
	myType = GeomAbs_Ellipse;
	// compute the angle offset
	gp_Dir D3 = D1.Crossed(gp::DZ());
	gp_Dir D2 = HLRBRep_BCurveTool::Circle(myCurve).XAxis().Direction();
	D2.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
	myOX = D3.AngleWithRef(D2,D1);
      }
    }
    break;

  case GeomAbs_Ellipse:
    if (!((HLRAlgo_Projector*) myProj)->Perspective()) {
      gp_Dir D1 = HLRBRep_BCurveTool::Ellipse(myCurve).Axis().Direction();
      D1.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
      if (D1.IsParallel(gp::DZ(),Precision::Angular())) {
	myOX = 0.;    // no offset on the angle
	myType = GeomAbs_Ellipse;
      }
    }
    break;

  case GeomAbs_BezierCurve:
    if (HLRBRep_BCurveTool::Degree(myCurve) == 1)
      myType = GeomAbs_Line;
    else if (!((HLRAlgo_Projector*) myProj)->Perspective())
      myType = typ;
    break;

  case GeomAbs_BSplineCurve:
    if (!((HLRAlgo_Projector*) myProj)->Perspective())
      myType = typ;
    break;

  default:
    break;
  }

  if (myType == GeomAbs_Line) {
    // compute the values for a line
    gp_Lin L;
    Standard_Real l3d = 1.; // length of the 3d bezier curve
    if (HLRBRep_BCurveTool::GetType(myCurve) == GeomAbs_Line) {
      L = HLRBRep_BCurveTool::Line(myCurve);
    }
    else {  // bezier degree 1
      gp_Pnt PL;
      gp_Vec VL;
      HLRBRep_BCurveTool::D1(myCurve,0,PL,VL);
      L = gp_Lin(PL,VL);
      l3d = PL.Distance(HLRBRep_BCurveTool::Value(myCurve,1.));
    }
    gp_Pnt P = L.Location();
    gp_Vec V = L.Direction();
    P.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
    V.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
    if (((HLRAlgo_Projector*) myProj)->Perspective()) {
      gp_Pnt2d F;
      gp_Vec2d VFX;
      D1(0.,F,VFX);
      VFX.Normalize();
      myVX = (VFX.X()*V.X()+VFX.Y()*V.Y()) * l3d;
      Standard_Real l = - (VFX.X()*F.X() + VFX.Y()*F.Y());
      F.SetCoord(F.X()+VFX.X()*l,F.Y()+VFX.Y()*l);
      myOX = VFX.X()*(P.X()-F.X()) + VFX.Y()*(P.Y()-F.Y());
      gp_Vec VFZ(-F.X(),-F.Y(),((HLRAlgo_Projector*) myProj)->Focus());
      myOF = VFZ.Magnitude();
      VFZ /= myOF;
      myVZ = VFZ * V;
      myVZ *= l3d;
      myOZ = VFZ * gp_Vec(P.X()-F.X(),P.Y()-F.Y(),P.Z());
    }
    else myVX = Sqrt(V.X() * V.X() + V.Y() * V.Y()) * l3d;
  }
  return(UpdateMinMax(TotMin,TotMax));
}

//=======================================================================
//function : UpdateMinMax
//purpose  : 
//=======================================================================

Standard_Real 
HLRBRep_Curve::UpdateMinMax (const Standard_Address TotMin,
			     const Standard_Address TotMax)
{
  Standard_Real a = HLRBRep_BCurveTool::FirstParameter(myCurve);
  Standard_Real b = HLRBRep_BCurveTool::LastParameter(myCurve);
  Standard_Real x,y,z,tolMinMax = 0;
  ((HLRAlgo_Projector*) myProj)->Project(Value3D(a),x,y,z);
  HLRAlgo::UpdateMinMax(x,y,z,TotMin,TotMax);

  if (myType != GeomAbs_Line) {
    Standard_Integer nbPnt = 30;
    Standard_Integer i;
    Standard_Real step = (b-a)/(nbPnt+1);
#ifndef DEB
    Standard_Real xa,ya,za,xb =0.,yb =0.,zb =0.;
#else
    Standard_Real xa,ya,za,xb,yb,zb;
#endif
    Standard_Real dx1,dy1,dz1,dd1;
    Standard_Real dx2,dy2,dz2,dd2;

    for (i = 1; i <= nbPnt; i++) {
      a += step;
      xa = xb; ya = yb; za = zb;
      xb = x ; yb = y ; zb = z ;
      ((HLRAlgo_Projector*) myProj)->Project(Value3D(a),x,y,z);
      HLRAlgo::UpdateMinMax(x,y,z,TotMin,TotMax);
      if (i >= 2) { 
	dx1 = x - xa; dy1 = y - ya; dz1 = z - za;
	dd1 = sqrt (dx1 * dx1 + dy1 * dy1 + dz1 * dz1);
	if (dd1 > 0) {
	  dx2 = xb - xa; dy2 = yb - ya; dz2 = zb - za;
	  dd2 = sqrt (dx2 * dx2 + dy2 * dy2 + dz2 * dz2);
	  if (dd2 > 0) {
	    Standard_Real p = (dx1 * dx2 + dy1 * dy2 + dz1 * dz2) / (dd1 * dd2);
	    dx1 = xa + p * dx1 - xb;
	    dy1 = ya + p * dy1 - yb;
	    dz1 = za + p * dz1 - zb;
	    dd1 = sqrt (dx1 * dx1 + dy1 * dy1 + dz1 * dz1);
	    if (dd1 > tolMinMax) tolMinMax = dd1;
	  }
	} 
      }
    }
  }
  ((HLRAlgo_Projector*) myProj)->Project(Value3D(b),x,y,z);
  HLRAlgo::UpdateMinMax(x,y,z,TotMin,TotMax);
  return tolMinMax;
}

//=======================================================================
//function : Z
//purpose  : 
//=======================================================================

Standard_Real HLRBRep_Curve::Z (const Standard_Real U) const 
{
  gp_Pnt P3d;
  HLRBRep_BCurveTool::D0(myCurve,U,P3d);
  P3d.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
  return P3d.Z();
}

//=======================================================================
//function : Tangent
//purpose  : 
//=======================================================================

void HLRBRep_Curve::Tangent (const Standard_Boolean AtStart,
			     gp_Pnt2d& P,
			     gp_Dir2d& D) const
{
  Standard_Real U;
  if (AtStart) U = HLRBRep_BCurveTool::FirstParameter(myCurve);
  else         U = HLRBRep_BCurveTool::LastParameter (myCurve);

  D0(U,P);
  HLRBRep_CLProps CLP(2,Epsilon(1.));
  const Standard_Address crv = (const Standard_Address)this;
  CLP.SetCurve(crv);
  CLP.SetParameter(U);
  StdFail_UndefinedDerivative_Raise_if
    (!CLP.IsTangentDefined(), "HLRBRep_Curve::Tangent");
  CLP.Tangent(D); 
}

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

void HLRBRep_Curve::D0 (const Standard_Real U, gp_Pnt2d& P) const
{
#if 0
  gp_Pnt P3d;
  HLRBRep_BCurveTool::D0(myCurve,U,P3d);
  P3d.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
  if (((HLRAlgo_Projector*) myProj)->Perspective()) {
    Standard_Real R = 1.-P3d.Z()/((HLRAlgo_Projector*) myProj)->Focus();
    P.SetCoord(P3d.X()/R,P3d.Y()/R);
  }
  else P.SetCoord(P3d.X(),P3d.Y());
#else 
  gp_Pnt P3d;
  HLRBRep_BCurveTool::D0(myCurve,U,P3d); 
  ((HLRAlgo_Projector*) myProj)->Project(P3d,P);
#endif
}

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

void HLRBRep_Curve::D1 (const Standard_Real U,
			gp_Pnt2d& P,
			gp_Vec2d& V) const
{

  // Mathematical formula for lines

  //        X'[t]      X[t] Z'[t]                                     
  // D1 =  -------- + -------------                                   
  //           Z[t]          Z[t] 2                                   
  //       1 - ----   f (1 - ----)                                    
  //            f             f                                       

#if 0
  gp_Pnt P3D;
  gp_Vec V13D;
  HLRBRep_BCurveTool::D1(myCurve,U,P3D,V13D);
  P3D .Transform(((HLRAlgo_Projector*) myProj)->Transformation());
  V13D.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
  if (((HLRAlgo_Projector*) myProj)->Perspective()) {
    Standard_Real f = ((HLRAlgo_Projector*) myProj)->Focus();
    Standard_Real R = 1. - P3D.Z()/f;
    Standard_Real e = V13D.Z()/(f*R*R);
    P.SetCoord(P3D .X()/R            , P3D .Y()/R            );
    V.SetCoord(V13D.X()/R + P3D.X()*e, V13D.Y()/R + P3D.Y()*e);
  }
  else {
    P.SetCoord(P3D .X(),P3D .Y());
    V.SetCoord(V13D.X(),V13D.Y());
  }
#else 
  gp_Pnt P3D;
  gp_Vec V13D;
  HLRBRep_BCurveTool::D1(myCurve,U,P3D,V13D);
  if (((HLRAlgo_Projector*) myProj)->Perspective()) {  
    Standard_Real f = ((HLRAlgo_Projector*) myProj)->Focus();
    Standard_Real R = 1. - P3D.Z()/f;
    Standard_Real e = V13D.Z()/(f*R*R);
    P.SetCoord(P3D .X()/R            , P3D .Y()/R            );
    V.SetCoord(V13D.X()/R + P3D.X()*e, V13D.Y()/R + P3D.Y()*e);
  }
  else {
#ifdef OCC155
    ((HLRAlgo_Projector*) myProj)->Project(P3D,V13D,P,V);
#else
    ((HLRAlgo_Projector*) myProj)->Project(P3D,P);
    gp_Pnt2d opop;
    gp_Pnt uiui(V13D.X(),V13D.Y(),V13D.Z());
    ((HLRAlgo_Projector*) myProj)->Project(uiui,opop);
    V.SetCoord(opop.X(),opop.Y());
#endif
  }
#endif
}

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

void HLRBRep_Curve::D2 (const Standard_Real U,
			gp_Pnt2d& P,
			gp_Vec2d& V1,
			gp_Vec2d& V2) const 
{

  // Mathematical formula for lines
  
  //                                   2
  //       2 X'[t] Z'[t]   2 X[t] Z'[t]      X''[t]     X[t] Z''[t]   
  // D2 =  ------------- + -------------- + -------- + -------------  
  //              Z[t] 2    2      Z[t] 3       Z[t]          Z[t] 2  
  //       f (1 - ----)    f  (1 - ----)    1 - ----   f (1 - ----)   
  //               f                f            f             f      

  gp_Pnt P3D;
  gp_Vec V13D,V23D;
  HLRBRep_BCurveTool::D2(myCurve,U,P3D,V13D,V23D);
  P3D .Transform(((HLRAlgo_Projector*) myProj)->Transformation());
  V13D.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
  V23D.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
  if (((HLRAlgo_Projector*) myProj)->Perspective()) {
    Standard_Real f = ((HLRAlgo_Projector*) myProj)->Focus();
    Standard_Real R = 1. - P3D.Z() / f;
    Standard_Real q = f*R*R;
    Standard_Real e = V13D.Z()/q;
    Standard_Real c = e*V13D.Z()/(f*R);
    P .SetCoord(P3D .X()/R            , P3D .Y()/R            );
    V1.SetCoord(V13D.X()/R + P3D.X()*e,	V13D.Y()/R + P3D.Y()*e);
    V2.SetCoord(V23D.X()/R + 2*V13D.X()*e + P3D.X()*V23D.Z()/q + 2*P3D.X()*c,
		V23D.Y()/R + 2*V13D.Y()*e + P3D.Y()*V23D.Z()/q + 2*P3D.Y()*c);
  }
  else {
    P .SetCoord(P3D .X(),P3D .Y());
    V1.SetCoord(V13D.X(),V13D.Y());
    V2.SetCoord(V23D.X(),V23D.Y());
  }
}

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

void HLRBRep_Curve::D3 (const Standard_Real,
				 gp_Pnt2d&,
				 gp_Vec2d&,
				 gp_Vec2d&,
				 gp_Vec2d&) const 
{
}

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

gp_Vec2d HLRBRep_Curve::DN (const Standard_Real,
			    const Standard_Integer) const
{ return gp_Vec2d(); }

//=======================================================================
//function : Line
//purpose  : 
//=======================================================================

gp_Lin2d HLRBRep_Curve::Line () const
{
  gp_Pnt2d P;
  gp_Vec2d V;
  D1(0.,P,V);
  return gp_Lin2d(P,V);
}

//=======================================================================
//function : Circle
//purpose  : 
//=======================================================================

gp_Circ2d HLRBRep_Curve::Circle () const
{
  gp_Circ C = HLRBRep_BCurveTool::Circle(myCurve);
  C.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
  return ProjLib::Project(gp_Pln(gp::XOY()),C);
}

//=======================================================================
//function : Ellipse
//purpose  : 
//=======================================================================

gp_Elips2d HLRBRep_Curve::Ellipse () const
{
  GeomAbs_CurveType typ = HLRBRep_BCurveTool::GetType(myCurve);
  if (typ == GeomAbs_Ellipse) {
    gp_Elips E = HLRBRep_BCurveTool::Ellipse(myCurve);
    E.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
    return ProjLib::Project(gp_Pln(gp::XOY()),E);
  }
  else { // this is a circle
    gp_Circ C = HLRBRep_BCurveTool::Circle(myCurve);
    C.Transform(((HLRAlgo_Projector*) myProj)->Transformation());
    const gp_Dir& D1 = C.Axis().Direction();
    const gp_Dir& D3 = D1.Crossed(gp::DZ());
    const gp_Dir& D2 = D1.Crossed(D3);
    Standard_Real rap = sqrt( D2.X()*D2.X() + D2.Y()*D2.Y() );
    gp_Dir2d d(D1.Y(),-D1.X());
    gp_Pnt2d p(C.Location().X(),C.Location().Y());
    gp_Elips2d El(gp_Ax2d(p,d),C.Radius(),C.Radius()*rap);
    if ( D1.Z() < 0 ) El.Reverse();
    return El;
  }
}

//=======================================================================
//function : Hyperbola
//purpose  : 
//=======================================================================

gp_Hypr2d HLRBRep_Curve::Hyperbola () const
{ return gp_Hypr2d(); }

//=======================================================================
//function : Parabola
//purpose  : 
//=======================================================================
gp_Parab2d HLRBRep_Curve::Parabola () const
{ return gp_Parab2d(); }

//=======================================================================
//function : Poles
//purpose  : 
//=======================================================================

void  HLRBRep_Curve::Poles (TColgp_Array1OfPnt2d& TP) const
{
  Standard_Integer i1 = TP.Lower();
  Standard_Integer i2 = TP.Upper();
  TColgp_Array1OfPnt TP3(i1,i2);
  //-- HLRBRep_BCurveTool::Poles(myCurve,TP3);
  if(HLRBRep_BCurveTool::GetType(myCurve) == GeomAbs_BSplineCurve) { 
    (HLRBRep_BCurveTool::BSpline(myCurve))->Poles(TP3);
  }
  else { 
    (HLRBRep_BCurveTool::Bezier(myCurve))->Poles(TP3);
  }
  for (Standard_Integer i = i1; i <= i2; i++) {
    ((HLRAlgo_Projector*) myProj)->Transform(TP3(i));
    TP(i).SetCoord(TP3(i).X(),TP3(i).Y());
  }
}

//=======================================================================
//function : PolesAndWeights
//purpose  : 
//=======================================================================

void  HLRBRep_Curve::PolesAndWeights (TColgp_Array1OfPnt2d& TP,
					       TColStd_Array1OfReal& TW) const
{
  Standard_Integer i1 = TP.Lower();
  Standard_Integer i2 = TP.Upper();
  TColgp_Array1OfPnt TP3(i1,i2);
  //-- HLRBRep_BCurveTool::PolesAndWeights(myCurve,TP3,TW);
  
  if(HLRBRep_BCurveTool::GetType(myCurve) == GeomAbs_BSplineCurve) {
    Handle(Geom_BSplineCurve) HB = (HLRBRep_BCurveTool::BSpline(myCurve));
    HB->Poles(TP3);
    HB->Weights(TW);  
    //-- (HLRBRep_BCurveTool::BSpline(myCurve))->PolesAndWeights(TP3,TW);
  }
  else { 
    Handle(Geom_BezierCurve) HB = (HLRBRep_BCurveTool::Bezier(myCurve));
    HB->Poles(TP3);
    HB->Weights(TW);
    //-- (HLRBRep_BCurveTool::Bezier(myCurve))->PolesAndWeights(TP3,TW);
  }
  for (Standard_Integer i = i1; i <= i2; i++) {
    ((HLRAlgo_Projector*) myProj)->Transform(TP3(i));
    TP(i).SetCoord(TP3(i).X(),TP3(i).Y());
  }
}


//=======================================================================
//function : Knots
//purpose  : 
//=======================================================================

void  HLRBRep_Curve::Knots (TColStd_Array1OfReal& kn) const
{
  if(HLRBRep_BCurveTool::GetType(myCurve) == GeomAbs_BSplineCurve) {
    Handle(Geom_BSplineCurve) HB = (HLRBRep_BCurveTool::BSpline(myCurve));
    HB->Knots(kn);
  }
}


//=======================================================================
//function : Multiplicities
//purpose  : 
//=======================================================================

void  HLRBRep_Curve::Multiplicities (TColStd_Array1OfInteger& mu) const
{
  if(HLRBRep_BCurveTool::GetType(myCurve) == GeomAbs_BSplineCurve) {
    Handle(Geom_BSplineCurve) HB = (HLRBRep_BCurveTool::BSpline(myCurve));
    HB->Multiplicities(mu);
  }
}

