// Created on: 2004-05-11
// Created by: Sergey ZARITCHNY
// Copyright (c) 2004-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 <BinTools.hxx>
#include <BinTools_Curve2dSet.hxx>
#include <BinTools_ShapeSet.hxx>
#include <BinTools_SurfaceSet.hxx>
#include <BRep_CurveOnClosedSurface.hxx>
#include <BRep_CurveOnSurface.hxx>
#include <BRep_CurveRepresentation.hxx>
#include <BRep_GCurve.hxx>
#include <BRep_PointRepresentation.hxx>
#include <NCollection_List.hxx>
#include <BRep_PointOnCurve.hxx>
#include <BRep_PointOnCurveOnSurface.hxx>
#include <BRep_PointOnSurface.hxx>
#include <BRep_Polygon3D.hxx>
#include <BRep_PolygonOnTriangulation.hxx>
#include <BRep_TEdge.hxx>
#include <BRep_TFace.hxx>
#include <BRep_Tool.hxx>
#include <BRep_TVertex.hxx>
#include <BRepTools.hxx>
#include <Poly_Polygon3D.hxx>
#include <Poly_PolygonOnTriangulation.hxx>
#include <Poly_Triangulation.hxx>
#include <Standard_ErrorHandler.hxx>
#include <Standard_Integer.hxx>
#include <NCollection_Array1.hxx>
#include <NCollection_HArray1.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Vertex.hxx>
#include <Message_ProgressRange.hxx>

#include <cstring>

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

BinTools_ShapeSet::BinTools_ShapeSet()

  = default;

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

BinTools_ShapeSet::~BinTools_ShapeSet() = default;

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

void BinTools_ShapeSet::Clear()
{
  mySurfaces.Clear();
  myCurves.Clear();
  myCurves2d.Clear();
  myPolygons3D.Clear();
  myPolygons2D.Clear();
  myNodes.Clear();
  myTriangulations.Clear();
  myShapes.Clear();
  myLocations.Clear();
}

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

int BinTools_ShapeSet::Add(const TopoDS_Shape& theShape)
{
  if (theShape.IsNull())
  {
    return 0;
  }
  myLocations.Add(theShape.Location());
  TopoDS_Shape aS2 = theShape;
  aS2.Location(TopLoc_Location());
  int anIndex = myShapes.FindIndex(aS2);
  if (anIndex == 0)
  {
    AddShape(aS2);
    for (TopoDS_Iterator its(aS2, false, false); its.More(); its.Next())
    {
      Add(its.Value());
    }
    anIndex = myShapes.Add(aS2);
  }
  return anIndex;
}

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

const TopoDS_Shape& BinTools_ShapeSet::Shape(const int theIndx)
{
  return myShapes(theIndx);
}

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

int BinTools_ShapeSet::Index(const TopoDS_Shape& theShape) const
{
  return myShapes.FindIndex(theShape);
}

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

const BinTools_LocationSet& BinTools_ShapeSet::Locations() const
{
  return myLocations;
}

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

BinTools_LocationSet& BinTools_ShapeSet::ChangeLocations()
{
  return myLocations;
}

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

void BinTools_ShapeSet::AddShape(const TopoDS_Shape& S)
{
  // Add the geometry

  if (S.ShapeType() == TopAbs_VERTEX)
  {

    occ::handle<BRep_TVertex> TV = occ::down_cast<BRep_TVertex>(S.TShape());
    NCollection_List<occ::handle<BRep_PointRepresentation>>::Iterator itrp(TV->Points());

    while (itrp.More())
    {
      const occ::handle<BRep_PointRepresentation>& PR = itrp.Value();

      if (PR->IsPointOnCurve())
      {
        myCurves.Add(PR->Curve());
      }

      else if (PR->IsPointOnCurveOnSurface())
      {
        myCurves2d.Add(PR->PCurve());
        mySurfaces.Add(PR->Surface());
      }

      else if (PR->IsPointOnSurface())
      {
        mySurfaces.Add(PR->Surface());
      }

      ChangeLocations().Add(PR->Location());
      itrp.Next();
    }
  }
  else if (S.ShapeType() == TopAbs_EDGE)
  {

    // Add the curve geometry
    occ::handle<BRep_TEdge> TE = occ::down_cast<BRep_TEdge>(S.TShape());
    NCollection_List<occ::handle<BRep_CurveRepresentation>>::Iterator itrc(TE->Curves());

    while (itrc.More())
    {
      const occ::handle<BRep_CurveRepresentation>& CR = itrc.Value();
      if (CR->IsCurve3D())
      {
        if (!CR->Curve3D().IsNull())
        {
          myCurves.Add(CR->Curve3D());
          ChangeLocations().Add(CR->Location());
        }
      }
      else if (CR->IsCurveOnSurface())
      {
        mySurfaces.Add(CR->Surface());
        myCurves2d.Add(CR->PCurve());
        ChangeLocations().Add(CR->Location());
        if (CR->IsCurveOnClosedSurface())
        {
          myCurves2d.Add(CR->PCurve2());
        }
      }
      else if (CR->IsRegularity())
      {
        mySurfaces.Add(CR->Surface());
        ChangeLocations().Add(CR->Location());
        mySurfaces.Add(CR->Surface2());
        ChangeLocations().Add(CR->Location2());
      }
      else if (IsWithTriangles())
      {
        if (CR->IsPolygon3D())
        {
          if (!CR->Polygon3D().IsNull())
          {
            myPolygons3D.Add(CR->Polygon3D());
            ChangeLocations().Add(CR->Location());
          }
        }
        else if (CR->IsPolygonOnTriangulation())
        {
          // NCollection_IndexedDataMap::Add() function use is correct because
          // Bin(Brep)Tools_ShapeSet::AddGeometry() is called from Bin(Brep)Tools_ShapeSet::Add()
          // that processes shapes recursively from complex to elementary ones.
          // As a result, the TopAbs_FACE's will be processed earlier than the TopAbs_EDGE's.
          // clang-format off
          myTriangulations.Add(CR->Triangulation(), false); // edge triangulation does not need normals
          // clang-format on
          myNodes.Add(CR->PolygonOnTriangulation());
          ChangeLocations().Add(CR->Location());
          if (CR->IsPolygonOnClosedTriangulation())
          {
            myNodes.Add(CR->PolygonOnTriangulation2());
          }
        }
        else if (CR->IsPolygonOnSurface())
        {
          mySurfaces.Add(CR->Surface());
          myPolygons2D.Add(CR->Polygon());
          ChangeLocations().Add(CR->Location());
          if (CR->IsPolygonOnClosedSurface())
          {
            myPolygons2D.Add(CR->Polygon2());
          }
        }
      }
      itrc.Next();
    }
  }

  else if (S.ShapeType() == TopAbs_FACE)
  {

    // Add the surface geometry
    bool                    needNormals(IsWithNormals());
    occ::handle<BRep_TFace> TF = occ::down_cast<BRep_TFace>(S.TShape());
    if (!TF->Surface().IsNull())
    {
      mySurfaces.Add(TF->Surface());
    }
    else
    {
      needNormals = true;
    }
    if (IsWithTriangles() || TF->Surface().IsNull())
    {
      occ::handle<Poly_Triangulation> Tr = TF->Triangulation();
      if (!Tr.IsNull())
      {
        myTriangulations.Add(Tr, needNormals);
      }
    }

    ChangeLocations().Add(TF->Location());
  }
}

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

