// Created on: 1995-07-18
// Created by: Joelle CHAUVET
// Copyright (c) 1995-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

// Modified:	Mon Jan 12 10:50:10 1998
//              gestion automatique de l'origine et de l'orientation
//              avec la methode ArrangeWires
// Modified:	Mon Jan 19 10:11:56 1998
//              traitement des cas particuliers cylindre, cone, plan
//              (methodes DetectKPart et CreateKPart)
// Modified:	Mon Feb 23 09:28:46 1998
//              traitement des sections avec nombre d'elements different
//              + quelques ameliorations pour les cas particuliers
//              + cas de la derniere section ponctuelle
// Modified:	Mon Apr  6 15:47:44 1998
//              traitement des cas particuliers deplace dans BRepFill
// Modified:	Thu Apr 30 15:24:17 1998
//              separation sections fermees / sections ouvertes + debug
// Modified:	Fri Jul 10 11:23:35 1998
//              surface de CreateSmoothed par concatenation,approximation
//              et segmentation (PRO13924, CTS21295)
// Modified:	Tue Jul 21 16:48:35 1998
//              pb de ratio (BUC60281)
// Modified:	Thu Jul 23 11:38:36 1998
//              sections bouclantes
// Modified:	Fri Aug 28 10:13:44 1998
//              traitement des sections ponctuelles
//              dans l'historique (cf. loft06 et loft09)
//              et dans le cas des solides
// Modified:	Tue Nov  3 10:06:15 1998
//              utilisation de BRepFill_CompatibleWires

#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepBuilderAPI_FindPlane.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <BRepFill_CompatibleWires.hxx>
#include <BRepFill_Generator.hxx>
#include <BRepLib.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <BRepTools_WireExplorer.hxx>
#include <BRepTools_ReShape.hxx>
#include <BSplCLib.hxx>
#include <Geom2d_Line.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_Conic.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Plane.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <GeomAbs_Shape.hxx>
#include <GeomConvert.hxx>
#include <GeomConvert_ApproxCurve.hxx>
#include <GeomConvert_CompCurveToBSplineCurve.hxx>
#include <GeomFill_AppSurf.hxx>
#include <GeomFill_Line.hxx>
#include <GeomFill_SectionGenerator.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Pnt.hxx>
#include <gp_Pnt2d.hxx>
#include <Precision.hxx>
#include <Standard_DomainError.hxx>
#include <Standard_NullObject.hxx>
#include <NCollection_Array1.hxx>
#include <TopExp.hxx>
#include <TopLoc_Location.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Solid.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <TopTools_ShapeMapHasher.hxx>
#include <NCollection_DataMap.hxx>
#include <NCollection_List.hxx>
#include <NCollection_IndexedDataMap.hxx>
#include <BRepAdaptor_Surface.hxx>

//=======================================================================
// function : PreciseUpar
// purpose  : pins the u-parameter of surface close to U-knot
//           to this U-knot
//=======================================================================
static double PreciseUpar(const double anUpar, const occ::handle<Geom_BSplineSurface>& aSurface)
{
  constexpr double Tol = Precision::PConfusion();
  int              i1, i2;

  aSurface->LocateU(anUpar, Tol, i1, i2);
  double U1 = aSurface->UKnot(i1);
  double U2 = aSurface->UKnot(i2);

  double NewU = anUpar;

  NewU = (anUpar - U1 < U2 - anUpar) ? U1 : U2;
  return NewU;
}

//=======================================================================
// function :  PerformPlan
// purpose  : Construct a plane of filling if exists
//=======================================================================

static bool PerformPlan(const TopoDS_Wire& W, const double presPln, TopoDS_Face& theFace)
{
  bool            isDegen = true;
  TopoDS_Iterator iter(W);
  for (; iter.More(); iter.Next())
  {
    const TopoDS_Edge& anEdge = TopoDS::Edge(iter.Value());
    if (!BRep_Tool::Degenerated(anEdge))
      isDegen = false;
  }
  if (isDegen)
    return true;

  bool Ok = false;
  if (!W.IsNull())
  {
    BRepBuilderAPI_FindPlane Searcher(W, presPln);
    if (Searcher.Found())
    {
      theFace = BRepBuilderAPI_MakeFace(Searcher.Plane(), W);
      Ok      = true;
    }
    else // try to find another surface
    {
      BRepBuilderAPI_MakeFace MF(W);
      if (MF.IsDone())
      {
        theFace = MF.Face();
        Ok      = true;
      }
    }
  }

  return Ok;
}

//=============================================================================
// function :  IsSameOriented
// purpose  : Checks whether aFace is oriented to the same side as aShell or not
//=============================================================================

static bool IsSameOriented(const TopoDS_Shape& aFace, const TopoDS_Shape& aShell)
{
  TopExp_Explorer    Explo(aFace, TopAbs_EDGE);
  TopoDS_Shape       anEdge = Explo.Current();
  TopAbs_Orientation Or1    = anEdge.Orientation();

  NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>
    EFmap;
  TopExp::MapShapesAndAncestors(aShell, TopAbs_EDGE, TopAbs_FACE, EFmap);

  const TopoDS_Shape& AdjacentFace = EFmap.FindFromKey(anEdge).First();
  TopoDS_Shape        theEdge;
  for (Explo.Init(AdjacentFace, TopAbs_EDGE); Explo.More(); Explo.Next())
  {
    theEdge = Explo.Current();
    if (theEdge.IsSame(anEdge))
      break;
  }

  TopAbs_Orientation Or2 = theEdge.Orientation();
  if (Or1 == Or2)
    return false;
  return true;
}

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

static TopoDS_Solid MakeSolid(TopoDS_Shell&      shell,
                              const TopoDS_Wire& wire1,
                              const TopoDS_Wire& wire2,
                              const double       presPln,
                              TopoDS_Face&       face1,
                              TopoDS_Face&       face2)
{
  if (shell.IsNull())
    throw StdFail_NotDone("Thrusections is not build");
  bool         B = shell.Closed();
  BRep_Builder BB;

  if (!B)
  {
    // It is necessary to close the extremities
    B = PerformPlan(wire1, presPln, face1);
    if (B)
    {
      B = PerformPlan(wire2, presPln, face2);
      if (B)
      {
        if (!face1.IsNull() && !IsSameOriented(face1, shell))
          face1.Reverse();
        if (!face2.IsNull() && !IsSameOriented(face2, shell))
          face2.Reverse();

        if (!face1.IsNull())
          BB.Add(shell, face1);
        if (!face2.IsNull())
          BB.Add(shell, face2);

        shell.Closed(true);
      }
    }
  }

  TopoDS_Solid solid;
  BB.MakeSolid(solid);
  BB.Add(solid, shell);

  // verify the orientation the solid
  BRepClass3d_SolidClassifier clas3d(solid);
  clas3d.PerformInfinitePoint(Precision::Confusion());
  if (clas3d.State() == TopAbs_IN)
  {
    BB.MakeSolid(solid);
    TopoDS_Shape aLocalShape = shell.Reversed();
    BB.Add(solid, TopoDS::Shell(aLocalShape));
    //    B.Add(solid, TopoDS::Shell(newShell.Reversed()));
  }

  solid.Closed(true);
  return solid;
}

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

