// Created on: 1997-12-15
// Created by: Philippe MANGIN
// Copyright (c) 1997-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 <Adaptor3d_Curve.hxx>
#include <Bnd_Box.hxx>
#include <BndLib_Add3dCurve.hxx>
#include <Extrema_ExtCC.hxx>
#include <Extrema_POnCurv.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_CartesianPoint.hxx>
#include <Geom_Conic.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Geometry.hxx>
#include <Geom_Line.hxx>
#include <Geom_Plane.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <GeomAbs_CurveType.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <GeomFill_LocationLaw.hxx>
#include <GeomFill_SectionPlacement.hxx>
#include <GeomLib.hxx>
#include <GeomLProp_CLProps.hxx>
#include <gp.hxx>
#include <gp_Ax2.hxx>
#include <gp_Ax3.hxx>
#include <gp_Dir.hxx>
#include <gp_Mat.hxx>
#include <gp_Pnt.hxx>
#include <gp_Trsf.hxx>
#include <gp_Vec.hxx>
#include <IntCurveSurface_HInter.hxx>
#include <IntCurveSurface_IntersectionPoint.hxx>
#include <Precision.hxx>
#include <StdFail_NotDone.hxx>
#include <TColgp_HArray1OfPnt.hxx>

//=================================================================================================

static void Tangente(const Adaptor3d_Curve& Path,
                     const Standard_Real    Param,
                     gp_Pnt&                P,
                     gp_Vec&                Tang)
{
  Path.D1(Param, P, Tang);
  Standard_Real Norm = Tang.Magnitude();

  for (Standard_Integer ii = 2; (ii < 12) && (Norm < Precision::Confusion()); ii++)
  {
    Tang = Path.DN(Param, ii);
    Norm = Tang.Magnitude();
  }

  if (Norm > 100 * gp::Resolution())
    Tang /= Norm;
}

static Standard_Real Penalite(const Standard_Real angle, const Standard_Real dist)
{
  Standard_Real penal;

  if (dist < 1)
    penal = Sqrt(dist);
  else if (dist < 2)
    penal = Pow(dist, 2);
  else
    penal = dist + 2;

  if (angle > 1.e-3)
  {
    penal += 1. / angle - 2. / M_PI;
  }
  else
  {
    penal += 1.e3;
  }

  return penal;
}

static Standard_Real EvalAngle(const gp_Vec& V1, const gp_Vec& V2)
{
  Standard_Real angle;
  angle = V1.Angle(V2);
  if (angle > M_PI / 2)
    angle = M_PI - angle;
  return angle;
}

//===============================================================
// Function :DistMini
// Purpose : Examine un extrema pour updater <Dist> & <Param>
//===============================================================
static void DistMini(const Extrema_ExtPC&   Ext,
                     const Adaptor3d_Curve& C,
                     Standard_Real&         Dist,
                     Standard_Real&         Param)
{
  Standard_Real    dist1, dist2;
  Standard_Integer ii;
  gp_Pnt           P1, P2;
  Standard_Real    Dist2 = RealLast();

  Ext.TrimmedSquareDistances(dist1, dist2, P1, P2);
  if ((dist1 < Dist2) || (dist2 < Dist2))
  {
    if (dist1 < dist2)
    {
      Dist2 = dist1;
      Param = C.FirstParameter();
    }
    else
    {
      Dist2 = dist2;
      Param = C.LastParameter();
    }
  }

  if (Ext.IsDone())
  {
    for (ii = 1; ii <= Ext.NbExt(); ii++)
    {
      if (Ext.SquareDistance(ii) < Dist2)
      {
        Dist2 = Ext.SquareDistance(ii);
        Param = Ext.Point(ii).Parameter();
      }
    }
  }
  Dist = sqrt(Dist2);
}

//=================================================================================================