void BinTools_ShapeSet::WriteGeometry(Standard_OStream&            OS,
                                      const Message_ProgressRange& theRange) const
{
  Message_ProgressScope aPS(theRange, "Writing geometry", 6);
  myCurves2d.Write(OS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }
  myCurves.Write(OS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }
  WritePolygon3D(OS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }
  WritePolygonOnTriangulation(OS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }
  mySurfaces.Write(OS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }
  WriteTriangulation(OS, aPS.Next());
}

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

void BinTools_ShapeSet::Write(Standard_OStream& OS, const Message_ProgressRange& theRange)
{
  // write the copyright
  OS << "\n" << THE_ASCII_VERSIONS[FormatNb()] << "\n";

  //-----------------------------------------
  // write the locations
  //-----------------------------------------

  myLocations.Write(OS);

  //-----------------------------------------
  // write the geometry
  //-----------------------------------------

  Message_ProgressScope aPS(theRange, "Writing geometry", 2);

  WriteGeometry(OS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }

  //-----------------------------------------
  // write the shapes
  //-----------------------------------------

  int                   i, nbShapes = myShapes.Extent();
  Message_ProgressScope aPSinner(aPS.Next(), "Writing shapes", nbShapes);
  OS << "\nTShapes " << nbShapes << "\n";

  // subshapes are written first
  for (i = 1; i <= nbShapes && aPSinner.More(); i++, aPSinner.Next())
  {

    const TopoDS_Shape& S = myShapes(i);

    // Type
    OS << (uint8_t)S.ShapeType();

    // Geometry
    WriteShape(S, OS);

    // Flags
    BinTools::PutBool(OS, S.Free());
    BinTools::PutBool(OS, S.Modified());
    BinTools::PutBool(OS, S.Checked());
    BinTools::PutBool(OS, S.Orientable());
    BinTools::PutBool(OS, S.Closed());
    BinTools::PutBool(OS, S.Infinite());
    BinTools::PutBool(OS, S.Convex());

    // sub-shapes

    TopoDS_Iterator its(S, false, false);
    while (its.More())
    {
      Write(its.Value(), OS);
      its.Next();
    }
    Write(TopoDS_Shape(), OS); // Null shape to end the list
  }
}

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

void BinTools_ShapeSet::Read(Standard_IStream& IS, const Message_ProgressRange& theRange)
{
  Clear();

  // Check the version
  char vers[101];
  bool anIsSetFormat = false;
  do
  {
    IS.getline(vers, 100, '\n');
    // BUC60769 PTV 18.10.2000: remove possible '\r' at the end of the line

    size_t lv = strlen(vers);
    if (lv > 0)
    {
      for (lv--; lv > 0 && (vers[lv] == '\r' || vers[lv] == '\n'); lv--)
      {
        vers[lv] = '\0';
      }
    }

    for (int i = BinTools_FormatVersion_LOWER; i <= BinTools_FormatVersion_UPPER; ++i)
    {
      if (!strcmp(vers, THE_ASCII_VERSIONS[i]))
      {
        SetFormatNb(i);
        anIsSetFormat = true;
        break;
      }
    }
    if (anIsSetFormat)
    {
      break;
    }
  } while (!IS.fail());
  if (IS.fail())
  {
    std::cout << "BinTools_ShapeSet::Read: File was not written with this version of the topology"
              << '\n';
    return;
  }

  //-----------------------------------------
  // read the locations
  //-----------------------------------------
  myLocations.Read(IS);
  //-----------------------------------------
  // read the geometry
  //-----------------------------------------
  Message_ProgressScope aPSouter(theRange, "Reading", 2);
  ReadGeometry(IS, aPSouter.Next());
  if (!aPSouter.More())
  {
    return;
  }
  //-----------------------------------------
  // read the shapes
  //-----------------------------------------

  char buffer[255];
  IS >> buffer;
  if (IS.fail() || strcmp(buffer, "TShapes"))
  {
    Standard_SStream aMsg;
    aMsg << "BinTools_ShapeSet::Read: Not a TShape table" << '\n';
    throw Standard_Failure(aMsg.str().c_str());
    return;
  }
  int nbShapes = 0;
  IS >> nbShapes;
  IS.get(); // remove lf
  Message_ProgressScope aPSinner(aPSouter.Next(), "Reading Shapes", nbShapes);
  for (int i = 1; i <= nbShapes && aPSinner.More(); i++, aPSinner.Next())
  {
    TopoDS_Shape     S;
    TopAbs_ShapeEnum T = (TopAbs_ShapeEnum)IS.get();
    ReadShape(T, IS, S);
    ReadFlagsAndSubs(S, T, IS, nbShapes);
    myShapes.Add(S);
  }
}

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

void BinTools_ShapeSet::Write(const TopoDS_Shape& S, Standard_OStream& OS)
{
  if (S.IsNull())
  {

    OS << '*';
  }
  else
  {
    // {TopAbs_FORWARD, TopAbs_REVERSED, TopAbs_INTERNAL, TopAbs_EXTERNAL}
    OS << (uint8_t)S.Orientation();
    BinTools::PutInteger(OS,
                         myShapes.Extent() - myShapes.FindIndex(S.Located(TopLoc_Location())) + 1);
    BinTools::PutInteger(OS, Locations().Index(S.Location()));
  }
}

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

