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 : }