// Created on: 1996-07-03
// Created by: Joelle CHAUVET
// Copyright (c) 1996-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 <AdvApp2Var_ApproxAFunc2Var.hxx>
#include <AdvApp2Var_EvaluatorFunc2Var.hxx>
#include <AdvApp2Var_Criterion.hxx>
#include <AdvApp2Var_Context.hxx>
#include <AdvApp2Var_Patch.hxx>
#include <AdvApp2Var_Network.hxx>
#include <AdvApp2Var_Node.hxx>
#include <AdvApp2Var_Iso.hxx>
#include <AdvApp2Var_Strip.hxx>
#include <AdvApp2Var_Framework.hxx>
#include <AdvApprox_Cutting.hxx>

#include <Standard_ConstructionError.hxx>
#include <Standard_OutOfRange.hxx>
#include <TColStd_HArray1OfInteger.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColStd_HArray2OfReal.hxx>

#include <gp_XY.hxx>
#include <TColgp_HArray2OfPnt.hxx>

#include <Convert_GridPolynomialToPoles.hxx>

#include <Geom_BezierSurface.hxx>
#include <Geom_BSplineSurface.hxx>

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

AdvApp2Var_ApproxAFunc2Var::AdvApp2Var_ApproxAFunc2Var(
  const Standard_Integer               Num1DSS,
  const Standard_Integer               Num2DSS,
  const Standard_Integer               Num3DSS,
  const Handle(TColStd_HArray1OfReal)& OneDTol,
  const Handle(TColStd_HArray1OfReal)& TwoDTol,
  const Handle(TColStd_HArray1OfReal)& ThreeDTol,
  const Handle(TColStd_HArray2OfReal)& OneDTolFr,
  const Handle(TColStd_HArray2OfReal)& TwoDTolFr,
  const Handle(TColStd_HArray2OfReal)& ThreeDTolFr,
  const Standard_Real                  FirstInU,
  const Standard_Real                  LastInU,
  const Standard_Real                  FirstInV,
  const Standard_Real                  LastInV,
  const GeomAbs_IsoType                FavorIso,
  const GeomAbs_Shape                  ContInU,
  const GeomAbs_Shape                  ContInV,
  const Standard_Integer               PrecisCode,
  const Standard_Integer               MaxDegInU,
  const Standard_Integer               MaxDegInV,
  const Standard_Integer               MaxPatch,
  const AdvApp2Var_EvaluatorFunc2Var&  Func,
  AdvApprox_Cutting&                   UChoice,
  AdvApprox_Cutting&                   VChoice)
    : my1DTolerances(OneDTol),
      my2DTolerances(TwoDTol),
      my3DTolerances(ThreeDTol),
      my1DTolOnFront(OneDTolFr),
      my2DTolOnFront(TwoDTolFr),
      my3DTolOnFront(ThreeDTolFr),
      myFirstParInU(FirstInU),
      myLastParInU(LastInU),
      myFirstParInV(FirstInV),
      myLastParInV(LastInV),
      myFavoriteIso(FavorIso),
      myContInU(ContInU),
      myContInV(ContInV),
      myPrecisionCode(PrecisCode),
      myMaxDegInU(MaxDegInU),
      myMaxDegInV(MaxDegInV),
      myMaxPatches(MaxPatch),
      myDone(Standard_False),
      myHasResult(Standard_False),
      myDegreeInU(0),
      myDegreeInV(0),
      myCriterionError(0.0)
{
  myNumSubSpaces[0] = Num1DSS;
  myNumSubSpaces[1] = Num2DSS;
  myNumSubSpaces[2] = Num3DSS;

  Init();
  Perform(UChoice, VChoice, Func);
  ConvertBS();
}

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

AdvApp2Var_ApproxAFunc2Var::AdvApp2Var_ApproxAFunc2Var(
  const Standard_Integer               Num1DSS,
  const Standard_Integer               Num2DSS,
  const Standard_Integer               Num3DSS,
  const Handle(TColStd_HArray1OfReal)& OneDTol,
  const Handle(TColStd_HArray1OfReal)& TwoDTol,
  const Handle(TColStd_HArray1OfReal)& ThreeDTol,
  const Handle(TColStd_HArray2OfReal)& OneDTolFr,
  const Handle(TColStd_HArray2OfReal)& TwoDTolFr,
  const Handle(TColStd_HArray2OfReal)& ThreeDTolFr,
  const Standard_Real                  FirstInU,
  const Standard_Real                  LastInU,
  const Standard_Real                  FirstInV,
  const Standard_Real                  LastInV,
  const GeomAbs_IsoType                FavorIso,
  const GeomAbs_Shape                  ContInU,
  const GeomAbs_Shape                  ContInV,
  const Standard_Integer               PrecisCode,
  const Standard_Integer               MaxDegInU,
  const Standard_Integer               MaxDegInV,
  const Standard_Integer               MaxPatch,
  const AdvApp2Var_EvaluatorFunc2Var&  Func,
  const AdvApp2Var_Criterion&          Crit,
  AdvApprox_Cutting&                   UChoice,
  AdvApprox_Cutting&                   VChoice)
    : my1DTolerances(OneDTol),
      my2DTolerances(TwoDTol),
      my3DTolerances(ThreeDTol),
      my1DTolOnFront(OneDTolFr),
      my2DTolOnFront(TwoDTolFr),
      my3DTolOnFront(ThreeDTolFr),
      myFirstParInU(FirstInU),
      myLastParInU(LastInU),
      myFirstParInV(FirstInV),
      myLastParInV(LastInV),
      myFavoriteIso(FavorIso),
      myContInU(ContInU),
      myContInV(ContInV),
      myPrecisionCode(PrecisCode),
      myMaxDegInU(MaxDegInU),
      myMaxDegInV(MaxDegInV),
      myMaxPatches(MaxPatch),
      myDone(Standard_False),
      myHasResult(Standard_False),
      myDegreeInU(0),
      myDegreeInV(0),
      myCriterionError(0.0)
{
  myNumSubSpaces[0] = Num1DSS;
  myNumSubSpaces[1] = Num2DSS;
  myNumSubSpaces[2] = Num3DSS;

  Init();
  Perform(UChoice, VChoice, Func, Crit);
  ConvertBS();
}