void BinTools_ShapeSet::ReadFlagsAndSubs(TopoDS_Shape&          S,
                                         const TopAbs_ShapeEnum T,
                                         Standard_IStream&      IS,
                                         const int              nbShapes)
{
  // Set the flags
  bool aFree, aMod, aChecked, anOrient, aClosed, anInf, aConv;
  BinTools::GetBool(IS, aFree);
  BinTools::GetBool(IS, aMod);
  BinTools::GetBool(IS, aChecked);
  BinTools::GetBool(IS, anOrient);
  BinTools::GetBool(IS, aClosed);
  BinTools::GetBool(IS, anInf);
  BinTools::GetBool(IS, aConv);

  // sub-shapes
  TopoDS_Shape SS;
  do
  {
    ReadSubs(SS, IS, nbShapes);
    if (!SS.IsNull())
    {
      AddShapes(S, SS);
    }
  } while (!SS.IsNull());

  S.Free(aFree);
  S.Modified(aMod);
  if (FormatNb() == BinTools_FormatVersion_VERSION_1)
  {
    aChecked = false; // force check at reading
  }
  S.Checked(aChecked);
  S.Orientable(anOrient);
  S.Closed(aClosed);
  S.Infinite(anInf);
  S.Convex(aConv);
  // check

  if (FormatNb() == BinTools_FormatVersion_VERSION_1)
  {
    if (T == TopAbs_FACE)
    {
      const TopoDS_Face& F = TopoDS::Face(S);
      BRepTools::Update(F);
    }
  }
}

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

void BinTools_ShapeSet::ReadSubs(TopoDS_Shape& S, Standard_IStream& IS, const int nbshapes)
{
  char aChar = '\0';
  IS >> aChar;
  if (aChar == '*')
  {
    S = TopoDS_Shape();
  }
  else
  {
    TopAbs_Orientation anOrient;
    anOrient = (TopAbs_Orientation)aChar;
    int anIndx;
    BinTools::GetInteger(IS, anIndx);
    S = Shape(nbshapes - anIndx + 1);
    S.Orientation(anOrient);

    int l;
    BinTools::GetInteger(IS, l);
    S.Location(myLocations.Location(l), false);
  }
}

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

void BinTools_ShapeSet::ReadGeometry(Standard_IStream& IS, const Message_ProgressRange& theRange)
{

  Message_ProgressScope aPS(theRange, "Reading geometry", 6);
  myCurves2d.Read(IS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }

  myCurves.Read(IS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }

  ReadPolygon3D(IS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }

  ReadPolygonOnTriangulation(IS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }

  mySurfaces.Read(IS, aPS.Next());
  if (!aPS.More())
  {
    return;
  }

  ReadTriangulation(IS, aPS.Next());
}

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