BRepOffsetAPI_ThruSections::BRepOffsetAPI_ThruSections(const bool   isSolid,
                                                       const bool   ruled,
                                                       const double pres3d)
    : myNbEdgesInSection(0),
      myIsSolid(isSolid),
      myIsRuled(ruled),
      myPres3d(pres3d),
      myDegen1(false),
      myDegen2(false)
{
  myWCheck       = true;
  myMutableInput = true;
  //----------------------------
  myParamType      = Approx_ChordLength;
  myDegMax         = 8;
  myContinuity     = GeomAbs_C2;
  myCritWeights[0] = .4;
  myCritWeights[1] = .2;
  myCritWeights[2] = .4;
  myUseSmoothing   = false;
  myStatus         = BRepFill_ThruSectionErrorStatus_NotDone;
}

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

void BRepOffsetAPI_ThruSections::Init(const bool isSolid, const bool ruled, const double pres3d)
{
  myIsSolid      = isSolid;
  myIsRuled      = ruled;
  myPres3d       = pres3d;
  myWCheck       = true;
  myMutableInput = true;
  //----------------------------
  myParamType      = Approx_ChordLength;
  myDegMax         = 6;
  myContinuity     = GeomAbs_C2;
  myCritWeights[0] = .4;
  myCritWeights[1] = .2;
  myCritWeights[2] = .4;
  myUseSmoothing   = false;
  myStatus         = BRepFill_ThruSectionErrorStatus_NotDone;
}

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

void BRepOffsetAPI_ThruSections::AddWire(const TopoDS_Wire& wire)
{
  myWires.Append(wire);
  myInputWires.Append(wire);
}

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

void BRepOffsetAPI_ThruSections::AddVertex(const TopoDS_Vertex& aVertex)
{
  BRep_Builder BB;

  TopoDS_Edge DegEdge;
  BB.MakeEdge(DegEdge);
  BB.Add(DegEdge, aVertex.Oriented(TopAbs_FORWARD));
  BB.Add(DegEdge, aVertex.Oriented(TopAbs_REVERSED));
  BB.Degenerated(DegEdge, true);

  TopoDS_Wire DegWire;
  BB.MakeWire(DegWire);
  BB.Add(DegWire, DegEdge);
  DegWire.Closed(true);

  myWires.Append(DegWire);
  myInputWires.Append(DegWire);
}

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

void BRepOffsetAPI_ThruSections::CheckCompatibility(const bool check)
{
  myWCheck = check;
}

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

void BRepOffsetAPI_ThruSections::Build(const Message_ProgressRange& /*theRange*/)
{
  myStatus = BRepFill_ThruSectionErrorStatus_Done;
  myBFGenerator.Nullify();
  // Check set of section for right configuration of punctual sections
  int             i;
  TopExp_Explorer explo;
  for (i = 2; i <= myWires.Length() - 1; i++)
  {
    bool wdeg = true;
    for (explo.Init(myWires(i), TopAbs_EDGE); explo.More(); explo.Next())
    {
      const TopoDS_Edge& anEdge = TopoDS::Edge(explo.Current());
      wdeg                      = wdeg && (BRep_Tool::Degenerated(anEdge));
    }
    if (wdeg)
    {
      myStatus = BRepFill_ThruSectionErrorStatus_WrongUsage;
      return;
    }
  }
  if (myWires.Length() <= 2)
  {
    bool wdeg = true;
    for (i = 1; i <= myWires.Length(); i++)
    {
      for (explo.Init(myWires(i), TopAbs_EDGE); explo.More(); explo.Next())
      {
        const TopoDS_Edge& anEdge = TopoDS::Edge(explo.Current());
        wdeg                      = wdeg && (BRep_Tool::Degenerated(anEdge));
      }
    }
    if (wdeg)
    {
      myStatus = BRepFill_ThruSectionErrorStatus_WrongUsage;
      return;
    }
  }

  myNbEdgesInSection = 0;

  if (myWCheck)
  {
    // compute origin and orientation on wires to avoid twisted results
    // and update wires to have same number of edges

    // use BRepFill_CompatibleWires
    NCollection_Sequence<TopoDS_Shape> WorkingSections;
    WorkingSections.Clear();
    NCollection_DataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>
      WorkingMap;
    WorkingMap.Clear();

    // Calculate the working sections
    BRepFill_CompatibleWires Georges(myWires);
    Georges.Perform();
    if (Georges.IsDone())
    {
      WorkingSections = Georges.Shape();
      WorkingMap      = Georges.Generated();
      myDegen1        = Georges.IsDegeneratedFirstSection();
      myDegen2        = Georges.IsDegeneratedLastSection();
      // For each sub-edge of each section
      // we save its splits
      int IndFirstSec = 1;
      if (Georges.IsDegeneratedFirstSection())
        IndFirstSec = 2;
      TopoDS_Wire aWorkingSection = TopoDS::Wire(WorkingSections(IndFirstSec));
      myNbEdgesInSection += aWorkingSection.NbChildren();
      for (int ii = 1; ii <= myWires.Length(); ii++)
      {
        TopoDS_Iterator itw(myWires(ii));
        for (; itw.More(); itw.Next())
        {
          const TopoDS_Edge& anEdge = TopoDS::Edge(itw.Value());
          int                aSign  = 1;
          TopoDS_Vertex      Vfirst, Vlast;
          TopExp::Vertices(anEdge, Vfirst, Vlast);
          const NCollection_List<TopoDS_Shape>& aNewEdges = Georges.GeneratedShapes(anEdge);
          NCollection_List<int>                 IList;
          aWorkingSection                                     = TopoDS::Wire(WorkingSections(ii));
          int                                      NbNewEdges = aNewEdges.Extent();
          NCollection_List<TopoDS_Shape>::Iterator itl(aNewEdges);
          for (int kk = 1; itl.More(); itl.Next(), kk++)
          {
            const TopoDS_Edge&     aNewEdge = TopoDS::Edge(itl.Value());
            int                    inde     = 1;
            BRepTools_WireExplorer wexp(aWorkingSection);
            for (; wexp.More(); wexp.Next(), inde++)
            {
              const TopoDS_Shape& aWorkingEdge = wexp.Current();
              if (aWorkingEdge.IsSame(aNewEdge))
              {
                aSign = (aWorkingEdge.Orientation() == TopAbs_FORWARD) ? 1 : -1;
                break;
              }
            }
            IList.Append(inde);
            if (kk == 1 || kk == NbNewEdges)
            {
              // For each sub-vertex of each section
              // we save its index of new edge
              TopoDS_Vertex NewVfirst, NewVlast;
              TopExp::Vertices(aNewEdge, NewVfirst, NewVlast);
              if (NewVfirst.IsSame(Vfirst) && !myVertexIndex.IsBound(Vfirst))
                myVertexIndex.Bind(Vfirst, aSign * inde);
              if (NewVlast.IsSame(Vlast) && !myVertexIndex.IsBound(Vlast))
                myVertexIndex.Bind(Vlast, aSign * (-inde));
            }
          }
          myEdgeNewIndices.Bind(anEdge, IList);
        }
      }
    }
    else
    {
      myStatus = Georges.GetStatus();
      NotDone();
      return;
    }

    myWires = WorkingSections;
  } // if (myWCheck)
  else // no check
  {
    TopoDS_Edge anEdge;
    for (int ii = 1; ii <= myWires.Length(); ii++)
    {
      TopExp_Explorer Explo(myWires(ii), TopAbs_EDGE);
      int             inde = 1;
      for (; Explo.More(); Explo.Next(), inde++)
      {
        anEdge = TopoDS::Edge(Explo.Current());
        NCollection_List<int> IList;
        IList.Append(inde);
        myEdgeNewIndices.Bind(anEdge, IList);
        TopoDS_Vertex V1, V2;
        TopExp::Vertices(anEdge, V1, V2);
        if (!myVertexIndex.IsBound(V1))
          myVertexIndex.Bind(V1, inde);
        if (!myVertexIndex.IsBound(V2))
          myVertexIndex.Bind(V2, -inde);
      }
      inde--;
      if (inde > myNbEdgesInSection)
        myNbEdgesInSection = inde;
      if (inde == 1 && BRep_Tool::Degenerated(anEdge))
      {
        if (ii == 1)
          myDegen1 = true;
        else
          myDegen2 = true;
      }
    }
  }

  try
  {
    // Calculate the resulting shape
    if (myWires.Length() == 2 || myIsRuled)
    {
      // create a ruled shell
      CreateRuled();
    }
    else
    {
      // create a smoothed shell
      CreateSmoothed();
    }
  }
  catch (Standard_Failure const&)
  {
    NotDone();
    return;
  }

  if (myStatus != BRepFill_ThruSectionErrorStatus_Done)
  {
    NotDone();
    return;
  }
  // Encode the Regularities
  BRepLib::EncodeRegularity(myShape);
}

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