//=======================================================================
// function : Init
// purpose  : Initialisation of the approximation
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::Init()
{
  Standard_Integer ifav, iu = 0, iv = 0, ndu, ndv;
  switch (myFavoriteIso)
  {
    case GeomAbs_IsoU:
      ifav = 1;
      break;
    case GeomAbs_IsoV:
      ifav = 2;
      break;
    default:
      ifav = 2;
      break;
  }
  switch (myContInU)
  {
    case GeomAbs_C0:
      iu = 0;
      break;
    case GeomAbs_C1:
      iu = 1;
      break;
    case GeomAbs_C2:
      iu = 2;
      break;
    default:
      throw Standard_ConstructionError("AdvApp2Var_ApproxAFunc2Var : UContinuity Error");
  }
  switch (myContInV)
  {
    case GeomAbs_C0:
      iv = 0;
      break;
    case GeomAbs_C1:
      iv = 1;
      break;
    case GeomAbs_C2:
      iv = 2;
      break;
    default:
      throw Standard_ConstructionError("AdvApp2Var_ApproxAFunc2Var : VContinuity Error");
  }
  ndu = Max(myMaxDegInU + 1, 2 * iu + 2);
  ndv = Max(myMaxDegInV + 1, 2 * iv + 2);
  if (ndu < 2 * iu + 2)
    throw Standard_ConstructionError("AdvApp2Var_ApproxAFunc2Var : UMaxDegree Error");
  if (ndv < 2 * iv + 2)
    throw Standard_ConstructionError("AdvApp2Var_ApproxAFunc2Var : VMaxDegree Error");
  myPrecisionCode = Max(0, Min(myPrecisionCode, 3));
  AdvApp2Var_Context Conditions(ifav,
                                iu,
                                iv,
                                ndu,
                                ndv,
                                myPrecisionCode,
                                myNumSubSpaces[0],
                                myNumSubSpaces[1],
                                myNumSubSpaces[2],
                                my1DTolerances,
                                my2DTolerances,
                                my3DTolerances,
                                my1DTolOnFront,
                                my2DTolOnFront,
                                my3DTolOnFront);
  myConditions = Conditions;
  InitGrid(1);
}

//=======================================================================
// function : InitGrid
// purpose  : Initialisation of the approximation with regular cuttings
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::InitGrid(const Standard_Integer NbInt)
{
  Standard_Integer iu = myConditions.UOrder(), iv = myConditions.VOrder(), iint;

  Handle(AdvApp2Var_Patch) M0 =
    new AdvApp2Var_Patch(myFirstParInU, myLastParInU, myFirstParInV, myLastParInV, iu, iv);

  AdvApp2Var_SequenceOfPatch Net;
  Net.Append(M0);

  TColStd_SequenceOfReal TheU, TheV;
  TheU.Append(myFirstParInU);
  TheV.Append(myFirstParInV);
  TheU.Append(myLastParInU);
  TheV.Append(myLastParInV);

  AdvApp2Var_Network Result(Net, TheU, TheV);

  gp_XY                     UV1(myFirstParInU, myFirstParInV);
  Handle(AdvApp2Var_Node)   C1 = new AdvApp2Var_Node(UV1, iu, iv);
  gp_XY                     UV2(myLastParInU, myFirstParInV);
  Handle(AdvApp2Var_Node)   C2 = new AdvApp2Var_Node(UV2, iu, iv);
  gp_XY                     UV4(myLastParInU, myLastParInV);
  Handle(AdvApp2Var_Node)   C4 = new AdvApp2Var_Node(UV4, iu, iv);
  gp_XY                     UV3(myFirstParInU, myLastParInV);
  Handle(AdvApp2Var_Node)   C3 = new AdvApp2Var_Node(UV3, iu, iv);
  AdvApp2Var_SequenceOfNode Bag;
  Bag.Append(C1);
  Bag.Append(C2);
  Bag.Append(C3);
  Bag.Append(C4);

  Handle(AdvApp2Var_Iso) V0 = new AdvApp2Var_Iso(GeomAbs_IsoV,
                                                 myFirstParInV,
                                                 myFirstParInU,
                                                 myLastParInU,
                                                 myFirstParInV,
                                                 myLastParInV,
                                                 1,
                                                 iu,
                                                 iv);
  Handle(AdvApp2Var_Iso) V1 = new AdvApp2Var_Iso(GeomAbs_IsoV,
                                                 myLastParInV,
                                                 myFirstParInU,
                                                 myLastParInU,
                                                 myFirstParInV,
                                                 myLastParInV,
                                                 2,
                                                 iu,
                                                 iv);
  Handle(AdvApp2Var_Iso) U0 = new AdvApp2Var_Iso(GeomAbs_IsoU,
                                                 myFirstParInU,
                                                 myFirstParInU,
                                                 myLastParInU,
                                                 myFirstParInV,
                                                 myLastParInV,
                                                 3,
                                                 iu,
                                                 iv);
  Handle(AdvApp2Var_Iso) U1 = new AdvApp2Var_Iso(GeomAbs_IsoU,
                                                 myLastParInU,
                                                 myFirstParInU,
                                                 myLastParInU,
                                                 myFirstParInV,
                                                 myLastParInV,
                                                 4,
                                                 iu,
                                                 iv);

  AdvApp2Var_Strip BU0, BV0;
  BU0.Append(V0);
  BU0.Append(V1);
  BV0.Append(U0);
  BV0.Append(U1);

  AdvApp2Var_SequenceOfStrip UStrip, VStrip;
  UStrip.Append(BU0);
  VStrip.Append(BV0);

  AdvApp2Var_Framework Constraints(Bag, UStrip, VStrip);

  // regular cutting if NbInt>1
  Standard_Real deltu = (myLastParInU - myFirstParInU) / NbInt,
                deltv = (myLastParInV - myFirstParInV) / NbInt;
  for (iint = 1; iint <= NbInt - 1; iint++)
  {
    Result.UpdateInU(myFirstParInU + iint * deltu);
    Constraints.UpdateInU(myFirstParInU + iint * deltu);
    Result.UpdateInV(myFirstParInV + iint * deltv);
    Constraints.UpdateInV(myFirstParInV + iint * deltv);
  }
  myResult      = Result;
  myConstraints = Constraints;
}

