diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 3e360f522..4c5f67b3c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -124,6 +124,8 @@ add_library(libslic3r STATIC "${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h" Line.cpp Line.hpp + Log.hpp + Log.cpp MedialAxis.cpp MedialAxis.hpp Milling/MillingPostProcess.cpp diff --git a/src/libslic3r/Log.cpp b/src/libslic3r/Log.cpp new file mode 100644 index 000000000..8cde37fc4 --- /dev/null +++ b/src/libslic3r/Log.cpp @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include + +// Boost +#include +#include + +#include "Log.hpp" + +/// Local class to suppress output +class NullStream : public std::streambuf +{ +public: + int overflow(int c) { return c; } +}; + +namespace Slic3r { + +static NullStream log_null; +static std::ostream null_log(&log_null); + +std::unique_ptr<_Log> slic3r_log {_Log::make_log()}; + +_Log::_Log() : _out(std::clog) { +} + +_Log::_Log(std::ostream& out) : _out(out) { +} + +bool _Log::_has_log_level(log_t lvl) { + if (!this->_inclusive_levels && this->_log_level.find(lvl) != this->_log_level.end()) { + return true; + } else if (this->_inclusive_levels && *(std::max_element(this->_log_level.cbegin(), this->_log_level.cend())) >= lvl) { + return true; + } + return false; +} + +bool _Log::_has_topic(const std::string& topic) { + return this->_topics.find(topic) != this->_topics.end() || this->_topics.size() == 0; +} + +void _Log::fatal_error(const std::string& topic, const std::wstring& message) { this->fatal_error(topic, boost::locale::conv::utf_to_utf(message)); } +void _Log::error(const std::string& topic, const std::wstring& message) { this->error(topic, boost::locale::conv::utf_to_utf(message)); } +void _Log::warn(const std::string& topic, const std::wstring& message) { this->warn(topic, boost::locale::conv::utf_to_utf(message)); } +void _Log::info(const std::string& topic, const std::wstring& message) { this->info(topic, boost::locale::conv::utf_to_utf(message)); } +void _Log::debug(const std::string& topic, const std::wstring& message) { this->debug(topic, boost::locale::conv::utf_to_utf(message)); } + +void _Log::fatal_error(const char topic[], const char message[]) { + this->fatal_error(std::string(topic), std::string(message)); +} + +void _Log::fatal_error(const char topic[], const wchar_t message[]) { + this->fatal_error(std::string(topic), std::wstring(message)); +} + +void _Log::fatal_error(const std::string& topic, const std::string& message) { + this->fatal_error(topic) << message << std::endl; +} + +std::ostream& _Log::fatal_error(const char topic[], bool multiline) { + return this->fatal_error(std::string(topic), multiline); +} +std::ostream& _Log::fatal_error(const std::string& topic, bool multiline) { + if (this->_has_log_level(log_t::FERR) && this->_has_topic(topic)) { + if (!multiline) + _out << topic << std::setfill(' ') << std::setw(6) << "FERR" << ": "; + return _out; + } + return null_log; +} + +void _Log::error(const char topic[], const char message[]) { + this->error(std::string(topic), std::string(message)); +} + +void _Log::error(const char topic[], const wchar_t message[]) { + this->error(std::string(topic), std::wstring(message)); +} + +void _Log::error(const std::string& topic, const std::string& message) { + this->error(topic) << message << std::endl; +} +std::ostream& _Log::error(const char topic[], bool multiline) { + return this->error(std::string(topic), multiline); +} +std::ostream& _Log::error(const std::string& topic, bool multiline) { + if (this->_has_log_level(log_t::ERR) && this->_has_topic(topic)) { + if (!multiline) + _out << topic << std::setfill(' ') << std::setw(6) << "ERR" << ": "; + return _out; + } + return null_log; +} + + +void _Log::info(const std::string& topic, const std::string& message) { + this->info(topic) << message << std::endl; +} + +void _Log::info(const char topic[], const wchar_t message[]) { + this->info(std::string(topic), std::wstring(message)); +} + +void _Log::info(const char topic[], const char message[]) { + this->info(std::string(topic), std::string(message)); +} + +std::ostream& _Log::info(const char topic[], bool multiline) { + return this->info(std::string(topic), multiline); +} + +std::ostream& _Log::info(const std::string& topic, bool multiline) { + if (this->_has_log_level(log_t::INFO) && this->_has_topic(topic)) { + if (!multiline) + _out << topic << std::setfill(' ') << std::setw(6) << "INFO" << ": "; + return _out; + } + return null_log; +} + +void _Log::warn(const char topic[], const char message[]) { + this->warn(std::string(topic), std::string(message)); +} + +void _Log::warn(const char topic[], const wchar_t message[]) { + this->warn(std::string(topic), std::wstring(message)); +} + +void _Log::warn(const std::string& topic, const std::string& message) { + this->warn(topic) << message << std::endl; +} + +std::ostream& _Log::warn(const char topic[], bool multiline) { + return this->warn(std::string(topic), multiline); +} + +std::ostream& _Log::warn(const std::string& topic, bool multiline) { + if (this->_has_log_level(log_t::WARN) && this->_has_topic(topic)) { + if (!multiline) + _out << topic << std::setfill(' ') << std::setw(6) << "WARN" << ": "; + return _out; + } + return null_log; +} + +void _Log::debug(const char topic[], const char message[]) { + this->debug(std::string(topic), std::string(message)); +} + +void _Log::debug(const char topic[], const wchar_t message[]) { + this->debug(std::string(topic), std::wstring(message)); +} + +void _Log::debug(const std::string& topic, const std::string& message) { + this->debug(topic) << message << std::endl; +} + +std::ostream& _Log::debug(const char topic[], bool multiline) { + return this->debug(std::string(topic), multiline); +} + +std::ostream& _Log::debug(const std::string& topic, bool multiline) { + if (this->_has_log_level(log_t::DEBUG) && this->_has_topic(topic)) { + if (!multiline) + _out << topic << std::setfill(' ') << std::setw(6) << "DEBUG" << ": "; + return _out; + } + return null_log; +} + +void _Log::raw(const char message[]) { + this->raw(std::string(message)); +} + +void _Log::raw(const wchar_t message[]) { + this->raw(std::wstring(message)); +} + +void _Log::raw(const std::string& message) { + this->raw() << message << std::endl; +} +void _Log::raw(const std::wstring& message) { this->raw(boost::locale::conv::utf_to_utf(message)); } + +std::ostream& _Log::raw() { + return _out; +} + + +void _Log::set_level(log_t level) { + if (this->_inclusive_levels) { + this->_log_level.clear(); + this->_log_level.insert(level); + } else if (level == log_t::ALL) { + this->_log_level.insert(log_t::FERR); + this->_log_level.insert(log_t::ERR); + this->_log_level.insert(log_t::WARN); + this->_log_level.insert(log_t::INFO); + this->_log_level.insert(log_t::DEBUG); + } else { + this->_log_level.insert(level); + } +} +void _Log::clear_level(log_t level) { + if (level == log_t::ALL) { + this->_log_level.clear(); + } else { + if (this->_log_level.find(level) != this->_log_level.end()) + this->_log_level.erase(level); + } +} + +void _Log::clear_topic(const std::string& topic) { + if (topic == "") { + this->_topics.clear(); + } else { + if (this->_topics.find(topic) != this->_topics.end()) this->_topics.erase(topic); + } +} + +} // Slic3r diff --git a/src/libslic3r/Log.hpp b/src/libslic3r/Log.hpp new file mode 100644 index 000000000..6a7ed19ba --- /dev/null +++ b/src/libslic3r/Log.hpp @@ -0,0 +1,256 @@ +#ifndef slic3r_LOG_HPP +#define slic3r_LOG_HPP + +#include +#include +#include +#include +#include +#include + + +namespace Slic3r { + +/// All available logging levels. +enum class log_t : uint8_t { FERR = 0, ERR = 4, WARN = 8, INFO = 16, DEBUG = 32, ALL = 255 }; + +inline bool operator>(const log_t lhs, const log_t rhs) { return static_cast(lhs) > static_cast(rhs); } +inline bool operator<(const log_t lhs, const log_t rhs) { return static_cast(lhs) < static_cast(rhs); } +inline bool operator>=(const log_t lhs, const log_t rhs) { return static_cast(lhs) > static_cast(rhs) || lhs == rhs; } +inline bool operator<=(const log_t lhs, const log_t rhs) { return static_cast(lhs) < static_cast(rhs) || lhs == rhs; } + +/// Singleton instance implementing logging functionality in Slic3r +/// Basic functionality is stubbed in currently, may pass through to Boost::Log +/// eventually. +class _Log { +public: + static std::unique_ptr<_Log> make_log() { + std::unique_ptr<_Log> tmp {new _Log()}; + tmp->_inclusive_levels = true; + tmp->set_level(log_t::ERR); + tmp->set_level(log_t::FERR); + tmp->set_level(log_t::WARN); + return tmp; + } + static std::unique_ptr<_Log> make_log(std::ostream& out) { + std::unique_ptr<_Log> tmp {new _Log(out)}; + tmp->_inclusive_levels = true; + tmp->set_level(log_t::ERR); + tmp->set_level(log_t::FERR); + tmp->set_level(log_t::WARN); + return tmp; + } + void fatal_error(const char topic[], const char message[]); + void fatal_error(const char topic[], const wchar_t message[]); + void fatal_error(const std::string& topic, const std::string& message); + void fatal_error(const std::string& topic, const std::wstring& message); + std::ostream& fatal_error(const std::string& topic, bool multiline = false); + std::ostream& fatal_error(const char topic[], bool multiline = false); + + void error(const char topic[], const char message[]); + void error(const char topic[], const wchar_t message[]); + void error(const std::string& topic, const std::string& message); + void error(const std::string& topic, const std::wstring& message); + std::ostream& error(const std::string& topic, bool multiline = false); + std::ostream& error(const char topic[], bool multiline = false); + + void info(const std::string& topic, const std::string& message); + void info(const std::string& topic, const std::wstring& message); + void info(const char topic[], const char message[]); + void info(const char topic[], const wchar_t message[]); + std::ostream& info(const std::string& topic, bool multiline = false); + std::ostream& info(const char topic[], bool multiline = false); + void debug(const char topic[], const char message[]); + void debug(const char topic[], const wchar_t message[]); + void debug(const std::string& topic, const std::string& message); + void debug(const std::string& topic, const std::wstring& message); + std::ostream& debug(const std::string& topic, bool multiline = false); + std::ostream& debug(const char topic[], bool multiline = false); + void warn(const char topic[], const char message[]); + void warn(const char topic[], const wchar_t message[]); + void warn(const std::string& topic, const std::string& message); + void warn(const std::string& topic, const std::wstring& message); + std::ostream& warn(const std::string& topic, bool multiline = false); + std::ostream& warn(const char topic[], bool multiline = false); + + void raw(const char message[]); + void raw(const wchar_t message[]); + void raw(const std::string& message); + void raw(const std::wstring& message); + std::ostream& raw(); + + template + void debug_svg(const std::string& topic, const T& path, bool append = true); + template + void debug_svg(const std::string& topic, const T* path, bool append = true); + + void set_level(log_t level); + void clear_level(log_t level); + void set_inclusive(bool v) { this->_inclusive_levels = v; } + void add_topic(const std::string& topic) { this->_topics.insert(topic); } + void clear_topic(const std::string& topic); + +// _Log(_Log const&) = delete; +// void operator=(_Log const&) = delete; +private: + std::ostream& _out; + _Log(); + _Log(std::ostream& out); + bool _inclusive_levels { true }; + std::set _log_level { }; + std::set _topics { }; + + bool _has_log_level(log_t lvl); + bool _has_topic(const std::string& topic); + +}; + +/// Global log reference; initialized in Log.cpp +extern std::unique_ptr<_Log> slic3r_log; + +/// Static class for referencing the various logging functions. Refers to +/// +class Log { +public: + + /// Logs a fatal error with Slic3r. + /// \param topic [in] file or heading for error + /// \param message [in] text of the logged error message + static void fatal_error(std::string topic, std::wstring message) { + slic3r_log->fatal_error(topic, message); + } + + /// Logs a regular error with Slic3r. + /// \param topic [in] file or heading for error + /// \param message [in] text of the logged error message + static void error(std::string topic, std::wstring message) { + slic3r_log->error(topic, message); + } + + /// Logs a fatal error with Slic3r. + /// \param topic [in] file or heading for error + /// \param message [in] text of the logged error message + static void error(std::string topic, std::string message) { + slic3r_log->error(topic, message); + } + + /// Logs an informational message with Slic3r. + /// \param topic [in] file or heading for message + /// \param message [in] text of the logged message + static void info(std::string topic, std::wstring message) { + slic3r_log->info(topic, message); + } + /// Logs an informational message with Slic3r. + /// \param topic [in] file or heading for message + /// \param message [in] text of the logged message + static void info(std::string topic, std::string message) { + slic3r_log->info(topic, message); + } + + /// Logs a warning message with Slic3r. + /// \param topic [in] file or heading for message + /// \param message [in] text of the logged message + static void warn(std::string topic, std::wstring message) { + slic3r_log->warn(topic, message); + } + + /// Logs a warning message with Slic3r. + /// \param topic [in] file or heading for message + /// \param message [in] text of the logged message + static void warn(std::string topic, std::string message) { + slic3r_log->warn(topic, message); + } + + /// Logs a debugging message with Slic3r. + /// \param topic [in] file or heading for message + /// \param message [in] text of the logged message + static void debug(std::string topic, std::wstring message) { + slic3r_log->debug(topic, message); + } + /// Logs a debugging message with Slic3r. + /// \param topic [in] file or heading for message + /// \param message [in] text of the logged message + static void debug(std::string topic, std::string message) { + slic3r_log->debug(topic, message); + } + + + /// Logs an error message with Slic3r. + /// \param topic [in] file or heading for message + /// \param multiline [in] Is this a following part of a multline output (default False) + /// \return reference to output ostream for << chaining. + /// \note Developer is expected to add newlines. + static std::ostream& error(std::string topic, bool multiline = false) { + return slic3r_log->error(topic, multiline); + } + /// Logs a debugging message with Slic3r. + /// \param topic [in] file or heading for message + /// \param multiline [in] Is this a following part of a multline output (default False) + /// \return reference to output ostream for << chaining. + /// \note Developer is expected to add newlines. + static std::ostream& debug(std::string topic, bool multiline = false) { + return slic3r_log->debug(topic, multiline); + } + + /// Logs a warning message with Slic3r. + /// \param topic [in] file or heading for message + /// \param multiline [in] Is this a following part of a multline output (default False) + /// \return reference to output ostream for << chaining. + /// \note Developer is expected to add newlines. + static std::ostream& warn(std::string topic, bool multiline = false) { + return slic3r_log->warn(topic, multiline); + } + /// Logs an informational message with Slic3r. + /// \param topic [in] file or heading for message + /// \param multiline [in] Is this a following part of a multline output (default False) + /// \return reference to output ostream for << chaining. + /// \note Developer is expected to add newlines. + static std::ostream& info(std::string topic, bool multiline = false) { + return slic3r_log->info(topic, multiline); + } + + /// Unadorned ostream output for multiline constructions. + static std::ostream& raw() { + return slic3r_log->raw(); + } + + /// Adds a topic to filter on with Slic3r's debug system. + /// \param topic [in] name of topic to filter on. + /// Only shows registered topics. + static void add_topic(const std::string& topic) { + slic3r_log->add_topic(topic); + } + + /// Removes a topic from the filter list with Slic3r's debug system. + /// \param topic [in] name of topic to remove from filter. + /// \note Default option removes all filters. + static void clear_topic(const std::string& topic = "") { + slic3r_log->clear_topic(topic); + } +}; + +/// Utility debug function to transform a std::vector of anything that +/// supports ostream& operator<<() into a std::string. +template +std::string +log_string(const std::vector& in) +{ + std::stringstream ss; + bool first {true}; + ss << "[ "; + for (auto& c : in) { + if (!first) { + ss << ", "; + } + ss << c; + first = false; + } + + ss << " ]"; + + return ss.str(); +} + +} + +#endif // slic3r_LOG_HPP