#ifdef WIN32 // Why? #define _WIN32_WINNT 0x0502 // The standard Windows includes. #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #include #ifdef SLIC3R_GUI extern "C" { // Let the NVIDIA and AMD know we want to use their graphics card // on a dual graphics card system. __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif /* SLIC3R_GUI */ #endif /* WIN32 */ #include #include #include #include #include #if defined(__linux__) || defined(__LINUX__) #include #include #include //add json logic #include "nlohmann/json.hpp" using namespace nlohmann; #endif #include #include #include #include #include #include #include #include #include "unix/fhs.hpp" // Generated by CMake from ../platform/unix/fhs.hpp.in #include "libslic3r/libslic3r.h" #include "libslic3r/Config.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/ModelArrange.hpp" #include "libslic3r/Platform.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Format/AMF.hpp" #include "libslic3r/Format/3mf.hpp" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/OBJ.hpp" #include "libslic3r/Format/SL1.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/Time.hpp" #include "libslic3r/Thread.hpp" #include "libslic3r/BlacklistedLibraryCheck.hpp" #include "libslic3r/Orient.hpp" #include "BambuStudio.hpp" //BBS: add exception handler for win32 #include #ifdef WIN32 #include "BaseException.h" #endif #include "slic3r/GUI/PartPlate.hpp" #include "slic3r/GUI/BitmapCache.hpp" #include "slic3r/GUI/OpenGLManager.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/Camera.hpp" #include #ifdef __WXGTK__ #include #endif #ifdef SLIC3R_GUI #include "slic3r/GUI/GUI_Init.hpp" #endif /* SLIC3R_GUI */ using namespace Slic3r; /*typedef struct _error_message{ int code; std::string message; }error_message;*/ std::map cli_errors = { {CLI_SUCCESS, "Success"}, {CLI_ENVIRONMENT_ERROR, "Environment setup failed"}, {CLI_INVALID_PARAMS, "Input param invalid"}, {CLI_FILE_NOTFOUND, "Input file not found"}, {CLI_FILELIST_INVALID_ORDER, "File list order invalid(please make sure 3mf in the first place)"}, {CLI_CONFIG_FILE_ERROR, "Invalid config file, could not be parsed"}, {CLI_DATA_FILE_ERROR, "Invalid model file, could not be loaded"}, {CLI_INVALID_PRINTER_TECH, "Invalid printer technoledge"}, {CLI_UNSUPPORTED_OPERATION, "Unsupported operation"}, {CLI_COPY_OBJECTS_ERROR, "Copy objects error"}, {CLI_SCALE_TO_FIT_ERROR, "Scale to fit error"}, {CLI_EXPORT_STL_ERROR, "Export stl error"}, {CLI_EXPORT_OBJ_ERROR, "Export obj error"}, {CLI_EXPORT_3MF_ERROR, "Export 3mf error"}, {CLI_OUT_OF_MEMORY, "Out of memory"}, {CLI_NO_SUITABLE_OBJECTS, "Found no objects in print volume to slice"}, {CLI_VALIDATE_ERROR, "Validate print error"}, {CLI_OBJECTS_PARTLY_INSIDE, "Objects partly inside"}, {CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED, "Objects partly inside"}, {CLI_EXPORT_CACHE_WRITE_FAILED, "export cached slicedata failed"}, {CLI_IMPORT_CACHE_NOT_FOUND, "cached slicedata can not be found"}, {CLI_IMPORT_CACHE_DATA_CAN_NOT_USE, "cached slicedata can not be used"}, {CLI_IMPORT_CACHE_LOAD_FAILED, "load cached slicedata failed"}, {CLI_SLICING_ERROR, "Slice error"} }; #if defined(__linux__) || defined(__LINUX__) #define PIPE_BUFFER_SIZE 512 typedef struct _cli_callback_mgr { int m_plate_count {0}; int m_plate_index {0}; int m_progress { 0 }; int m_total_progress { 0 }; std::string m_message; int m_warning_step; bool m_exit {false}; bool m_data_ready {false}; bool m_started {false}; boost::thread m_thread; // Mutex and condition variable to synchronize m_thread with the UI thread. std::mutex m_mutex; std::condition_variable m_condition; int m_pipe_fd{-1}; bool is_started() { bool result; std::unique_lock lck(m_mutex); result = m_started; lck.unlock(); return result; } void set_plate_info(int index, int count) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": index="<= 0) j["warning"] = m_message; else j["message"] = m_message; std::string notify_message = j.dump(); //notify_message = "Plate "+ std::to_string(m_plate_index) + "/" +std::to_string(m_plate_count)+ ": Percent " + std::to_string(m_progress) + ": "+m_message; char pipe_message[PIPE_BUFFER_SIZE] = {0}; sprintf(pipe_message, "%s\n", notify_message.c_str()); int ret = write(m_pipe_fd, pipe_message, strlen(pipe_message)); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": write returns "< lck(m_mutex); m_started = true; m_data_ready = false; lck.unlock(); m_condition.notify_one(); BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::thread_proc started."; while(1) { lck.lock(); m_condition.wait(lck, [this](){ return m_data_ready || m_exit; }); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": wakup."; if (m_data_ready) { notify(); m_data_ready = false; } if (m_exit) { BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::thread_proc will exit."; break; } lck.unlock(); m_condition.notify_one(); } lck.unlock(); BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::thread_proc exit."; } void update(int percent, std::string message, int warning_step) { std::unique_lock lck(m_mutex); if (!m_started) { lck.unlock(); return; } if ((m_progress >= percent)&&(warning_step == -1)) { //already update before lck.unlock(); return; } int old_total_progress = m_total_progress; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": percent="< lck(m_mutex); m_thread = create_thread([this]{ this->thread_proc(); }); lck.unlock(); BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::start successfully."; return true; } void stop() { BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::stop enter."; std::unique_lock lck(m_mutex); if (!m_started) { lck.unlock(); BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::stop not started before, return directly."; return; } m_exit = true; lck.unlock(); m_condition.notify_one(); // Wait until the worker thread exits. m_thread.join(); if (m_pipe_fd > 0) { close(m_pipe_fd); m_pipe_fd = -1; } BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::stop successfully."; } }cli_callback_mgr_t; cli_callback_mgr_t g_cli_callback_mgr; void cli_status_callback(const PrintBase::SlicingStatus& slicing_status) { g_cli_callback_mgr.update(slicing_status.percent, slicing_status.text, slicing_status.warning_step); return; } #endif static PrinterTechnology get_printer_technology(const DynamicConfig &config) { const ConfigOptionEnum *opt = config.option>("printer_technology"); return (opt == nullptr) ? ptUnknown : opt->value; } //BBS: add flush and exit #if defined(__linux__) || defined(__LINUX__) #define flush_and_exit(ret) { boost::nowide::cout << __FUNCTION__ << " found error, return "<setup(debug_argc, debug_argv))*/ if (!this->setup(argc, argv)) { boost::nowide::cerr << "setup params error" << std::endl; return CLI_INVALID_PARAMS; } BOOST_LOG_TRIVIAL(info) << "finished setup params, argc="<< argc << std::endl; std::string temp_path = wxFileName::GetTempDir().utf8_str().data(); set_temporary_dir(temp_path); m_extra_config.apply(m_config, true); m_extra_config.normalize_fdm(); PrinterTechnology printer_technology = get_printer_technology(m_config); bool start_gui = m_actions.empty(); //BBS: remove GCodeViewer as seperate APP logic /*bool start_as_gcodeviewer = #ifdef _WIN32 false; #else // On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning. boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer"); #endif // _WIN32*/ bool translate_old = false; int current_width, current_depth, current_height; const std::vector &load_configs = m_config.option("load_settings", true)->values; //BBS: always use ForwardCompatibilitySubstitutionRule::Enable //const ForwardCompatibilitySubstitutionRule config_substitution_rule = m_config.option>("config_compatibility", true)->value; const ForwardCompatibilitySubstitutionRule config_substitution_rule = ForwardCompatibilitySubstitutionRule::Enable; const std::vector &load_filaments = m_config.option("load_filaments", true)->values; const std::vector &skip_objects = m_config.option("skip_objects", true)->values; bool need_skip = (skip_objects.size() > 0)?true:false; if (start_gui) { BOOST_LOG_TRIVIAL(info) << "no action, start gui directly" << std::endl; ::Label::initSysFont(); #ifdef SLIC3R_GUI /*#if !defined(_WIN32) && !defined(__APPLE__) // likely some linux / unix system const char *display = boost::nowide::getenv("DISPLAY"); // const char *wayland_display = boost::nowide::getenv("WAYLAND_DISPLAY"); //if (! ((display && *display) || (wayland_display && *wayland_display))) { if (! (display && *display)) { // DISPLAY not set. boost::nowide::cerr << "DISPLAY not set, GUI mode not available." << std::endl << std::endl; this->print_help(false); // Indicate an error. return 1; } #endif // some linux / unix system*/ Slic3r::GUI::GUI_InitParams params; params.argc = argc; params.argv = argv; params.load_configs = load_configs; params.extra_config = std::move(m_extra_config); std::vector gcode_files; std::vector non_gcode_files; for (const auto& filename : m_input_files) { if (is_gcode_file(filename)) gcode_files.emplace_back(filename); else { non_gcode_files.emplace_back(filename); } } if (non_gcode_files.empty() && !gcode_files.empty()) { params.input_gcode = true; params.input_files = std::move(gcode_files); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", gcode only, gcode_files size = "<print_help(false); // If started without a parameter, consider it to be OK, otherwise report an error code (no action etc). return (argc == 0) ? 0 : 1; #endif // SLIC3R_GUI } else { const ConfigOptionInt *opt_loglevel = m_config.opt("debug"); if (opt_loglevel) { set_logging_level(opt_loglevel->value); } else { set_logging_level(2); } } BOOST_LOG_TRIVIAL(info) << "start_gui="<< start_gui << std::endl; //BBS: add plate data related logic PlateDataPtrs plate_data_src; int arrange_option; int plate_to_slice = 0, filament_count = 0; bool first_file = true, is_bbl_3mf = false, need_arrange = true, has_thumbnails = false, up_config_to_date = false, normative_check = true; Semver file_version; std::map orients_requirement; std::vector project_presets; std::string new_printer_name, current_printer_name, new_process_name, current_process_name, current_printer_system_name, current_process_system_name;//, printer_inherits, print_inherits; std::vector upward_compatible_printers, new_print_compatible_printers, current_print_compatible_printers, current_different_settings; std::vector current_filaments_name, current_filaments_system_name, current_inherits_group; DynamicPrintConfig load_process_config, load_machine_config; std::string pipe_name; // Read input file(s) if any. BOOST_LOG_TRIVIAL(info) << "Will start to read model file now, file count :" << m_input_files.size() << "\n"; ConfigOptionInt* slice_option = m_config.option("slice"); if (slice_option) plate_to_slice = slice_option->value; ConfigOptionBool* normative_check_option = m_config.option("normative_check"); if (normative_check_option) normative_check = normative_check_option->value; ConfigOptionBool* uptodate_option = m_config.option("uptodate"); if (uptodate_option) up_config_to_date = uptodate_option->value; ConfigOptionString* pipe_option = m_config.option("pipe"); if (pipe_option) { pipe_name = pipe_option->value; BOOST_LOG_TRIVIAL(info) << boost::format("Will use pipe %1%")%pipe_name; #if defined(__linux__) || defined(__LINUX__) g_cli_callback_mgr.start(pipe_name); #endif } /*for (const std::string& file : m_input_files) if (is_gcode_file(file) && boost::filesystem::exists(file)) { start_as_gcodeviewer = true; BOOST_LOG_TRIVIAL(info) << "found a gcode file:" << file << ", will start as gcode viewer\n"; break; }*/ BOOST_LOG_TRIVIAL(info) << boost::format("plate_to_slice=%1%, normative_check=%2%")%plate_to_slice %normative_check; //if (!start_as_gcodeviewer) { for (const std::string& file : m_input_files) { if (!boost::filesystem::exists(file)) { boost::nowide::cerr << "No such file: " << file << std::endl; flush_and_exit(CLI_FILE_NOTFOUND); } Model model; //BBS: add plate related logic //bool load_aux = false; BOOST_LOG_TRIVIAL(info) << "read model file:" << file << "\n"; try { // When loading an AMF or 3MF, config is imported as well, including the printer technology. DynamicPrintConfig config; ConfigSubstitutionContext config_substitutions(config_substitution_rule); //FIXME should we check the version here? // | LoadStrategy::CheckVersion ? is_bbl_3mf = false; LoadStrategy strategy; if (boost::algorithm::iends_with(file, ".3mf") && first_file) { strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances | LoadStrategy::LoadAuxiliary; //load_aux = true; } else strategy = LoadStrategy::LoadModel | LoadStrategy::AddDefaultInstances; // BBS: adjust whebackup //LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances; //if (load_aux) strategy = strategy | LoadStrategy::LoadAuxiliary; model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, nullptr, nullptr, plate_to_slice); if (is_bbl_3mf) { if (!first_file) { BOOST_LOG_TRIVIAL(info) << "The BBL 3mf file should be placed at the first position, filename=" << file << "\n"; flush_and_exit(CLI_FILELIST_INVALID_ORDER); } BOOST_LOG_TRIVIAL(info) << "the first file is a 3mf, got plate count:" << plate_data_src.size() << "\n"; need_arrange = false; for (ModelObject* o : model.objects) { orients_requirement.insert(std::pair(o->id().id, false)); BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << ", from bbl 3mf\n"; } Semver old_version(1, 5, 9); if ((file_version < old_version) && !config.empty()) { translate_old = true; BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to translate")%file_version.to_string(); } if (normative_check) { ConfigOptionStrings* postprocess_scripts = config.option("post_process"); if (postprocess_scripts) { std::vector postprocess_values = postprocess_scripts->values; if (postprocess_values.size() > 0) { BOOST_LOG_TRIVIAL(error) << boost::format("normative_check: postprocess not supported, array size %1%")%postprocess_values.size(); flush_and_exit(CLI_POSTPROCESS_NOT_SUPPORTED); } } } /*for (ModelObject *model_object : model.objects) for (ModelInstance *model_instance : model_object->instances) { const Vec3d &instance_offset = model_instance->get_offset(); BOOST_LOG_TRIVIAL(info) << boost::format("instance %1% transform {%2%,%3%,%4%} at %5%:%6%")% model_object->name % instance_offset.x() % instance_offset.y() %instance_offset.z() % __FUNCTION__ % __LINE__<< std::endl; }*/ current_printer_name = config.option("printer_settings_id")->value; current_process_name = config.option("print_settings_id")->value; current_filaments_name = config.option("filament_settings_id")->values; ConfigOptionStrings* option_strings = config.option("inherits_group"); if (option_strings) { current_inherits_group = option_strings->values; size_t size = current_inherits_group.size(); if (current_inherits_group[size-1].empty()) current_printer_system_name = current_printer_name; else current_printer_system_name = current_inherits_group[size-1]; if (current_inherits_group[0].empty()) current_process_system_name = current_process_name; else current_process_system_name = current_inherits_group[0]; current_filaments_system_name.resize(size - 2); for (int index = 1; index < (size - 1); index++) { if (current_inherits_group[index].empty()) current_filaments_system_name[index-1] = current_filaments_name[index-1]; else current_filaments_system_name[index-1] = current_inherits_group[index]; } } else { current_printer_system_name = current_printer_name; current_process_system_name = current_process_name; current_filaments_system_name = current_filaments_name; } filament_count = current_filaments_name.size(); upward_compatible_printers = config.option("upward_compatible_machine", true)->values; current_print_compatible_printers = config.option("print_compatible_printers", true)->values; current_different_settings = config.option("different_settings_to_system", true)->values; } else { need_arrange = true; for (ModelObject* o : model.objects) { orients_requirement.insert(std::pair(o->id().id, true)); BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << ", from stl or other 3mf\n"; o->ensure_on_bed(); } } first_file = false; PrinterTechnology other_printer_technology = get_printer_technology(config); if (printer_technology == ptUnknown) { printer_technology = other_printer_technology; } if ((printer_technology != other_printer_technology) && (other_printer_technology != ptUnknown)) { boost::nowide::cerr << "invalid printer_technology " <opt_key << "\"\t old_value = \"" << subst.old_value << "\tnew_value = \"" << subst.new_value->serialize() << "\"\n"; } // config is applied to m_print_config before the current m_config values. config += std::move(m_print_config); m_print_config = std::move(config); } catch (std::exception& e) { boost::nowide::cerr << file << ": " << e.what() << std::endl; flush_and_exit(CLI_DATA_FILE_ERROR); } if (model.objects.empty()) { boost::nowide::cerr << "Error: file is empty: " << file << std::endl; continue; } m_models.push_back(std::move(model)); } //} auto load_system_config_file = [config_substitution_rule](const std::string& file, DynamicPrintConfig& config, std::string& config_type, std::string& config_name, std::string& filament_id) { if (! boost::filesystem::exists(file)) { boost::nowide::cerr << __FUNCTION__<< ": can not find setting file: " << file << std::endl; return CLI_FILE_NOTFOUND; } ConfigSubstitutions config_substitutions; try { BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ":load setting file "<< file << ", with rule "<< config_substitution_rule << std::endl; std::map key_values; std::string reason, config_from; config_substitutions = config.load_from_json(file, config_substitution_rule, key_values, reason); if (!reason.empty()) { BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< ":Can not load config from file "<second; } if (config_from != "system") { boost::nowide::cerr <<__FUNCTION__ << boost::format(":file %1%'s from %2% unsupported") % file % config_from; return CLI_CONFIG_FILE_ERROR; } auto type_iter = key_values.find(BBL_JSON_KEY_TYPE); if (type_iter != key_values.end()) { config_type = type_iter->second; } if (config_type == "machine") { //config.set("printer_settings_id", config_name, true); //printer_inherits = config.option("inherits", true)->value; } else if (config_type == "process") { //config.set("print_settings_id", config_name, true); //print_inherits = config.option("inherits", true)->value; } else if (config_type == "filament") { auto filament_id_iter = key_values.find(BBL_JSON_KEY_FILAMENT_ID); if (filament_id_iter != key_values.end()) filament_id = filament_id_iter->second; } else { boost::nowide::cerr <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-settings") % config_type % file; return CLI_CONFIG_FILE_ERROR; } config.normalize_fdm(); if (! config_substitutions.empty()) { BOOST_LOG_TRIVIAL(info) << "Found legacy configuration values, substituted when loading " << file << ":\n"; for (const ConfigSubstitution &subst : config_substitutions) BOOST_LOG_TRIVIAL(info) << "\tkey = \"" << subst.opt_def->opt_key << "\"\t old_value = \"" << subst.old_value << "\tnew_value = \"" << subst.new_value->serialize() << "\"\n"; } else { BOOST_LOG_TRIVIAL(info) << "no substitutions performed from file " << file << "\n"; } //config.erase("inherits"); //config.erase("compatible_printers"); //BOOST_LOG_TRIVIAL(info) << "got printable_area "<< config.option("printable_area")->serialize() << std::endl; } catch (std::exception &ex) { boost::nowide::cerr << __FUNCTION__<< ":Loading setting file \"" << file << "\" failed: " << ex.what() << std::endl; return CLI_CONFIG_FILE_ERROR; } return 0; }; BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ":before load settings, file count="<< load_configs.size() << std::endl; //std::vector filament_compatible_printers; // load config files supplied via --load for (auto const &file : load_configs) { DynamicPrintConfig config; std::string config_type, config_name, filament_id; int ret = load_system_config_file(file, config, config_type, config_name, filament_id); if (ret) { flush_and_exit(ret); } if (config_type == "machine") { if (!new_printer_name.empty()) { boost::nowide::cerr << "duplicate machine config file: " << file << std::endl; flush_and_exit(CLI_CONFIG_FILE_ERROR); } new_printer_name = config_name; config.set("printer_settings_id", new_printer_name, true); //printer_inherits = config.option("inherits", true)->value; load_machine_config = std::move(config); } else if (config_type == "process") { if (!new_process_name.empty()) { boost::nowide::cerr << "duplicate process config file: " << file << std::endl; flush_and_exit(CLI_CONFIG_FILE_ERROR); } new_process_name = config_name; config.set("print_settings_id", new_process_name, true); //print_inherits = config.option("inherits", true)->value; new_print_compatible_printers = config.option("compatible_printers", true)->values; load_process_config = std::move(config); } PrinterTechnology other_printer_technology = get_printer_technology(config); if (printer_technology == ptUnknown) { printer_technology = other_printer_technology; } if ((printer_technology != other_printer_technology)&&(other_printer_technology != ptUnknown)){ boost::nowide::cerr << "invalid printer_technology " < load_filaments_index; std::vector load_filaments_config; std::vector load_filaments_id; std::vector load_filaments_name; int current_index = 0; for (int index = 0; index < load_filament_count; index++) { const std::string& file = load_filaments[index]; current_index++; if (!file.empty()) { DynamicPrintConfig config; std::string config_type, config_name, filament_id; int ret = load_system_config_file(file, config, config_type, config_name, filament_id); if (ret) { flush_and_exit(ret); } if (config_type != "filament") { boost::nowide::cerr <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-filaments") % config_type % file; flush_and_exit(CLI_CONFIG_FILE_ERROR); } PrinterTechnology other_printer_technology = get_printer_technology(config); if (printer_technology == ptUnknown) { printer_technology = other_printer_technology; } if ((printer_technology != other_printer_technology) && (other_printer_technology != ptUnknown)) { boost::nowide::cerr << "invalid printer_technology " <("upward_compatible_machine", true)->values; config.set("printer_settings_id", config_name, true); load_machine_config = std::move(config); } } else fetch_upward_values = true; if (new_process_name.empty() && !current_process_system_name.empty()) { //use the original printer name in 3mf std::string system_process_path = resources_dir() + "/profiles/BBL/process_full/"+current_process_system_name+".json"; if (! boost::filesystem::exists(system_process_path)) { boost::nowide::cerr << __FUNCTION__<< ": can not find system preset file: " << system_process_path << std::endl; //use original one } else { DynamicPrintConfig config; std::string config_type, config_name, filament_id; int ret = load_system_config_file(system_process_path, config, config_type, config_name, filament_id); if (ret) flush_and_exit(ret); current_print_compatible_printers = config.option("compatible_printers", true)->values; config.set("print_settings_id", config_name, true); load_process_config = std::move(config); } } else fetch_compatible_values = true; if (load_filaments_config.empty() && !current_filaments_system_name.empty()) { for (int index = 0; index < current_filaments_system_name.size(); index++) { std::string system_filament_path = resources_dir() + "/profiles/BBL/filament_full/"+current_filaments_system_name[index]+".json"; current_index++; if (! boost::filesystem::exists(system_filament_path)) { boost::nowide::cerr << __FUNCTION__<< ": can not find system preset file: " << system_filament_path << std::endl; continue; } DynamicPrintConfig config; std::string config_type, config_name, filament_id; int ret = load_system_config_file(system_filament_path, config, config_type, config_name, filament_id); if (ret) flush_and_exit(ret); load_filaments_id.push_back(filament_id); load_filaments_name.push_back(config_name); load_filaments_config.push_back(std::move(config)); load_filaments_index.push_back(current_index); } } } else if (is_bbl_3mf){ fetch_upward_values = true; fetch_compatible_values = true; } //fetch upward_compatible_machine if (fetch_upward_values) { if (!current_printer_system_name.empty()) { //use the original printer name in 3mf std::string system_printer_path = resources_dir() + "/profiles/BBL/machine_full/"+current_printer_system_name+".json"; if (! boost::filesystem::exists(system_printer_path)) { boost::nowide::cerr << __FUNCTION__<< ": can not find system preset file: " << system_printer_path << std::endl; //skip } else { DynamicPrintConfig config; std::string config_type, config_name, filament_id; int ret = load_system_config_file(system_printer_path, config, config_type, config_name, filament_id); if (ret) flush_and_exit(ret); upward_compatible_printers = config.option("upward_compatible_machine", true)->values; } } } //fetch print_compatible_printers if (fetch_compatible_values) { if (!current_process_system_name.empty()) { //use the original printer name in 3mf std::string system_process_path = resources_dir() + "/profiles/BBL/process_full/"+current_process_system_name+".json"; if (! boost::filesystem::exists(system_process_path)) { boost::nowide::cerr << __FUNCTION__<< ": can not find system preset file: " << system_process_path << std::endl; //use original one } else { DynamicPrintConfig config; std::string config_type, config_name, filament_id; int ret = load_system_config_file(system_process_path, config, config_type, config_name, filament_id); if (ret) flush_and_exit(ret); current_print_compatible_printers = config.option("compatible_printers", true)->values; } } } //upwards check bool process_compatible = true, machine_upwards = false; if (!new_printer_name.empty()) { process_compatible = false; if (!new_process_name.empty()) { for (int index = 0; index < new_print_compatible_printers.size(); index++) { if (new_print_compatible_printers[index] == new_printer_name) { process_compatible = true; break; } } BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, new process %2%, compatible %3%")%new_printer_name %new_process_name %process_compatible; } else { for (int index = 0; index < current_print_compatible_printers.size(); index++) { if (current_print_compatible_printers[index] == new_printer_name) { process_compatible = true; break; } } BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, old process %2%, compatible %3%")%new_printer_name %current_process_name %process_compatible; } } else if (!new_process_name.empty()) { process_compatible = false; for (int index = 0; index < new_print_compatible_printers.size(); index++) { if (new_print_compatible_printers[index] == current_printer_name) { process_compatible = true; break; } } BOOST_LOG_TRIVIAL(info) << boost::format("old printer %1%, new process %2%, compatible %3%")%current_printer_name %new_process_name %process_compatible; } if (!process_compatible && !new_printer_name.empty() && !current_printer_name.empty() && (new_printer_name != current_printer_name)) { if (upward_compatible_printers.size() > 0) { for (int index = 0; index < upward_compatible_printers.size(); index++) { if (upward_compatible_printers[index] == new_printer_name) { process_compatible = true; machine_upwards = true; break; } } if (!process_compatible) { boost::nowide::cout <<__FUNCTION__ << boost::format(": current 3mf file not support the new printer %1%")%new_printer_name; flush_and_exit(CLI_3MF_NEW_MACHINE_NOT_SUPPORTED); } } else { boost::nowide::cout <<__FUNCTION__ << boost::format(": current 3mf file not support upward_compatible_printers, can not change machine preset."); flush_and_exit(CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE); } } if (!process_compatible) { boost::nowide::cout <<__FUNCTION__ << boost::format(": process not compatible with printer."); flush_and_exit(CLI_PROCESS_NOT_COMPATIBLE); } //create project embedded preset if needed Preset *new_preset = NULL; if (is_bbl_3mf && machine_upwards) { Preset *current_preset = NULL; size_t project_presets_count = project_presets.size(); for (int index = 0; index < project_presets_count; index++) { if (project_presets[index]->name == current_process_name) { current_preset = project_presets[index]; break; } } new_preset = new Preset(Preset::TYPE_PRINT, current_process_name); if (current_preset) { *new_preset = *current_preset; std::vector& compatible_printers = new_preset->config.option("compatible_printers", true)->values; bool need_insert = true; for (int index = 0; index < compatible_printers.size(); index++) { if (compatible_printers[index] == new_printer_name) { need_insert = false; break; } } if (need_insert) compatible_printers.push_back(new_printer_name); } else { //store a project-embedded preset const std::vector& process_keys = Preset::print_options(); new_preset->config.apply_only(m_print_config, process_keys); std::vector& compatible_printers = new_preset->config.option("compatible_printers", true)->values; compatible_printers = current_print_compatible_printers; compatible_printers.push_back(new_printer_name); if (current_process_system_name != current_process_name) { std::string& inherits = new_preset->config.option("inherits", true)->value; inherits = current_process_system_name; } new_preset->is_project_embedded = true; new_preset->type = Preset::TYPE_PRINT; //new_preset->name = current_process_name; } new_preset->name += "(auto)"; new_preset->config.set("print_settings_id", new_preset->name, true); m_print_config.set("print_settings_id", new_preset->name, true); project_presets.push_back(new_preset); } //update seperate configs into full config auto update_full_config = [](DynamicPrintConfig& full_config, const DynamicPrintConfig& config, std::set& diff_key_sets) { for (const t_config_option_key &opt_key : config.keys()) { if (!diff_key_sets.empty() && (diff_key_sets.find(opt_key) != diff_key_sets.end())) { //uptodate, diff keys, continue BOOST_LOG_TRIVIAL(info) << boost::format("keep key %1%")%opt_key; continue; } const ConfigOption *source_opt = config.option(opt_key); if (source_opt == nullptr) { // The key was not found in the source config, therefore it will not be initialized! boost::nowide::cerr << __FUNCTION__<<": can not found option " <set(source_opt); //*dest_opt = *source_opt; } } return 0; }; std::vector& different_settings = m_print_config.option("different_settings_to_system", true)->values; std::vector& inherits_group = m_print_config.option("inherits_group", true)->values; inherits_group.resize(filament_count + 2, std::string()); different_settings.resize(filament_count + 2, std::string()); //set the machine settings into print config if (!new_printer_name.empty() || up_config_to_date) { std::vector different_keys; if (new_printer_name.empty()) { std::string diff_settings; if (!different_settings.empty()) { diff_settings = different_settings[filament_count+1]; Slic3r::unescape_strings_cstyle(diff_settings, different_keys); } } else { different_settings[filament_count+1] = ""; inherits_group[filament_count+1] = ""; } std::set different_keys_set(different_keys.begin(), different_keys.end()); BOOST_LOG_TRIVIAL(info) << boost::format("update printer config to newest, different size %1%")%different_keys_set.size(); int ret = update_full_config(m_print_config, load_machine_config, different_keys_set); if (ret) flush_and_exit(ret); } //set the process settings into print config std::vector& print_compatible_printers = m_print_config.option("print_compatible_printers", true)->values; if (!new_process_name.empty() || up_config_to_date) { std::vector different_keys; if (new_process_name.empty()) { std::string diff_settings; if (!different_settings.empty()) { diff_settings = different_settings[0]; Slic3r::unescape_strings_cstyle(diff_settings, different_keys); int remove_index = -1; for (int index = 0; index < different_keys.size(); index++) { if (different_keys[index] == "compatible_printers") { remove_index = index; break; } } if (remove_index != -1) { different_keys[remove_index] = different_keys[different_keys.size() - 1]; different_keys.erase(different_keys.begin() + different_keys.size() - 1); different_settings[0] = Slic3r::escape_strings_cstyle(different_keys); } } print_compatible_printers = std::move(current_print_compatible_printers); } else { different_settings[0] = ""; inherits_group[0] = ""; print_compatible_printers = std::move(new_print_compatible_printers); } std::set different_keys_set(different_keys.begin(), different_keys.end()); BOOST_LOG_TRIVIAL(info) << boost::format("update process config to newest, different size %1%")%different_keys_set.size(); int ret = update_full_config(m_print_config, load_machine_config, different_keys_set); if (ret) flush_and_exit(ret); } if (machine_upwards) { print_compatible_printers.push_back(new_printer_name); std::string old_setting = different_settings[0]; if (old_setting.empty()) different_settings[0] = "compatible_printers"; else { std::vector different_keys; Slic3r::unescape_strings_cstyle(old_setting, different_keys); bool need_insert = true; for (int index = 0; index < different_keys.size(); index++) { if (different_keys[index] == "compatible_printers") { need_insert = false; break; } } if (need_insert) different_settings[0] = old_setting + ";compatible_printers"; } } //set the filament settings into print config if ((load_filament_count > 0) || (up_config_to_date)) { for (int index = 0; index < load_filaments_config.size(); index++) { DynamicPrintConfig& config = load_filaments_config[index]; int filament_index = load_filaments_index[index]; std::vector different_keys; if (load_filament_count > 0) { ConfigOptionStrings *opt_filament_settings = static_cast (m_print_config.option("filament_settings_id", true)); std::string& filament_name = load_filaments_name[index]; ConfigOptionString* filament_name_setting = new ConfigOptionString(filament_name); if (opt_filament_settings->size() < filament_count) opt_filament_settings->resize(filament_count, filament_name_setting); opt_filament_settings->set_at(filament_name_setting, filament_index-1, 0); config.erase("filament_settings_id"); std::string& filament_id = load_filaments_id[index]; ConfigOptionStrings *opt_filament_ids = static_cast (m_print_config.option("filament_ids", true)); ConfigOptionString* filament_id_setting = new ConfigOptionString(filament_id); if (opt_filament_ids->size() < filament_count) opt_filament_ids->resize(filament_count, filament_id_setting); opt_filament_ids->set_at(filament_id_setting, filament_index-1, 0); different_settings[filament_index] = ""; inherits_group[filament_index] = ""; } else { std::string diff_settings; if (!different_settings.empty()) { diff_settings = different_settings[filament_index]; Slic3r::unescape_strings_cstyle(diff_settings, different_keys); } } //parse the filament value to index th //loop through options and apply them std::set different_keys_set(different_keys.begin(), different_keys.end()); BOOST_LOG_TRIVIAL(info) << boost::format("update filament %1%'s config to newest, different size %2%")%filament_index%different_keys_set.size(); for (const t_config_option_key &opt_key : config.keys()) { if (!different_keys_set.empty() && (different_keys_set.find(opt_key) != different_keys_set.end())) { //uptodate, diff keys, continue BOOST_LOG_TRIVIAL(info) << boost::format("keep key %1%")%opt_key; continue; } // Create a new option with default value for the key. // If the key is not in the parameter definition, or this ConfigBase is a static type and it does not support the parameter, // an exception is thrown if not ignore_nonexistent. const ConfigOption *source_opt = config.option(opt_key); if (source_opt == nullptr) { // The key was not found in the source config, therefore it will not be initialized! BOOST_LOG_TRIVIAL(error) << boost::format("can not find %1% from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index]; flush_and_exit(CLI_CONFIG_FILE_ERROR); } if (source_opt->is_scalar()) { if (opt_key == "compatible_printers_condition") { ConfigOption *opt = m_print_config.option("compatible_machine_expression_group", true); ConfigOptionStrings* opt_vec_dst = static_cast(opt); if (opt_vec_dst->size() == 0) opt_vec_dst->resize(filament_count+2, new ConfigOptionString()); opt_vec_dst->set_at(source_opt, filament_index, 0); } else if (opt_key == "compatible_prints_condition") { ConfigOption *opt = m_print_config.option("compatible_process_expression_group", true); ConfigOptionStrings* opt_vec_dst = static_cast(opt); if (opt_vec_dst->size() == 0) opt_vec_dst->resize(filament_count, new ConfigOptionString()); opt_vec_dst->set_at(source_opt, filament_index-1, 0); } else { //skip the scalar values BOOST_LOG_TRIVIAL(info) << boost::format("skip scalar option %1% from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index];; continue; } } else { if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "model_id" || opt_key == "dev_model_name" || opt_key == "filament_settings_id") continue; ConfigOption *opt = m_print_config.option(opt_key, true); if (opt == nullptr) { // opt_key does not exist in this ConfigBase and it cannot be created, because it is not defined by this->def(). // This is only possible if other is of DynamicConfig type. BOOST_LOG_TRIVIAL(error) << boost::format("can not create option %1% to config, from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index]; flush_and_exit(CLI_CONFIG_FILE_ERROR); } ConfigOptionVectorBase* opt_vec_dst = static_cast(opt); const ConfigOptionVectorBase* opt_vec_src = static_cast(source_opt); opt_vec_dst->set_at(opt_vec_src, filament_index-1, 0); } } } } //BBS: set default to ptFFF if (printer_technology == ptUnknown) printer_technology = ptFFF; //BBS: merge these models into one BOOST_LOG_TRIVIAL(info) << "total " << m_models.size() << " models, "< 1) { BOOST_LOG_TRIVIAL(info) << "merge all the models into one\n"; Model m; m.set_backup_path(m_models[0].get_backup_path()); for (auto& model : m_models) for (ModelObject* o : model.objects) { ModelObject* new_object = m.add_object(*o); //BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << "\n"; orients_requirement.emplace(new_object->id().id, orients_requirement[o->id().id]); orients_requirement.erase(o->id().id); } m.add_default_instances(); m_models.clear(); m_models.emplace_back(std::move(m)); } // Apply command line options to a more specific DynamicPrintConfig which provides normalize() // (command line options override --load files) m_print_config.apply(m_extra_config, true); // Normalizing after importing the 3MFs / AMFs m_print_config.normalize_fdm(); m_print_config.option>("printer_technology", true)->value = printer_technology; // Initialize full print configs for both the FFF and SLA technologies. FullPrintConfig fff_print_config; //SLAFullPrintConfig sla_print_config; // Synchronize the default parameters and the ones received on the command line. if (printer_technology == ptFFF) { fff_print_config.apply(m_print_config, true); m_print_config.apply(fff_print_config, true); } else { boost::nowide::cerr << "invalid printer_technology " << std::endl; flush_and_exit(CLI_INVALID_PRINTER_TECH); /*assert(printer_technology == ptSLA); sla_print_config.filename_format.value = "[input_filename_base].sl1"; // The default bed shape should reflect the default display parameters // and not the fff defaults. double w = sla_print_config.display_width.getFloat(); double h = sla_print_config.display_height.getFloat(); sla_print_config.printable_area.values = { Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) }; sla_print_config.apply(m_print_config, true); m_print_config.apply(sla_print_config, true);*/ } std::map validity = m_print_config.validate(true); if (!validity.empty()) { boost::nowide::cerr << "Param values in 3mf/config error: "<< std::endl; for (std::map::iterator it=validity.begin(); it!=validity.end(); ++it) boost::nowide::cerr << it->first <<": "<< it->second << std::endl; flush_and_exit(CLI_INVALID_VALUES_IN_3MF); } //BBS: partplate list Slic3r::GUI::PartPlateList partplate_list(NULL, m_models.data(), printer_technology); //use Pointfs insteadof Points Pointfs bedfs = m_print_config.opt("printable_area")->values; Pointfs excluse_areas = m_print_config.opt("bed_exclude_area")->values; //update part plate's size double print_height = m_print_config.opt_float("printable_height"); double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid"); double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod"); double plate_stride; std::string bed_texture; if (m_models.size() > 0) { if (translate_old) { current_width = bedfs[2].x() - bedfs[0].x(); current_depth = bedfs[2].y() - bedfs[0].y(); current_height = print_height; BOOST_LOG_TRIVIAL(info) << boost::format("translate old 3mf, switch to old bed size,{%1%, %2%, %3%}")%(current_width + bed3d_ax3s_default_tip_radius)%(current_depth+bed3d_ax3s_default_tip_radius) %current_height; partplate_list.reset_size(current_width + bed3d_ax3s_default_tip_radius, current_depth + bed3d_ax3s_default_tip_radius, current_height, false); } else { partplate_list.reset_size(bedfs[2].x() - bedfs[0].x(), bedfs[2].y() - bedfs[0].y(), print_height, false); } partplate_list.set_shapes(bedfs, excluse_areas, bed_texture, height_to_lid, height_to_rod); plate_stride = partplate_list.plate_stride_x(); BOOST_LOG_TRIVIAL(info) << "bed size, x="< 0) { partplate_list.load_from_3mf_structure(plate_data_src); //BBS: translate old 3mf to correct positions if (translate_old) { //translate the objects int plate_count = partplate_list.get_plate_count(); for (int index = 1; index < plate_count; index ++) { Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(index); Vec3d cur_origin = cur_plate->get_origin(); Vec3d new_origin = partplate_list.compute_origin_using_new_size(index, current_width, current_depth); cur_plate->translate_all_instance(new_origin - cur_origin); } BOOST_LOG_TRIVIAL(info) << boost::format("translate old 3mf, switch back to current bed size,{%1%, %2%, %3%}")%current_width %current_depth %current_height; partplate_list.reset_size(current_width, current_depth, current_height, true, true); } } /*for (ModelObject *model_object : m_models[0].objects) for (ModelInstance *model_instance : model_object->instances) { const Vec3d &instance_offset = model_instance->get_offset(); BOOST_LOG_TRIVIAL(info) << boost::format("instance %1% transform {%2%,%3%,%4%} at %5%:%6%")% model_object->name % instance_offset.x() % instance_offset.y() %instance_offset.z() % __FUNCTION__ % __LINE__<< std::endl; }*/ // Loop through transform options. bool user_center_specified = false; Points beds = get_bed_shape(m_print_config); ArrangeParams arrange_cfg; arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config)); BOOST_LOG_TRIVIAL(info) << "will start transforms, commands count " << m_transforms.size() << "\n"; #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { PrintBase::SlicingStatus slicing_status{1, "Loading files finished"}; cli_status_callback(slicing_status); } #endif for (auto const &opt_key : m_transforms) { BOOST_LOG_TRIVIAL(info) << "process transform " << opt_key << "\n"; if (opt_key == "merge") { //BBS: always merge, do nothing here /*Model m; for (auto& model : m_models) for (ModelObject* o : model.objects) m.add_object(*o); // Rearrange instances unless --dont-arrange is supplied if (!m_config.opt_bool("dont_arrange")) { m.add_default_instances(); if (this->has_print_action()) arrange_objects(m, bed, arrange_cfg); else arrange_objects(m, InfiniteBed{}, arrange_cfg); } m_models.clear(); m_models.emplace_back(std::move(m));*/ } else if (opt_key == "convert_unit") { for (auto& model : m_models) { if (model.looks_like_saved_in_meters()) { BOOST_LOG_TRIVIAL(info) << "convert from meter to millimeter\n"; model.convert_from_meters(true); } else if (model.looks_like_imperial_units()) { BOOST_LOG_TRIVIAL(info) << "convert from inch to millimeter\n"; model.convert_from_imperial_units(true); } } } else if (opt_key == "orient") { for (auto& model : m_models) for (ModelObject* o : model.objects) { // coconut: always orient instance instead of object for (ModelInstance* mi : o->instances) { orientation::orient(mi); } BOOST_LOG_TRIVIAL(info) << "orient object, name=" << o->name <<",id="<id().id<id().id] = false; } } else if (opt_key == "copy") { for (auto &model : m_models) { const bool all_objects_have_instances = std::none_of( model.objects.begin(), model.objects.end(), [](ModelObject* o){ return o->instances.empty(); } ); int dups = m_config.opt_int("copy"); if (!all_objects_have_instances) model.add_default_instances(); try { if (dups > 1) { // if all input objects have defined position(s) apply duplication to the whole model duplicate(model, size_t(dups), beds, arrange_cfg); } else { arrange_objects(model, beds, arrange_cfg); } } catch (std::exception &ex) { boost::nowide::cerr << "error: " << ex.what() << std::endl; flush_and_exit(CLI_COPY_OBJECTS_ERROR); } } } else if (opt_key == "center") { user_center_specified = true; for (auto &model : m_models) { model.add_default_instances(); // this affects instances: model.center_instances_around_point(m_config.option("center")->value); // this affects volumes: //FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body? //model.align_to_ground(); BoundingBoxf3 bbox; for (ModelObject *model_object : model.objects) // We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only. bbox.merge(model_object->instance_bounding_box(0, false)); for (ModelObject *model_object : model.objects) for (ModelInstance *model_instance : model_object->instances) model_instance->set_offset(Z, model_instance->get_offset(Z) - bbox.min.z()); } } else if (opt_key == "align_xy") { const Vec2d &p = m_config.option("align_xy")->value; for (auto &model : m_models) { BoundingBoxf3 bb = model.bounding_box(); // this affects volumes: model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z()); } } else if (opt_key == "arrange") { //BBS: arrange 0 means disable, 1 means force arrange, others means auto int arrange_option = m_config.option("arrange")->value; if (arrange_option == 0) { need_arrange = false; } else if (arrange_option == 1) { need_arrange = true; } else { //auto arrange, keep the original logic } } else if (opt_key == "ensure_on_bed") { // do nothing, the value is used later } else if (opt_key == "rotate") { for (auto &model : m_models) for (auto &o : model.objects) // this affects volumes: o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Z); } else if (opt_key == "rotate_x") { for (auto &model : m_models) for (auto &o : model.objects) // this affects volumes: o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), X); } else if (opt_key == "rotate_y") { for (auto &model : m_models) for (auto &o : model.objects) // this affects volumes: o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Y); } else if (opt_key == "scale") { for (auto &model : m_models) for (auto &o : model.objects) // this affects volumes: o->scale(m_config.get_abs_value(opt_key, 1)); } else if (opt_key == "scale_to_fit") { const Vec3d &opt = m_config.opt(opt_key)->value; if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) { boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl; flush_and_exit(CLI_SCALE_TO_FIT_ERROR); } for (auto &model : m_models) for (auto &o : model.objects) // this affects volumes: o->scale_to_fit(opt); } else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") { std::vector new_models; for (auto &model : m_models) { model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0 size_t num_objects = model.objects.size(); for (size_t i = 0; i < num_objects; ++ i) { #if 0 if (opt_key == "cut_x") { o->cut(X, m_config.opt_float("cut_x"), &out); } else if (opt_key == "cut_y") { o->cut(Y, m_config.opt_float("cut_y"), &out); } else if (opt_key == "cut") { o->cut(Z, m_config.opt_float("cut"), &out); } #else ModelObject* object = model.objects.front(); const BoundingBoxf3& box = object->bounding_box(); const float Margin = 20.0; const float max_x = box.size()(0) / 2.0 + Margin; const float min_x = -max_x; const float max_y = box.size()(1) / 2.0 + Margin; const float min_y = -max_y; std::array plane_points; plane_points[0] = { min_x, min_y, 0 }; plane_points[1] = { max_x, min_y, 0 }; plane_points[2] = { max_x, max_y, 0 }; plane_points[3] = { min_x, max_y, 0 }; for (Vec3d& point : plane_points) { point += box.center(); } model.objects.front()->cut(0, plane_points, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower); #endif model.delete_object(size_t(0)); } } // TODO: copy less stuff around using pointers m_models = new_models; if (m_actions.empty()) m_actions.push_back("export_stl"); } #if 0 else if (opt_key == "cut_grid") { std::vector new_models; for (auto &model : m_models) { TriangleMesh mesh = model.mesh(); mesh.repair(); std::vector meshes = mesh.cut_by_grid(m_config.option("cut_grid")->value); size_t i = 0; for (TriangleMesh* m : meshes) { Model out; auto o = out.add_object(); o->add_volume(*m); o->input_file += "_" + std::to_string(i++); delete m; } } // TODO: copy less stuff around using pointers m_models = new_models; if (m_actions.empty()) m_actions.push_back("export_stl"); } #endif else if (opt_key == "split") { for (Model &model : m_models) { size_t num_objects = model.objects.size(); for (size_t i = 0; i < num_objects; ++ i) { ModelObjectPtrs new_objects; model.objects.front()->split(&new_objects); model.delete_object(size_t(0)); } } } else if (opt_key == "repair") { // Models are repaired by default. //for (auto &model : m_models) // model.repair(); } else { boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl; flush_and_exit(CLI_UNSUPPORTED_OPERATION); } } BOOST_LOG_TRIVIAL(info) << "finished model pre-process commands\n"; bool oriented_or_arranged = false; //BBS: add orient and arrange logic here for (auto& model : m_models) { for (ModelObject* o : model.objects) { if (orients_requirement[o->id().id]) { BOOST_LOG_TRIVIAL(info) << "Before process command, Orient object, name=" << o->name <<",id="<id().id<id().id<instances.size(); ++inst_idx) { ModelInstance* minst = mo->instances[inst_idx]; ArrangePolygon ap = get_instance_arrange_poly(minst, m_print_config); //preprocess by partplate list //remove the locked plate's instances, neither in selected, nor in un-selected bool locked = partplate_list.preprocess_arrange_polygon(oidx, inst_idx, ap, true); if (!locked) { ap.itemid = selected.size(); if (minst->printable) selected.emplace_back(ap); else unprintable.emplace_back(ap); } else { //skip this object due to be locked in plate ap.itemid = locked_aps.size(); locked_aps.emplace_back(ap); boost::nowide::cout <<__FUNCTION__ << boost::format(": skip locked instance, obj_id %1%, instance_id %2%") % oidx % inst_idx; } } } //add the virtual object into unselect list if has partplate_list.preprocess_exclude_areas(unselected); //Step-2:prepare the arrange params arrange_cfg.allow_rotations = true; arrange_cfg.min_obj_distance = scaled(6.0); //BBS: add specific params arrange_cfg.is_seq_print = false; arrange_cfg.bed_shrink_x = 0; arrange_cfg.bed_shrink_y = 0; double skirt_distance = m_print_config.opt_float("skirt_distance"); double brim_width = m_print_config.opt_float("brim_width"); arrange_cfg.brim_skirt_distance = skirt_distance + brim_width; BOOST_LOG_TRIVIAL(info) << boost::format("Arrange Params: brim_skirt_distance=%1%, min_obj_distance=%2%, is_seq_print=%3%\n") % arrange_cfg.brim_skirt_distance % arrange_cfg.min_obj_distance % arrange_cfg.is_seq_print; // Note: skirt_distance is now defined between outermost brim and skirt, not the object and skirt. // So we can't do max but do adding instead. arrange_cfg.bed_shrink_x += arrange_cfg.brim_skirt_distance; arrange_cfg.bed_shrink_y += arrange_cfg.brim_skirt_distance; // shrink bed beds[0] += Point(scaled(arrange_cfg.bed_shrink_x), scaled(arrange_cfg.bed_shrink_y)); beds[1] += Point(-scaled(arrange_cfg.bed_shrink_x), scaled(arrange_cfg.bed_shrink_y)); beds[2] += Point(-scaled(arrange_cfg.bed_shrink_x), -scaled(arrange_cfg.bed_shrink_y)); beds[3] += Point(scaled(arrange_cfg.bed_shrink_x), -scaled(arrange_cfg.bed_shrink_y)); // do not inflate brim_width. Objects are allowed to have overlapped brim. std::for_each(selected.begin(), selected.end(), [&](auto& ap) {ap.inflation = arrange_cfg.min_obj_distance / 2; }); { BOOST_LOG_TRIVIAL(info) << "items selected before arranging: "; for (auto selected : selected) BOOST_LOG_TRIVIAL(info) << selected.name << ", extruder: " << selected.extrude_ids.back() << ", bed: " << selected.bed_idx << ", trans: " << selected.translation.transpose(); } arrange_cfg.progressind= [](unsigned st, std::string str = "") { boost::nowide::cout << "st=" << st << ", " << str << std::endl; }; //Step-3:do the arrange arrangement::arrange(selected, unselected, beds, arrange_cfg); arrangement::arrange(unprintable, {}, beds, arrange_cfg); //Step-4:postprocess by partplate list&&apply the result int bed_idx_max = 0; //clear all the relations before apply the arrangement results partplate_list.clear(); // Apply the arrange result to all selected objects for (ArrangePolygon &ap : selected) { //BBS: partplate postprocess partplate_list.postprocess_bed_index_for_selected(ap); bed_idx_max = std::max(ap.bed_idx, bed_idx_max); boost::nowide::cout<< "after arrange: name=" << ap.name << boost::format(",bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale(ap.translation(X)) % unscale(ap.translation(Y)) << "\n"; } for (ArrangePolygon &ap : locked_aps) { bed_idx_max = std::max(ap.bed_idx, bed_idx_max); partplate_list.postprocess_arrange_polygon(ap, false); ap.apply(); } // Apply the arrange result to all selected objects for (ArrangePolygon &ap : selected) { //BBS: partplate postprocess partplate_list.postprocess_arrange_polygon(ap, true); ap.apply(); } // Move the unprintable items to the last virtual bed. for (ArrangePolygon &ap : unprintable) { ap.bed_idx += bed_idx_max + 1; partplate_list.postprocess_arrange_polygon(ap, true); ap.apply(); } //BBS: reload all objects due to arrange partplate_list.rebuild_plates_after_arrangement(); } } // All transforms have been dealt with. Now ensure that the objects are on bed. // (Unless the user said otherwise.) //BBS: current only support models on bed //if (m_config.opt_bool("ensure_on_bed")) for (auto &model : m_models) for (auto &o : model.objects) o->ensure_on_bed(); // loop through action options bool export_to_3mf = false, load_slicedata = false, export_slicedata = false, export_slicedata_error = false; bool no_check = false; std::string export_3mf_file, load_slice_data_dir, export_slice_data_dir; std::string outfile_dir = m_config.opt_string("outputdir"); std::vector calibration_thumbnails; int max_slicing_time_per_plate = 0, max_triangle_count_per_plate = 0; for (auto const &opt_key : m_actions) { if (opt_key == "help") { this->print_help(); } else if (opt_key == "help_fff") { this->print_help(true, ptFFF); } else if (opt_key == "help_sla") { this->print_help(true, ptSLA); } else if (opt_key == "pipe") { //already processed before } else if (opt_key == "load_slicedata") { load_slicedata = true; load_slice_data_dir = m_config.opt_string(opt_key); if (export_slicedata) { BOOST_LOG_TRIVIAL(error) << "should not set load_slicedata and export_slicedata together." << std::endl; flush_and_exit(CLI_INVALID_PARAMS); } } else if (opt_key == "export_settings") { //FIXME check for mixing the FFF / SLA parameters. // or better save fff_print_config vs. sla_print_config //m_print_config.save(m_config.opt_string("save")); m_print_config.save_to_json(m_config.opt_string(opt_key), std::string("project_settings"), std::string("project"), std::string(SLIC3R_VERSION)); } else if (opt_key == "info") { // --info works on unrepaired model for (Model &model : m_models) { model.add_default_instances(); model.print_info(); } } else if (opt_key == "uptodate") { //already processed before } else if (opt_key == "mtcpp") { max_triangle_count_per_plate = m_config.option("mtcpp")->value; } else if (opt_key == "mstpp") { max_slicing_time_per_plate = m_config.option("mstpp")->value; } else if (opt_key == "export_stl") { for (auto &model : m_models) model.add_default_instances(); if (! this->export_models(IO::STL)) flush_and_exit(CLI_EXPORT_STL_ERROR); } else if (opt_key == "export_obj") { for (auto &model : m_models) model.add_default_instances(); if (! this->export_models(IO::OBJ)) flush_and_exit(CLI_EXPORT_OBJ_ERROR); }/* else if (opt_key == "export_amf") { if (! this->export_models(IO::AMF)) return 1; } */else if (opt_key == "export_3mf") { export_to_3mf = true; export_3mf_file = m_config.opt_string(opt_key); }else if(opt_key=="no_check"){ no_check = m_config.opt_bool(opt_key); //} else if (opt_key == "export_gcode" || opt_key == "export_sla" || opt_key == "slice") { } else if (opt_key == "normative_check") { //already processed before } else if (opt_key == "export_slicedata") { export_slicedata = true; export_slice_data_dir = m_config.opt_string(opt_key); if (load_slicedata) { BOOST_LOG_TRIVIAL(error) << "should not set load_slicedata and export_slicedata together." << std::endl; flush_and_exit(CLI_INVALID_PARAMS); } } else if (opt_key == "slice") { //BBS: slice 0 means all plates, i means plate i; plate_to_slice = m_config.option("slice")->value; bool pre_check = (plate_to_slice == 0)?true:false; if (partplate_list.get_plate_count() == 1) pre_check = false; bool finished = false; //skip model object std::map skip_maps; if (need_skip) { BOOST_LOG_TRIVIAL(info) << boost::format("need to skip objects, size %1%:")%skip_objects.size(); for (int index = 0; index < skip_objects.size(); index++) { skip_maps[skip_objects[index]] = false; BOOST_LOG_TRIVIAL(info) << boost::format("object %1%, id %2%")%index %skip_objects[index]; } } /*if (opt_key == "export_gcode" && printer_technology == ptSLA) { boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl; flush_and_exit(1); } else if (opt_key == "export_sla" && printer_technology == ptFFF) { boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl; flush_and_exit(1); }*/ BOOST_LOG_TRIVIAL(info) << "Need to slice for plate "<get_print(&print, &gcode_result, &print_index); /*if (outfile_config.empty()) { outfile = "plate_" + std::to_string(index + 1) + ".gcode"; } else { outfile = "plate_" + std::to_string(index + 1) + "_" + outfile_config + ".gcode"; }*/ //update plate's bounding box to model #if 0 BoundingBoxf3 print_volume = part_plate->get_bounding_box(false); print_volume.max(2) = z; print_volume.min(2) = -1e10; model.update_print_volume_state(print_volume); BOOST_LOG_TRIVIAL(info) << boost::format("print_volume {%1%,%2%,%3%}->{%4%, %5%, %6%}") % print_volume.min(0) % print_volume.min(1) % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) << std::endl; #else BuildVolume build_volume(part_plate->get_shape(), print_height); //model.update_print_volume_state(build_volume); unsigned int count = model.update_print_volume_state(build_volume); if (count == 0) { BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, Either the print is empty or no object is fully inside the print volume before apply." << std::endl; flush_and_exit(CLI_NO_SUITABLE_OBJECTS); } else if ((plate_to_slice != 0) || pre_check) { long long triangle_count = 0; int printable_instances = 0; int skipped_count = 0; for (ModelObject* model_object : model.objects) for (ModelInstance *i : model_object->instances) { if (skip_maps.find(i->loaded_id) != skip_maps.end()) { skip_maps[i->loaded_id] = true; i->printable = false; if (i->print_volume_state == ModelInstancePVS_Inside) { skipped_count++; BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: skip object %2%.")%(index+1)%i->loaded_id; //need to regenerate the thumbnail if (plate_data_src.size() > index) { if (!plate_data_src[index]->thumbnail_file.empty()) { BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded thumbnail %2%.")%(index+1)%plate_data_src[index]->thumbnail_file; plate_data_src[index]->thumbnail_file.clear(); } if (!plate_data_src[index]->top_file.empty()) { BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded top_thumbnail %2%.")%(index+1)%plate_data_src[index]->top_file; plate_data_src[index]->top_file.clear(); } if (!plate_data_src[index]->pick_file.empty()) { BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded pick_thumbnail %2%.")%(index+1)%plate_data_src[index]->pick_file; plate_data_src[index]->pick_file.clear(); } } } continue; } if (i->print_volume_state == ModelInstancePVS_Partly_Outside) { BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Found Object " << model_object->name <<" partly inside, can not be sliced." << std::endl; flush_and_exit(CLI_OBJECTS_PARTLY_INSIDE); } else if ((max_triangle_count_per_plate != 0) && (i->print_volume_state == ModelInstancePVS_Inside)) { for (const ModelVolume* vol : model_object->volumes) { if (vol->is_model_part()) { size_t volume_triangle_count = vol->mesh().facets_count(); triangle_count += volume_triangle_count; BOOST_LOG_TRIVIAL(info) << boost::format("volume triangle count %1%, total %2%")%volume_triangle_count %triangle_count; if (triangle_count > max_triangle_count_per_plate) { BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": triangle count " << triangle_count <<" exceeds the limit:" << max_triangle_count_per_plate; flush_and_exit(CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT); } } } } if (i->print_volume_state == ModelInstancePVS_Inside) printable_instances++; } if (printable_instances == 0) { BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, after skipping "<{%4%, %5%, %6%}, has %7% printables") % print_volume.min(0) % print_volume.min(1) // % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) % count << std::endl; #endif DynamicPrintConfig new_print_config = m_print_config; new_print_config.apply(*part_plate->config()); new_print_config.apply(m_extra_config, true); print->apply(model, new_print_config); BOOST_LOG_TRIVIAL(info) << boost::format("set no_check to %1%:")%no_check; print->set_no_check_flag(no_check);//BBS StringObjectException warning; auto err = print->validate(&warning); if (!err.string.empty()) { BOOST_LOG_TRIVIAL(info) << "got error when validate: "<< err.string << std::endl; boost::nowide::cerr << err.string << std::endl; //BBS: continue for other plates //continue; flush_and_exit(CLI_VALIDATE_ERROR); } else if (!warning.string.empty()) BOOST_LOG_TRIVIAL(info) << "got warnings: "<< warning.string << std::endl; if (print->empty()) { BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, Either the print is empty or no object is fully inside the print volume after apply." << std::endl; flush_and_exit(CLI_NO_SUITABLE_OBJECTS); } else { if (pre_check) //continue to next plate directly continue; try { std::string outfile_final; BOOST_LOG_TRIVIAL(info) << "start Print::process for partplate "<set_status_callback(cli_status_callback); g_cli_callback_mgr.set_plate_info(index+1, (plate_to_slice== 0)?partplate_list.get_plate_count():1); if (!warning.string.empty()) { PrintBase::SlicingStatus slicing_status{4, warning.string, 0, 0}; cli_status_callback(slicing_status); } else { PrintBase::SlicingStatus slicing_status{4, "Slicing begins"}; cli_status_callback(slicing_status); } } #endif if (load_slicedata) { std::string plate_dir = load_slice_data_dir+"/"+std::to_string(index+1); int ret = print->load_cached_data(plate_dir); if (ret) { BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": load Slicing data error, ret=" << ret; BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": switch normal slicing"; print->process(); } else { BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": load cached data success, go on."; #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { PrintBase::SlicingStatus slicing_status{69, "Cache data loaded"}; cli_status_callback(slicing_status); } #endif print->process(true); BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": finished print::process."; } } else { print->process(); } if (printer_technology == ptFFF) { // The outfile is processed by a PlaceholderParser. //outfile = part_plate->get_tmp_gcode_path(); if (outfile_dir.empty()) { outfile = part_plate->get_tmp_gcode_path(); } else { outfile = outfile_dir + "/plate_" + std::to_string(index + 1) + ".gcode"; part_plate->set_tmp_gcode_path(outfile); } BOOST_LOG_TRIVIAL(info) << "process finished, will export gcode temporily to " << outfile << std::endl; outfile = (dynamic_cast(print))->export_gcode(outfile, gcode_result, nullptr); //outfile_final = (dynamic_cast(print))->print_statistics().finalize_output_path(outfile); //m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, [this](const ThumbnailsParams& params) { return this->render_thumbnails(params); }); }/* else { outfile = sla_print.output_filepath(outfile); // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata outfile_final = sla_print.print_statistics().finalize_output_path(outfile); sla_archive.export_print(outfile_final, sla_print); }*/ /*if (outfile != outfile_final) { if (Slic3r::rename_file(outfile, outfile_final)) { boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; flush_and_exit(1); } outfile = outfile_final; }*/ // Run the post-processing scripts if defined. run_post_process_scripts(outfile, print->full_print_config()); BOOST_LOG_TRIVIAL(info) << "Slicing result exported to " << outfile << std::endl; part_plate->update_slice_result_valid_state(true); #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { PrintBase::SlicingStatus slicing_status{100, "Slicing finished"}; cli_status_callback(slicing_status); } #endif if (export_slicedata) { BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ":will export Slicing data to " << export_slice_data_dir; std::string plate_dir = export_slice_data_dir+"/"+std::to_string(index+1); bool with_space = (get_logging_level() >= 4)?true:false; int ret = print->export_cached_data(plate_dir, with_space); if (ret) { BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": export Slicing data error, ret=" << ret; export_slicedata_error = true; if (fs::exists(plate_dir)) fs::remove_all(plate_dir); } } if (max_slicing_time_per_plate != 0) { end_time = (long long)Slic3r::Utils::get_current_time_utc(); long long time_cost = end_time - start_time; if (time_cost > max_slicing_time_per_plate) { BOOST_LOG_TRIVIAL(error) << boost::format("plate %1%'s slice time %2% exceeds the limit %3%, return error.") %(index+1) %time_cost %max_slicing_time_per_plate; flush_and_exit(CLI_SLICING_TIME_EXCEEDS_LIMIT); } } } catch (const std::exception &ex) { BOOST_LOG_TRIVIAL(error) << "found slicing or export error for partplate "< > second_; std::chrono::time_point t0{ clock_::now() }; const std::string outfile = this->output_filepath(model, IO::Gcode); try { print.export_gcode(outfile); } catch (std::runtime_error &e) { boost::nowide::cerr << e.what() << std::endl; return 1; } BOOST_LOG_TRIVIAL(info) << "G-code exported to " << outfile << std::endl; // output some statistics double duration { std::chrono::duration_cast(clock_::now() - t0).count() }; BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) << "Done. Process took " << (duration/60) << " minutes and " << std::setprecision(3) << std::fmod(duration, 60.0) << " seconds." << std::endl << std::setprecision(2) << "Filament required: " << print.total_used_filament() << "mm" << " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl; */ } } else { boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl; flush_and_exit(CLI_UNSUPPORTED_OPERATION); } } if (export_to_3mf) { //BBS: export as bbl 3mf Slic3r::GUI::OpenGLManager opengl_mgr; std::vector thumbnails, top_thumbnails, pick_thumbnails; std::vector plate_bboxes; PlateDataPtrs plate_data_list; partplate_list.store_to_3mf_structure(plate_data_list); if (!outfile_dir.empty()) { export_3mf_file = outfile_dir + "/"+export_3mf_file; } #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { PrintBase::SlicingStatus slicing_status{94, "Generate thumbnails"}; cli_status_callback(slicing_status); } #endif bool need_regenerate_thumbnail = oriented_or_arranged; bool need_regenerate_top_thumbnail = oriented_or_arranged; bool need_create_thumbnail_group = false, need_create_top_group = false; // get type and color for platedata auto* filament_types = dynamic_cast(m_print_config.option("filament_type")); const ConfigOptionStrings* filament_color = dynamic_cast(m_print_config.option("filament_colour")); //auto* filament_id = dynamic_cast(m_print_config.option("filament_ids")); for (int i = 0; i < plate_data_list.size(); i++) { PlateData *plate_data = plate_data_list[i]; bool skip_this_plate = ((plate_to_slice != 0) && (plate_to_slice != (i + 1)))?true:false; for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) { //it->filament_id = filament_id?filament_id->get_at(it->id):"unknown"; std::string display_filament_type; it->type = m_print_config.get_filament_type(display_filament_type, it->id); it->color = filament_color ? filament_color->get_at(it->id) : "#FFFFFF"; } if (!plate_data->plate_thumbnail.is_valid()) { if (!oriented_or_arranged && plate_data_src.size() > i) plate_data->thumbnail_file = plate_data_src[i]->thumbnail_file; BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail data is invalid, check the file %2% exist or not")%(i+1) %plate_data->thumbnail_file; if (plate_data->thumbnail_file.empty() || (!boost::filesystem::exists(plate_data->thumbnail_file))) { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file also not there, need to regenerate")%(i+1); if (!skip_this_plate) { need_regenerate_thumbnail = true; need_create_thumbnail_group = true; } } else { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file exists, no need to regenerate")%(i+1); } } else { if (!skip_this_plate) { need_create_thumbnail_group = true; } } if (plate_data->top_file.empty() || plate_data->pick_file.empty()) { if (plate_data_src.size() > i) { plate_data->top_file = plate_data_src[i]->top_file; plate_data->pick_file = plate_data_src[i]->pick_file; } if (plate_data->top_file.empty()|| plate_data->pick_file.empty() || (!boost::filesystem::exists(plate_data->top_file)) || (!boost::filesystem::exists(plate_data->pick_file))) { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_file %2% also not there, need to regenerate")%(i+1)%plate_data->top_file; if (!skip_this_plate) { need_regenerate_top_thumbnail = true; need_create_top_group = true; } } else { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_thumbnail file exists, no need to regenerate")%(i+1); } } } if (need_regenerate_thumbnail || need_regenerate_top_thumbnail) { std::vector colors; if (filament_color) { colors= filament_color->vserialize(); } else colors.push_back("#FFFFFF"); std::vector> colors_out(colors.size()); unsigned char rgb_color[3] = {}; for (const std::string& color : colors) { Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); size_t color_idx = &color - &colors.front(); colors_out[color_idx] = { float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f }; } int gl_major, gl_minor, gl_verbos; glfwGetVersion(&gl_major, &gl_minor, &gl_verbos); BOOST_LOG_TRIVIAL(info) << boost::format("opengl version %1%.%2%.%3%")%gl_major %gl_minor %gl_verbos; glfwSetErrorCallback(glfw_callback); int ret = glfwInit(); if (ret == GLFW_FALSE) { int code = glfwGetError(NULL); BOOST_LOG_TRIVIAL(error) << "glfwInit return error, code " <(option))->getInt(); for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) { const ModelVolume &model_volume = *model_object.volumes[volume_idx]; option = model_volume.config.option("extruder"); if (option) extruder_id = (dynamic_cast(option))->getInt(); //if (!model_volume.is_model_part()) // continue; for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { const ModelInstance &model_instance = *model_object.instances[instance_idx]; glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, "volume", true, false, true); //glvolume_collection.volumes.back()->geometry_id = key.geometry_id; std::string color = filament_color?filament_color->get_at(extruder_id - 1):"#00FF00"; unsigned char rgb_color[3] = {}; Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); glvolume_collection.volumes.back()->set_render_color( float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f); std::array new_color; new_color[0] = float(rgb_color[0]) / 255.f; new_color[1] = float(rgb_color[1]) / 255.f; new_color[2] = float(rgb_color[2]) / 255.f; new_color[3] = 1.f; glvolume_collection.volumes.back()->set_color(new_color); glvolume_collection.volumes.back()->printable = model_instance.printable; } } } ThumbnailsParams thumbnail_params; GLShaderProgram* shader = opengl_mgr.get_shader("gouraud_light"); if (!shader) { BOOST_LOG_TRIVIAL(error) << boost::format("can not get shader for rendering thumbnail"); } else { for (int i = 0; i < partplate_list.get_plate_count(); i++) { Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); PlateData *plate_data = plate_data_list[i]; if (plate_data->plate_thumbnail.is_valid()) { if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, reset plate %2%'s thumbnail.")%__LINE__%(i+1); plate_data->plate_thumbnail.reset(); } else BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid thumbnail, width %2%, height %3%, directly using it")%(i+1) %plate_data->plate_thumbnail.width %plate_data->plate_thumbnail.height; } else if (!plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))) { if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s thumbnail file path to empty.")%__LINE__%(i+1); plate_data->thumbnail_file.clear(); } else BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid thumbnail %2% extracted from 3mf, directly using it")%(i+1) %plate_data->thumbnail_file; } else { ThumbnailData* thumbnail_data = &plate_data->plate_thumbnail; if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1); } else { unsigned int thumbnail_width = 512, thumbnail_height = 512; const ThumbnailsParams thumbnail_params = {{}, false, true, true, true, i}; BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail, need to regenerate")%(i+1); switch (Slic3r::GUI::OpenGLManager::get_framebuffers_type()) { case Slic3r::GUI::OpenGLManager::EFramebufferType::Arb: { BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB"); Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*thumbnail_data, thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho); break; } case Slic3r::GUI::OpenGLManager::EFramebufferType::Ext: { BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT"); Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*thumbnail_data, thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho); break; } default: BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown"); break; } BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail,finished rendering")%(i+1); } } if (need_create_thumbnail_group) { thumbnails.push_back(&plate_data->plate_thumbnail); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1); } //top thumbnails /*if (part_plate->top_thumbnail_data.is_valid() && part_plate->pick_thumbnail_data.is_valid()) { if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, reset plate %2%'s top/pick thumbnail.")%__LINE__%(i+1); part_plate->top_thumbnail_data.reset(); part_plate->pick_thumbnail_data.reset(); plate_data->top_file.clear(); plate_data->pick_file.clear(); } else { plate_data->top_file = "valid_top"; plate_data->pick_file = "valid_pick"; BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid top/pick thumbnail data, directly using it")%(i+1); } } else*/ if ((!plate_data->top_file.empty() && (boost::filesystem::exists(plate_data->top_file))) &&(!plate_data->pick_file.empty() && (boost::filesystem::exists(plate_data->pick_file)))) { if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s top/pick thumbnail file path to empty.")%__LINE__%(i+1); plate_data->top_file.clear(); plate_data->pick_file.clear(); } else BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has valid top/pick thumbnail extracted from 3mf, directly using it")%(i+1); } else{ ThumbnailData* top_thumbnail = &part_plate->top_thumbnail_data; ThumbnailData* picking_thumbnail = &part_plate->pick_thumbnail_data; if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1); part_plate->top_thumbnail_data.reset(); part_plate->pick_thumbnail_data.reset(); plate_data->top_file.clear(); plate_data->pick_file.clear(); } else { unsigned int thumbnail_width = 512, thumbnail_height = 512; const ThumbnailsParams thumbnail_params = { {}, false, true, false, true, i }; BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s top/pick thumbnail missed, need to regenerate")%(i+1); switch (Slic3r::GUI::OpenGLManager::get_framebuffers_type()) { case Slic3r::GUI::OpenGLManager::EFramebufferType::Arb: { BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB"); Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*top_thumbnail, thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, false); Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*picking_thumbnail, thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, true); break; } case Slic3r::GUI::OpenGLManager::EFramebufferType::Ext: { BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT"); Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*top_thumbnail, thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, false); Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*picking_thumbnail, thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, true); break; } default: BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown"); break; } plate_data->top_file = "valid_top"; plate_data->pick_file = "valid_pick"; BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s top_thumbnail,finished rendering")%(i+1); } } if (need_create_top_group) { top_thumbnails.push_back(&part_plate->top_thumbnail_data); pick_thumbnails.push_back(&part_plate->pick_thumbnail_data); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for top and pick into group")%(i+1); } } } } //BBS: release glfw glfwTerminate(); } else { BOOST_LOG_TRIVIAL(info) << boost::format("use previous thumbnails, no need to regenerate"); for (int i = 0; i < partplate_list.get_plate_count(); i++) { PlateData *plate_data = plate_data_list[i]; bool skip_this_plate = ((plate_to_slice != 0) && (plate_to_slice != (i + 1)))?true:false; Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); if (skip_this_plate) { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s all the thumbnails skipped, reset here")%(i+1); plate_data->plate_thumbnail.reset(); plate_data->thumbnail_file.clear(); part_plate->top_thumbnail_data.reset(); part_plate->pick_thumbnail_data.reset(); plate_data->top_file.clear(); plate_data->pick_file.clear(); } if (need_create_thumbnail_group) { thumbnails.push_back(&plate_data->plate_thumbnail); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1); } if (need_create_top_group) { top_thumbnails.push_back(&part_plate->top_thumbnail_data); pick_thumbnails.push_back(&part_plate->pick_thumbnail_data); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for top and pick into group")%(i+1); } } } //generate first layer bboxes for (int i = 0; i < partplate_list.get_plate_count(); i++) { if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: generate bbox, Skip plate %2%.")%__LINE__%(i+1); plate_bboxes.push_back(new PlateBBoxData()); continue; } Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); //render calibration thumbnail if (!part_plate->get_slice_result() || !part_plate->is_slice_result_valid()) { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% doesn't have a valid sliced result, skip it")%(i+1); //calibration_thumbnails.push_back(new ThumbnailData()); plate_bboxes.push_back(new PlateBBoxData()); continue; } PrintBase *print_base=NULL; Slic3r::GUI::GCodeResult *gcode_result = NULL; int print_index; part_plate->get_print(&print_base, &gcode_result, &print_index); Print *print = dynamic_cast(print_base); //don't render calibration picture /*BuildVolume build_volume(part_plate->get_shape(), print_height); const std::vector& exclude_bounding_box = part_plate->get_exclude_areas(); Slic3r::GUI::GCodeViewer gcode_viewer; gcode_viewer.init(ConfigOptionMode::comAdvanced, nullptr); gcode_viewer.load(*gcode_result, *print, build_volume, exclude_bounding_box, false, ConfigOptionMode::comAdvanced, false); std::vector colors; if (filament_color) colors = filament_color->values; gcode_viewer.refresh(*gcode_result, colors); ThumbnailData* calibration_data = new ThumbnailData(); const ThumbnailsParams calibration_params = { {}, false, true, true, true, i }; //BBS fixed size const int cali_thumbnail_width = 2560; const int cali_thumbnail_height = 2560; gcode_viewer.render_calibration_thumbnail(*calibration_data, cali_thumbnail_width, cali_thumbnail_height, calibration_params, partplate_list, opengl_mgr); //generate_calibration_thumbnail(*calibration_data, thumbnail_width, thumbnail_height, calibration_params); //*plate_bboxes[index] = p->generate_first_layer_bbox(); calibration_thumbnails.push_back(calibration_data);*/ PlateBBoxData* plate_bbox = new PlateBBoxData(); std::vector& id_bboxes = plate_bbox->bbox_objs; BoundingBoxf bbox_all; PrintSequence curr_plate_seq = part_plate->get_print_seq(); if (curr_plate_seq == PrintSequence::ByDefault) { auto seq_print = m_print_config.option>("print_sequence"); if (seq_print && (seq_print->value == PrintSequence::ByObject)) { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from global")%(i+1); plate_bbox->is_seq_print = true; } } else if (curr_plate_seq == PrintSequence::ByObject) { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from plate self")%(i+1); plate_bbox->is_seq_print = true; } plate_bbox->first_extruder = print->get_tool_ordering().first_extruder(); //bed type; BedType plate_bed_type = part_plate->get_bed_type(); if (plate_bed_type == btDefault) { auto cur_bed_type = m_print_config.option>("curr_bed_type"); if (cur_bed_type) { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% bed type: %2%, set from global")%(i+1) %cur_bed_type->serialize(); plate_bbox->bed_type = bed_type_to_gcode_string(cur_bed_type->value); } } else { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% bed type: %2%, set from plate self")%(i+1) %plate_bed_type; plate_bbox->bed_type = bed_type_to_gcode_string(plate_bed_type); } // get nozzle diameter auto opt_nozzle_diameters = m_print_config.option("nozzle_diameter"); if (opt_nozzle_diameters != nullptr) plate_bbox->nozzle_diameter = float(opt_nozzle_diameters->get_at(plate_bbox->first_extruder)); auto objects = print->objects(); auto orig = part_plate->get_origin(); Vec2d orig2d = { orig[0], orig[1] }; for (auto obj : objects) { BBoxData data; auto bb_scaled = obj->get_first_layer_bbox(data.area, data.layer_height, data.name); auto bb = unscaled(bb_scaled); bb.min -= orig2d; bb.max -= orig2d; bbox_all.merge(bb); data.area *= (SCALING_FACTOR * SCALING_FACTOR); // unscale area data.id = obj->id().id; data.bbox = { bb.min.x(),bb.min.y(),bb.max.x(),bb.max.y() }; id_bboxes.emplace_back(std::move(data)); } // add wipe tower bounding box if (print->has_wipe_tower()) { BBoxData data; auto wt_corners = print->first_layer_wipe_tower_corners(); // when loading gcode.3mf, wipe tower info may not be correct if (!wt_corners.empty()) { BoundingBox bb_scaled = {wt_corners[0], wt_corners[2]}; auto bb = unscaled(bb_scaled); bb.min -= orig2d; bb.max -= orig2d; bbox_all.merge(bb); data.name = "wipe_tower"; data.id = partplate_list.get_curr_plate()->get_index() + 1000; data.bbox = {bb.min.x(), bb.min.y(), bb.max.x(), bb.max.y()}; id_bboxes.emplace_back(std::move(data)); } } plate_bbox->bbox_all = { bbox_all.min.x(),bbox_all.min.y(),bbox_all.max.x(),bbox_all.max.y() }; PlateData *plate_data = plate_data_list[i]; for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) { plate_bbox->filament_ids.push_back(it->id); plate_bbox->filament_colors.push_back(it->color); } plate_bboxes.push_back(plate_bbox); } #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { PrintBase::SlicingStatus slicing_status{97, "Exporting 3mf"}; cli_status_callback(slicing_status); } #endif BOOST_LOG_TRIVIAL(info) << "will export 3mf to " << export_3mf_file << std::endl; if (! this->export_project(&m_models[0], export_3mf_file, plate_data_list, project_presets, thumbnails, top_thumbnails, pick_thumbnails, calibration_thumbnails, plate_bboxes, &m_print_config)) { release_PlateData_list(plate_data_list); flush_and_exit(CLI_EXPORT_3MF_ERROR); } release_PlateData_list(plate_data_list); for (unsigned int i = 0; i < thumbnails.size(); i++) thumbnails[i]->reset(); for (unsigned int i = 0; i < top_thumbnails.size(); i++) top_thumbnails[i]->reset(); for (unsigned int i = 0; i < pick_thumbnails.size(); i++) pick_thumbnails[i]->reset(); for (unsigned int i = 0; i < calibration_thumbnails.size(); i++) delete calibration_thumbnails[i]; for (int i = 0; i < plate_bboxes.size(); i++) delete plate_bboxes[i]; } if (plate_data_src.size() > 0) { release_PlateData_list(plate_data_src); } #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { PrintBase::SlicingStatus slicing_status{100, "All done, Success"}; cli_status_callback(slicing_status); } g_cli_callback_mgr.stop(); #endif for (Model &model : m_models) { model.remove_backup_path_if_exist(); } //BBS: flush logs BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", Finished" << std::endl; boost::nowide::cout.flush(); boost::nowide::cerr.flush(); return 0; } bool CLI::setup(int argc, char **argv) { // Detect the operating system flavor after SLIC3R_LOGLEVEL is set. detect_platform(); #ifdef WIN32 // Notify user that a blacklisted DLL was injected into BambuStudio process (for example Nahimic, see GH #5573). // We hope that if a DLL is being injected into a BambuStudio process, it happens at the very start of the application, // thus we shall detect them now. if (BlacklistedLibraryCheck::get_instance().perform_check()) { std::wstring text = L"Following DLLs have been injected into the BambuStudio process:\n\n"; text += BlacklistedLibraryCheck::get_instance().get_blacklisted_string(); text += L"\n\n" L"BambuStudio is known to not run correctly with these DLLs injected. " L"We suggest stopping or uninstalling these services if you experience " L"crashes or unexpected behaviour while using BambuStudio.\n" L"For example, ASUS Sonic Studio injects a Nahimic driver, which makes BambuStudio " L"to crash on a secondary monitor"; MessageBoxW(NULL, text.c_str(), L"Warning"/*L"Incopatible library found"*/, MB_OK); } #endif // See Invoking prusa-slicer from $PATH environment variable crashes #5542 // boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]); boost::filesystem::path path_to_binary = boost::dll::program_location(); // Path from the Slic3r binary to its resources. #ifdef __APPLE__ // The application is packed in the .dmg archive as 'Slic3r.app/Contents/MacOS/Slic3r' // The resources are packed to 'Slic3r.app/Contents/Resources' boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path().parent_path() / "Resources"; #elif defined _WIN32 // The application is packed in the .zip archive in the root, // The resources are packed to 'resources' // Path from Slic3r binary to resources: boost::filesystem::path path_resources = path_to_binary.parent_path() / "resources"; #elif defined SLIC3R_FHS // The application is packaged according to the Linux Filesystem Hierarchy Standard // Resources are set to the 'Architecture-independent (shared) data', typically /usr/share or /usr/local/share boost::filesystem::path path_resources = SLIC3R_FHS_RESOURCES; #else // The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r', // The resources are packed to 'resources' // Path from Slic3r binary to resources: boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path().parent_path() / "resources"; #endif set_resources_dir(path_resources.string()); set_var_dir((path_resources / "images").string()); set_local_dir((path_resources / "i18n").string()); set_sys_shapes_dir((path_resources / "shapes").string()); // Parse all command line options into a DynamicConfig. // If any option is unsupported, print usage and abort immediately. t_config_option_keys opt_order; if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) { // Separate error message reported by the CLI parser from the help. boost::nowide::cerr << std::endl; this->print_help(); return false; } // Parse actions and transform options. for (auto const &opt_key : opt_order) { if (cli_actions_config_def.has(opt_key)) m_actions.emplace_back(opt_key); else if (cli_transform_config_def.has(opt_key)) m_transforms.emplace_back(opt_key); } //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet. std::map validity = m_config.validate(true); // Initialize with defaults. for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options }) for (const t_optiondef_map::value_type &optdef : *options) m_config.option(optdef.first, true); //set_data_dir(m_config.opt_string("datadir")); //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet. if (!validity.empty()) { boost::nowide::cerr << "Params in command line error: "<< std::endl; for (std::map::iterator it=validity.begin(); it!=validity.end(); ++it) boost::nowide::cerr << it->first <<": "<< it->second << std::endl; return false; } return true; } void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const { boost::nowide::cout << SLIC3R_APP_KEY <<"-"<< SLIC3R_VERSION << ":" << std::endl << "Usage: bambu-studio [ OPTIONS ] [ file.3mf/file.stl ... ]" << std::endl << std::endl << "OPTIONS:" << std::endl; cli_misc_config_def.print_cli_help(boost::nowide::cout, false); cli_transform_config_def.print_cli_help(boost::nowide::cout, false); cli_actions_config_def.print_cli_help(boost::nowide::cout, false); boost::nowide::cout << std::endl << "Print settings priorites:" << std::endl << "\t1) setting values from the command line (highest priority)"<< std::endl << "\t2) setting values loaded with --load_settings and --load_filaments" << std::endl << "\t3) setting values loaded from 3mf(lowest priority)" << std::endl; /*if (include_print_options) { boost::nowide::cout << std::endl; print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def) { return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; }); } else { boost::nowide::cout << std::endl << "Run --help-fff / --help-sla to see the full listing of print options." << std::endl; }*/ } bool CLI::export_models(IO::ExportFormat format) { for (Model &model : m_models) { const std::string path = this->output_filepath(model, format); bool success = false; switch (format) { //case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr, false); break; case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break; case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break; //BBS: use bbs 3mf instead of original //case IO::TMF: success = Slic3r::store_bbs_3mf(path.c_str(), &model, nullptr, false); break; default: assert(false); break; } if (success) BOOST_LOG_TRIVIAL(info) << "Model exported to " << path << std::endl; else { boost::nowide::cerr << "Model export to " << path << " failed" << std::endl; return false; } } return true; } //BBS: add export_project function bool CLI::export_project(Model *model, std::string& path, PlateDataPtrs &partplate_data, std::vector& project_presets, std::vector& thumbnails, std::vector& top_thumbnails, std::vector& pick_thumbnails, std::vector& calibration_thumbnails, std::vector& plate_bboxes, const DynamicPrintConfig* config) { //const std::string path = this->output_filepath(*model, IO::TMF); bool success = false; StoreParams store_params; store_params.path = path.c_str(); store_params.model = model; store_params.plate_data_list = partplate_data; store_params.project_presets = project_presets; store_params.config = (DynamicPrintConfig*)config; store_params.thumbnail_data = thumbnails; store_params.top_thumbnail_data = top_thumbnails; store_params.pick_thumbnail_data = pick_thumbnails; store_params.calibration_thumbnail_data = calibration_thumbnails; store_params.id_bboxes = plate_bboxes; store_params.strategy = SaveStrategy::Silence|SaveStrategy::WithGcode|SaveStrategy::SplitModel|SaveStrategy::UseLoadedId; success = Slic3r::store_bbs_3mf(store_params); if (success) BOOST_LOG_TRIVIAL(info) << "Project exported to " << path << std::endl; else { boost::nowide::cerr << "Project export to " << path << " failed" << std::endl; return false; } return true; } std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) const { std::string ext; switch (format) { case IO::AMF: ext = ".zip.amf"; break; case IO::OBJ: ext = ".obj"; break; case IO::STL: ext = ".stl"; break; case IO::TMF: ext = ".3mf"; break; default: assert(false); break; }; auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext)); // use --output when available std::string cmdline_param = m_config.opt_string("output"); if (! cmdline_param.empty()) { // if we were supplied a directory, use it and append our automatically generated filename boost::filesystem::path cmdline_path(cmdline_param); if (boost::filesystem::is_directory(cmdline_path)) proposed_path = cmdline_path / proposed_path.filename(); else proposed_path = cmdline_param + ext; } return proposed_path.string(); } //BBS: dump stack debug codes, don't delete currently //#include //#pragma comment(lib, "version.lib") //#pragma comment( lib, "dbghelp.lib" ) /*DWORD main_thread_id; std::string TraceStack() { static const int MAX_STACK_FRAMES = 16; void* pStack[MAX_STACK_FRAMES]; HANDLE process = GetCurrentProcess(); SymInitialize(process, NULL, TRUE); WORD frames = CaptureStackBackTrace(0, MAX_STACK_FRAMES, pStack, NULL); std::ostringstream oss; oss << "stack traceback: frames="<< frames << std::endl; for (WORD i = 0; i < frames; ++i) { DWORD64 address = (DWORD64)(pStack[i]); DWORD64 displacementSym = 0; char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; DWORD displacementLine = 0; IMAGEHLP_LINE64 line; //SymSetOptions(SYMOPT_LOAD_LINES); line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); if (SymFromAddr(process, address, &displacementSym, pSymbol) && SymGetLineFromAddr64(process, address, &displacementLine, &line)) { oss << "\t" << pSymbol->Name << " at " << line.FileName << ":" << line.LineNumber << "(0x" << std::hex << pSymbol->Address << std::dec << ")" << std::endl; } else { oss << "\terror: " << GetLastError() << std::endl; } } return oss.str(); } LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { std::ofstream f; DWORD cur_thread_id = GetCurrentThreadId(); f.open("VectoredExceptionHandler.txt", std::ios::out | std::ios::app); f << "main thread id="< argv_narrow; std::vector argv_ptrs(argc + 1, nullptr); for (size_t i = 0; i < argc; ++ i) argv_narrow.emplace_back(boost::nowide::narrow(argv[i])); for (size_t i = 0; i < argc; ++ i) argv_ptrs[i] = argv_narrow[i].data(); //BBS: register default exception handler #if BBL_RELEASE_TO_PUBLIC SET_DEFULTER_HANDLER(); #else AddVectoredExceptionHandler(1, CBaseException::UnhandledExceptionFilter); #endif // Call the UTF8 main. return CLI().run(argc, argv_ptrs.data()); } } #else /* _MSC_VER */ int main(int argc, char **argv) { return CLI().run(argc, argv); } #endif /* _MSC_VER */