GeomFill_SectionPlacement::GeomFill_SectionPlacement(const Handle(GeomFill_LocationLaw)& L,
                                                     const Handle(Geom_Geometry)&        Section)
    : myLaw(L), /* myAdpSection(Section),  mySection(Section), */
      SecParam(0.0),
      PathParam(0.0),
      Dist(RealLast()),
      AngleMax(0.)
{

  done      = Standard_False;
  isplan    = Standard_False;
  myIsPoint = Standard_False;

  if (Section->IsInstance(STANDARD_TYPE(Geom_CartesianPoint)))
  {
    myIsPoint = Standard_True;
    myPoint   = Handle(Geom_CartesianPoint)::DownCast(Section)->Pnt();
    isplan    = Standard_True;
  }
  else
  {
    Handle(Geom_Curve) CurveSection = Handle(Geom_Curve)::DownCast(Section);
    myAdpSection.Load(CurveSection);
    mySection = CurveSection;
  }

  Standard_Integer i, j, NbPoles = 0;
  Standard_Real    aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;

  // Boite d'encombrement de la section pour en deduire le gabarit
  Bnd_Box box;
  if (myIsPoint)
    box.Add(myPoint);
  else
    BndLib_Add3dCurve::Add(myAdpSection, 1.e-4, box);
  box.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);

  Standard_Real DX = aXmax - aXmin;
  Standard_Real DY = aYmax - aYmin;
  Standard_Real DZ = aZmax - aZmin;
  Gabarit          = Sqrt(DX * DX + DY * DY + DZ * DZ) / 2.;

  Gabarit += Precision::Confusion(); // Cas des toute petite

  // Initialisation de TheAxe pour les cas singulier
  if (!myIsPoint)
  {
    gp_Pnt P;
    gp_Vec V;
    Tangente(myAdpSection,
             (myAdpSection.FirstParameter() + myAdpSection.LastParameter()) / 2,
             P,
             V);
    TheAxe.SetLocation(P);
    TheAxe.SetDirection(V);

    // y a t'il un Plan moyen ?
    GeomAbs_CurveType TheType = myAdpSection.GetType();
    switch (TheType)
    {
      case GeomAbs_Circle: {
        isplan = Standard_True;
        TheAxe = myAdpSection.Circle().Axis();
        break;
      }
      case GeomAbs_Ellipse: {
        isplan = Standard_True;
        TheAxe = myAdpSection.Ellipse().Axis();
        break;
      }
      case GeomAbs_Hyperbola: {
        isplan = Standard_True;
        TheAxe = myAdpSection.Hyperbola().Axis();
        break;
      }
      case GeomAbs_Parabola: {
        isplan = Standard_True;
        TheAxe = myAdpSection.Parabola().Axis();
        break;
      }
      case GeomAbs_Line: {
        NbPoles = 0; // Pas de Plan !!
        break;
      }
      case GeomAbs_BezierCurve:
      case GeomAbs_BSplineCurve: {
        NbPoles = myAdpSection.NbPoles();
        break;
      }
      default:
        NbPoles = 21;
    }

    if (!isplan && NbPoles > 2)
    {
      // Calcul d'un plan moyen.
      Handle(TColgp_HArray1OfPnt) Pnts;
      Standard_Real               first = myAdpSection.FirstParameter();
      Standard_Real               last  = myAdpSection.LastParameter();
      if (myAdpSection.IsPeriodic())
      {
        // Correct boundaries to avoid mistake of LocateU
        Handle(Geom_Curve) aCurve = myAdpSection.Curve();
        if (aCurve->IsInstance(STANDARD_TYPE(Geom_TrimmedCurve)))
          aCurve = (Handle(Geom_TrimmedCurve)::DownCast(aCurve))->BasisCurve();
        Standard_Real Ufirst  = aCurve->FirstParameter();
        Standard_Real aPeriod = aCurve->Period();
        Standard_Real U1      = Ufirst + Floor((first - Ufirst) / aPeriod) * aPeriod;
        Standard_Real U2      = U1 + aPeriod;
        if (Abs(first - U1) <= Precision::PConfusion())
          first = U1;
        if (Abs(last - U2) <= Precision::PConfusion())
          last = U2;
      }
      Standard_Real t, delta;
      if (myAdpSection.GetType() == GeomAbs_BSplineCurve)
      {
        Handle(Geom_BSplineCurve) BC = Handle(Geom_BSplineCurve)::DownCast(myAdpSection.Curve());
        Standard_Integer          I1, I2, I3, I4;
        BC->LocateU(first, Precision::Confusion(), I1, I2);
        BC->LocateU(last, Precision::Confusion(), I3, I4);
        Standard_Integer NbKnots = I3 - I2 + 1;

        Standard_Integer NbLocalPnts = 10;
        Standard_Integer NbPnts      = (NbKnots - 1) * NbLocalPnts;
        if (NbPnts < 0)
          NbPnts = 0;
        if (I1 != I2)
          NbPnts += NbLocalPnts;
        if (I3 != I4 && first < BC->Knot(I3))
          NbPnts += NbLocalPnts;
        if (!myAdpSection.IsClosed())
          NbPnts++;
        Pnts                = new TColgp_HArray1OfPnt(1, NbPnts);
        Standard_Integer nb = 1;
        if (I1 != I2)
        {
          Standard_Real locallast = (BC->Knot(I2) < last) ? BC->Knot(I2) : last;
          delta                   = (locallast - first) / NbLocalPnts;
          for (j = 0; j < NbLocalPnts; j++)
          {
            t = first + j * delta;
            Pnts->SetValue(nb++, myAdpSection.Value(t));
          }
        }
        for (i = I2; i < I3; i++)
        {
          t     = BC->Knot(i);
          delta = (BC->Knot(i + 1) - t) / NbLocalPnts;
          for (j = 0; j < NbLocalPnts; j++)
          {
            Pnts->SetValue(nb++, myAdpSection.Value(t));
            t += delta;
          }
        }
        if (I3 != I4 && first < BC->Knot(I3))
        {
          t     = BC->Knot(I3);
          delta = (last - t) / NbLocalPnts;
          for (j = 0; j < NbLocalPnts; j++)
          {
            Pnts->SetValue(nb++, myAdpSection.Value(t));
            t += delta;
          }
        }
        if (!myAdpSection.IsClosed())
          Pnts->SetValue(nb, myAdpSection.Value(last));
      }
      else // other type
      {
        Standard_Integer NbPnts = NbPoles - 1;
        if (!myAdpSection.IsClosed())
          NbPnts++;
        Pnts  = new TColgp_HArray1OfPnt(1, NbPnts);
        delta = (last - first) / (NbPoles - 1);
        for (i = 0; i < NbPoles - 1; i++)
        {
          t = first + i * delta;
          Pnts->SetValue(i + 1, myAdpSection.Value(t));
        }
        if (!myAdpSection.IsClosed())
          Pnts->SetValue(NbPnts, myAdpSection.Value(last));
      }

      Standard_Boolean issing;
      gp_Ax2           axe;
      GeomLib::AxeOfInertia(Pnts->Array1(), axe, issing, Precision::Confusion());
      if (!issing)
      {
        isplan = Standard_True;
        TheAxe.SetLocation(axe.Location());
        TheAxe.SetDirection(axe.Direction());
      }
    }

    myExt.Initialize(myAdpSection,
                     myAdpSection.FirstParameter(),
                     myAdpSection.LastParameter(),
                     Precision::Confusion());
  }
}

