How to solve STL Read&Write error

Hello everybody!

I'm confused by the StlAPI when I trying to import/export stl file. I have one stl file like:

solid 
 facet normal  0.000000e+000  0.000000e+000 -1.000000e+000
   outer loop
     vertex -1.100000e+002 -1.350000e+002  0.000000e+000
     vertex  0.000000e+000 -1.250000e+002  0.000000e+000
     vertex  0.000000e+000 -1.350000e+002  0.000000e+000
   endloop
 endfacet
endsolid

I've tried use StlAPI to read it, the RWStl_Reader report a error as follow:

TopoDS_Shape theShape;
StlAPI::Read(theShape, "test0.stl");

Error: unexpected format of facet at line 2

Another problem is occur when I was trying to export STL from TopoDS_Shape, I found the aNbNodes and aNbTriangles are zero:

Standard_Boolean StlAPI_Writer::Write (const TopoDS_Shape&    theShape,
                                       const Standard_CString theFileName)
{
  Standard_Integer aNbNodes = 0;
  Standard_Integer aNbTriangles = 0;

  // calculate total number of the nodes and triangles
  for (TopExp_Explorer anExpSF (theShape, TopAbs_FACE); anExpSF.More(); anExpSF.Next())
  {
    TopLoc_Location aLoc;
    Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (anExpSF.Current()), aLoc);
    if (! aTriangulation.IsNull())
    {
      aNbNodes += aTriangulation->NbNodes ();
      aNbTriangles += aTriangulation->NbTriangles ();
    }
  }

  // create temporary triangulation
  Handle(Poly_Triangulation) aMesh = new Poly_Triangulation (aNbNodes, aNbTriangles, Standard_False);

Open CASCADE version: 7.3.0

I don't know how to solve this problem, maybe it just my fault, can you help me?

Thanks in advance!!!

Kirill Gavrilov's picture

I've tried use StlAPI to read it, the RWStl_Reader report a error as follow:

OCCT 7.3.0 reads STL file with such content without issues - probably the real file is different.

Another problem is occur when I was trying to export STL from TopoDS_Shape, I found the aNbNodes and aNbTriangles are zero:

Shape should be triangulated before StlAPI usage, which can be done using BRepMesh_IncrementalMesh tool.

Zehuan Zhang's picture

Really appreciate your help, I've checked my STL file again and it is accord the STL format, then I go back to RWStl_Reader.cxx file:

Standard_Boolean RWStl_Reader::Read (const char* theFile,
                                     const Handle(Message_ProgressIndicator)& theProgress)
{
  std::filebuf aBuf;
  OSD_OpenStream (aBuf, theFile, std::ios::in | std::ios::binary);
  if (!aBuf.is_open())
  {
    return Standard_False;
  }

  Standard_IStream aStream (&aBuf);

  // get length of file to feed progress indicator in Ascii mode
  aStream.seekg (0, aStream.end);
  std::streampos theEnd = aStream.tellg();
  aStream.seekg (0, aStream.beg);

  // binary STL files cannot be shorter than 134 bytes 
  // (80 bytes header + 4 bytes facet count + 50 bytes for one facet);
  // thus assume files shorter than 134 as Ascii without probing
  // (probing may bring stream to fail state if EOF is reached)
  bool isAscii = ((size_t)theEnd < THE_STL_MIN_FILE_SIZE || IsAscii (aStream));

  while (aStream.good())
  {
    if (isAscii)
    {
      if (!ReadAscii (aStream, theEnd, theProgress))
      {
        break;
      }
    }
    else
    {
      if (!ReadBinary (aStream, theProgress))
      {
        break;
      }
    }
    aStream >> std::ws; // skip any white spaces
  }
  return ! aStream.fail();
}

This scope is no wrong but in called function ReadAscii:

Standard_Boolean RWStl_Reader::ReadAscii (Standard_IStream& theStream,
                                          const std::streampos theUntilPos,
                                          const Handle(Message_ProgressIndicator)& theProgress)
{
  // use method seekpos() to get true 64-bit offset to enable
  // handling of large files (VS 2010 64-bit)
  const int64_t aStartPos = GETPOS(theStream.tellg());
  // Note: 1 is added to theUntilPos to be sure to read the last symbol (relevant for files without EOL at the end)
  const int64_t aEndPos = (theUntilPos > 0 ? 1 + GETPOS(theUntilPos) : std::numeric_limits<int64_t>::max());

  // skip header "solid ..."
  theStream.ignore ((std::streamsize)(aEndPos - aStartPos), '\n');
  if (!theStream)
  {
    Message::DefaultMessenger()->Send ("Error: premature end of file", Message_Fail);
    return false;
  }

The aEndPos is calculated by GETPOS macro-code, my enviroment is MSVC v141 so it defined as aPos.seekpos():

#if defined(_MSC_VER)
  #define GETPOS(aPos) aPos.seekpos()
#else
  #define GETPOS(aPos) ((int64_t)aPos)
#endif

I test the seekpos method and ((int64_t)aPos) method, the ((int64_t)aPos) return 214138 but seekpos return 0, I guess the source of the problem might be here? Hope yours confirmation, thanks.

Attachments: 
Kirill Gavrilov's picture

I test the seekpos method and ((int64_t)aPos) method, the ((int64_t)aPos) return 214138 but seekpos return 0, I guess the source of the problem might be here? Hope yours confirmation, thanks.

The seekpos() issue comes from your msvc - they have deprecated method and changed its behavior (to do nothing) with the same release VS2017
(e.g. method worked as expected within initial VS2017 release, but has been broken with update).
The issue has been already fixed within development OCCT branch (#0030130) and will be included with the next release.