//=======================================================================
// function : Perform
// purpose  : Computation of the approximation
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::Perform(const AdvApprox_Cutting&            UChoice,
                                         const AdvApprox_Cutting&            VChoice,
                                         const AdvApp2Var_EvaluatorFunc2Var& Func)
{
  ComputePatches(UChoice, VChoice, Func);
  myHasResult = myDone = Standard_True;
  Compute3DErrors();
}

//=======================================================================
// function : Perform
// purpose  : Computation of the approximation
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::Perform(const AdvApprox_Cutting&            UChoice,
                                         const AdvApprox_Cutting&            VChoice,
                                         const AdvApp2Var_EvaluatorFunc2Var& Func,
                                         const AdvApp2Var_Criterion&         Crit)
{
  ComputePatches(UChoice, VChoice, Func, Crit);
  myHasResult = myDone = Standard_True;
  Compute3DErrors();
  ComputeCritError();
}

//=======================================================================
// function : ComputePatches
// purpose  : Computation of the polynomial approximations
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::ComputePatches(const AdvApprox_Cutting&            UChoice,
                                                const AdvApprox_Cutting&            VChoice,
                                                const AdvApp2Var_EvaluatorFunc2Var& Func)
{
  Standard_Real    Udec, Vdec;
  Standard_Boolean Umore, Vmore;
  Standard_Integer NbPatch, NbU, NbV, NumDec;
  Standard_Integer FirstNA;

  while (myResult.FirstNotApprox(FirstNA))
  {

    // complete the set of constraints
    ComputeConstraints(UChoice, VChoice, Func);

    // discretization of constraints relative to the square
    myResult(FirstNA).Discretise(myConditions, myConstraints, Func);
    if (!myResult(FirstNA).IsDiscretised())
    {
      myHasResult = myDone = Standard_False;
      throw Standard_ConstructionError("AdvApp2Var_ApproxAFunc2Var : Surface Discretisation Error");
    }

    // calculate the number and the type of authorized cuts
    // depending on the max number of squares and the validity of next cuts.
    NbU     = myResult.NbPatchInU();
    NbV     = myResult.NbPatchInV();
    NbPatch = NbU * NbV;
    Umore   = UChoice.Value(myResult(FirstNA).U0(), myResult(FirstNA).U1(), Udec);
    Vmore   = VChoice.Value(myResult(FirstNA).V0(), myResult(FirstNA).V1(), Vdec);

    NumDec = 0;
    if (((NbPatch + NbV) <= myMaxPatches) && ((NbPatch + NbU) > myMaxPatches) && (Umore))
      NumDec = 1;
    if (((NbPatch + NbV) > myMaxPatches) && ((NbPatch + NbU) <= myMaxPatches) && (Vmore))
      NumDec = 2;
    if (((NbPatch + NbV) <= myMaxPatches) && ((NbPatch + NbU) <= myMaxPatches))
    {
      if (Umore)
        NumDec = 3;
      if ((NbV > NbU) && Vmore)
        NumDec = 4;
    }
    if ((NbU + 1) * (NbV + 1) <= myMaxPatches)
    {
      if (!Umore && !Vmore)
        NumDec = 0;
      if (Umore && !Vmore)
        NumDec = 3;
      if (!Umore && Vmore)
        NumDec = 4;
      if (Umore && Vmore)
        NumDec = 5;
    }

    // approximation of the square
    myResult(FirstNA).MakeApprox(myConditions, myConstraints, NumDec);

    if (!myResult(FirstNA).IsApproximated())
    {
      switch (myResult(FirstNA).CutSense())
      {
        case 0:
          //	It is not possible to cut : the result is preserved
          if (myResult(FirstNA).HasResult())
          {
            myResult(FirstNA).OverwriteApprox();
          }
          else
          {
            myHasResult = myDone = Standard_False;
            throw Standard_ConstructionError(
              "AdvApp2Var_ApproxAFunc2Var : Surface Approximation Error");
          }
          break;
        case 1:
          //      It is necessary to cut in U
          myResult.UpdateInU(Udec);
          myConstraints.UpdateInU(Udec);
          break;
        case 2:
          //      It is necessary to cut in V
          myResult.UpdateInV(Vdec);
          myConstraints.UpdateInV(Vdec);
          break;
        case 3:
          //      It is necessary to cut in U and V
          myResult.UpdateInU(Udec);
          myConstraints.UpdateInU(Udec);
          myResult.UpdateInV(Vdec);
          myConstraints.UpdateInV(Vdec);
          break;
        default:
          myHasResult = myDone = Standard_False;
          throw Standard_ConstructionError(
            "AdvApp2Var_ApproxAFunc2Var : Surface Approximation Error");
      }
    }
  }
}