//=================================================================================================

void GeomFill_SectionPlacement::SetLocation(const Handle(GeomFill_LocationLaw)& L)
{
  myLaw = L;
}

//===============================================================
// Function : Perform
// Purpose : Le plus simple
//===============================================================
void GeomFill_SectionPlacement::Perform(const Standard_Real Tol)
{
  Handle(Adaptor3d_Curve) Path;
  Path = myLaw->GetCurve();
  Perform(Path, Tol);
}

//===============================================================
// Function :Perform
// Purpose : Recherche automatique
//===============================================================
void GeomFill_SectionPlacement::Perform(const Handle(Adaptor3d_Curve)& Path,
                                        const Standard_Real            Tol)
{
  Standard_Real IntTol     = 1.e-5;
  Standard_Real DistCenter = Precision::Infinite();

  if (myIsPoint)
  {
    Extrema_ExtPC Projector(myPoint, *Path, Precision::Confusion());
    DistMini(Projector, *Path, Dist, PathParam);
    AngleMax = M_PI / 2;
  }
  else
  {
    PathParam = Path->FirstParameter();
    SecParam  = myAdpSection.FirstParameter();

    Standard_Real distaux, taux = 0.0, alpha;
    gp_Pnt        PonPath, PonSec, P;
    gp_Vec        VRef, dp1;
    VRef.SetXYZ(TheAxe.Direction().XYZ());

    Tangente(*Path, PathParam, PonPath, dp1);
    PonSec = myAdpSection.Value(SecParam);
    Dist   = PonPath.Distance(PonSec);
    if (Dist > Tol)
    { // On Cherche un meilleur point sur la section
      myExt.Perform(PonPath);
      if (myExt.IsDone())
      {
        DistMini(myExt, myAdpSection, Dist, SecParam);
        PonSec = myAdpSection.Value(SecParam);
      }
    }
    AngleMax = EvalAngle(VRef, dp1);
    if (isplan)
      AngleMax = M_PI / 2 - AngleMax;

    Standard_Boolean Trouve = Standard_False;
    Standard_Integer ii;

    if (isplan)
    {
      // (1.1) Distances Point-Plan
      Standard_Real DistPlan;
      gp_Vec        V1(PonPath, TheAxe.Location());
      DistPlan = Abs(V1.Dot(VRef));
      if (DistPlan <= IntTol)
        DistCenter = V1.Magnitude();

      gp_Pnt Plast = Path->Value(Path->LastParameter());
      V1.SetXYZ(TheAxe.Location().XYZ() - Plast.XYZ());
      DistPlan = Abs(V1.Dot(VRef));
      if (DistPlan <= IntTol)
      {
        Standard_Real aDist = V1.Magnitude();
        if (aDist < DistCenter)
        {
          DistCenter = aDist;
          PonPath    = Plast;
          PathParam  = Path->LastParameter();
        }
      }

      // (1.2) Intersection Plan-courbe
      gp_Ax3                      axe(TheAxe.Location(), TheAxe.Direction());
      Handle(Geom_Plane)          plan   = new (Geom_Plane)(axe);
      Handle(GeomAdaptor_Surface) adplan = new (GeomAdaptor_Surface)(plan);
      IntCurveSurface_HInter      Intersector;
      Intersector.Perform(Path, adplan);
      if (Intersector.IsDone())
      {
        Standard_Real w;
        Standard_Real aDist;
        for (ii = 1; ii <= Intersector.NbPoints(); ii++)
        {
          w     = Intersector.Point(ii).W();
          P     = Path->Value(w);
          aDist = P.Distance(TheAxe.Location());
          if (aDist < DistCenter)
          {
            DistCenter = aDist;
            PonPath    = P;
            PathParam  = w;
          }
        }
      }
      if (!Intersector.IsDone() || Intersector.NbPoints() == 0)
      {
        // Comparing the distances from the path's endpoints to the best matching plane of the
        // profile.
        const gp_Pnt  firstPoint    = Path->Value(Path->FirstParameter());
        const gp_Pnt  lastPoint     = Path->Value(Path->LastParameter());
        const gp_Pln  plane         = plan->Pln();
        Standard_Real firstDistance = plane.SquareDistance(firstPoint);
        Standard_Real lastDistance  = plane.SquareDistance(lastPoint);

        if (((Abs(firstDistance) < Precision::SquareConfusion())
             && Abs(lastDistance) < Precision::SquareConfusion())
            || firstDistance < lastDistance)
        {
          PathParam = Path->FirstParameter();
        }
        else
        {
          PathParam = Path->LastParameter();
          Tangente(*Path, PathParam, PonPath, dp1);
          PonSec = myAdpSection.Value(SecParam);
          Dist   = PonPath.Distance(PonSec);
          if (Dist > Tol)
          { // On Cherche un meilleur point sur la section
            myExt.Perform(PonPath);
            if (myExt.IsDone())
            {
              DistMini(myExt, myAdpSection, Dist, SecParam);
              PonSec = myAdpSection.Value(SecParam);
            }
          }
          AngleMax = EvalAngle(VRef, dp1);
          AngleMax = M_PI / 2 - AngleMax;
        }
      }

      /*
         // (1.1) Distances Point-Plan
         Standard_Real DistPlan;
         gp_Vec V1 (PonPath, TheAxe.Location());
         DistPlan = Abs(V1.Dot(VRef));

         // On examine l'autre extremite
         gp_Pnt P;
         Tangente(Path->Curve(), Path->LastParameter(), P, dp1);
         V1.SetXYZ(TheAxe.Location().XYZ()-P.XYZ());
         if  (Abs(V1.Dot(VRef)) <= DistPlan ) { // On prend l'autre extremite
         alpha =  M_PI/2 - EvalAngle(VRef, dp1);
         distaux =  PonPath.Distance(PonSec);
         if (distaux > Tol) {
         myExt.Perform(P);
         if ( myExt.IsDone() )
         DistMini(myExt, myAdpSection, distaux, taux);
         }
         else
         taux = SecParam;

         if (Choix(distaux, alpha)) {
         Dist = distaux;
         AngleMax = alpha;
         PonPath = P;
         PathParam = Path->LastParameter();
         }
         }

         // (1.2) Intersection Plan-courbe
         gp_Ax3 axe (TheAxe.Location(), TheAxe.Direction());
         Handle(Geom_Plane) plan = new (Geom_Plane)(axe);
         Handle(GeomAdaptor_Surface) adplan =
         new (GeomAdaptor_Surface)(plan);
         IntCurveSurface_HInter Intersector;
         Intersector.Perform(Path, adplan);
         if (Intersector.IsDone()) {
         Standard_Real w;
         gp_Vec V;
         for (ii=1; ii<=Intersector.NbPoints(); ii++) {
         w = Intersector.Point(ii).W();
         //(1.3) test d'angle
         Tangente( Path->Curve(), w, P, V);
         alpha = M_PI/2 - EvalAngle(V, VRef);
         //(1.4) Test de distance Point-Courbe
         myExt.Perform(P);
         if ( myExt.IsDone() ) {
         DistMini(myExt, myAdpSection, distaux, taux);
         if (Choix(distaux, alpha)) {
         Dist = distaux;
         SecParam  = taux;
         AngleMax = alpha;
         PonPath = P;
         PathParam = w;
         PonSec = myAdpSection.Value(SecParam);
         }
         }
         else {
         distaux = P.Distance(PonSec);
         if (Choix(distaux, alpha)) {
         Dist = distaux;
         AngleMax = alpha;
         PonPath = P;
         PathParam = w;
         }
         }
         }
         }
      */
#ifdef OCCT_DEBUG
      if (Intersector.NbPoints() == 0)
      {
        Intersector.Dump();
      }
#endif
    }

    // Cas General
    if (!isplan)
    {
      // (2.1) Distance avec les extremites ...
      myExt.Perform(PonPath);
      if (myExt.IsDone())
      {
        DistMini(myExt, myAdpSection, distaux, taux);
        if (distaux < Dist)
        {
          Dist     = distaux;
          SecParam = taux;
        }
      }
      Trouve = (Dist <= Tol);
      if (!Trouve)
      {
        Tangente(*Path, Path->LastParameter(), P, dp1);
        alpha = EvalAngle(VRef, dp1);
        myExt.Perform(P);
        if (myExt.IsDone())
        {
          if (myExt.IsDone())
          {
            DistMini(myExt, myAdpSection, distaux, taux);
            if (Choix(distaux, alpha))
            {
              Dist      = distaux;
              SecParam  = taux;
              AngleMax  = alpha;
              PonPath   = P;
              PathParam = Path->LastParameter();
            }
          }
        }
        Trouve = (Dist <= Tol);
      }

      // (2.2) Distance courbe-courbe
      if (!Trouve)
      {
        Extrema_ExtCC Ext(*Path,
                          myAdpSection,
                          Path->FirstParameter(),
                          Path->LastParameter(),
                          myAdpSection.FirstParameter(),
                          myAdpSection.LastParameter(),
                          Path->Resolution(Tol / 100),
                          myAdpSection.Resolution(Tol / 100));
        if (Ext.IsDone() && !Ext.IsParallel())
        {
          Extrema_POnCurv P1, P2;
          for (ii = 1; ii <= Ext.NbExt(); ii++)
          {
            distaux = sqrt(Ext.SquareDistance(ii));
            Ext.Points(ii, P1, P2);
            Tangente(*Path, P1.Parameter(), P, dp1);
            alpha = EvalAngle(VRef, dp1);
            if (Choix(distaux, alpha))
            {
              Trouve    = Standard_True;
              Dist      = distaux;
              PathParam = P1.Parameter();
              SecParam  = P2.Parameter();
              PonSec    = P2.Value();
              PonPath   = P;
              AngleMax  = alpha;
            }
          }
        }
        if (!Trouve)
        {
          // Si l'on a toujours rien, on essai une distance point/path
          // c'est la derniere chance.
          Extrema_ExtPC PExt;
          PExt.Initialize(*Path,
                          Path->FirstParameter(),
                          Path->LastParameter(),
                          Precision::Confusion());
          PExt.Perform(PonSec);
          if (PExt.IsDone())
          {
            // modified for OCC13595  Tue Oct 17 15:00:08 2006.BEGIN
            // 	      DistMini(PExt, myAdpSection, distaux, taux);
            DistMini(PExt, *Path, distaux, taux);
            // modified for OCC13595  Tue Oct 17 15:00:11 2006.END
            Tangente(*Path, taux, P, dp1);
            alpha = EvalAngle(VRef, dp1);
            if (Choix(distaux, alpha))
            {
              Dist      = distaux;
              PonPath   = P;
              AngleMax  = alpha;
              PathParam = taux;
            }
          }
        }
      }
    }
  }

  done = Standard_True;
}