void BRepOffsetAPI_ThruSections::CreateRuled()
{
  int nbSects   = myWires.Length();
  myBFGenerator = new BRepFill_Generator();
  myBFGenerator->SetMutableInput(IsMutableInput());
  //  for (int i=1; i<=nbSects; i++) {
  int i;
  for (i = 1; i <= nbSects; i++)
  {
    myBFGenerator->AddWire(TopoDS::Wire(myWires(i)));
  }
  myBFGenerator->Perform();
  BRepFill_ThruSectionErrorStatus aStatus = myBFGenerator->GetStatus();
  if (aStatus != BRepFill_ThruSectionErrorStatus_Done)
  {
    myStatus = aStatus;
    return;
  }
  TopoDS_Shell shell = myBFGenerator->Shell();

  if (myIsSolid)
  {

    // check if the first wire is the same as the last
    bool vClosed = (myWires(1).IsSame(myWires(nbSects)));

    if (vClosed)
    {

      TopoDS_Solid solid;
      BRep_Builder B;
      B.MakeSolid(solid);
      B.Add(solid, shell);

      // verify the orientation of the solid
      BRepClass3d_SolidClassifier clas3d(solid);
      clas3d.PerformInfinitePoint(Precision::Confusion());
      if (clas3d.State() == TopAbs_IN)
      {
        B.MakeSolid(solid);
        TopoDS_Shape aLocalShape = shell.Reversed();
        B.Add(solid, TopoDS::Shell(aLocalShape));
        //	B.Add(solid, TopoDS::Shell(shell.Reversed()));
      }
      myShape = solid;
    }

    else
    {
      // myBFGenerator stores the same 'myWires'
      TopoDS_Wire wire1 = TopoDS::Wire(myBFGenerator->ResultShape(myWires.First()));
      TopoDS_Wire wire2 = TopoDS::Wire(myBFGenerator->ResultShape(myWires.Last()));

      myShape = MakeSolid(shell, wire1, wire2, myPres3d, myFirst, myLast);
    }

    Done();
  }

  else
  {
    myShape = shell;
    Done();
  }

  // history
  BRepTools_WireExplorer anExp1, anExp2;
  NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>
    M;
  TopExp::MapShapesAndAncestors(shell, TopAbs_EDGE, TopAbs_FACE, M);
  NCollection_List<TopoDS_Shape>::Iterator it;

  NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>
    MV;
  TopExp::MapShapesAndAncestors(shell, TopAbs_VERTEX, TopAbs_FACE, MV);

  for (i = 1; i <= nbSects - 1; i++)
  {

    const TopoDS_Wire& wire1 = TopoDS::Wire(myWires(i));
    const TopoDS_Wire& wire2 = TopoDS::Wire(myWires(i + 1));

    anExp1.Init(wire1);
    anExp2.Init(wire2);

    bool tantque = anExp1.More() && anExp2.More();

    while (tantque)
    {

      const TopoDS_Shape& edge1  = anExp1.Current();
      const TopoDS_Shape& edge2  = anExp2.Current();
      bool                degen1 = BRep_Tool::Degenerated(anExp1.Current());
      bool                degen2 = BRep_Tool::Degenerated(anExp2.Current());

      NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> MapFaces;
      if (degen2)
      {
        TopoDS_Vertex Vdegen =
          TopoDS::Vertex(myBFGenerator->ResultShape(TopExp::FirstVertex(TopoDS::Edge(edge2))));
        for (it.Initialize(MV.FindFromKey(Vdegen)); it.More(); it.Next())
        {
          MapFaces.Add(it.Value());
        }
      }
      else
      {
        for (it.Initialize(M.FindFromKey(myBFGenerator->ResultShape(edge2))); it.More(); it.Next())
        {
          MapFaces.Add(it.Value());
        }
      }

      if (degen1)
      {
        TopoDS_Vertex Vdegen =
          TopoDS::Vertex(myBFGenerator->ResultShape(TopExp::FirstVertex(TopoDS::Edge(edge1))));
        for (it.Initialize(MV.FindFromKey(Vdegen)); it.More(); it.Next())
        {
          const TopoDS_Shape& Face = it.Value();
          if (MapFaces.Contains(Face))
          {
            myEdgeFace.Bind(edge1, Face);
            break;
          }
        }
      }
      else
      {
        for (it.Initialize(M.FindFromKey(myBFGenerator->ResultShape(edge1))); it.More(); it.Next())
        {
          const TopoDS_Shape& Face = it.Value();
          if (MapFaces.Contains(Face))
          {
            myEdgeFace.Bind(edge1, Face);
            break;
          }
        }
      }

      if (!degen1)
        anExp1.Next();
      if (!degen2)
        anExp2.Next();

      tantque = anExp1.More() && anExp2.More();
      if (degen1)
        tantque = anExp2.More();
      if (degen2)
        tantque = anExp1.More();
    }
  }
}

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