//=======================================================================
// function : ComputePatches
// purpose  : Computation of the polynomial approximations
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::ComputePatches(const AdvApprox_Cutting&            UChoice,
                                                const AdvApprox_Cutting&            VChoice,
                                                const AdvApp2Var_EvaluatorFunc2Var& Func,
                                                const AdvApp2Var_Criterion&         Crit)
{
  Standard_Real    Udec, Vdec, CritValue, m1 = 0.;
  Standard_Boolean Umore, Vmore, CritAbs = (Crit.Type() == AdvApp2Var_Absolute);
  Standard_Integer NbPatch, NbU, NbV, NbInt, NumDec;
  Standard_Integer FirstNA, decision = 0;

  while (myResult.FirstNotApprox(FirstNA))
  {

    // complete the set of constraints
    ComputeConstraints(UChoice, VChoice, Func, Crit);
    if (decision > 0)
    {
      m1 = 0.;
    }

    // discretize the constraints relative to the square
    myResult(FirstNA).Discretise(myConditions, myConstraints, Func);
    if (!myResult(FirstNA).IsDiscretised())
    {
      myHasResult = myDone = Standard_False;
      throw Standard_ConstructionError("AdvApp2Var_ApproxAFunc2Var : Surface Discretisation Error");
    }

    // calculate the number and type of authorized cuts
    // depending on the max number of squares and the validity of next cuts
    NbU     = myResult.NbPatchInU();
    NbV     = myResult.NbPatchInV();
    NbPatch = NbU * NbV;
    NbInt   = NbU;
    Umore   = UChoice.Value(myResult(FirstNA).U0(), myResult(FirstNA).U1(), Udec);
    Vmore   = VChoice.Value(myResult(FirstNA).V0(), myResult(FirstNA).V1(), Vdec);

    NumDec = 0;
    if (((NbPatch + NbV) <= myMaxPatches) && ((NbPatch + NbU) > myMaxPatches) && (Umore))
      NumDec = 1;
    if (((NbPatch + NbV) > myMaxPatches) && ((NbPatch + NbU) <= myMaxPatches) && (Vmore))
      NumDec = 2;
    if (((NbPatch + NbV) <= myMaxPatches) && ((NbPatch + NbU) <= myMaxPatches))
    {
      if (Umore)
        NumDec = 3;
      if ((NbV > NbU) && Vmore)
        NumDec = 4;
    }
    if ((NbU + 1) * (NbV + 1) <= myMaxPatches)
    {
      if (!Umore && !Vmore)
        NumDec = 0;
      if (Umore && !Vmore)
        NumDec = 1;
      if (!Umore && Vmore)
        NumDec = 2;
      if (Umore && Vmore)
        NumDec = 5;
    }

    // approximation of the square
    if (CritAbs)
    {
      myResult(FirstNA).MakeApprox(myConditions, myConstraints, 0);
    }
    else
    {
      myResult(FirstNA).MakeApprox(myConditions, myConstraints, NumDec);
    }
    if (NumDec >= 3)
      NumDec = NumDec - 2;

    // evaluation of the criterion on the square
    if (myResult(FirstNA).HasResult())
    {
      Crit.Value(myResult(FirstNA), myConditions);
      CritValue = myResult(FirstNA).CritValue();
      if (m1 < CritValue)
        m1 = CritValue;
    }
    // is it necessary to cut ?
    decision                 = myResult(FirstNA).CutSense(Crit, NumDec);
    Standard_Boolean Regular = (Crit.Repartition() == AdvApp2Var_Regular);
    //    Standard_Boolean Regular = Standard_True;
    if (Regular && decision > 0)
    {
      NbInt++;
      InitGrid(NbInt);
    }
    else
    {
      switch (decision)
      {
        case 0:
          //	Impossible to cut : the result is preserved
          if (myResult(FirstNA).HasResult())
          {
            myResult(FirstNA).OverwriteApprox();
          }
          else
          {
            myHasResult = myDone = Standard_False;
            throw Standard_ConstructionError(
              "AdvApp2Var_ApproxAFunc2Var : Surface Approximation Error");
          }
          break;
        case 1:
          //      It is necessary to cut in U
          myResult.UpdateInU(Udec);
          myConstraints.UpdateInU(Udec);
          break;
        case 2:
          //      It is necessary to cut in V
          myResult.UpdateInV(Vdec);
          myConstraints.UpdateInV(Vdec);
          break;
        case 3:
          //      It is necessary to cut in U and V
          myResult.UpdateInU(Udec);
          myConstraints.UpdateInU(Udec);
          myResult.UpdateInV(Vdec);
          myConstraints.UpdateInV(Vdec);
          break;
        default:
          myHasResult = myDone = Standard_False;
          throw Standard_ConstructionError(
            "AdvApp2Var_ApproxAFunc2Var : Surface Approximation Error");
      }
    }
  }
}