//===============================================================
// Function : Perform
// Purpose : Calcul le placement pour un parametre donne.
//===============================================================
void GeomFill_SectionPlacement::Perform(const Standard_Real Param, const Standard_Real Tol)
{
  done = Standard_True;
  Handle(Adaptor3d_Curve) Path;
  Path = myLaw->GetCurve();

  PathParam = Param;
  if (myIsPoint)
  {
    gp_Pnt PonPath = Path->Value(PathParam);
    Dist           = PonPath.Distance(myPoint);
    AngleMax       = M_PI / 2;
  }
  else
  {
    SecParam = myAdpSection.FirstParameter();

    //  Standard_Real distaux, taux, alpha;
    //  gp_Pnt PonPath, PonSec, P;
    gp_Pnt PonPath, PonSec;
    gp_Vec VRef, dp1;
    VRef.SetXYZ(TheAxe.Direction().XYZ());

    Tangente(*Path, PathParam, PonPath, dp1);
    PonSec = myAdpSection.Value(SecParam);
    Dist   = PonPath.Distance(PonSec);
    if (Dist > Tol)
    { // On Cherche un meilleur point sur la section
      myExt.Perform(PonPath);
      if (myExt.IsDone())
      {
        DistMini(myExt, myAdpSection, Dist, SecParam);
        PonSec = myAdpSection.Value(SecParam);
      }
    }
    AngleMax = EvalAngle(VRef, dp1);
    if (isplan)
      AngleMax = M_PI / 2 - AngleMax;
  }

  done = Standard_True;
}