void BRepOffsetAPI_ThruSections::CreateSmoothed()
{
  // initialisation
  int                    nbSects = myWires.Length();
  BRepTools_WireExplorer anExp;

  bool w1Point = true;
  // check if the first wire is punctual
  for (anExp.Init(TopoDS::Wire(myWires(1))); anExp.More(); anExp.Next())
  {
    w1Point = w1Point && (BRep_Tool::Degenerated(anExp.Current()));
  }

  bool w2Point = true;
  // check if the last wire is punctual
  for (anExp.Init(TopoDS::Wire(myWires(nbSects))); anExp.More(); anExp.Next())
  {
    w2Point = w2Point && (BRep_Tool::Degenerated(anExp.Current()));
  }

  bool vClosed = false;
  // check if the first wire is the same as last
  if (myWires(1).IsSame(myWires(myWires.Length())))
    vClosed = true;

  // find the dimension
  int nbEdges = 0;
  if (!w1Point)
  {
    for (anExp.Init(TopoDS::Wire(myWires(1))); anExp.More(); anExp.Next())
    {
      nbEdges++;
    }
  }
  else
  {
    for (anExp.Init(TopoDS::Wire(myWires(2))); anExp.More(); anExp.Next())
    {
      nbEdges++;
    }
  }

  // recover the shapes
  bool                             uClosed = true;
  NCollection_Array1<TopoDS_Shape> shapes(1, nbSects * nbEdges);
  int                              nb = 0, i, j;

  for (i = 1; i <= nbSects; i++)
  {
    const TopoDS_Wire& wire = TopoDS::Wire(myWires(i));
    if (!wire.Closed())
    {
      // check if the vertices are the same
      TopoDS_Vertex V1, V2;
      TopExp::Vertices(wire, V1, V2);
      if (!V1.IsSame(V2))
        uClosed = false;
    }
    if ((i == 1 && w1Point) || (i == nbSects && w2Point))
    {
      // if the wire is punctual
      anExp.Init(TopoDS::Wire(wire));
      for (j = 1; j <= nbEdges; j++)
      {
        nb++;
        shapes(nb) = anExp.Current();
      }
    }
    else
    {
      // otherwise
      for (anExp.Init(TopoDS::Wire(wire)); anExp.More(); anExp.Next())
      {
        nb++;
        shapes(nb) = anExp.Current();
      }
    }
  }

  // create the new surface
  TopoDS_Shell                     shell;
  TopoDS_Face                      face;
  TopoDS_Wire                      W;
  TopoDS_Edge                      edge, edge1, edge2, edge3, edge4, couture;
  NCollection_Array1<TopoDS_Shape> vcouture(1, nbEdges);

  BRep_Builder B;
  B.MakeShell(shell);

  TopoDS_Wire  newW1, newW2;
  BRep_Builder BW1, BW2;
  BW1.MakeWire(newW1);
  BW2.MakeWire(newW2);

  TopLoc_Location loc;
  TopoDS_Vertex   v1f, v1l, v2f, v2l;

  int                        nbPnts = 21;
  NCollection_Array2<gp_Pnt> points(1, nbPnts, 1, nbSects);

  // concatenate each section to get a total surface that will be segmented
  occ::handle<Geom_BSplineSurface> TS;
  TS = TotalSurf(shapes, nbSects, nbEdges, w1Point, w2Point, vClosed);

  if (TS.IsNull())
  {
    myStatus = BRepFill_ThruSectionErrorStatus_Failed;
    return;
  }

  TopoDS_Shape firstEdge;
  for (i = 1; i <= nbEdges; i++)
  {

    // segmentation of TS
    occ::handle<Geom_BSplineSurface> surface;
    surface = occ::down_cast<Geom_BSplineSurface>(TS->Copy());
    double Ui1, Ui2, V0, V1;
    Ui1 = i - 1;
    Ui2 = i;
    Ui1 = PreciseUpar(Ui1, surface);
    Ui2 = PreciseUpar(Ui2, surface);
    V0  = surface->VKnot(surface->FirstVKnotIndex());
    V1  = surface->VKnot(surface->LastVKnotIndex());
    surface->Segment(Ui1, Ui2, V0, V1);

    // return vertices
    edge = TopoDS::Edge(shapes(i));
    TopExp::Vertices(edge, v1f, v1l);
    if (edge.Orientation() == TopAbs_REVERSED)
      TopExp::Vertices(edge, v1l, v1f);
    firstEdge = edge;

    edge = TopoDS::Edge(shapes((nbSects - 1) * nbEdges + i));
    TopExp::Vertices(edge, v2f, v2l);
    if (edge.Orientation() == TopAbs_REVERSED)
      TopExp::Vertices(edge, v2l, v2f);

    // make the face
    B.MakeFace(face, surface, Precision::Confusion());

    // make the wire
    B.MakeWire(W);

    // make the missing edges
    double f1, f2, l1, l2;
    surface->Bounds(f1, l1, f2, l2);

    // --- edge 1
    if (w1Point)
    {
      // copy the degenerated edge
      TopoDS_Shape aLocalShape = shapes(1).EmptyCopied();
      edge1                    = TopoDS::Edge(aLocalShape);
      //      edge1 =  TopoDS::Edge(shapes(1).EmptyCopied());
      edge1.Orientation(TopAbs_FORWARD);
    }
    else
    {
      B.MakeEdge(edge1, surface->VIso(f2), Precision::Confusion());
    }
    v1f.Orientation(TopAbs_FORWARD);
    B.Add(edge1, v1f);
    v1l.Orientation(TopAbs_REVERSED);
    B.Add(edge1, v1l);
    B.Range(edge1, f1, l1);
    // processing of looping sections
    // store edges of the 1st section
    if (vClosed)
      vcouture(i) = edge1;

    // --- edge 2
    if (vClosed)
      edge2 = TopoDS::Edge(vcouture(i));
    else
    {
      if (w2Point)
      {
        // copy of the degenerated edge
        TopoDS_Shape aLocalShape = shapes(nbSects * nbEdges).EmptyCopied();
        edge2                    = TopoDS::Edge(aLocalShape);
        //	edge2 =  TopoDS::Edge(shapes(nbSects*nbEdges).EmptyCopied());
        edge2.Orientation(TopAbs_FORWARD);
      }
      else
      {
        B.MakeEdge(edge2, surface->VIso(l2), Precision::Confusion());
      }
      v2f.Orientation(TopAbs_FORWARD);
      B.Add(edge2, v2f);
      v2l.Orientation(TopAbs_REVERSED);
      B.Add(edge2, v2l);
      B.Range(edge2, f1, l1);
    }
    edge2.Reverse();

    // --- edge 3
    if (i == 1)
    {
      B.MakeEdge(edge3, surface->UIso(f1), Precision::Confusion());
      v1f.Orientation(TopAbs_FORWARD);
      B.Add(edge3, v1f);
      v2f.Orientation(TopAbs_REVERSED);
      B.Add(edge3, v2f);
      B.Range(edge3, f2, l2);
      if (uClosed)
      {
        couture = edge3;
      }
    }
    else
    {
      edge3 = edge4;
    }
    edge3.Reverse();

    // --- edge 4
    if (uClosed && i == nbEdges)
    {
      edge4 = couture;
    }
    else
    {
      B.MakeEdge(edge4, surface->UIso(l1), Precision::Confusion());
      v1l.Orientation(TopAbs_FORWARD);
      B.Add(edge4, v1l);
      v2l.Orientation(TopAbs_REVERSED);
      B.Add(edge4, v2l);
      B.Range(edge4, f2, l2);
    }

    B.Add(W, edge1);
    B.Add(W, edge4);
    B.Add(W, edge2);
    B.Add(W, edge3);

    // set PCurve
    if (vClosed)
    {
      B.UpdateEdge(edge1,
                   new Geom2d_Line(gp_Pnt2d(0, f2), gp_Dir2d(gp_Dir2d::D::X)),
                   new Geom2d_Line(gp_Pnt2d(0, l2), gp_Dir2d(gp_Dir2d::D::X)),
                   face,
                   Precision::Confusion());
      B.Range(edge1, face, f1, l1);
    }
    else
    {
      B.UpdateEdge(edge1,
                   new Geom2d_Line(gp_Pnt2d(0, f2), gp_Dir2d(gp_Dir2d::D::X)),
                   face,
                   Precision::Confusion());
      B.Range(edge1, face, f1, l1);
      B.UpdateEdge(edge2,
                   new Geom2d_Line(gp_Pnt2d(0, l2), gp_Dir2d(gp_Dir2d::D::X)),
                   face,
                   Precision::Confusion());
      B.Range(edge2, face, f1, l1);
    }

    if (uClosed && nbEdges == 1)
    {
      B.UpdateEdge(edge3,
                   new Geom2d_Line(gp_Pnt2d(l1, 0), gp_Dir2d(gp_Dir2d::D::Y)),
                   new Geom2d_Line(gp_Pnt2d(f1, 0), gp_Dir2d(gp_Dir2d::D::Y)),
                   face,
                   Precision::Confusion());
      B.Range(edge3, face, f2, l2);
    }
    else
    {
      B.UpdateEdge(edge3,
                   new Geom2d_Line(gp_Pnt2d(f1, 0), gp_Dir2d(gp_Dir2d::D::Y)),
                   face,
                   Precision::Confusion());
      B.Range(edge3, face, f2, l2);
      B.UpdateEdge(edge4,
                   new Geom2d_Line(gp_Pnt2d(l1, 0), gp_Dir2d(gp_Dir2d::D::Y)),
                   face,
                   Precision::Confusion());
      B.Range(edge4, face, f2, l2);
    }
    B.Add(face, W);
    B.Add(shell, face);

    // complete newW1 newW2
    TopoDS_Edge edge12 = edge1;
    TopoDS_Edge edge22 = edge2;
    edge12.Reverse();
    edge22.Reverse();
    BW1.Add(newW1, edge12);
    BW2.Add(newW2, edge22);

    // history
    myEdgeFace.Bind(firstEdge, face);
  }

  if (uClosed && w1Point && w2Point)
    shell.Closed(true);

  if (myIsSolid)
  {

    if (vClosed)
    {

      TopoDS_Solid solid;
      B.MakeSolid(solid);
      B.Add(solid, shell);

      // verify the orientation the solid
      BRepClass3d_SolidClassifier clas3d(solid);
      clas3d.PerformInfinitePoint(Precision::Confusion());
      if (clas3d.State() == TopAbs_IN)
      {
        B.MakeSolid(solid);
        TopoDS_Shape aLocalShape = shell.Reversed();
        B.Add(solid, TopoDS::Shell(aLocalShape));
        //	B.Add(solid, TopoDS::Shell(shell.Reversed()));
      }
      myShape = solid;
    }

    else
    {
      myShape = MakeSolid(shell, newW1, newW2, myPres3d, myFirst, myLast);
    }
  }

  else
  {
    myShape = shell;
  }
  Done();

  NCollection_DataMap<TopoDS_Shape, double, TopTools_ShapeMapHasher> aVertexToleranceMap;
  TopExp_Explorer aTopExplorer(myShape, TopAbs_EDGE);
  while (aTopExplorer.More())
  {
    const TopoDS_Edge& aCurEdge = TopoDS::Edge(aTopExplorer.Current());
    B.SameRange(aCurEdge, false);
    B.SameParameter(aCurEdge, false);
    double aTolerance = BRep_Tool::Tolerance(aCurEdge);
    if (myMutableInput)
    {
      BRepLib::SameParameter(aCurEdge, aTolerance);
    }
    else
    {
      // all edges from myShape can be safely updated/changed
      // all vertices from myShape are the part of the original wires
      double aNewTolerance = -1;
      BRepLib::SameParameter(aCurEdge, aTolerance, aNewTolerance, true);
      if (aNewTolerance > 0)
      {
        TopoDS_Vertex aVertex1, aVertex2;
        TopExp::Vertices(aCurEdge, aVertex1, aVertex2);
        if (!aVertex1.IsNull())
        {
          const double* anOldTolerance = aVertexToleranceMap.Seek(aVertex1);
          if (!anOldTolerance || (anOldTolerance && *anOldTolerance < aNewTolerance))
          {
            aVertexToleranceMap.Bind(aVertex1, aNewTolerance);
          }
        }
        if (!aVertex2.IsNull())
        {
          const double* anOldTolerance = aVertexToleranceMap.Seek(aVertex2);
          if (!anOldTolerance || (anOldTolerance && *anOldTolerance < aNewTolerance))
          {
            aVertexToleranceMap.Bind(aVertex2, aNewTolerance);
          }
        }
      }
    }
    aTopExplorer.Next();
  }

  if (!myMutableInput)
  {
    BRepTools_ReShape                                                            aReshaper;
    NCollection_DataMap<TopoDS_Shape, double, TopTools_ShapeMapHasher>::Iterator aMapIterator(
      aVertexToleranceMap);
    for (; aMapIterator.More(); aMapIterator.Next())
    {
      const TopoDS_Vertex& aVertex       = TopoDS::Vertex(aMapIterator.Key());
      double               aNewTolerance = aMapIterator.Value();
      if (BRep_Tool::Tolerance(aVertex) < aNewTolerance)
      {
        TopoDS_Vertex aNnewVertex = TopoDS::Vertex(aVertex.EmptyCopied());
        B.UpdateVertex(aNnewVertex, aNewTolerance);
        aReshaper.Replace(aVertex, aNnewVertex);
      }
    }
    myShape = aReshaper.Apply(myShape);
  }
}