void BinTools_ShapeSet::WriteShape(const TopoDS_Shape& S, Standard_OStream& OS) const
{
  // Write the geometry
  try
  {
    OCC_CATCH_SIGNALS
    if (S.ShapeType() == TopAbs_VERTEX)
    {

      // Write the point geometry
      TopoDS_Vertex V = TopoDS::Vertex(S);
      BinTools::PutReal(OS, BRep_Tool::Tolerance(V));
      gp_Pnt p = BRep_Tool::Pnt(V);
      OS << p;
#ifdef OCCT_DEBUG_POS
      std::streamoff aPos;
#endif
      occ::handle<BRep_TVertex> TV = occ::down_cast<BRep_TVertex>(S.TShape());
      NCollection_List<occ::handle<BRep_PointRepresentation>>::Iterator itrp(TV->Points());
      while (itrp.More())
      {
        const occ::handle<BRep_PointRepresentation>& PR = itrp.Value();
        //	BinTools::PutReal(OS, PR->Parameter());
        if (PR->IsPointOnCurve())
        {
#ifdef OCCT_DEBUG_POS
          aPos = OS.tellp();
#endif
          OS << (uint8_t)1; // 1
          BinTools::PutReal(OS, PR->Parameter());
          BinTools::PutInteger(OS, myCurves.Index(PR->Curve()));
        }

        else if (PR->IsPointOnCurveOnSurface())
        {
#ifdef OCCT_DEBUG_POS
          aPos = OS.tellp();
#endif
          OS << (uint8_t)2; // 2
          BinTools::PutReal(OS, PR->Parameter());
          BinTools::PutInteger(OS, myCurves2d.Index(PR->PCurve()));
          BinTools::PutInteger(OS, mySurfaces.Index(PR->Surface()));
        }

        else if (PR->IsPointOnSurface())
        {
#ifdef OCCT_DEBUG_POS
          aPos = OS.tellp();
#endif
          OS << (uint8_t)3; // 3
          BinTools::PutReal(OS, PR->Parameter2());
          BinTools::PutReal(OS, PR->Parameter());
          BinTools::PutInteger(OS, mySurfaces.Index(PR->Surface()));
        }
        BinTools::PutInteger(OS, Locations().Index(PR->Location()));
        itrp.Next();
      }

      //    OS << "0 0\n"; // end representations
      OS.put((uint8_t)0);
    }

    else if (S.ShapeType() == TopAbs_EDGE)
    {

      // Write the curve geometry

      occ::handle<BRep_TEdge> TE = occ::down_cast<BRep_TEdge>(S.TShape());

      BinTools::PutReal(OS, TE->Tolerance());

      bool aVal = TE->SameParameter();
      BinTools::PutBool(OS, aVal);
      aVal = TE->SameRange();
      BinTools::PutBool(OS, aVal);
      aVal = TE->Degenerated();
      BinTools::PutBool(OS, aVal);

      double                                                            first, last;
      NCollection_List<occ::handle<BRep_CurveRepresentation>>::Iterator itrc = TE->Curves();
      while (itrc.More())
      {
        const occ::handle<BRep_CurveRepresentation>& CR = itrc.Value();
        if (CR->IsCurve3D())
        {
          if (!CR->Curve3D().IsNull())
          {
            occ::handle<BRep_GCurve> GC = occ::down_cast<BRep_GCurve>(itrc.Value());
            GC->Range(first, last);
            OS << (uint8_t)1; // CURVE_3D;
            BinTools::PutInteger(OS, myCurves.Index(CR->Curve3D()));
            BinTools::PutInteger(OS, Locations().Index(CR->Location()));
            BinTools::PutReal(OS, first);
            BinTools::PutReal(OS, last);
          }
        }
        else if (CR->IsCurveOnSurface())
        {
          occ::handle<BRep_GCurve> GC = occ::down_cast<BRep_GCurve>(itrc.Value());
          GC->Range(first, last);
          if (!CR->IsCurveOnClosedSurface())
          {
            // -2- Curve on surf
            OS << (uint8_t)2;
          }
          else
          {
            // -3- Curve on closed surf
            OS << (uint8_t)3;
          }
          BinTools::PutInteger(OS, myCurves2d.Index(CR->PCurve()));
          if (CR->IsCurveOnClosedSurface())
          { //+ int|char
            BinTools::PutInteger(OS, myCurves2d.Index(CR->PCurve2()));
            OS << (uint8_t)CR->Continuity();
          }
          BinTools::PutInteger(OS, mySurfaces.Index(CR->Surface()));
          BinTools::PutInteger(OS, Locations().Index(CR->Location()));
          BinTools::PutReal(OS, first);
          BinTools::PutReal(OS, last);

          // Write UV Points for higher performance
          if (FormatNb() == BinTools_FormatVersion_VERSION_2
              || FormatNb() == BinTools_FormatVersion_VERSION_3)
          {
            gp_Pnt2d Pf, Pl;
            if (CR->IsCurveOnClosedSurface())
            {
              occ::handle<BRep_CurveOnClosedSurface> COCS =
                occ::down_cast<BRep_CurveOnClosedSurface>(CR);
              COCS->UVPoints2(Pf, Pl);
            }
            else
            {
              occ::handle<BRep_CurveOnSurface> COS = occ::down_cast<BRep_CurveOnSurface>(CR);
              COS->UVPoints(Pf, Pl);
            }
            BinTools::PutReal(OS, Pf.X());
            BinTools::PutReal(OS, Pf.Y());
            BinTools::PutReal(OS, Pl.X());
            BinTools::PutReal(OS, Pl.Y());
          }
        }
        else if (CR->IsRegularity())
        {
          // -4- Regularity
          OS << (uint8_t)4;
          OS << (uint8_t)CR->Continuity();
          BinTools::PutInteger(OS, mySurfaces.Index(CR->Surface()));
          BinTools::PutInteger(OS, Locations().Index(CR->Location()));
          BinTools::PutInteger(OS, mySurfaces.Index(CR->Surface2()));
          BinTools::PutInteger(OS, Locations().Index(CR->Location2()));
        }

        else if (IsWithTriangles())
        {
          if (CR->IsPolygon3D())
          {
            occ::handle<BRep_Polygon3D> GC = occ::down_cast<BRep_Polygon3D>(itrc.Value());
            if (!GC->Polygon3D().IsNull())
            {
              // -5- Polygon3D
              OS << (uint8_t)5;
              BinTools::PutInteger(OS, myPolygons3D.FindIndex(CR->Polygon3D()));
              BinTools::PutInteger(OS, Locations().Index(CR->Location()));
            }
          }
          else if (CR->IsPolygonOnTriangulation())
          {
            occ::handle<BRep_PolygonOnTriangulation> PT =
              occ::down_cast<BRep_PolygonOnTriangulation>(itrc.Value());
            if (!CR->IsPolygonOnClosedTriangulation())
            {
              // -6- Polygon on triangulation
              OS << (uint8_t)6;
            }
            else
            {
              // -7- Polygon on closed triangulation
              OS << (uint8_t)7;
            }
            BinTools::PutInteger(OS, myNodes.FindIndex(PT->PolygonOnTriangulation()));

            if (CR->IsPolygonOnClosedTriangulation())
            {
              BinTools::PutInteger(OS, myNodes.FindIndex(PT->PolygonOnTriangulation2()));
            }
            BinTools::PutInteger(OS, myTriangulations.FindIndex(PT->Triangulation()));
            BinTools::PutInteger(OS, Locations().Index(CR->Location()));
          }
        }

        itrc.Next();
      }
      //   OS << "0\n"; // end of the list of representations

      OS << (uint8_t)0;
    }

    else if (S.ShapeType() == TopAbs_FACE)
    {

      occ::handle<BRep_TFace> TF = occ::down_cast<BRep_TFace>(S.TShape());
      const TopoDS_Face&      F  = TopoDS::Face(S);

      // Write the surface geometry
      bool aNatRes = BRep_Tool::NaturalRestriction(F);
      BinTools::PutBool(OS, aNatRes);
      BinTools::PutReal(OS, TF->Tolerance());
      BinTools::PutInteger(OS, !TF->Surface().IsNull() ? mySurfaces.Index(TF->Surface()) : 0);
      BinTools::PutInteger(OS, Locations().Index(TF->Location()));

      if (IsWithTriangles() || TF->Surface().IsNull())
      {
        if (!(TF->Triangulation()).IsNull())
        {
          OS << (uint8_t)2;
          // Write the triangulation
          BinTools::PutInteger(OS, myTriangulations.FindIndex(TF->Triangulation()));
        }
        else
        {
          OS << (uint8_t)1;
        }
      }
      else
      {
        OS << (uint8_t)0; // without triangulation
      }
    }
  }
  catch (Standard_Failure const& anException)
  {
    Standard_SStream aMsg;
    aMsg << "EXCEPTION in BinTools_ShapeSet::WriteGeometry(S,OS)" << '\n';
    aMsg << anException << '\n';
    throw Standard_Failure(aMsg.str().c_str());
  }
}

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