//=================================================================================================

Standard_Boolean GeomFill_SectionPlacement::IsDone() const
{
  return done;
}

//=================================================================================================

Standard_Real GeomFill_SectionPlacement::ParameterOnPath() const
{
  return PathParam;
}

//=================================================================================================

Standard_Real GeomFill_SectionPlacement::ParameterOnSection() const
{
  return SecParam;
}

//=================================================================================================

Standard_Real GeomFill_SectionPlacement::Distance() const
{
  return Dist;
}

//=================================================================================================

Standard_Real GeomFill_SectionPlacement::Angle() const
{
  return AngleMax;
}

//=================================================================================================

gp_Trsf GeomFill_SectionPlacement::Transformation(const Standard_Boolean WithTranslation,
                                                  const Standard_Boolean WithCorrection) const
{
  gp_Vec V;
  gp_Mat M;
  gp_Dir DT, DN, D;
  // modified by NIZHNY-MKK  Fri Oct 17 15:27:07 2003
  gp_Pnt P(0., 0., 0.), PSection(0., 0., 0.);

  // Calcul des reperes
  myLaw->D0(PathParam, M, V);

  P.SetXYZ(V.XYZ());
  D.SetXYZ(M.Column(3));
  DN.SetXYZ(M.Column(1));
  gp_Ax3 Paxe(P, D, DN);

  if (WithTranslation || WithCorrection)
  {
    if (myIsPoint)
      PSection = myPoint;
    else
      PSection = mySection->Value(SecParam);
  }
  // modified by NIZHNY-MKK  Wed Oct  8 15:03:19 2003.BEGIN
  gp_Trsf Rot;

  if (WithCorrection && !myIsPoint)
  {
    Standard_Real angle;

    if (!isplan)
      throw Standard_Failure("Illegal usage: can't rotate non-planar profile");

    gp_Dir ProfileNormal = TheAxe.Direction();
    gp_Dir SpineStartDir = Paxe.Direction();

    if (!ProfileNormal.IsParallel(SpineStartDir, Precision::Angular()))
    {
      gp_Dir DirAxeOfRotation = ProfileNormal ^ SpineStartDir;
      angle                   = ProfileNormal.AngleWithRef(SpineStartDir, DirAxeOfRotation);
      gp_Ax1 AxeOfRotation(TheAxe.Location(), DirAxeOfRotation);
      Rot.SetRotation(AxeOfRotation, angle);
    }
    PSection.Transform(Rot);
  }
  // modified by NIZHNY-MKK  Wed Oct  8 15:03:21 2003.END

  if (WithTranslation)
  {
    P.ChangeCoord().SetLinearForm(-1, PSection.XYZ(), V.XYZ());
  }
  else
  {
    P.SetCoord(0., 0., 0.);
  }

  gp_Ax3 Saxe(P, gp::DZ(), gp::DX());

  // Transfo
  gp_Trsf Tf;
  Tf.SetTransformation(Saxe, Paxe);

  if (WithCorrection)
  {
    // modified by NIZHNY-MKK  Fri Oct 17 15:26:36 2003.BEGIN
    //     Standard_Real angle;
    //     gp_Trsf Rot;

    //     if (!isplan)
    //       throw Standard_Failure("Illegal usage: can't rotate non-planar profile");

    //     gp_Dir ProfileNormal = TheAxe.Direction();
    //     gp_Dir SpineStartDir = Paxe.Direction();
    //     if (! ProfileNormal.IsParallel( SpineStartDir, Precision::Angular() ))
    //       {
    // 	gp_Dir DirAxeOfRotation = ProfileNormal ^ SpineStartDir;
    // 	angle = ProfileNormal.AngleWithRef( SpineStartDir, DirAxeOfRotation );
    // 	gp_Ax1 AxeOfRotation( TheAxe.Location(), DirAxeOfRotation );
    // 	Rot.SetRotation( AxeOfRotation, angle );
    //       }
    // modified by NIZHNY-MKK  Fri Oct 17 15:26:42 2003.END

    Tf *= Rot;
  }

  return Tf;
}