//=======================================================================
// function : ComputeConstraints without Criterion
// purpose  : Approximation of the constraints
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::ComputeConstraints(const AdvApprox_Cutting&            UChoice,
                                                    const AdvApprox_Cutting&            VChoice,
                                                    const AdvApp2Var_EvaluatorFunc2Var& Func)
{
  Standard_Real    dec;
  Standard_Boolean more;
  Standard_Integer ind1, ind2, NbPatch, NbU, NbV;
  Standard_Integer iu = myConditions.UOrder(), iv = myConditions.VOrder();
  AdvApp2Var_Node  N1(iu, iv), N2(iu, iv);

  for (Handle(AdvApp2Var_Iso) anIso = myConstraints.FirstNotApprox(ind1, ind2); !anIso.IsNull();
       anIso                        = myConstraints.FirstNotApprox(ind1, ind2))
  {
    // approximation of iso and calculation of constraints at extremities
    const Standard_Integer indN1 = myConstraints.FirstNode(anIso->Type(), ind1, ind2);
    N1                           = *myConstraints.Node(indN1);
    const Standard_Integer indN2 = myConstraints.LastNode(anIso->Type(), ind1, ind2);
    N2                           = *myConstraints.Node(indN2);

    // note that old code attempted to make copy of anIso here (but copy was incomplete)
    anIso->MakeApprox(myConditions,
                      myFirstParInU,
                      myLastParInU,
                      myFirstParInV,
                      myLastParInV,
                      Func,
                      N1,
                      N2);
    if (anIso->IsApproximated())
    {
      // iso is approached at the required tolerance
      myConstraints.ChangeIso(ind1, ind2, anIso);
      *myConstraints.Node(indN1) = N1;
      *myConstraints.Node(indN2) = N2;
    }
    else
    {
      // Approximation is not satisfactory
      NbU = myResult.NbPatchInU();
      NbV = myResult.NbPatchInV();
      if (anIso->Type() == GeomAbs_IsoV)
      {
        NbPatch = (NbU + 1) * NbV;
        more    = UChoice.Value(anIso->T0(), anIso->T1(), dec);
      }
      else
      {
        NbPatch = (NbV + 1) * NbU;
        more    = VChoice.Value(anIso->T0(), anIso->T1(), dec);
      }

      if (NbPatch <= myMaxPatches && more)
      {
        // It is possible to cut iso
        if (anIso->Type() == GeomAbs_IsoV)
        {
          myResult.UpdateInU(dec);
          myConstraints.UpdateInU(dec);
        }
        else
        {
          myResult.UpdateInV(dec);
          myConstraints.UpdateInV(dec);
        }
      }
      else
      {
        // It is not possible to cut : the result is preserved
        if (anIso->HasResult())
        {
          anIso->OverwriteApprox();
          myConstraints.ChangeIso(ind1, ind2, anIso);
          *myConstraints.Node(indN1) = N1;
          *myConstraints.Node(indN2) = N2;
        }
        else
        {
          myHasResult = myDone = Standard_False;
          throw Standard_ConstructionError(
            "AdvApp2Var_ApproxAFunc2Var : Curve Approximation Error");
        }
      }
    }
  }
}

//=======================================================================
// function : ComputeConstraints with Criterion
// purpose  : Approximation of the constraints
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::ComputeConstraints(const AdvApprox_Cutting&            UChoice,
                                                    const AdvApprox_Cutting&            VChoice,
                                                    const AdvApp2Var_EvaluatorFunc2Var& Func,
                                                    const AdvApp2Var_Criterion&         Crit)
{
  Standard_Real    dec;
  Standard_Boolean more, CritRel = (Crit.Type() == AdvApp2Var_Relative);
  Standard_Integer ind1, ind2, NbPatch, NbU, NbV;
  Standard_Integer indN1, indN2;
  Standard_Integer iu = myConditions.UOrder(), iv = myConditions.VOrder();
  AdvApp2Var_Node  N1(iu, iv), N2(iu, iv);

  for (Handle(AdvApp2Var_Iso) anIso = myConstraints.FirstNotApprox(ind1, ind2); !anIso.IsNull();
       anIso                        = myConstraints.FirstNotApprox(ind1, ind2))
  {
    // approximation of the iso and calculation of constraints at the extremities
    indN1 = myConstraints.FirstNode(anIso->Type(), ind1, ind2);
    N1    = *myConstraints.Node(indN1);
    indN2 = myConstraints.LastNode(anIso->Type(), ind1, ind2);
    N2    = *myConstraints.Node(indN2);

    // note that old code attempted to make copy of anIso here (but copy was incomplete)
    anIso->MakeApprox(myConditions,
                      myFirstParInU,
                      myLastParInU,
                      myFirstParInV,
                      myLastParInV,
                      Func,
                      N1,
                      N2);

    if (anIso->IsApproximated())
    {
      // iso is approached at the required tolerance
      myConstraints.ChangeIso(ind1, ind2, anIso);
      *myConstraints.Node(indN1) = N1;
      *myConstraints.Node(indN2) = N2;
    }
    else
    {
      // Approximation is not satisfactory
      NbU = myResult.NbPatchInU();
      NbV = myResult.NbPatchInV();
      if (anIso->Type() == GeomAbs_IsoV)
      {
        NbPatch = (NbU + 1) * NbV;
        more    = UChoice.Value(anIso->T0(), anIso->T1(), dec);
      }
      else
      {
        NbPatch = (NbV + 1) * NbU;
        more    = VChoice.Value(anIso->T0(), anIso->T1(), dec);
      }

      // To force Overwrite if the criterion is Absolute
      more = more && (CritRel);

      if (NbPatch <= myMaxPatches && more)
      {
        // It is possible to cut iso
        if (anIso->Type() == GeomAbs_IsoV)
        {
          myResult.UpdateInU(dec);
          myConstraints.UpdateInU(dec);
        }
        else
        {
          myResult.UpdateInV(dec);
          myConstraints.UpdateInV(dec);
        }
      }
      else
      {
        // It is not possible to cut: the result is preserved
        if (anIso->HasResult())
        {
          anIso->OverwriteApprox();
          myConstraints.ChangeIso(ind1, ind2, anIso);
          *myConstraints.Node(indN1) = N1;
          *myConstraints.Node(indN2) = N2;
        }
        else
        {
          myHasResult = myDone = Standard_False;
          throw Standard_ConstructionError(
            "AdvApp2Var_ApproxAFunc2Var : Curve Approximation Error");
        }
      }
    }
  }
}