void BinTools_ShapeSet::ReadShape(const TopAbs_ShapeEnum T, Standard_IStream& IS, TopoDS_Shape& S)
{
  // Read the geometry

  int           val, c, pc, pc2 = 0, s, s2, l, l2, t, pt, pt2 = 0;
  double        tol, X, Y, Z, first, last, p1 = 0., p2;
  double        PfX, PfY, PlX, PlY;
  gp_Pnt2d      aPf, aPl;
  bool          closed, bval;
  GeomAbs_Shape reg = GeomAbs_C0;
  try
  {
    OCC_CATCH_SIGNALS
    switch (T)
    {

        //---------
        // vertex
        //---------

      case TopAbs_VERTEX: {
        TopoDS_Vertex& V = TopoDS::Vertex(S);

        // Read the point geometry
        BinTools::GetReal(IS, tol);
        BinTools::GetReal(IS, X);
        BinTools::GetReal(IS, Y);
        BinTools::GetReal(IS, Z);
        gp_Pnt aPnt(X, Y, Z);
        myBuilder.MakeVertex(V, aPnt, tol);
        occ::handle<BRep_TVertex> TV = occ::down_cast<BRep_TVertex>(V.TShape());

        NCollection_List<occ::handle<BRep_PointRepresentation>>& lpr = TV->ChangePoints();
        TopLoc_Location                                          L;
        do
        {
          if (FormatNb() == BinTools_FormatVersion_VERSION_3)
          {
            val = (int)IS.get(); // case {0|1|2|3}
            if (val > 0 && val <= 3)
            {
              BinTools::GetReal(IS, p1);
            }
          }
          else
          {
            std::streampos aPos = IS.tellg();
            BinTools::GetReal(IS, p1);
            val = (int)IS.get(); // case {0|1|2|3}
#ifdef OCCT_DEBUG
            std::cout << "\nVal = " << val << std::endl;
#endif
            if (val != 1 && val != 2 && val != 3)
            {
              IS.seekg(aPos);
              val = (int)IS.get();
              if (val > 0 && val <= 3)
              {
                BinTools::GetReal(IS, p1);
              }
            }
          }
          occ::handle<BRep_PointRepresentation> PR;
          switch (val)
          {
            case 0:
              break;

            case 1: {
              BinTools::GetInteger(IS, c);
              if (myCurves.Curve(c).IsNull())
              {
                break;
              }
              occ::handle<BRep_PointOnCurve> POC = new BRep_PointOnCurve(p1, myCurves.Curve(c), L);
              PR                                 = POC;
            }
            break;

            case 2: {
              BinTools::GetInteger(IS, pc);
              BinTools::GetInteger(IS, s);
              if (myCurves2d.Curve2d(pc).IsNull() || mySurfaces.Surface(s).IsNull())
              {
                break;
              }

              occ::handle<BRep_PointOnCurveOnSurface> POC =
                new BRep_PointOnCurveOnSurface(p1,
                                               myCurves2d.Curve2d(pc),
                                               mySurfaces.Surface(s),
                                               L);
              PR = POC;
            }
            break;

            case 3: {
              BinTools::GetReal(IS, p2);
              BinTools::GetInteger(IS, s);
              if (mySurfaces.Surface(s).IsNull())
              {
                break;
              }

              occ::handle<BRep_PointOnSurface> POC =
                new BRep_PointOnSurface(p1, p2, mySurfaces.Surface(s), L);
              PR = POC;
            }
            break;

            default: {
              Standard_SStream aMsg;
              aMsg << "BinTools_SurfaceSet::ReadGeometry: UnExpected BRep_PointRepresentation = "
                   << val << '\n';
              throw Standard_Failure(aMsg.str().c_str());
            }
          }

          if (val > 0)
          {
            BinTools::GetInteger(IS, l); // Locations index

            if (!PR.IsNull())
            {
              PR->Location(Locations().Location(l));
              lpr.Append(PR);
            }
          }
        } while (val > 0);
      }
      break;

        //---------
        // edge
        //---------

      case TopAbs_EDGE:

        // Create an edge
        {
          TopoDS_Edge& E = TopoDS::Edge(S);

          myBuilder.MakeEdge(E);

          // Read the curve geometry
          BinTools::GetReal(IS, tol);
          BinTools::GetBool(IS, bval);
          myBuilder.SameParameter(E, bval);

          BinTools::GetBool(IS, bval);
          myBuilder.SameRange(E, bval);

          BinTools::GetBool(IS, bval);
          myBuilder.Degenerated(E, bval);

          do
          {
            val = (int)IS.get(); //{0|1|2|3|4|5|6|7}
            // -0- no representation
            // -1- Curve 3D
            // -2- Curve on surf
            // -3- Curve on closed surf
            // -4- Regularity
            // -5- Polygon3D
            // -6- Polygon on triangulation
            // -7- Polygon on closed triangulation

            switch (val)
            {
              case 0:
                break;

              case 1: // -1- Curve 3D
                BinTools::GetInteger(IS, c);
                BinTools::GetInteger(IS, l);
                if (!myCurves.Curve(c).IsNull())
                {
                  myBuilder.UpdateEdge(E, myCurves.Curve(c), Locations().Location(l), tol);
                }
                BinTools::GetReal(IS, first);
                BinTools::GetReal(IS, last);
                if (!myCurves.Curve(c).IsNull())
                {
                  bool Only3d = true;
                  myBuilder.Range(E, first, last, Only3d);
                }
                break;

              case 2: // -2- Curve on surf
              case 3: // -3- Curve on closed surf
                closed = (val == 3);
                BinTools::GetInteger(IS, pc);
                if (closed)
                {
                  BinTools::GetInteger(IS, pc2);
                  reg = (GeomAbs_Shape)IS.get();
                }

                // surface, location
                BinTools::GetInteger(IS, s);
                BinTools::GetInteger(IS, l);

                // range
                BinTools::GetReal(IS, first);
                BinTools::GetReal(IS, last);

                // read UV Points // for XML Persistence higher performance
                if (FormatNb() == BinTools_FormatVersion_VERSION_2
                    || FormatNb() == BinTools_FormatVersion_VERSION_3)
                {
                  BinTools::GetReal(IS, PfX);
                  BinTools::GetReal(IS, PfY);
                  BinTools::GetReal(IS, PlX);
                  BinTools::GetReal(IS, PlY);
                  aPf = gp_Pnt2d(PfX, PfY);
                  aPl = gp_Pnt2d(PlX, PlY);
                }

                if (myCurves2d.Curve2d(pc).IsNull() || (closed && myCurves2d.Curve2d(pc2).IsNull())
                    || mySurfaces.Surface(s).IsNull())
                {
                  break;
                }

                if (closed)
                {
                  if (FormatNb() == BinTools_FormatVersion_VERSION_2
                      || FormatNb() == BinTools_FormatVersion_VERSION_3)
                  {
                    myBuilder.UpdateEdge(E,
                                         myCurves2d.Curve2d(pc),
                                         myCurves2d.Curve2d(pc2),
                                         mySurfaces.Surface(s),
                                         Locations().Location(l),
                                         tol,
                                         aPf,
                                         aPl);
                  }
                  else
                  {
                    myBuilder.UpdateEdge(E,
                                         myCurves2d.Curve2d(pc),
                                         myCurves2d.Curve2d(pc2),
                                         mySurfaces.Surface(s),
                                         Locations().Location(l),
                                         tol);
                  }

                  myBuilder.Continuity(E,
                                       mySurfaces.Surface(s),
                                       mySurfaces.Surface(s),
                                       Locations().Location(l),
                                       Locations().Location(l),
                                       reg);
                }
                else
                {
                  if (FormatNb() == BinTools_FormatVersion_VERSION_2
                      || FormatNb() == BinTools_FormatVersion_VERSION_3)
                  {
                    myBuilder.UpdateEdge(E,
                                         myCurves2d.Curve2d(pc),
                                         mySurfaces.Surface(s),
                                         Locations().Location(l),
                                         tol,
                                         aPf,
                                         aPl);
                  }
                  else
                  {
                    myBuilder.UpdateEdge(E,
                                         myCurves2d.Curve2d(pc),
                                         mySurfaces.Surface(s),
                                         Locations().Location(l),
                                         tol);
                  }
                }
                myBuilder.Range(E, mySurfaces.Surface(s), Locations().Location(l), first, last);
                break;

              case 4: // -4- Regularity
                reg = (GeomAbs_Shape)IS.get();
                BinTools::GetInteger(IS, s);
                BinTools::GetInteger(IS, l);
                BinTools::GetInteger(IS, s2);
                BinTools::GetInteger(IS, l2);
                if (mySurfaces.Surface(s).IsNull() || mySurfaces.Surface(s2).IsNull())
                {
                  break;
                }
                myBuilder.Continuity(E,
                                     mySurfaces.Surface(s),
                                     mySurfaces.Surface(s2),
                                     Locations().Location(l),
                                     Locations().Location(l2),
                                     reg);
                break;

              case 5: // -5- Polygon3D
                BinTools::GetInteger(IS, c);
                BinTools::GetInteger(IS, l);
                //??? Bug?  myBuilder.UpdateEdge(E,myPolygons3D(c));
                myBuilder.UpdateEdge(E, myPolygons3D(c), Locations().Location(l));
                break;

              case 6: // -6- Polygon on triangulation
              case 7: // -7- Polygon on closed triangulation
                closed = (val == 7);
                BinTools::GetInteger(IS, pt);
                if (closed)
                {
                  BinTools::GetInteger(IS, pt2);
                }

                BinTools::GetInteger(IS, t);
                BinTools::GetInteger(IS, l);
                if (closed)
                {
                  myBuilder.UpdateEdge(E,
                                       myNodes(pt),
                                       myNodes(pt2),
                                       myTriangulations.FindKey(t),
                                       Locations().Location(l));
                }
                else
                {
                  myBuilder.UpdateEdge(E,
                                       myNodes(pt),
                                       myTriangulations.FindKey(t),
                                       Locations().Location(l));
                }
                // range
                break;
              default: {
                Standard_SStream aMsg;
                aMsg << "Unexpected Curve Representation =" << val << '\n';
                throw Standard_Failure(aMsg.str().c_str());
              }
            }
          } while (val > 0);
        }
        break;

        //---------
        // wire
        //---------

      case TopAbs_WIRE:
        myBuilder.MakeWire(TopoDS::Wire(S));
        break;

        //---------
        // face
        //---------

      case TopAbs_FACE: {
        // create a face :
        TopoDS_Face& F = TopoDS::Face(S);
        myBuilder.MakeFace(F);
        BinTools::GetBool(IS, bval); // NaturalRestriction flag
        BinTools::GetReal(IS, tol);
        BinTools::GetInteger(IS, s); // surface indx
        BinTools::GetInteger(IS, l); // location indx
        myBuilder.UpdateFace(F,
                             s > 0 ? mySurfaces.Surface(s) : occ::handle<Geom_Surface>(),
                             Locations().Location(l),
                             tol);
        myBuilder.NaturalRestriction(F, bval);

        uint8_t aByte = (uint8_t)IS.get();
        // triangulation case
        if (aByte == 2)
        {
          BinTools::GetInteger(IS, s);
          myBuilder.UpdateFace(TopoDS::Face(S), myTriangulations.FindKey(s));
        }
      }
      break;

        //---------
        // shell
        //---------

      case TopAbs_SHELL:
        myBuilder.MakeShell(TopoDS::Shell(S));
        break;

        //---------
        // solid
        //---------

      case TopAbs_SOLID:
        myBuilder.MakeSolid(TopoDS::Solid(S));
        break;

        //---------
        // compsolid
        //---------

      case TopAbs_COMPSOLID:
        myBuilder.MakeCompSolid(TopoDS::CompSolid(S));
        break;

        //---------
        // compound
        //---------

      case TopAbs_COMPOUND:
        myBuilder.MakeCompound(TopoDS::Compound(S));
        break;

      default: {
        Standard_SStream aMsg;
        aMsg << "Unexpected topology type = " << T << '\n';
        throw Standard_Failure(aMsg.str().c_str());
        break;
      }
    }
  }
  catch (Standard_Failure const& anException)
  {
    Standard_SStream aMsg;
    aMsg << "EXCEPTION in BinTools_ShapeSet::ReadGeometry(S,OS)" << '\n';
    aMsg << anException << '\n';
    throw Standard_Failure(aMsg.str().c_str());
  }
}

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