//=======================================================================
// function : EdgeToBSpline
// purpose  : auxiliary -- get curve from edge and convert it to bspline
//           parameterized from 0 to 1
//=======================================================================

// NOTE: this code duplicates the same function in BRepFill_NSections.cxx
static occ::handle<Geom_BSplineCurve> EdgeToBSpline(const TopoDS_Edge& theEdge)
{
  occ::handle<Geom_BSplineCurve> aBSCurve;
  if (BRep_Tool::Degenerated(theEdge))
  {
    // degenerated edge : construction of a point curve
    NCollection_Array1<double> aKnots(1, 2);
    aKnots(1) = 0.;
    aKnots(2) = 1.;

    NCollection_Array1<int> aMults(1, 2);
    aMults(1) = 2;
    aMults(2) = 2;

    NCollection_Array1<gp_Pnt> aPoles(1, 2);
    TopoDS_Vertex              vf, vl;
    TopExp::Vertices(theEdge, vl, vf);
    aPoles(1) = BRep_Tool::Pnt(vf);
    aPoles(2) = BRep_Tool::Pnt(vl);

    aBSCurve = new Geom_BSplineCurve(aPoles, aKnots, aMults, 1);
  }
  else
  {
    // get the curve of the edge
    TopLoc_Location         aLoc;
    double                  aFirst, aLast;
    occ::handle<Geom_Curve> aCurve = BRep_Tool::Curve(theEdge, aLoc, aFirst, aLast);
    if (aCurve.IsNull())
    {
      return nullptr;
    }

    // convert its part used by edge to bspline; note that if edge curve is bspline,
    // conversion made via trimmed curve is still needed -- it will copy it, segment
    // as appropriate, and remove periodicity if it is periodic (deadly for approximator)
    occ::handle<Geom_TrimmedCurve> aTrimCurve = new Geom_TrimmedCurve(aCurve, aFirst, aLast);

    // special treatment of conic curve
    if (aTrimCurve->BasisCurve()->IsKind(STANDARD_TYPE(Geom_Conic)))
    {
      const occ::handle<Geom_Curve>& aCurveTrimmed = aTrimCurve; // to avoid ambiguity
      GeomConvert_ApproxCurve anAppr(aCurveTrimmed, Precision::Confusion(), GeomAbs_C1, 16, 14);
      if (anAppr.HasResult())
        aBSCurve = anAppr.Curve();
    }

    // general case
    if (aBSCurve.IsNull())
      aBSCurve = GeomConvert::CurveToBSplineCurve(aTrimCurve);

    // apply transformation if needed
    if (!aLoc.IsIdentity())
      aBSCurve->Transform(aLoc.Transformation());

    // reparameterize to [0,1]
    NCollection_Array1<double> aKnots(1, aBSCurve->NbKnots());
    aBSCurve->Knots(aKnots);
    BSplCLib::Reparametrize(0., 1., aKnots);
    aBSCurve->SetKnots(aKnots);
  }

  // reverse curve if edge is reversed
  if (theEdge.Orientation() == TopAbs_REVERSED)
    aBSCurve->Reverse();

  return aBSCurve;
}

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