//=======================================================================
// function : Compute3DErrors
// purpose  : Computation of the 3D errors
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::Compute3DErrors()
{

  Standard_Integer iesp, ipat;
  Standard_Real    error_max, error_moy, error_U0, error_V0, error_U1, error_V1;
  Standard_Real    Tol, F1Tol, F2Tol, F3Tol, F4Tol;
  if (myNumSubSpaces[2] > 0)
  {
    my3DMaxError     = new (TColStd_HArray1OfReal)(1, myNumSubSpaces[2]);
    my3DAverageError = new (TColStd_HArray1OfReal)(1, myNumSubSpaces[2]);
    my3DUFrontError  = new (TColStd_HArray1OfReal)(1, myNumSubSpaces[2]);
    my3DVFrontError  = new (TColStd_HArray1OfReal)(1, myNumSubSpaces[2]);
    for (iesp = 1; iesp <= myNumSubSpaces[2]; iesp++)
    {
      error_max = 0;
      error_moy = 0.;
      error_U0  = 0.;
      error_V0  = 0.;
      error_U1  = 0.;
      error_V1  = 0.;
      Tol       = my3DTolerances->Value(iesp);
      F1Tol     = my3DTolOnFront->Value(iesp, 1);
      F2Tol     = my3DTolOnFront->Value(iesp, 2);
      F3Tol     = my3DTolOnFront->Value(iesp, 3);
      F4Tol     = my3DTolOnFront->Value(iesp, 4);
      for (ipat = 1; ipat <= myResult.NbPatch(); ipat++)
      {
        error_max = Max((myResult(ipat).MaxErrors())->Value(iesp), error_max);
        error_U0  = Max((myResult(ipat).IsoErrors())->Value(iesp, 3), error_U0);
        error_U1  = Max((myResult(ipat).IsoErrors())->Value(iesp, 4), error_U1);
        error_V0  = Max((myResult(ipat).IsoErrors())->Value(iesp, 1), error_V0);
        error_V1  = Max((myResult(ipat).IsoErrors())->Value(iesp, 2), error_V1);
        error_moy += (myResult(ipat).AverageErrors())->Value(iesp);
      }
      my3DMaxError->SetValue(iesp, error_max);
      my3DUFrontError->SetValue(iesp, Max(error_U0, error_U1));
      my3DVFrontError->SetValue(iesp, Max(error_V0, error_V1));
      error_moy /= (Standard_Real)myResult.NbPatch();
      my3DAverageError->SetValue(iesp, error_moy);
      if (error_max > Tol || error_U0 > F3Tol || error_U1 > F4Tol || error_V0 > F1Tol
          || error_V1 > F2Tol)
      {
        myDone = Standard_False;
      }
    }
  }
}

//=======================================================================
// function : ComputeCritError
// purpose  : Computation of the max value of the Criterion
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::ComputeCritError()
{

  Standard_Integer iesp, ipat;
  Standard_Real    crit_max;
  if (myNumSubSpaces[2] > 0)
  {
    for (iesp = 1; iesp <= myNumSubSpaces[2]; iesp++)
    {
      crit_max = 0.;
      for (ipat = 1; ipat <= myResult.NbPatch(); ipat++)
      {
        crit_max = Max((myResult(ipat).CritValue()), crit_max);
      }
      myCriterionError = crit_max;
    }
  }
}

//=======================================================================
// function : ConvertBS
// purpose  : Conversion of the approximation in BSpline Surface
//=======================================================================