void BinTools_ShapeSet::AddShapes(TopoDS_Shape& S1, const TopoDS_Shape& S2)
{
  myBuilder.Add(S1, S2);
}

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

void BinTools_ShapeSet::WritePolygonOnTriangulation(Standard_OStream&            OS,
                                                    const Message_ProgressRange& theRange) const
{
  const int aNbPol = myNodes.Extent();
  OS << "PolygonOnTriangulations " << aNbPol << "\n";
  try
  {
    OCC_CATCH_SIGNALS
    Message_ProgressScope aPS(theRange, "Writing polygons on triangulation", aNbPol);
    for (int aPolIter = 1; aPolIter <= aNbPol && aPS.More(); ++aPolIter, aPS.Next())
    {
      const occ::handle<Poly_PolygonOnTriangulation>& aPoly  = myNodes.FindKey(aPolIter);
      const NCollection_Array1<int>&                  aNodes = aPoly->Nodes();
      BinTools::PutInteger(OS, aNodes.Length());
      for (int aNodeIter = 1; aNodeIter <= aNodes.Length(); ++aNodeIter)
      {
        BinTools::PutInteger(OS, aNodes.Value(aNodeIter));
      }

      // write the deflection
      BinTools::PutReal(OS, aPoly->Deflection());

      // writing parameters
      if (const occ::handle<NCollection_HArray1<double>>& aParam = aPoly->Parameters())
      {
        BinTools::PutBool(OS, true);
        for (int aNodeIter = 1; aNodeIter <= aParam->Length(); ++aNodeIter)
        {
          BinTools::PutReal(OS, aParam->Value(aNodeIter));
        }
      }
      else
      {
        BinTools::PutBool(OS, false);
      }
    }
  }
  catch (Standard_Failure const& anException)
  {
    Standard_SStream aMsg;
    aMsg << "EXCEPTION in BinTools_ShapeSet::WritePolygonOnTriangulation(..)\n"
         << anException << "\n";
    throw Standard_Failure(aMsg.str().c_str());
  }
}

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