occ::handle<Geom_BSplineSurface> BRepOffsetAPI_ThruSections::TotalSurf(
  const NCollection_Array1<TopoDS_Shape>& shapes,
  const int                               NbSects,
  const int                               NbEdges,
  const bool                              w1Point,
  const bool                              w2Point,
  const bool                              vClosed) const
{
  int           i, j, jdeb = 1, jfin = NbSects;
  TopoDS_Vertex vf, vl;

  GeomFill_SectionGenerator        section;
  occ::handle<Geom_BSplineSurface> surface;
  occ::handle<Geom_BSplineCurve>   BS, BS1;
  occ::handle<Geom_TrimmedCurve>   curvTrim;

  if (w1Point)
  {
    jdeb++;
    TopoDS_Edge edge = TopoDS::Edge(shapes(1));
    TopExp::Vertices(edge, vl, vf);
    NCollection_Array1<gp_Pnt> Extremities(1, 2);
    Extremities(1) = BRep_Tool::Pnt(vf);
    Extremities(2) = BRep_Tool::Pnt(vl);
    NCollection_Array1<double> Bounds(1, 2);
    Bounds(1) = 0.;
    Bounds(2) = 1.;
    NCollection_Array1<int> Mult(1, 2);
    Mult(1)                                = 2;
    Mult(2)                                = 2;
    occ::handle<Geom_BSplineCurve> BSPoint = new Geom_BSplineCurve(Extremities, Bounds, Mult, 1);
    section.AddCurve(BSPoint);
  }

  if (w2Point)
  {
    jfin--;
  }

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

    // case of looping sections
    if (j == jfin && vClosed)
    {
      section.AddCurve(BS1);
    }

    else
    {
      // read the first edge to initialise CompBS;
      TopoDS_Edge                    aPrevEdge = TopoDS::Edge(shapes((j - 1) * NbEdges + 1));
      occ::handle<Geom_BSplineCurve> curvBS    = EdgeToBSpline(aPrevEdge);
      if (curvBS.IsNull())
      {
        return nullptr;
      }

      // initialization
      GeomConvert_CompCurveToBSplineCurve CompBS(curvBS);

      for (i = 2; i <= NbEdges; i++)
      {
        // read the edge
        TopoDS_Edge aNextEdge = TopoDS::Edge(shapes((j - 1) * NbEdges + i));
        double      aTolV     = Precision::Confusion();
        TopExp::Vertices(aNextEdge, vf, vl);
        aTolV  = std::max(aTolV, BRep_Tool::Tolerance(vf));
        aTolV  = std::max(aTolV, BRep_Tool::Tolerance(vl));
        aTolV  = std::min(aTolV, 1.e-3);
        curvBS = EdgeToBSpline(aNextEdge);
        if (curvBS.IsNull())
        {
          return nullptr;
        }

        // concatenation
        CompBS.Add(curvBS, aTolV, true, false, 1);
      }

      // return the final section
      BS = CompBS.BSplineCurve();
      section.AddCurve(BS);

      // case of looping sections
      if (j == jdeb && vClosed)
      {
        BS1 = BS;
      }
    }
  }

  if (w2Point)
  {
    TopoDS_Edge edge = TopoDS::Edge(shapes(NbSects * NbEdges));
    TopExp::Vertices(edge, vl, vf);
    NCollection_Array1<gp_Pnt> Extremities(1, 2);
    Extremities(1) = BRep_Tool::Pnt(vf);
    Extremities(2) = BRep_Tool::Pnt(vl);
    NCollection_Array1<double> Bounds(1, 2);
    Bounds(1) = 0.;
    Bounds(2) = 1.;
    NCollection_Array1<int> Mult(1, 2);
    Mult(1)                                = 2;
    Mult(2)                                = 2;
    occ::handle<Geom_BSplineCurve> BSPoint = new Geom_BSplineCurve(Extremities, Bounds, Mult, 1);
    section.AddCurve(BSPoint);
  }

  section.Perform(Precision::PConfusion());
  occ::handle<GeomFill_Line> line = new GeomFill_Line(NbSects);

  int nbIt = 3;
  if (myPres3d <= 1.e-3)
    nbIt = 0;

  int  degmin = 2, degmax = std::max(myDegMax, degmin);
  bool SpApprox = true;

  GeomFill_AppSurf anApprox(degmin, degmax, myPres3d, myPres3d, nbIt);
  anApprox.SetContinuity(myContinuity);

  if (myUseSmoothing)
  {
    anApprox.SetCriteriumWeight(myCritWeights[0], myCritWeights[1], myCritWeights[2]);
    anApprox.PerformSmoothing(line, section);
  }
  else
  {
    anApprox.SetParType(myParamType);
    anApprox.Perform(line, section, SpApprox);
  }

  if (anApprox.IsDone())
  {
    surface = new Geom_BSplineSurface(anApprox.SurfPoles(),
                                      anApprox.SurfWeights(),
                                      anApprox.SurfUKnots(),
                                      anApprox.SurfVKnots(),
                                      anApprox.SurfUMults(),
                                      anApprox.SurfVMults(),
                                      anApprox.UDegree(),
                                      anApprox.VDegree());
  }

  return surface;
}

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