void AdvApp2Var_ApproxAFunc2Var::ConvertBS()
{
  // Homogeneization of degrees
  Standard_Integer iu = myConditions.UOrder(), iv = myConditions.VOrder();
  Standard_Integer ncfu = myConditions.ULimit(), ncfv = myConditions.VLimit();
  myResult.SameDegree(iu, iv, ncfu, ncfv);
  myDegreeInU = ncfu - 1;
  myDegreeInV = ncfv - 1;

  // Calculate resulting surfaces
  mySurfaces = new (TColGeom_HArray1OfSurface)(1, myNumSubSpaces[2]);

  Standard_Integer     j;
  TColStd_Array1OfReal UKnots(1, myResult.NbPatchInU() + 1);
  for (j = 1; j <= UKnots.Length(); j++)
  {
    UKnots.SetValue(j, myResult.UParameter(j));
  }

  TColStd_Array1OfReal VKnots(1, myResult.NbPatchInV() + 1);
  for (j = 1; j <= VKnots.Length(); j++)
  {
    VKnots.SetValue(j, myResult.VParameter(j));
  }

  // Prepare data for conversion grid of polynoms --> poles
  Handle(TColStd_HArray1OfReal) Uint1 = new (TColStd_HArray1OfReal)(1, 2);
  Uint1->SetValue(1, -1);
  Uint1->SetValue(2, 1);
  Handle(TColStd_HArray1OfReal) Vint1 = new (TColStd_HArray1OfReal)(1, 2);
  Vint1->SetValue(1, -1);
  Vint1->SetValue(2, 1);

  Handle(TColStd_HArray1OfReal) Uint2 = new (TColStd_HArray1OfReal)(1, myResult.NbPatchInU() + 1);
  for (j = 1; j <= Uint2->Length(); j++)
  {
    Uint2->SetValue(j, myResult.UParameter(j));
  }
  Handle(TColStd_HArray1OfReal) Vint2 = new (TColStd_HArray1OfReal)(1, myResult.NbPatchInV() + 1);
  for (j = 1; j <= Vint2->Length(); j++)
  {
    Vint2->SetValue(j, myResult.VParameter(j));
  }

  Standard_Integer nmax    = myResult.NbPatchInU() * myResult.NbPatchInV(),
                   Size_eq = myConditions.ULimit() * myConditions.VLimit() * 3;

  Handle(TColStd_HArray2OfInteger) NbCoeff = new (TColStd_HArray2OfInteger)(1, nmax, 1, 2);
  Handle(TColStd_HArray1OfReal)    Poly    = new (TColStd_HArray1OfReal)(1, nmax * Size_eq);

  Standard_Integer SSP, i;
  for (SSP = 1; SSP <= myNumSubSpaces[2]; SSP++)
  {

    // Creation of the grid of polynoms
    Standard_Integer n = 0, icf = 1, ieq;
    for (j = 1; j <= myResult.NbPatchInV(); j++)
    {
      for (i = 1; i <= myResult.NbPatchInU(); i++)
      {
        n++;
        NbCoeff->SetValue(n, 1, myResult.Patch(i, j).NbCoeffInU());
        NbCoeff->SetValue(n, 2, myResult.Patch(i, j).NbCoeffInV());
        for (ieq = 1; ieq <= Size_eq; ieq++)
        {
          Poly->SetValue(icf, (myResult.Patch(i, j).Coefficients(SSP, myConditions))->Value(ieq));
          icf++;
        }
      }
    }

    // Conversion into poles
    Convert_GridPolynomialToPoles CvP(myResult.NbPatchInU(),
                                      myResult.NbPatchInV(),
                                      iu,
                                      iv,
                                      myMaxDegInU,
                                      myMaxDegInV,
                                      NbCoeff,
                                      Poly,
                                      Uint1,
                                      Vint1,
                                      Uint2,
                                      Vint2);
    if (!CvP.IsDone())
    {
      myDone = Standard_False;
    }

    // Conversion into BSpline
    mySurfaces->ChangeValue(SSP) = new (Geom_BSplineSurface)(CvP.Poles()->Array2(),
                                                             CvP.UKnots()->Array1(),
                                                             CvP.VKnots()->Array1(),
                                                             CvP.UMultiplicities()->Array1(),
                                                             CvP.VMultiplicities()->Array1(),
                                                             CvP.UDegree(),
                                                             CvP.VDegree());
  }
}

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

Handle(TColStd_HArray1OfReal) AdvApp2Var_ApproxAFunc2Var::MaxError(
  const Standard_Integer Dimension) const
{
  Handle(TColStd_HArray1OfReal) EPtr;
  if (Dimension < 1 || Dimension > 3)
  {
    throw Standard_OutOfRange(
      "AdvApp2Var_ApproxAFunc2Var::MaxError : Dimension must be equal to 1,2 or 3 !");
  }
  switch (Dimension)
  {
    case 1:
      EPtr = my1DMaxError;
      break;
    case 2:
      EPtr = my2DMaxError;
      break;
    case 3:
      EPtr = my3DMaxError;
      break;
  }
  return EPtr;
}

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

Handle(TColStd_HArray1OfReal) AdvApp2Var_ApproxAFunc2Var::AverageError(
  const Standard_Integer Dimension) const
{
  Handle(TColStd_HArray1OfReal) EPtr;
  if (Dimension < 1 || Dimension > 3)
  {
    throw Standard_OutOfRange(
      "AdvApp2Var_ApproxAFunc2Var::AverageError : Dimension must be equal to 1,2 or 3 !");
  }
  switch (Dimension)
  {
    case 1:
      EPtr = my1DAverageError;
      break;
    case 2:
      EPtr = my2DAverageError;
      break;
    case 3:
      EPtr = my3DAverageError;
      break;
  }
  return EPtr;
}

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

Handle(TColStd_HArray1OfReal) AdvApp2Var_ApproxAFunc2Var::UFrontError(
  const Standard_Integer Dimension) const
{
  Handle(TColStd_HArray1OfReal) EPtr;
  if (Dimension < 1 || Dimension > 3)
  {
    throw Standard_OutOfRange(
      "AdvApp2Var_ApproxAFunc2Var::UFrontError : Dimension must be equal to 1,2 or 3 !");
  }
  switch (Dimension)
  {
    case 1:
      EPtr = my1DUFrontError;
      break;
    case 2:
      EPtr = my2DUFrontError;
      break;
    case 3:
      EPtr = my3DUFrontError;
      break;
  }
  return EPtr;
}

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