void BinTools_ShapeSet::ReadPolygonOnTriangulation(Standard_IStream&            IS,
                                                   const Message_ProgressRange& theRange)
{
  char aHeader[255];
  IS >> aHeader;
  if (IS.fail() || (strstr(aHeader, "PolygonOnTriangulations") == nullptr))
  {
    throw Standard_Failure(
      "BinTools_ShapeSet::ReadPolygonOnTriangulation: Not a PolygonOnTriangulation section");
  }

  int aNbPol = 0;
  IS >> aNbPol;
  IS.get(); // remove LF
  try
  {
    OCC_CATCH_SIGNALS
    Message_ProgressScope aPS(theRange, "Reading Polygones on triangulation", aNbPol);
    for (int aPolIter = 1; aPolIter <= aNbPol && aPS.More(); ++aPolIter, aPS.Next())
    {
      int aNbNodes = 0;
      BinTools::GetInteger(IS, aNbNodes);
      occ::handle<Poly_PolygonOnTriangulation> aPoly =
        new Poly_PolygonOnTriangulation(aNbNodes, false);
      for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
      {
        int aNode = 0;
        BinTools::GetInteger(IS, aNode);
        aPoly->SetNode(aNodeIter, aNode);
      }

      double aDefl = 0.0;
      BinTools::GetReal(IS, aDefl);
      aPoly->Deflection(aDefl);

      bool hasParameters = false;
      BinTools::GetBool(IS, hasParameters);
      if (hasParameters)
      {
        occ::handle<NCollection_HArray1<double>> aParams =
          new NCollection_HArray1<double>(1, aNbNodes);
        for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
        {
          BinTools::GetReal(IS, aParams->ChangeValue(aNodeIter));
        }
        aPoly->SetParameters(aParams);
      }
      myNodes.Add(aPoly);
    }
  }
  catch (Standard_Failure const& anException)
  {
    Standard_SStream aMsg;
    aMsg << "EXCEPTION in BinTools_ShapeSet::ReadPolygonOnTriangulation(..)\n"
         << anException << "\n";
    throw Standard_Failure(aMsg.str().c_str());
  }
}

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

void BinTools_ShapeSet::WritePolygon3D(Standard_OStream&            OS,
                                       const Message_ProgressRange& theRange) const
{
  const int aNbPol = myPolygons3D.Extent();
  OS << "Polygon3D " << aNbPol << "\n";
  try
  {
    OCC_CATCH_SIGNALS
    Message_ProgressScope aPS(theRange, "Writing polygons 3D", aNbPol);
    for (int aPolIter = 1; aPolIter <= aNbPol && aPS.More(); ++aPolIter, aPS.Next())
    {
      const occ::handle<Poly_Polygon3D>& aPoly = myPolygons3D.FindKey(aPolIter);
      BinTools::PutInteger(OS, aPoly->NbNodes());
      BinTools::PutBool(OS, aPoly->HasParameters());

      // write the deflection
      BinTools::PutReal(OS, aPoly->Deflection());

      // write the nodes
      const int                         aNbNodes = aPoly->NbNodes();
      const NCollection_Array1<gp_Pnt>& aNodes   = aPoly->Nodes();
      for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
      {
        const gp_Pnt& aPnt = aNodes.Value(aNodeIter);
        BinTools::PutReal(OS, aPnt.X());
        BinTools::PutReal(OS, aPnt.Y());
        BinTools::PutReal(OS, aPnt.Z());
      }
      if (aPoly->HasParameters())
      {
        const NCollection_Array1<double>& aParam = aPoly->Parameters();
        for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
        {
          BinTools::PutReal(OS, aParam.Value(aNodeIter));
        }
      }
    }
  }
  catch (Standard_Failure const& anException)
  {
    Standard_SStream aMsg;
    aMsg << "EXCEPTION in BinTools_ShapeSet::WritePolygon3D(..)\n" << anException << "\n";
    throw Standard_Failure(aMsg.str().c_str());
  }
}

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

void BinTools_ShapeSet::ReadPolygon3D(Standard_IStream& IS, const Message_ProgressRange& theRange)
{
  char aHeader[255];
  IS >> aHeader;

  if (IS.fail() || strstr(aHeader, "Polygon3D") == nullptr)
  {
#ifdef OCCT_DEBUG
    std::cout << "Buffer: " << aHeader << std::endl;
#endif
    throw Standard_Failure("BinTools_ShapeSet::ReadPolygon3D: Not a Polygon3D section");
  }

  int aNbPol = 0;
  IS >> aNbPol;
  IS.get(); // remove LF
  try
  {
    OCC_CATCH_SIGNALS
    Message_ProgressScope aPS(theRange, "Reading polygones 3D", aNbPol);
    for (int aPolIter = 1; aPolIter <= aNbPol && aPS.More(); ++aPolIter, aPS.Next())
    {
      int    aNbNodes      = 0;
      bool   hasParameters = false;
      double aDefl         = 0.0;
      BinTools::GetInteger(IS, aNbNodes);
      BinTools::GetBool(IS, hasParameters);
      BinTools::GetReal(IS, aDefl);

      occ::handle<Poly_Polygon3D> aPoly = new Poly_Polygon3D(aNbNodes, hasParameters);
      aPoly->Deflection(aDefl);

      NCollection_Array1<gp_Pnt>& aNodes = aPoly->ChangeNodes();
      for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
      {
        gp_XYZ& aPnt = aNodes.ChangeValue(aNodeIter).ChangeCoord();
        BinTools::GetReal(IS, aPnt.ChangeCoord(1));
        BinTools::GetReal(IS, aPnt.ChangeCoord(2));
        BinTools::GetReal(IS, aPnt.ChangeCoord(3));
      }
      if (hasParameters)
      {
        NCollection_Array1<double>& aParam = aPoly->ChangeParameters();
        for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
        {
          BinTools::GetReal(IS, aParam.ChangeValue(aNodeIter));
        }
      }

      myPolygons3D.Add(aPoly);
    }
  }
  catch (Standard_Failure const& anException)
  {
    Standard_SStream aMsg;
    aMsg << "EXCEPTION in BinTools_ShapeSet::ReadPolygon3D(..)\n" << anException << "\n";
    throw Standard_Failure(aMsg.str().c_str());
  }
}

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

