LCOV - code coverage report
Current view: top level - meshio/details - stl.inl (source / functions) Hit Total Coverage
Test: coverage.info Lines: 174 176 98.9 %
Date: 2020-10-29 15:47:51 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2015, Lakshman Anumolu, Pradeep Garigipati
       3             :  * All rights reserved.
       4             :  *
       5             :  * This file is part of MeshIO whose distribution is governed by
       6             :  * the BSD 2-Clause License contained in the accompanying LICENSE.txt
       7             :  * file.
       8             :  */
       9             : 
      10             : namespace internal {
      11             : 
      12             : template<typename T = float>
      13             : void readAsciiSTL(std::vector< meshio::stl::Data<T> > &pObjects,
      14             :                   const char* pFileName)
      15             : {
      16             :     auto readNormal = [](const std::string& pLine) -> Vec3<float> {
      17          84 :         Vec3<float> N;
      18          84 :         std::stringstream lineSS(pLine);
      19          84 :         std::string facet, normal;
      20         420 :         lineSS >> facet >> normal >> N.x >> N.y >> N.z;
      21             :         return N;
      22          84 :     };
      23             : 
      24             :     auto readVertex = [](const std::string& pLine) -> Vec4<float> {
      25         252 :         Vec4<float> V;
      26         252 :         std::stringstream lineSS(pLine);
      27         252 :         std::string vertex;
      28        1008 :         lineSS >> vertex >> V.x >> V.y >> V.z;
      29         252 :         V.w = 1.0;
      30             :         return V;
      31         252 :     };
      32             : 
      33           7 :     std::ifstream stlFile(pFileName);
      34           7 :     std::string line;
      35             : 
      36          14 :     const bool isFileOpen = stlFile.is_open();
      37             : 
      38          70 :     while (isFileOpen && std::getline(stlFile, line)) {
      39           7 :         std::stringstream lineSS(line);
      40           7 :         std::string keyWord;
      41             : 
      42           7 :         lineSS >> keyWord;
      43             : 
      44           7 :         if (keyWord == "solid") {
      45           7 :             meshio::stl::Data<T> currObj;
      46           7 :             bool isFacetRead = false;
      47           7 :             unsigned outerCount = 0;
      48             : 
      49        2380 :             while (std::getline(stlFile, line)) {
      50         588 :                 std::stringstream ss(line);
      51         588 :                 std::string key;
      52             : 
      53         588 :                 ss >> key;
      54         588 :                 if (key == "endsolid")
      55           0 :                     break;
      56             : 
      57         588 :                 if (key == "facet") {
      58         168 :                     currObj.mNormals.push_back(readNormal(line));
      59          84 :                     isFacetRead = true;
      60         588 :                 } else if (key == "outer") {
      61             :                     /* Check if already facet is being read
      62             :                        if true, that means another primitive is having the
      63             :                        same normal as it's predecessor. Therefore, duplicate
      64             :                        the normal
      65             :                      */
      66         168 :                     if (isFacetRead && outerCount > 1)
      67           0 :                         currObj.mNormals.push_back(currObj.mNormals.back());
      68             :                     /* No need to do anything else specific just
      69             :                     just proceed to next line to read vertices */
      70          84 :                     outerCount++;
      71         504 :                 } else if (key == "endloop") {
      72             :                     /* Marks end of current primitive
      73             :                        we don't need to do anything specific
      74             :                        here */
      75         420 :                 } else if (key == "endfacet") {
      76             :                     /* Marks end of current facet, hence reset readFacet flag */
      77          84 :                     isFacetRead = false;
      78          84 :                     outerCount = 0;
      79          84 :                 }
      80         252 :                 else if (key == "vertex") {
      81         504 :                     currObj.mPositions.push_back(readVertex(line));
      82         252 :                 }
      83        1176 :             }
      84           7 :             pObjects.push_back(currObj);
      85           7 :         }
      86           7 :     }
      87           7 :     stlFile.close();
      88           7 : }
      89             : 
      90             : /*
      91             :  * Reads binary STL file assuming the format as described in
      92             :  * https://en.wikipedia.org/wiki/STL_(file_format). After the end of first
      93             :  * object, we assume that the next object's information starts with number of
      94             :  * triangles and the format continues.
      95             :  *
      96             :  * Note that this function assumes valid file path and it's existence.
      97             :  * All necessary checks are carried out in stl::read wrapper.
      98             :  */
      99             : template<typename T = float>
     100             : void readBinarySTL(std::vector< meshio::stl::Data<T> > &pObjects,
     101             :                    const char* pFileName)
     102             : {
     103             :     uint32_t numTriangles;
     104             :     uint16_t attribByteCount;
     105             :     float value;
     106           9 :     std::vector<float> values;
     107           9 :     std::size_t sizeFloat = sizeof(float);
     108           9 :     std::size_t sizeUInt16 = sizeof(uint16_t);
     109           9 :     unsigned objectCount = 0;
     110             : 
     111           9 :     std::stringstream strErr;
     112           9 :     std::ifstream ifs(pFileName, std::ios::binary | std::ios::in);
     113             : 
     114             :     char header[80];
     115           9 :     ifs.read(&header[0], 80);
     116             : 
     117           9 :     ifs.read((char *)&numTriangles, sizeof(uint32_t));
     118             : 
     119          36 :     while (numTriangles) {
     120           9 :         meshio::stl::Data<T> stlObject;
     121           9 :         stlObject.resize(numTriangles);
     122           9 :         Vec4<T> position;
     123           9 :         Vec3<float> normal;
     124             : 
     125             :         // Default assignment
     126           9 :         position.w = (T)1.;
     127             : 
     128         234 :         for (uint32_t facet = 0; facet < numTriangles; ++facet) {
     129         108 :             values.clear();
     130         864 :             for (short i = 0; i < 3; ++i) {
     131         324 :                 ifs.read((char *)&value, sizeFloat);
     132         324 :                 values.push_back(value);
     133         324 :             }
     134         216 :             normal.x = values[0];
     135         216 :             normal.y = values[1];
     136         216 :             normal.z = values[2];
     137         216 :             stlObject.mNormals[facet] = normal;
     138             : 
     139         864 :             for (short i = 0; i < 3; ++i) {
     140         324 :                 values.clear();
     141        2592 :                 for (short j = 0; j < 3; ++j) {
     142         972 :                     ifs.read((char *)&value, sizeFloat);
     143         972 :                     values.push_back(value);
     144         972 :                 }
     145         648 :                 position.x = values[0];
     146         648 :                 position.y = values[1];
     147         648 :                 position.z = values[2];
     148         648 :                 stlObject.mPositions[(3 * facet) + i] = position;
     149         324 :             }
     150             : 
     151         108 :             ifs.read((char *)&attribByteCount, sizeUInt16);
     152         108 :         }
     153           9 :         pObjects.push_back(stlObject);
     154             : 
     155           9 :         numTriangles = 0;
     156           9 :         ifs.read((char *)&numTriangles, sizeof(uint32_t));
     157           9 :     }
     158             : 
     159           9 :     ifs.close();
     160           9 : }
     161             : 
     162             : template<typename T = float>
     163             : bool writeAsciiSTL(const char* pFileName,
     164             :                    const std::vector< meshio::stl::Data<T> > &pObjects)
     165             : {
     166             :     using CSTLIter  = typename std::vector< meshio::stl::Data<T> >::const_iterator;
     167             :     using CVec3Iter = typename std::vector< Vec3<float> >::const_iterator;
     168             :     using CVec4Iter = typename std::vector< Vec4<float> >::const_iterator;
     169             : 
     170           2 :     std::ofstream objFile(pFileName);
     171             : 
     172           4 :     if (!objFile.is_open())
     173           1 :         return false;
     174             : 
     175           4 :     for (CSTLIter obj = pObjects.begin(); obj != pObjects.end(); ++obj) {
     176           2 :         objFile << "solid " << std::endl;
     177             : 
     178           1 :         CVec4Iter v = obj->mPositions.begin();
     179          26 :         for (CVec3Iter f = obj->mNormals.begin(); f != obj->mNormals.end(); ++f) {
     180          36 :             objFile << "facet normal " << std::scientific <<
     181          72 :                 f->x << " " << f->y << " " << f->z << std::endl;
     182          24 :             objFile << "outer loop" << std::endl;
     183          36 :             objFile << "vertex " << std::scientific <<
     184          72 :                 v->x << " " << v->y << " " << v->z << std::endl;
     185          12 :             ++v;
     186          36 :             objFile << "vertex " << std::scientific <<
     187          72 :                 v->x << " " << v->y << " " << v->z << std::endl;
     188          12 :             ++v;
     189          36 :             objFile << "vertex " << std::scientific <<
     190          72 :                 v->x << " " << v->y << " " << v->z << std::endl;
     191          12 :             ++v;
     192          24 :             objFile << "endloop" << std::endl;
     193          24 :             objFile << "endfacet" << std::endl;
     194          12 :         }
     195             : 
     196           2 :         objFile << "endsolid" << std::endl;
     197           1 :     }
     198             : 
     199           1 :     objFile.close();
     200             : 
     201           1 :     return true;
     202           2 : }
     203             : 
     204             : template<typename T = float>
     205             : bool writeBinarySTL(const char* pFileName,
     206             :                     const std::vector< meshio::stl::Data<T> > &pObjects)
     207             : {
     208             :     uint32_t numTriangles;
     209           3 :     uint16_t attribByteCount = 0;
     210           3 :     std::size_t sizeFloat = sizeof(float);
     211           3 :     std::size_t sizeUInt16 = sizeof(uint16_t);
     212           3 :     unsigned objectCount = pObjects.size();
     213             : 
     214           3 :     std::stringstream strErr;
     215           3 :     std::ofstream ofs(pFileName, std::ios::binary | std::ios::out);
     216           6 :     if (!ofs) {
     217           8 :         strErr << "Cannot open file (" << pFileName << ")" << std::endl;
     218           8 :         std::cerr << strErr.str() << std::endl;
     219           2 :         return false;
     220             :     }
     221             : 
     222           1 :     std::string headerStr = "<<<<<<<<<<<<<<<<<<<<<<";
     223           2 :     headerStr = headerStr + "Binary STL file written using MeshIO";
     224           2 :     headerStr = headerStr + ">>>>>>>>>>>>>>>>>>>>>>";
     225           1 :     const char *header = headerStr.c_str();
     226           1 :     ofs.write(header, 80);
     227             : 
     228           4 :     for (unsigned object = 0; object < objectCount; ++object) {
     229           2 :         numTriangles = pObjects[object].mNormals.size();
     230           1 :         ofs.write((char *)&numTriangles, sizeof(uint32_t));
     231             : 
     232          26 :         for (unsigned facet = 0; facet < numTriangles; ++facet) {
     233          36 :             Vec3<float> normal = pObjects[object].mNormals[facet];
     234          12 :             ofs.write((char *)&normal.x, sizeFloat);
     235          12 :             ofs.write((char *)&normal.y, sizeFloat);
     236          12 :             ofs.write((char *)&normal.z, sizeFloat);
     237          96 :             for (short i = 0; i < 3; ++i) {
     238         108 :                 Vec4<T> position = pObjects[object].mPositions[3 * facet + i];
     239          36 :                 ofs.write((char *)&position.x, sizeFloat);
     240          36 :                 ofs.write((char *)&position.y, sizeFloat);
     241          36 :                 ofs.write((char *)&position.z, sizeFloat);
     242          36 :             }
     243          12 :             ofs.write((char *)&attribByteCount, sizeUInt16);
     244          12 :         }
     245           1 :     }
     246             : 
     247           1 :     ofs.close();
     248             : 
     249           1 :     return true;
     250           4 : }
     251             : 
     252             : }  // namespace internal
     253             : 
     254             : template<typename T>
     255             : bool read(std::vector< meshio::stl::Data<T> > &pObjects,
     256             :           const char* pFileName)
     257             : {
     258          36 :     for (unsigned int i = 0; i < pObjects.size(); ++i)
     259           1 :         pObjects[i].clear();
     260          17 :     pObjects.clear();
     261             : 
     262          17 :     std::stringstream strErr;
     263          17 :     std::ifstream ifs(pFileName);
     264          34 :     if (!ifs) {
     265           4 :         strErr << "Cannot open file (" << pFileName << ")" << std::endl;
     266           4 :         std::cerr << strErr.str() << std::endl;
     267           1 :         return false;
     268             :     }
     269             : 
     270          16 :     const int bufferMaxSize = 80; // Only to read header
     271             :     char buffer[bufferMaxSize];
     272             : 
     273          16 :     ifs.getline(&buffer[0], bufferMaxSize);
     274          16 :     std::string line(&buffer[0]);
     275          16 :     ifs.close();
     276             : 
     277          32 :     if (line.substr(0, 5) == "solid")
     278          14 :         internal::readAsciiSTL<T>(pObjects, pFileName);
     279             :     else
     280           9 :         internal::readBinarySTL<T>(pObjects, pFileName);
     281             : 
     282          16 :     return true;
     283          33 : }
     284             : 
     285             : template<typename T>
     286             : bool write(const char* pFileName,
     287             :            const meshio::stl::Format pFormat,
     288             :            const std::vector< meshio::stl::Data<T> > &pObjects)
     289             : {
     290           5 :     if(pFormat == Format::Ascii) {
     291           2 :         return internal::writeAsciiSTL(pFileName, pObjects);
     292             :     } else { //Binary STL
     293           3 :         return internal::writeBinarySTL(pFileName, pObjects);
     294             :     }
     295           5 : }

Generated by: LCOV version 1.15