diff --git a/App/Header.h b/App/Header.h index a89dad5..eb0198f 100644 --- a/App/Header.h +++ b/App/Header.h @@ -11,6 +11,7 @@ namespace slm { struct Header { std::string fileName; + std::string creator; void setVersion(std::tuple version) { std::tie(vMajor, vMinor) = version; } std::tuple version() const { return std::make_tuple(vMajor, vMinor); } diff --git a/App/Model.cpp b/App/Model.cpp index 2bff125..e737a66 100644 --- a/App/Model.cpp +++ b/App/Model.cpp @@ -18,6 +18,7 @@ BuildStyle::BuildStyle() : id(0), pointExposureTime(0), laserId(1), laserMode(1), + pointDelay(0), jumpSpeed(0), jumpDelay(0) diff --git a/App/Model.h b/App/Model.h index ee1533c..179608a 100644 --- a/App/Model.h +++ b/App/Model.h @@ -53,6 +53,7 @@ class SLM_EXPORT BuildStyle float laserSpeed; uint64_t pointDistance; + uint64_t pointDelay; uint64_t pointExposureTime; uint64_t jumpSpeed; uint64_t jumpDelay; diff --git a/App/Reader.cpp b/App/Reader.cpp index c1ea745..5456d41 100644 --- a/App/Reader.cpp +++ b/App/Reader.cpp @@ -6,6 +6,7 @@ #include #include + #include "Layer.h" #include "Model.h" @@ -31,6 +32,23 @@ Reader::~Reader() layers.clear(); } +int64_t Reader::getFileSize() const +{ + + if(!this->isReady()) { + return -1; + } + + fs::path checkPath(this->filePath); + return checkPath.file_size(); + + //const auto begin = myfile.tellg(); + //testFile.seekg (0, ios::end); + //const auto end = testFile.tellg(); + //const auto fsize = (end-begin); + +} + void Reader::setFilePath(std::string path) { fs::path checkPath(path); @@ -59,6 +77,42 @@ Model::Ptr Reader::getModelById(uint64_t mid) const return (result != models.cend()) ? *result : Model::Ptr(); } + +Layer::Ptr Reader::getTopLayerByPosition(const std::vector &layers) +{ + uint64_t zMax = 0; + + Layer::Ptr fndLayer; + + for(auto layer : layers) { + + if(layer->getZ() > zMax) { + fndLayer = layer; + zMax = layer->getZ(); + } + } + + return fndLayer; +} + +Layer::Ptr Reader::getTopLayerById(const std::vector &layers) +{ + uint64_t zId = 0; + + Layer::Ptr fndLayer; + + for(auto layer : layers) { + + if(layer->getLayerId() > zId) { + fndLayer = layer; + zId = layer->getLayerId(); + } + } + + return fndLayer; +} + + int Reader::parse() { if(!this->isReady()) { diff --git a/App/Reader.h b/App/Reader.h index 17dfda7..904fe93 100644 --- a/App/Reader.h +++ b/App/Reader.h @@ -27,13 +27,17 @@ class SLM_EXPORT Reader std::string getFilePath() { return filePath; } void setFilePath(std::string path); + int64_t getFileSize() const; virtual double getLayerThickness() const = 0; Model::Ptr getModelById(uint64_t mid) const; std::vector getModels() const { return models;} std::vector getLayers() const { return layers;} - + + Layer::Ptr getTopLayerByPosition(const std::vector &layers); + Layer::Ptr getTopLayerById(const std::vector &layers); + protected: void setReady(bool state) { ready = state; } std::string filePath; diff --git a/App/Writer.cpp b/App/Writer.cpp index 7135559..2a7ac7b 100644 --- a/App/Writer.cpp +++ b/App/Writer.cpp @@ -12,12 +12,14 @@ namespace fs = filesystem; using namespace slm; using namespace base; -Writer::Writer(const char * fname) : ready(false) +Writer::Writer(const char * fname) : ready(false), + mSortLayers(false) { this->setFilePath(std::string(fname)); } -Writer::Writer(const std::string &fname) : ready(false) +Writer::Writer(const std::string &fname) : ready(false), + mSortLayers(false) { this->setFilePath(fname); } @@ -35,7 +37,7 @@ void Writer::getFileHandle(std::fstream &file) const if(this->isReady()) { file.open(filePath,std::fstream::in | std::fstream::out | std::fstream::trunc | std::fstream::binary); if(!file.is_open()) { - std::cerr << "Cannot create file handler - " << filePath; + std::cerr << "Cannot create file handler - " << filePath << std::endl; } } } @@ -46,3 +48,132 @@ void Writer::setFilePath(const std::string &path) this->filePath = path; std::cout << "File '" << path << "' is ready to write" << std::endl; } + +Layer::Ptr Writer::getTopLayerByPosition(const std::vector &layers) +{ + uint64_t zMax = 0; + + Layer::Ptr fndLayer; + + for(auto layer : layers) { + + if(layer->getZ() > zMax) { + fndLayer = layer; + zMax = layer->getZ(); + } + } + + return fndLayer; +} + +Layer::Ptr Writer::getTopLayerById(const std::vector &layers) +{ + uint64_t zId = 0; + + Layer::Ptr fndLayer; + + for(auto layer : layers) { + + if(layer->getLayerId() > zId) { + fndLayer = layer; + zId = layer->getLayerId(); + } + } + + return fndLayer; +} + +std::vector Writer::sortLayers(const std::vector &layers) +{ + std::vector layersCpy(layers); + + std::sort(layersCpy.begin(), layersCpy.end(), [](Layer::Ptr a, Layer::Ptr b) { + return a->getZ() > b->getZ(); + }); + + return layersCpy; +} + +int64_t Writer::getTotalNumHatches(const std::vector &layers) +{ + return Writer::getTotalGeoms(layers); +} + + +int64_t Writer::getTotalNumContours(const std::vector &layers) +{ + return Writer::getTotalGeoms(layers); +} + +void Writer::getLayerBoundingBox(float *bbox, Layer::Ptr layer) +{ + float minX = 1e9, minY = 1e9 , maxX = -1e9, maxY = -1e9; + + for(auto geom : layer->geometry()) { + auto minCols = geom->coords.colwise().minCoeff(); + auto maxCols = geom->coords.colwise().maxCoeff(); + + if(minCols[0, 0] < minX) + minX = minCols[0,0]; + + if(minCols[0,1] < minY) + minY = minCols[0,1]; + + if(maxCols[0,0] > maxX) + maxX = maxCols[0,0]; + + if(maxCols[0,1] > maxY) + maxY = maxCols[0,1]; + } +} + +void Writer::getBoundingBox(float *bbox, const std::vector &layers) +{ + float minX = 1e9, minY = 1e9 , maxX = -1e9, maxY = -1e9; + + for (auto layer: layers) { + for(auto geom : layer->geometry()) { + auto minCols = geom->coords.colwise().minCoeff(); + auto maxCols = geom->coords.colwise().maxCoeff(); + + if(minCols[0, 0] < minX) + minX = minCols[0,0]; + + if(minCols[0,1] < minY) + minY = minCols[0,1]; + + if(maxCols[0,0] > maxX) + maxX = maxCols[0,0]; + + if(maxCols[0,1] > maxY) + maxY = maxCols[0,1]; + + } + } + + bbox[0] = minX; + bbox[1] = maxX; + bbox[2] = minY; + bbox[3] = maxY; +} + +std::tuple Writer::getLayerMinMax(const std::vector &layers) +{ + float zMin = 0.0; + float zMax = 0.0; + float zPos = 0.0; + + for (auto layer : layers) { + + zPos = layer->getZ(); + + if(zPos < zMin) + zMin = zPos; + + if(zPos > zMax) + zMax = zPos; + } + + return std::make_tuple(zMin, zMax); +} + diff --git a/App/Writer.h b/App/Writer.h index ae9fcdc..fb23ab0 100644 --- a/App/Writer.h +++ b/App/Writer.h @@ -34,16 +34,41 @@ class SLM_EXPORT Writer const std::vector &models, const std::vector &layers) = 0; + bool isSortingLayers() const { return mSortLayers; } + void setSortLayers(bool state) { mSortLayers = state; } + +public: + static void getBoundingBox(float *bbox, const std::vector &layers); + static void getLayerBoundingBox(float *bbox, Layer::Ptr layer); + static std::tuple getLayerMinMax(const std::vector &layers); + + static Layer::Ptr getTopLayerByPosition(const std::vector &layers); + static Layer::Ptr getTopLayerById(const std::vector &layers); + + static int64_t getTotalNumHatches(const std::vector &layers); + static int64_t getTotalNumContours(const std::vector &layers); + static std::vector sortLayers(const std::vector &layers); + template + static int64_t getTotalGeoms(const std::vector &layers) + { + int64_t numGeomT = 0; + + for(auto layer : layers) + numGeomT += layer->getGeometryByType().size(); + + return numGeomT; + } + protected: void setReady(bool state) { ready = state; } void getFileHandle(std::fstream &file) const; - protected: std::string filePath; private: bool ready; + bool mSortLayers; }; } // End of Namespace Base diff --git a/CMakeLists.txt b/CMakeLists.txt index b41cdd1..372972a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0) -project(libSLM VERSION 0.2.3) +project(libSLM VERSION 0.2.5) # Set c++ to use cx11 as a requirement set(CMAKE_CXX_STANDARD 11) @@ -26,7 +26,7 @@ if(WIN32) # Remove Security definitions for the library # Remove run time checks for windows IF(MSVC) - + set(COMMON_LANGUAGE_RUNTIME "") set(EIGEN3_INCLUDE_DIR External/eigen) @@ -37,7 +37,7 @@ if(WIN32) SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -else(WIN32) +else(WIN32) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") @@ -78,21 +78,17 @@ SOURCE_GROUP("Base" FILES ${BASE_SRCS}) set(APP_H_SRCS App/Header.h -# App/Iterator.h App/Layer.h App/Model.h App/Reader.h -# App/Slm.h App/Writer.h App/Utils.h ) set(APP_CPP_SRCS -# App/Iterator.cpp App/Layer.cpp App/Model.cpp App/Reader.cpp -# App/Slm.cpp App/Writer.cpp App/Utils.cpp ) @@ -159,14 +155,14 @@ if(BUILD_PYTHON) add_executable(main ${App_SRCS}) - target_link_libraries(main SLM_static MTT_static SLMSol_static) + target_link_libraries(main SLM_static MTT_static EOS_static SLMSol_static Realizer_static) #install(TARGETS slm DESTINATION lib/libSLM) else() add_executable(main ${App_SRCS}) - target_link_libraries(main SLM MTT) + target_link_libraries(main SLM EOS MTT Realizer SLMSol) endif(BUILD_PYTHON) diff --git a/External/eigen b/External/eigen index ecb7bc9..ec4efbd 160000 --- a/External/eigen +++ b/External/eigen @@ -1 +1 @@ -Subproject commit ecb7bc9514b12051d050299234b2a74ac76b5a8e +Subproject commit ec4efbd69601694e600e6261d4a3afed08e0bfd3 diff --git a/External/filesystem b/External/filesystem index 4efd262..c5f9de3 160000 --- a/External/filesystem +++ b/External/filesystem @@ -1 +1 @@ -Subproject commit 4efd2628b05a13c2bd591cd253e1e201122fda4e +Subproject commit c5f9de30142453eb3c6fe991e82dfc2583373116 diff --git a/External/pybind11 b/External/pybind11 index a54eab9..70a58c5 160000 --- a/External/pybind11 +++ b/External/pybind11 @@ -1 +1 @@ -Subproject commit a54eab92d265337996b8e4b4149d9176c2d428a6 +Subproject commit 70a58c577eaf067748c2ec31bfd0b0a614cffba6 diff --git a/README.rst b/README.rst index af0d6b4..0d4dd3b 100644 --- a/README.rst +++ b/README.rst @@ -23,8 +23,8 @@ Current importing and exporting capabilities available via libSLM: * Renishaw (**.mtt**) (Currently working with QuantAM 5.3) * DMG Mori - Realizer (**.rea**) * Fockele and Schwarze File Format (**.f&s**) - Currently work in progress -* EOS (**.sli**) - currently work in progress -* SLM Solutions (**.slm**) -Currently work in progress +* EOS (**.sli**) - 1st Revision +* SLM Solutions (**.slm**) * CLI (**.cli** - Common Layer Interface) Access to these specific translators are currently available on request as pre-compiled modules due to sensitivity of @@ -130,7 +130,7 @@ generic method for storing this information. Additional output may be generated buildFileLayers = layers # Layer Thickness currently for the file - layerThicknessMicrons = reader.getZUnit() + layerThicknessMicrons = reader.getZUnit() # zUnit is typically 1000 layerThickness = reader.getLayerThickness() diff --git a/main.cpp b/main.cpp index 303f35f..1b6a5fa 100644 --- a/main.cpp +++ b/main.cpp @@ -3,8 +3,15 @@ #include +#include +#include + #include #include + +#include +#include + #include #include @@ -16,24 +23,72 @@ int main(int argc, char *argv[]) } std::string path = argv[0]; - std::string file = argv[1]; + std::string mode = argv[1]; + std::string inputFilename = argv[2]; + std::string outputFilename = argv[3]; + + std::cout << "Reading file " << inputFilename << std::endl; + + if(mode.find("mtt") != std::string::npos) { + slm::MTT::Reader reader(inputFilename); + + std::cout << "Parsing File" << std::endl; + reader.parse(); + + slm::MTT::Writer writer(outputFilename); + + slm::Header header; + header.zUnit = reader.getZUnit(); + header.vMajor = 1; + header.vMinor = 6; + header.fileName = "MTT Layerfile"; + + writer.write(header, reader.getModels(), reader.getLayers()); + + } else if(mode.find("realizer") != std::string::npos) { - std::string filePath = file; + slm::realizer::Reader reader(inputFilename); - std::cout << "Reading file" << filePath << std::endl; + std::cout << "Parsing File" << std::endl; + reader.parse(); - slm::MTT::Reader reader(filePath); + slm::realizer::Writer writer(outputFilename); - std::cout << "Parsing File" << std::endl; - reader.parse(); + slm::Header header; - slm::MTT::Writer writer("out.mtt"); + header.zUnit = 1000; + header.vMajor = 4; + header.vMinor = 7; + header.fileName = outputFilename; - slm::Header header; - header.zUnit = reader.getZUnit(); - header.vMajor = 1; - header.vMinor = 6; - header.fileName = "MTT Layerfile"; + std::string headerStr = reader.getHeader(); + writer.setHeaderText(headerStr); - writer.write(header, reader.getModels(), reader.getLayers()); + writer.write(header, reader.getModels(), reader.getLayers()); + + } else if(mode.find("eos") != std::string::npos) { + + slm::eos::Reader reader(inputFilename); + + std::cout << "Parsing File" << std::endl; + reader.parse(); + + slm::eos::Writer writer(outputFilename); + + slm::Header header; + + header.zUnit = 1000; + header.vMajor = 4; + header.vMinor = 7; + header.fileName = outputFilename; + + float sf = reader.getScaleFactor(); + writer.setScaleFactor(sf); + + writer.write(header, reader.getModels(), reader.getLayers()); + + } else { + std::cerr << "Error: Invalid option given"; + } + } diff --git a/python/libSLM/module.cpp b/python/libSLM/module.cpp index 7cb1a99..976a3bd 100644 --- a/python/libSLM/module.cpp +++ b/python/libSLM/module.cpp @@ -15,9 +15,7 @@ #include #include #include - -//#include -//#include +#include #include "utils.h" @@ -39,6 +37,78 @@ PYBIND11_MODULE(slm, m) { )pbdoc"; +#if 1 + /* + * Create a trampolise class for slm::Reader as this contains a pure virtual function + */ + class PyReader : public slm::base::Reader { + public: + /* Inherit the constructors */ + using slm::base::Reader::Reader; + + /* Trampoline (need one for each virtual function) */ + int parse() override { + PYBIND11_OVERRIDE_PURE( + int, /* Return type */ + Reader, /* Parent class */ + parse /* Name of function in C++ (must match Python name) */ + /* Argument(s) */ + ); + } + + double getLayerThickness() const override { + PYBIND11_OVERRIDE_PURE( + double, /* Return type */ + Reader, /* Parent class */ + getLayerThickness /* Name of function in C++ (must match Python name) */ + /* Argument(s) */ + ); + } + }; + + class PyWriter : public slm::base::Writer { + public: + /* Inherit the constructors */ + using slm::base::Writer::Writer; + + /* Trampoline (need one for each virtual function) */ + void write(const Header &header, + const std::vector &models, + const std::vector &layers) override { + PYBIND11_OVERRIDE_PURE( + void, /* Return type */ + Writer, /* Parent class */ + write, /* Name of function in C++ (must match Python name) */ + header, models, layers /* Argument(s) */ + ); + } + + }; + + py::class_(m, "Reader") + .def(py::init()) + .def("setFilePath", &slm::base::Reader::setFilePath, py::arg("filename")) + .def("getFilePath", &slm::base::Reader::getFilePath) + .def("parse", &slm::base::Reader::parse) + .def("getFileSize", &slm::base::Reader::getFileSize) + .def("getLayerThickness", &slm::base::Reader::getLayerThickness) + .def("getModelById", &slm::base::Reader::getModelById, py::arg("mid")) + .def_property_readonly("layers", &slm::base::Reader::getLayers) + .def_property_readonly("models", &slm::base::Reader::getModels); + + py::class_(m, "Writer") + .def(py::init()) + .def(py::init()) + .def("setFilePath", &slm::base::Writer::setFilePath) + .def("getFilePath", &slm::base::Writer::getFilePath) + .def("getLayerMinMax", &slm::base::Writer::getLayerMinMax) + .def("getTotalNumHatches", &slm::base::Writer::getTotalNumHatches) + .def("getTotalNumContours", &slm::base::Writer::getTotalNumContours) + .def("getBoundingBox", &slm::base::Writer::getBoundingBox) + .def_property("sortLayers", &slm::base::Writer::isSortingLayers, &slm::base::Writer::setSortLayers) + .def("write", &slm::base::Writer::write, py::arg("header"), py::arg("models"), py::arg("layers")); + +#endif py::enum_(m, "LaserMode") .value("Default", LaserMode::PULSE) @@ -183,7 +253,8 @@ PYBIND11_MODULE(slm, m) { py::class_(m, "Header", py::dynamic_attr()) .def(py::init()) .def_readwrite("filename", &Header::fileName) - .def_property("version", &Header::version, &Header::setVersion) + .def_readwrite("creator", &Header::creator) + .def_property("version", &Header::version, &Header::setVersion) .def_readwrite("zUnit", &Header::zUnit) .def(py::pickle( [](py::object self) { // __getstate__ @@ -215,8 +286,9 @@ PYBIND11_MODULE(slm, m) { .def_readwrite("laserFocus", &BuildStyle::laserFocus) .def_readwrite("pointDistance", &BuildStyle::pointDistance) .def_readwrite("pointExposureTime", &BuildStyle::pointExposureTime) - .def_readwrite("laserId", &BuildStyle::laserId) + .def_readwrite("laserId", &BuildStyle::laserId) .def_readwrite("laserMode", &BuildStyle::laserMode) + .def_readwrite("pointDelay", &BuildStyle::pointDelay) .def_readwrite("jumpDelay", &BuildStyle::jumpDelay) .def_readwrite("jumpSpeed", &BuildStyle::jumpSpeed) .def("setStyle", &BuildStyle::setStyle, "Sets the paramters of the buildstyle", @@ -236,6 +308,7 @@ PYBIND11_MODULE(slm, m) { self.attr("pointDistance"), self.attr("pointExposureTime"), self.attr("laserId"), self.attr("laserMode"), self.attr("name"), self.attr("description"), + self.attr("pointDelay"), self.attr("jumpDelay"), self.attr("jumpSpeed"), self.attr("description"), self.attr("__dict__")); @@ -256,11 +329,12 @@ PYBIND11_MODULE(slm, m) { p->laserMode = t[7].cast(); p->name = t[8].cast(); p->description = t[9].cast(); - p->jumpDelay = t[10].cast(); - p->jumpSpeed = t[11].cast(); - p->description = t[12].cast(); + p->pointDelay = t[10].cast(); + p->jumpDelay = t[11].cast(); + p->jumpSpeed = t[12].cast(); + p->description = t[13].cast(); - auto py_state = t[13].cast(); + auto py_state = t[14].cast(); return std::make_pair(p, py_state); } @@ -268,6 +342,7 @@ PYBIND11_MODULE(slm, m) { py::class_>(m, "Model", py::dynamic_attr()) .def(py::init()) + .def(py::init(), py::arg("mid"), py::arg("topSliceNum")) .def_property("mid", &Model::getId, &Model::setId) .def("__len__", [](const Model &s ) { return s.getBuildStyles().size(); }) .def_property("buildStyles",py::cpp_function(&slm::Model::buildStylesRef,py::return_value_policy::reference, py::keep_alive<1,0>()), diff --git a/python/libSLM/translators/__init__.py b/python/libSLM/translators/__init__.py index 2c072e4..d7f53e2 100644 --- a/python/libSLM/translators/__init__.py +++ b/python/libSLM/translators/__init__.py @@ -1,2 +1,4 @@ +from .eos import * from .mtt import * +from .realizer import * from .slmsol import * diff --git a/setup.py b/setup.py index d1ff45d..cf7bbcf 100644 --- a/setup.py +++ b/setup.py @@ -76,7 +76,7 @@ def build_extension(self, ext): setup( name='libSLM', - version='0.2.3', + version='0.2.5', author='Luke Parry', author_email='dev@lukeparry.uk', url='https://github.com/drlukeparry/libslm', @@ -84,8 +84,10 @@ def build_extension(self, ext): long_description_content_type = 'text/x-rst', description='libSLM is a python library for reading and writing to common machine build files used by commerical Additive Manufacturing systems', ext_modules=[CMakeExtension('libSLM.slm', 'slm'), + CMakeExtension('libSLM.translators.eos', 'eos'), CMakeExtension('libSLM.translators.mtt', 'mtt'), - CMakeExtension('libSLM.translators.slmsol', 'slmsol')], + CMakeExtension('libSLM.translators.slmsol', 'slmsol'), + CMakeExtension('libSLM.translators.realizer', 'realizer')], cmdclass=dict(build_ext=CMakeBuild), packages = ['libSLM', 'libSLM.translators'], package_dir={'': 'python'}, @@ -99,6 +101,7 @@ def build_extension(self, ext): 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Natural Language :: English', 'Topic :: Scientific/Engineering'], license="",