const TopoDS_Shape& BRepOffsetAPI_ThruSections::FirstShape() const
{
  return myFirst;
}

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

const TopoDS_Shape& BRepOffsetAPI_ThruSections::LastShape() const
{
  return myLast;
}

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

const NCollection_List<TopoDS_Shape>& BRepOffsetAPI_ThruSections::Generated(const TopoDS_Shape& S)
{
  myGenerated.Clear();

  NCollection_Sequence<TopoDS_Shape> AllFaces;
  TopExp_Explorer                    Explo(myShape, TopAbs_FACE);
  for (; Explo.More(); Explo.Next())
    AllFaces.Append(Explo.Current());

  if (S.ShapeType() == TopAbs_EDGE)
  {
    if (!myEdgeNewIndices.IsBound(S))
      return myGenerated;

    const NCollection_List<int>& Indices = myEdgeNewIndices(S);
    // Append the faces corresponding to <Indices>
    // These faces "grow" from the first section
    NCollection_List<int>::Iterator itl(Indices);
    for (; itl.More(); itl.Next())
    {
      int IndOfFace = itl.Value();
      if (AllFaces.Size() < IndOfFace)
      {
        continue;
      }
      myGenerated.Append(AllFaces(IndOfFace));
    }

    if (myIsRuled)
      // Append the next faces corresponding to <Indices>
      for (int i = 2; i < myWires.Length(); i++)
        for (itl.Initialize(Indices); itl.More(); itl.Next())
        {
          int IndOfFace = itl.Value();
          IndOfFace += (i - 1) * myNbEdgesInSection;
          if (AllFaces.Size() < IndOfFace)
          {
            continue;
          }
          myGenerated.Append(AllFaces(IndOfFace));
        }
  }
  else if (S.ShapeType() == TopAbs_VERTEX)
  {
    if (!myVertexIndex.IsBound(S))
      return myGenerated;

    NCollection_IndexedDataMap<TopoDS_Shape,
                               NCollection_List<TopoDS_Shape>,
                               TopTools_ShapeMapHasher>
      VEmap;

    bool IsDegen[2] = {false, false};
    if (myDegen1 || myDegen2)
    {
      TopoDS_Shape EndSections[2];
      EndSections[0] = myWires(1);
      EndSections[1] = myWires(myWires.Length());
      for (int i = 0; i < 2; i++)
      {
        if (i == 0 && !myDegen1)
          continue;
        if (i == 1 && !myDegen2)
          continue;

        Explo.Init(EndSections[i], TopAbs_VERTEX);
        const TopoDS_Shape& aVertex = Explo.Current();
        if (S.IsSame(aVertex))
        {
          IsDegen[i] = true;
          break;
        }
      }
    }
    // Only one of <IsDegen> can be True:
    // in case of one vertex for start and end degenerated sections
    // IsDegen[0] is True;
    if (IsDegen[0] || IsDegen[1])
    {
      // For start or end degenerated section
      // we return the whole bunch of longitudinal edges
      TopExp::MapShapesAndAncestors(myShape, TopAbs_VERTEX, TopAbs_EDGE, VEmap);
      NCollection_IndexedMap<TopoDS_Shape, TopTools_ShapeMapHasher> Emap;
      TopoDS_Shape                                                  aNewShape = S;
      if ((myIsRuled || !myMutableInput) && !myBFGenerator.IsNull())
      {
        aNewShape = myBFGenerator->ResultShape(S);
      }

      const NCollection_List<TopoDS_Shape>&    anEdgeList = VEmap.FindFromKey(aNewShape);
      NCollection_List<TopoDS_Shape>::Iterator aListIterator(anEdgeList);
      for (; aListIterator.More(); aListIterator.Next())
      {
        const TopoDS_Edge& anEdge = TopoDS::Edge(aListIterator.Value());
        if (!BRep_Tool::Degenerated(anEdge))
        {
          TopoDS_Vertex VV[2];
          TopExp::Vertices(anEdge, VV[0], VV[1]);
          // Comprehensive check for possible case of
          // one vertex for start and end degenerated sections:
          // we must take only outgoing or only ingoing edges
          if ((IsDegen[0] && aNewShape.IsSame(VV[0])) || (IsDegen[1] && aNewShape.IsSame(VV[1])))
          {
            Emap.Add(anEdge);
          }
        }
      }
      for (int j = 1; j <= Emap.Extent(); j++)
      {
        TopoDS_Edge anEdge = TopoDS::Edge(Emap(j));
        myGenerated.Append(anEdge);
        if (myIsRuled)
        {
          int i, k;
          for (i = 2, k = myWires.Length() - 1; i < myWires.Length(); i++, k--)
          {
            int           IndOfSec = (IsDegen[0]) ? i : k;
            TopoDS_Vertex aVertex =
              (IsDegen[0]) ? TopExp::LastVertex(anEdge) : TopExp::FirstVertex(anEdge);
            const NCollection_List<TopoDS_Shape>& EElist = VEmap.FindFromKey(aVertex);
            NCollection_IndexedMap<TopoDS_Shape, TopTools_ShapeMapHasher> EmapOfSection;
            TopoDS_Shape aWireSection = myWires(IndOfSec);
            if ((myIsRuled || !myMutableInput) && !myBFGenerator.IsNull())
            {
              aWireSection = myBFGenerator->ResultShape(aWireSection);
            }
            TopExp::MapShapes(aWireSection, TopAbs_EDGE, EmapOfSection);
            TopoDS_Edge NextEdge;
            for (aListIterator.Initialize(EElist); aListIterator.More(); aListIterator.Next())
            {
              NextEdge = TopoDS::Edge(aListIterator.Value());
              if (!NextEdge.IsSame(anEdge) && !EmapOfSection.Contains(NextEdge))
                break;
            }
            myGenerated.Append(NextEdge);
            anEdge = NextEdge;
          }
        }
      }
      return myGenerated;
    } // end of if (IsDegen[0] || IsDegen[1])

    int Eindex = myVertexIndex(S);
    int Vindex = (Eindex > 0) ? 0 : 1;
    Eindex     = std::abs(Eindex);

    // Find the first longitudinal edge
    TopoDS_Face FirstFace = TopoDS::Face(AllFaces(Eindex));
    FirstFace.Orientation(TopAbs_FORWARD);
    Explo.Init(FirstFace, TopAbs_EDGE);
    TopoDS_Edge         anEdge;
    BRepAdaptor_Surface BAsurf(FirstFace, false);
    TopoDS_Vertex       FirstVertex;
    TopExp::MapShapesAndAncestors(FirstFace, TopAbs_VERTEX, TopAbs_EDGE, VEmap);
    if (myDegen1 && BAsurf.GetType() == GeomAbs_Plane)
    {
      // There are only 3 edges in the face in this case:
      // we take 1-st or 3-rd edge
      if (Vindex == 0)
      {
        Explo.Next();
        Explo.Next();
      }
      anEdge = TopoDS::Edge(Explo.Current());
    }
    else
    {
      TopoDS_Edge            FirstEdge;
      TopoDS_Vertex          FirstVertexOfFirstEdge;
      const TopoDS_Wire&     FirstSection = TopoDS::Wire(myWires(1));
      BRepTools_WireExplorer aWireExplorer(FirstSection);
      for (int i = 1; aWireExplorer.More(); aWireExplorer.Next(), i++)
      {
        FirstEdge = aWireExplorer.Current();
        if (i == Eindex)
        {
          if ((myIsRuled || !myMutableInput) && !myBFGenerator.IsNull())
          {
            FirstEdge = TopoDS::Edge(myBFGenerator->ResultShape(FirstEdge));
          }
          FirstVertexOfFirstEdge = aWireExplorer.CurrentVertex();
          break;
        }
      }

      TopoDS_Shape FirstEdgeInFace;
      FirstEdgeInFace = Explo.Current();
      TopoDS_Vertex VV[2];
      TopExp::Vertices(FirstEdge, VV[0], VV[1]);
      if (Vindex == 0)
      {
        if (VV[0].IsSame(FirstVertexOfFirstEdge))
          FirstVertex = VV[0];
        else
          FirstVertex = VV[1];
      }
      else // Vindex == 1
      {
        if (VV[0].IsSame(FirstVertexOfFirstEdge))
          FirstVertex = VV[1];
        else
          FirstVertex = VV[0];
      }
      const NCollection_List<TopoDS_Shape>&    Elist = VEmap.FindFromKey(FirstVertex);
      NCollection_List<TopoDS_Shape>::Iterator itl(Elist);
      TopAbs_Orientation anEdgeOr = (Vindex == 0) ? TopAbs_REVERSED : TopAbs_FORWARD;
      for (; itl.More(); itl.Next())
      {
        anEdge = TopoDS::Edge(itl.Value());
        if (!anEdge.IsSame(FirstEdgeInFace) && !BRep_Tool::Degenerated(anEdge)
            && anEdge.Orientation() == anEdgeOr)
          break;
      }
    }
    myGenerated.Append(anEdge);
    if (myIsRuled)
      // Find the chain of longitudinal edges from first to last
      for (int i = 2; i < myWires.Length(); i++)
      {
        FirstVertex                                  = TopExp::LastVertex(anEdge);
        const NCollection_List<TopoDS_Shape>& Elist1 = VEmap.FindFromKey(FirstVertex);
        TopoDS_Edge FirstEdge = (anEdge.IsSame(Elist1.First())) ? TopoDS::Edge(Elist1.Last())
                                                                : TopoDS::Edge(Elist1.First());
        Eindex += myNbEdgesInSection;
        FirstFace = TopoDS::Face(AllFaces(Eindex));
        FirstFace.Orientation(TopAbs_FORWARD);
        VEmap.Clear();
        TopExp::MapShapesAndAncestors(FirstFace, TopAbs_VERTEX, TopAbs_EDGE, VEmap);
        const NCollection_List<TopoDS_Shape>& Elist2 = VEmap.FindFromKey(FirstVertex);
        anEdge = (FirstEdge.IsSame(Elist2.First())) ? TopoDS::Edge(Elist2.Last())
                                                    : TopoDS::Edge(Elist2.First());
        myGenerated.Append(anEdge);
      }
  }

  return myGenerated;
}

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