void BinTools_ShapeSet::WriteTriangulation(Standard_OStream&            OS,
                                           const Message_ProgressRange& theRange) const
{
  const int aNbTriangulations = myTriangulations.Extent();
  OS << "Triangulations " << aNbTriangulations << "\n";

  try
  {
    OCC_CATCH_SIGNALS
    Message_ProgressScope aPS(theRange, "Writing triangulation", aNbTriangulations);
    for (int aTriangulationIter = 1; aTriangulationIter <= aNbTriangulations && aPS.More();
         ++aTriangulationIter, aPS.Next())
    {
      const occ::handle<Poly_Triangulation>& aTriangulation =
        myTriangulations.FindKey(aTriangulationIter);
      bool      NeedToWriteNormals = myTriangulations.FindFromIndex(aTriangulationIter);
      const int aNbNodes           = aTriangulation->NbNodes();
      const int aNbTriangles       = aTriangulation->NbTriangles();
      BinTools::PutInteger(OS, aNbNodes);
      BinTools::PutInteger(OS, aNbTriangles);
      BinTools::PutBool(OS, aTriangulation->HasUVNodes());
      if (FormatNb() >= BinTools_FormatVersion_VERSION_4)
      {
        BinTools::PutBool(OS, aTriangulation->HasNormals() && NeedToWriteNormals);
      }
      BinTools::PutReal(OS, aTriangulation->Deflection());

      // write the 3d nodes
      for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
      {
        const gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
        BinTools::PutReal(OS, aPnt.X());
        BinTools::PutReal(OS, aPnt.Y());
        BinTools::PutReal(OS, aPnt.Z());
      }

      if (aTriangulation->HasUVNodes())
      {
        for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
        {
          const gp_Pnt2d aUV = aTriangulation->UVNode(aNodeIter);
          BinTools::PutReal(OS, aUV.X());
          BinTools::PutReal(OS, aUV.Y());
        }
      }

      for (int aTriIter = 1; aTriIter <= aNbTriangles; ++aTriIter)
      {
        const Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
        BinTools::PutInteger(OS, aTri.Value(1));
        BinTools::PutInteger(OS, aTri.Value(2));
        BinTools::PutInteger(OS, aTri.Value(3));
      }

      // write the normals
      if (FormatNb() >= BinTools_FormatVersion_VERSION_4)
      {
        if (aTriangulation->HasNormals() && NeedToWriteNormals)
        {
          NCollection_Vec3<float> aNormal;
          for (int aNormalIter = 1; aNormalIter <= aNbNodes; ++aNormalIter)
          {
            aTriangulation->Normal(aNormalIter, aNormal);
            BinTools::PutShortReal(OS, aNormal.x());
            BinTools::PutShortReal(OS, aNormal.y());
            BinTools::PutShortReal(OS, aNormal.z());
          }
        }
      }
    }
  }
  catch (Standard_Failure const& anException)
  {
    Standard_SStream aMsg;
    aMsg << "EXCEPTION in BinTools_ShapeSet::WriteTriangulation(..)\n" << anException << "\n";
    throw Standard_Failure(aMsg.str().c_str());
  }
}

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

void BinTools_ShapeSet::ReadTriangulation(Standard_IStream&            IS,
                                          const Message_ProgressRange& theRange)
{
  char aHeader[255];
  IS >> aHeader;
  if (IS.fail() || (strstr(aHeader, "Triangulations") == nullptr))
  {
    throw Standard_Failure("BinTools_ShapeSet::Triangulation: Not a Triangulation section");
  }

  int aNbTriangulations = 0;
  IS >> aNbTriangulations;
  IS.get(); // remove LF

  try
  {
    OCC_CATCH_SIGNALS
    Message_ProgressScope aPS(theRange, "Reading triangulation", aNbTriangulations);
    for (int aTriangulationIter = 1; aTriangulationIter <= aNbTriangulations && aPS.More();
         ++aTriangulationIter, aPS.Next())
    {
      int    aNbNodes = 0, aNbTriangles = 0;
      bool   hasUV      = false;
      bool   hasNormals = false;
      double aDefl      = 0.0;
      BinTools::GetInteger(IS, aNbNodes);
      BinTools::GetInteger(IS, aNbTriangles);
      BinTools::GetBool(IS, hasUV);
      if (FormatNb() >= BinTools_FormatVersion_VERSION_4)
      {
        BinTools::GetBool(IS, hasNormals);
      }
      BinTools::GetReal(IS, aDefl); // deflection
      occ::handle<Poly_Triangulation> aTriangulation =
        new Poly_Triangulation(aNbNodes, aNbTriangles, hasUV, hasNormals);
      aTriangulation->Deflection(aDefl);

      gp_Pnt aNode;
      for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
      {
        BinTools::GetReal(IS, aNode.ChangeCoord().ChangeCoord(1));
        BinTools::GetReal(IS, aNode.ChangeCoord().ChangeCoord(2));
        BinTools::GetReal(IS, aNode.ChangeCoord().ChangeCoord(3));
        aTriangulation->SetNode(aNodeIter, aNode);
      }

      if (hasUV)
      {
        gp_Pnt2d aNode2d;
        for (int aNodeIter = 1; aNodeIter <= aNbNodes; ++aNodeIter)
        {
          BinTools::GetReal(IS, aNode2d.ChangeCoord().ChangeCoord(1));
          BinTools::GetReal(IS, aNode2d.ChangeCoord().ChangeCoord(2));
          aTriangulation->SetUVNode(aNodeIter, aNode2d);
        }
      }

      // read the triangles
      int aTriNodes[3] = {};
      for (int aTriIter = 1; aTriIter <= aNbTriangles; ++aTriIter)
      {
        BinTools::GetInteger(IS, aTriNodes[0]);
        BinTools::GetInteger(IS, aTriNodes[1]);
        BinTools::GetInteger(IS, aTriNodes[2]);
        aTriangulation->SetTriangle(aTriIter,
                                    Poly_Triangle(aTriNodes[0], aTriNodes[1], aTriNodes[2]));
      }
      // IS.ignore(sizeof(double) * (hasUV ? 5 : 3) * aNbNodes + sizeof(int) * 3
      // * aNbTriangles);

      if (hasNormals)
      {
        NCollection_Vec3<float> aNormal;
        for (int aNormalIter = 1; aNormalIter <= aNbNodes; ++aNormalIter)
        {
          BinTools::GetShortReal(IS, aNormal.x());
          BinTools::GetShortReal(IS, aNormal.y());
          BinTools::GetShortReal(IS, aNormal.z());
          aTriangulation->SetNormal(aNormalIter, aNormal);
        }
      }

      myTriangulations.Add(aTriangulation, hasNormals);
    }
  }
  catch (Standard_Failure const& anException)
  {
    Standard_SStream aMsg;
    aMsg << "EXCEPTION in BinTools_ShapeSet::ReadTriangulation(..)\n" << anException << "\n";
    throw Standard_Failure(aMsg.str().c_str());
  }
}

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

int BinTools_ShapeSet::NbShapes() const
{
  return myShapes.Extent();
}