//=================================================================================================

Handle(Geom_Curve) GeomFill_SectionPlacement::Section(const Standard_Boolean WithTranslation) const
{
  Handle(Geom_Curve) TheSection = Handle(Geom_Curve)::DownCast(mySection->Copy());
  TheSection->Transform(Transformation(WithTranslation, Standard_False));
  return TheSection;
}

//=================================================================================================

Handle(Geom_Curve) GeomFill_SectionPlacement::ModifiedSection(
  const Standard_Boolean WithTranslation) const
{
  Handle(Geom_Curve) TheSection = Handle(Geom_Curve)::DownCast(mySection->Copy());
  TheSection->Transform(Transformation(WithTranslation, Standard_True));
  return TheSection;
}

//=================================================================================================

void GeomFill_SectionPlacement::SectionAxis(const gp_Mat& M, gp_Vec& T, gp_Vec& N, gp_Vec& BN) const
{
  Standard_Real     Eps = 1.e-10;
  gp_Dir            D;
  gp_Vec            PathNormal;
  GeomLProp_CLProps CP(mySection, SecParam, 2, Eps);
  if (CP.IsTangentDefined())
  {
    CP.Tangent(D);
    T.SetXYZ(D.XYZ());
    T.Normalize();
    if (CP.Curvature() > Eps)
    {
      CP.Normal(D);
      N.SetXYZ(D.XYZ());
    }
    else
    {
      // Cas ambigu, on essai de recuperer la normal a la trajectoire
      // Reste le probleme des points d'inflexions, qui n'est pas
      // bien traiter par LProp (pas de recuperation de la normal) :
      // A voir...
      PathNormal.SetXYZ(M.Column(1));
      PathNormal.Normalize();
      BN = T ^ PathNormal;
      if (BN.Magnitude() > Eps)
      {
        BN.Normalize();
        // N = BN ^ T;
      }
      N = BN ^ T;
    }
  }
  else
  { // Cas indefinie, on prend le triedre
    // complet sur la trajectoire
    T.SetXYZ(M.Column(3));
    N.SetXYZ(M.Column(2));
  }
  BN = T ^ N;
}

//===============================================================
// Function :Choix
// Purpose : Decide si le couple (dist, angle) est "meilleur" que
// couple courrant.
//===============================================================
Standard_Boolean GeomFill_SectionPlacement::Choix(const Standard_Real dist,
                                                  const Standard_Real angle) const
{
  Standard_Real evoldist, evolangle;
  evoldist  = dist - Dist;
  evolangle = angle - AngleMax;
  // (1) Si la gain en distance est > que le gabarit, on prend
  if (evoldist < -Gabarit)
    return Standard_True;

  //  (2) si l'ecart en distance est de l'ordre du gabarit
  if (Abs(evoldist) < Gabarit)
  {
    //  (2.1) si le gain en angle est important on garde
    if (evolangle > 0.5)
      return Standard_True;
    //  (2.2) si la variation d'angle est moderee on evalue
    //  une fonction de penalite
    if (Penalite(angle, dist / Gabarit) < Penalite(AngleMax, Dist / Gabarit))
      return Standard_True;
  }

  return Standard_False;
}