TopoDS_Shape BRepOffsetAPI_ThruSections::GeneratedFace(const TopoDS_Shape& edge) const
{
  TopoDS_Shape bid;
  if (myEdgeFace.IsBound(edge))
  {
    return myEdgeFace(edge);
  }
  else
  {
    return bid;
  }
}

//=======================================================================
// function : CriteriumWeight
// purpose  : returns the Weights associated  to the criterium used in
//           the  optimization.
//=======================================================================
//
void BRepOffsetAPI_ThruSections::CriteriumWeight(double& W1, double& W2, double& W3) const
{
  W1 = myCritWeights[0];
  W2 = myCritWeights[1];
  W3 = myCritWeights[2];
}

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

void BRepOffsetAPI_ThruSections::SetCriteriumWeight(const double W1,
                                                    const double W2,
                                                    const double W3)
{
  if (W1 < 0 || W2 < 0 || W3 < 0)
  {
    myStatus = BRepFill_ThruSectionErrorStatus_Failed;
    return;
  }
  myCritWeights[0] = W1;
  myCritWeights[1] = W2;
  myCritWeights[2] = W3;
}

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

void BRepOffsetAPI_ThruSections::SetContinuity(const GeomAbs_Shape TheCont)
{
  myContinuity = TheCont;
}

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

GeomAbs_Shape BRepOffsetAPI_ThruSections::Continuity() const
{
  return myContinuity;
}

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

void BRepOffsetAPI_ThruSections::SetParType(const Approx_ParametrizationType ParType)
{
  myParamType = ParType;
}

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

Approx_ParametrizationType BRepOffsetAPI_ThruSections::ParType() const
{
  return myParamType;
}

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

void BRepOffsetAPI_ThruSections::SetMaxDegree(const int MaxDeg)
{
  myDegMax = MaxDeg;
}

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

int BRepOffsetAPI_ThruSections::MaxDegree() const
{
  return myDegMax;
}

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

void BRepOffsetAPI_ThruSections::SetSmoothing(const bool UseVar)
{
  myUseSmoothing = UseVar;
}

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

bool BRepOffsetAPI_ThruSections::UseSmoothing() const
{
  return myUseSmoothing;
}

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

void BRepOffsetAPI_ThruSections::SetMutableInput(const bool theIsMutableInput)
{
  myMutableInput = theIsMutableInput;
}

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

bool BRepOffsetAPI_ThruSections::IsMutableInput() const
{
  return myMutableInput;
}