Handle(TColStd_HArray1OfReal) AdvApp2Var_ApproxAFunc2Var::VFrontError(
  const Standard_Integer Dimension) const
{
  Handle(TColStd_HArray1OfReal) EPtr;
  if (Dimension <= 0 || Dimension > 3)
  {
    throw Standard_OutOfRange(
      "AdvApp2Var_ApproxAFunc2Var::VFrontError : Dimension must be equal to 1,2 or 3 !");
  }
  switch (Dimension)
  {
    case 1:
      EPtr = my1DVFrontError;
      break;
    case 2:
      EPtr = my2DVFrontError;
      break;
    case 3:
      EPtr = my3DVFrontError;
      break;
  }
  return EPtr;
}

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

Standard_Real AdvApp2Var_ApproxAFunc2Var::MaxError(const Standard_Integer Dimension,
                                                   const Standard_Integer SSPIndex) const
{
  if (Dimension != 3 || SSPIndex != 1)
  {
    throw Standard_OutOfRange("AdvApp2Var_ApproxAFunc2Var::MaxError: ONE Surface 3D only !");
  }
  Handle(TColStd_HArray1OfReal) EPtr = MaxError(Dimension);
  return EPtr->Value(SSPIndex);
}

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

Standard_Real AdvApp2Var_ApproxAFunc2Var::AverageError(const Standard_Integer Dimension,
                                                       const Standard_Integer SSPIndex) const
{
  if (Dimension != 3 || SSPIndex != 1)
  {
    throw Standard_OutOfRange("AdvApp2Var_ApproxAFunc2Var::AverageError : ONE Surface 3D only !");
  }
  Handle(TColStd_HArray1OfReal) EPtr = AverageError(Dimension);
  return EPtr->Value(SSPIndex);
}

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

Standard_Real AdvApp2Var_ApproxAFunc2Var::UFrontError(const Standard_Integer Dimension,
                                                      const Standard_Integer SSPIndex) const
{
  if (Dimension != 3 || SSPIndex != 1)
  {
    throw Standard_OutOfRange("AdvApp2Var_ApproxAFunc2Var::UFrontError : ONE Surface 3D only !");
  }
  Handle(TColStd_HArray1OfReal) EPtr = UFrontError(Dimension);
  return EPtr->Value(SSPIndex);
}

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

Standard_Real AdvApp2Var_ApproxAFunc2Var::VFrontError(const Standard_Integer Dimension,
                                                      const Standard_Integer SSPIndex) const
{
  if (Dimension != 3 || SSPIndex != 1)
  {
    throw Standard_OutOfRange("AdvApp2Var_ApproxAFunc2Var::VFrontError : ONE Surface 3D only !");
  }
  Handle(TColStd_HArray1OfReal) EPtr = VFrontError(Dimension);
  return EPtr->Value(SSPIndex);
}

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

Standard_Real AdvApp2Var_ApproxAFunc2Var::CritError(const Standard_Integer Dimension,
                                                    const Standard_Integer SSPIndex) const
{
  if (Dimension != 3 || SSPIndex != 1)
  {
    throw Standard_OutOfRange("AdvApp2Var_ApproxAFunc2Var::CritError: ONE Surface 3D only !");
  }
  return myCriterionError;
}

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

void AdvApp2Var_ApproxAFunc2Var::Dump(Standard_OStream& o) const
{
  Standard_Integer iesp = 1, NbKU, NbKV, ik;
  o << std::endl;
  if (!myHasResult)
  {
    o << "No result" << std::endl;
  }
  else
  {
    o << "There is a result";
    if (myDone)
    {
      o << " within the requested tolerance " << my3DTolerances->Value(iesp) << std::endl;
    }
    else if (my3DMaxError->Value(iesp) > my3DTolerances->Value(iesp))
    {
      o << " WITHOUT the requested tolerance " << my3DTolerances->Value(iesp) << std::endl;
    }
    else
    {
      o << " WITHOUT the requested continuities " << std::endl;
    }
    o << std::endl;
    o << "Result max error :" << my3DMaxError->Value(iesp) << std::endl;
    o << "Result average error :" << my3DAverageError->Value(iesp) << std::endl;
    o << "Result max error on U frontiers :" << my3DUFrontError->Value(iesp) << std::endl;
    o << "Result max error on V frontiers :" << my3DVFrontError->Value(iesp) << std::endl;
    o << std::endl;
    o << "Degree of Bezier patches in U : " << myDegreeInU << "  in V : " << myDegreeInV
      << std::endl;
    o << std::endl;
    Handle(Geom_BSplineSurface) S = Handle(Geom_BSplineSurface)::DownCast(mySurfaces->Value(iesp));
    o << "Number of poles in U : " << S->NbUPoles() << "  in V : " << S->NbVPoles() << std::endl;
    o << std::endl;
    NbKU = S->NbUKnots();
    NbKV = S->NbVKnots();
    o << "Number of knots in U : " << NbKU << std::endl;
    for (ik = 1; ik <= NbKU; ik++)
    {
      o << "   " << ik << " : " << S->UKnot(ik) << "   mult : " << S->UMultiplicity(ik)
        << std::endl;
    }
    o << std::endl;
    o << "Number of knots in V : " << NbKV << std::endl;
    for (ik = 1; ik <= NbKV; ik++)
    {
      o << "   " << ik << " : " << S->VKnot(ik) << "   mult : " << S->VMultiplicity(ik)
        << std::endl;
    }
    o << std::endl;
  }
}
