diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ReadStlFileFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ReadStlFileFilter.cpp index d270a1193d..e8ef45faca 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ReadStlFileFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ReadStlFileFilter.cpp @@ -112,37 +112,28 @@ IFilter::PreflightResult ReadStlFileFilter::preflightImpl(const DataStructure& d std::vector preflightUpdatedValues; - // If the filter needs to pass back some updated values via a key:value string:string set of values - // you can declare and update that string here. - - // Collect all the errors - std::vector errors; - // Validate that the STL File is binary and readable. - int32_t stlFileType = StlUtilities::DetermineStlFileType(pStlFilePathValue); - if(stlFileType < 0) + StlConstants::StlFileType stlFileType = StlUtilities::DetermineStlFileType(pStlFilePathValue); + if(stlFileType == StlConstants::StlFileType::ASCI) + { + return {MakeErrorResult( + StlConstants::k_UnsupportedFileType, + fmt::format("The Input STL File is ASCII which is not currently supported. Please convert it to a binary STL file using another program.", pStlFilePathValue.string()))}; + } + if(stlFileType == StlConstants::StlFileType::FileOpenError) { - Error result = {StlConstants::k_UnsupportedFileType, - fmt::format("The Input STL File is ASCII which is not currently supported. Please convert it to a binary STL file using another program.", pStlFilePathValue.string())}; - errors.push_back(result); + return {MakeErrorResult(StlConstants::k_ErrorOpeningFile, fmt::format("Error opening the STL file.", pStlFilePathValue.string()))}; } - if(stlFileType > 0) + if(stlFileType == StlConstants::StlFileType::HeaderParseError) { - Error result = {StlConstants::k_ErrorOpeningFile, fmt::format("Error reading the STL file.", pStlFilePathValue.string())}; - errors.push_back(result); + return {MakeErrorResult(StlConstants::k_ErrorOpeningFile, fmt::format("Error reading the header from STL file.", pStlFilePathValue.string()))}; } // Now get the number of Triangles according to the STL Header int32_t numTriangles = StlUtilities::NumFacesFromHeader(pStlFilePathValue); if(numTriangles < 0) { - Error result = {StlConstants::k_ErrorOpeningFile, fmt::format("Error reading the STL file.", pStlFilePathValue.string())}; - errors.push_back(result); - } - - if(!errors.empty()) - { - return {nonstd::make_unexpected(std::move(errors))}; + return {MakeErrorResult(numTriangles, fmt::format("Error extracting the number of triangles from the STL file.", pStlFilePathValue.string()))}; } // This can happen in a LOT of STL files. Just means the writer didn't go back and update the header. diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/StlUtilities.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/StlUtilities.cpp index ccc6b1aae7..dda4a335ff 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/StlUtilities.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/StlUtilities.cpp @@ -1,14 +1,21 @@ #include "StlUtilities.hpp" +#include +#include +#include +#include +#include +#include + using namespace nx::core; -int32_t StlUtilities::DetermineStlFileType(const fs::path& path) +StlConstants::StlFileType StlUtilities::DetermineStlFileType(const fs::path& path) { // Open File FILE* f = std::fopen(path.string().c_str(), "rb"); if(nullptr == f) { - return StlConstants::k_ErrorOpeningFile; + return StlConstants::StlFileType::FileOpenError; } // Read the first 256 bytes of data, that should be enough but I'm sure someone will write @@ -17,29 +24,29 @@ int32_t StlUtilities::DetermineStlFileType(const fs::path& path) if(std::fread(header.data(), 1, StlConstants::k_STL_HEADER_LENGTH, f) != StlConstants::k_STL_HEADER_LENGTH) { std::ignore = std::fclose(f); - return StlConstants::k_StlHeaderParseError; + return StlConstants::StlFileType::HeaderParseError; } // close the file std::ignore = std::fclose(f); - size_t solid_pos = header.find("solid", 0); + size_t solidPos = header.find("solid", 0); // The word 'solid' was not found ANYWHERE in the first 80 bytes. - if(solid_pos == std::string::npos) + if(solidPos == std::string::npos) { - return 0; + return StlConstants::StlFileType::Binary; } // 'solid' was found as the first 5 bytes of the header. This is am ambiguous case so let's try to find 'facet' - if(solid_pos == 0) + if(solidPos == 0) { - size_t facet_pos = header.find("facet", solid_pos + 6); - if(facet_pos == std::string::npos) + size_t facetPos = header.find("facet", solidPos + 6); + if(facetPos == std::string::npos) { // 'facet' was NOT found so this is a binary file. - return 0; + return StlConstants::StlFileType::Binary; } - return 1; + return StlConstants::StlFileType::ASCI; } - return 0; + return StlConstants::StlFileType::Binary; } int32_t StlUtilities::NumFacesFromHeader(const fs::path& path) @@ -71,3 +78,80 @@ int32_t StlUtilities::NumFacesFromHeader(const fs::path& path) std::ignore = std::fclose(f); return triCount; } + +struct Triangle +{ + float normal[3]; + float vertex1[3]; + float vertex2[3]; + float vertex3[3]; +}; + +void StlUtilities::ConvertAsciiToBinaryStl(const std::filesystem::path& inputPath, const std::filesystem::path& outputPath) +{ + std::ifstream asciiFile(inputPath); + std::ofstream binaryFile(outputPath, std::ios::binary); + + if(!asciiFile.is_open() || !binaryFile.is_open()) + { + throw std::runtime_error("Could not open files"); + } + + // Write the header + std::string header = "Converted by ChatGPT generated Algorithm"; + header.resize(80, ' '); + binaryFile.write(header.c_str(), 80); + + // Read ASCII STL and store triangles + std::vector triangles; + std::string line; + Triangle tri; + + while(std::getline(asciiFile, line)) + { + std::istringstream iss(line); + std::string token; + iss >> token; + + if(token == "facet") + { + iss >> token; // Skip "normal" word + for(int i = 0; i < 3; ++i) + { + iss >> tri.normal[i]; + } + } + else if(token == "vertex") + { + if(iss >> tri.vertex1[0] >> tri.vertex1[1] >> tri.vertex1[2]) + { + std::getline(asciiFile, line); // Read next vertex line + std::istringstream iss2(line); + iss2 >> token; // Skip "vertex" word + iss2 >> tri.vertex2[0] >> tri.vertex2[1] >> tri.vertex2[2]; + + std::getline(asciiFile, line); // Read next vertex line + std::istringstream iss3(line); + iss3 >> token; // Skip "vertex" word + iss3 >> tri.vertex3[0] >> tri.vertex3[1] >> tri.vertex3[2]; + + triangles.push_back(tri); + } + } + } + + // Write the number of triangles + uint32_t numTriangles = static_cast(triangles.size()); + binaryFile.write(reinterpret_cast(&numTriangles), sizeof(numTriangles)); + + // Write triangles + for(const auto& t : triangles) + { + binaryFile.write(reinterpret_cast(&t), sizeof(Triangle)); + uint16_t attributeByteCount = 0; + binaryFile.write(reinterpret_cast(&attributeByteCount), sizeof(attributeByteCount)); + } +} + +// Example usage: +// convertAsciiToBinarySTL("input_ascii.stl", "output_binary.stl"); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/StlUtilities.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/StlUtilities.hpp index 6136e0d346..9333358a4b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/StlUtilities.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/StlUtilities.hpp @@ -19,13 +19,28 @@ inline constexpr int32_t k_StlHeaderParseError = -1104; inline constexpr int32_t k_TriangleCountParseError = -1105; inline constexpr int32_t k_TriangleParseError = -1106; inline constexpr int32_t k_AttributeParseError = -1107; + +enum class StlFileType : int +{ + Binary = 0, + ASCI = 1, + FileOpenError = 2, + HeaderParseError = 3 +}; } // namespace StlConstants namespace StlUtilities { // ----------------------------------------------------------------------------- -// Returns 0 for Binary, 1 for ASCII, anything else is an error. -int32_t DetermineStlFileType(const fs::path& path); +/** + * @brief This function will determine if the given STL file is ASCII or BINARY. + * + * This could give a false positive for BINARY for _any_ file that doesn't have + * the first few lines of a valid ASCII STL file. + * @param path The path to the file to check + * @return Enumeration that represents either the type of file or a possible parsing error + */ +StlConstants::StlFileType DetermineStlFileType(const fs::path& path); /** * @brief Returns the number of triangles in the file according to the header. This @@ -34,5 +49,13 @@ int32_t DetermineStlFileType(const fs::path& path); * @return Number of triangle faces */ int32_t NumFacesFromHeader(const fs::path& path); + +/** + * @brief A very basic function to convert a well behaved ASCII STL File into a binary STL file + * @param inputPath The input ASCII STL File + * @param outputPath The output Binary STL file + */ +void ConvertAsciiToBinaryStl(const std::filesystem::path& inputPath, const std::filesystem::path& outputPath); + } // namespace StlUtilities } // namespace nx::core