#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 #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/FlushVolCalc.hpp" #include "libslic3r/Orient.hpp" #include "libslic3r/PNGReadWrite.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, "Failed setting up server environment."}, {CLI_INVALID_PARAMS, "Invalid parameters to the slicer."}, {CLI_FILE_NOTFOUND, "The input files to the slicer are not found."}, {CLI_FILELIST_INVALID_ORDER, "File list order to the slicer is invalid. Please make sure the 3mf in the first place."}, {CLI_CONFIG_FILE_ERROR, "The input preset file is invalid and can not be parsed."}, {CLI_DATA_FILE_ERROR, "The input model file to the slicer can not be parsed."}, {CLI_INVALID_PRINTER_TECH, "Unsupported printer technology (not FDM)."}, {CLI_UNSUPPORTED_OPERATION, "Unsupported CLI instruction."}, {CLI_COPY_OBJECTS_ERROR, "Failed copying objects."}, {CLI_SCALE_TO_FIT_ERROR, "Failed scaling an object to fit the plate."}, {CLI_EXPORT_STL_ERROR, "Failed exporting STL files."}, {CLI_EXPORT_OBJ_ERROR, "Failed exporting OBJ files."}, {CLI_EXPORT_3MF_ERROR, "Failed exporting 3mf files."}, {CLI_OUT_OF_MEMORY, "Out of memory during slicing. Please upload a model with lower geometry resolution and try again."}, {CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE, "The selected printer is not supported."}, {CLI_3MF_NEW_MACHINE_NOT_SUPPORTED, "The selected printer is not compatible with the 3mf."}, {CLI_PROCESS_NOT_COMPATIBLE, "The selected printer is not compatible with the process preset in the 3mf."}, {CLI_INVALID_VALUES_IN_3MF, "Invalid parameter value(s) included in the 3mf file."}, {CLI_POSTPROCESS_NOT_SUPPORTED, "post_process is not supported under CLI."}, {CLI_PRINTABLE_SIZE_REDUCED, "The selected printer's bed size is smaller than the bed size used in the print profile."}, {CLI_OBJECT_ARRANGE_FAILED, "An error occurred when auto-arranging object(s)."}, {CLI_OBJECT_ORIENT_FAILED, "An error occurred when auto-orienting object(s)."}, {CLI_NO_SUITABLE_OBJECTS, "An empty plate was found. Please check that all plates are not empty in Bambu Studio before uploading."}, {CLI_VALIDATE_ERROR, "There are some incorrect slicing parameters in the 3mf. Please verify the slicing of all plates in Bambu Studio before uploading."}, {CLI_OBJECTS_PARTLY_INSIDE, "Some objects are located over the boundary of the heated bed."}, {CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED, "Failed creating directory when exporting cache data."}, {CLI_EXPORT_CACHE_WRITE_FAILED, "Failed exporting cache data."}, {CLI_IMPORT_CACHE_NOT_FOUND, "Cache data not found."}, {CLI_IMPORT_CACHE_DATA_CAN_NOT_USE, "Cache data can not be parsed."}, {CLI_IMPORT_CACHE_LOAD_FAILED, "Failed importing cache data."}, {CLI_SLICING_TIME_EXCEEDS_LIMIT, "Slicing time of a certain plate exceeds the limit. Please simplify the model or use a larger slicing layer height."}, {CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT, "Triangle count of single plate exceeds the limit. Please simplify the model and try to upload again."}, {CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP, "No printable objects to slice after skipping."}, {CLI_FILAMENT_NOT_MATCH_BED_TYPE, "Filaments are not compatible with the plate type. Please verify the slicing of all plates in Bambu Studio before uploading."}, {CLI_FILAMENTS_DIFFERENT_TEMP, "The temperature difference of the filaments used is too large. Please verify the slicing of all plates in Bambu Studio before uploading."}, {CLI_OBJECT_COLLISION_IN_SEQ_PRINT, "Object conflicts were detected when using print-by-object mode. Please verify the slicing of all plates in Bambu Studio before uploading."}, {CLI_OBJECT_COLLISION_IN_LAYER_PRINT, "Object conflicts were detected. Please verify the slicing of all plates in Bambu Studio before uploading."}, {CLI_SLICING_ERROR, "Failed slicing the model. Please verify the slicing of all plates on Bambu Studio before uploading."}, {CLI_GCODE_PATH_CONFLICTS, " G-code conflicts detected after slicing. Please make sure the 3mf file can be successfully sliced in the latest Bambu Studio."} }; typedef struct _sliced_plate_info{ int plate_id{0}; size_t sliced_time {0}; size_t triangle_count{0}; std::string warning_message; }sliced_plate_info_t; typedef struct _sliced_info { int plate_count {0}; int plate_to_slice {0}; std::vector sliced_plates; size_t prepare_time; size_t export_time; }sliced_info_t; std::vector g_slicing_warnings; #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::this_thread::sleep(boost::posix_time::milliseconds(20)); 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(); }); m_condition.wait(lck, [this](){ return m_started; }); lck.unlock(); m_condition.notify_one(); 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) { if (slicing_status.warning_step != -1) { g_slicing_warnings.push_back(slicing_status); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": percent=%1%, warning_step=%2%, message=%3%, message_type=%4%, flag=%5%") %slicing_status.percent %slicing_status.warning_step %slicing_status.text %(int)(slicing_status.message_type) %slicing_status.flags; } g_cli_callback_mgr.update(slicing_status.percent, slicing_status.text, slicing_status.warning_step); return; } #endif void default_status_callback(const PrintBase::SlicingStatus& slicing_status) { if (slicing_status.warning_step != -1) { g_slicing_warnings.push_back(slicing_status); } BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": percent=%1%, warning_step=%2%, message=%3%, message_type=%4%")%slicing_status.percent %slicing_status.warning_step %slicing_status.text %(int)(slicing_status.message_type); return; } 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 "< key_values = std::map()) { #if defined(__linux__) || defined(__LINUX__) std::string result_file; if (!outputdir.empty()) result_file = outputdir + "/result.json"; else result_file = "result.json"; try { json j; //record the headers j["plate_index"] = plate_id; j["return_code"] = code; j["error_string"] = error_message; j["prepare_time"] = sliced_info.prepare_time; j["export_time"] = sliced_info.export_time; for (size_t index = 0; index < sliced_info.sliced_plates.size(); index++) { json plate_json; plate_json["id"] = sliced_info.sliced_plates[index].plate_id; plate_json["sliced_time"] = sliced_info.sliced_plates[index].sliced_time; plate_json["triangle_count"] = sliced_info.sliced_plates[index].triangle_count; plate_json["warning_message"] = sliced_info.sliced_plates[index].warning_message; j["sliced_plates"].push_back(plate_json); } for (auto& iter: key_values) j[iter.first] = iter.second; boost::nowide::ofstream c; c.open(result_file, std::ios::out | std::ios::trunc); c << std::setw(4) << j << std::endl; c.close(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", saved config to %1%\n")%result_file; } catch (...) {} #endif } static int decode_png_to_thumbnail(std::string png_file, ThumbnailData& thumbnail_data) { if (!boost::filesystem::exists(png_file)) { BOOST_LOG_TRIVIAL(error) << boost::format("can not find file %1%")%png_file; return -1; } const std::size_t &size = boost::filesystem::file_size(png_file); std::string png_buffer(size, '\0'); png_buffer.reserve(size); boost::filesystem::ifstream ifs(png_file, std::ios::binary); ifs.read(png_buffer.data(), png_buffer.size()); ifs.close(); Slic3r::png::ImageColorscale img; Slic3r::png::ReadBuf rb{png_buffer.data(), png_buffer.size()}; BOOST_LOG_TRIVIAL(info) << boost::format("read png file %1%, size %2%")%png_file %size; if ( !Slic3r::png::decode_colored_png(rb, img)) { BOOST_LOG_TRIVIAL(error) << boost::format("decode png file %1% failed")%png_file; return -2; } thumbnail_data.width = img.cols; thumbnail_data.height = img.rows; thumbnail_data.pixels = std::move(img.buf); return 0; } static void glfw_callback(int error_code, const char* description) { BOOST_LOG_TRIVIAL(error) << "error_code " <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, regenerate_thumbnails = false, shrink_to_new_bed = false, filament_color_changed = false; int current_printable_width, current_printable_depth, current_printable_height; int old_printable_height = 0, old_printable_width = 0, old_printable_depth = 0; Pointfs old_printable_area, old_exclude_area; float old_max_radius = 0.f, old_height_to_rod = 0.f, old_height_to_lid = 0.f; std::string outfile_dir = m_config.opt_string("outputdir", true); const std::vector &load_configs = m_config.option("load_settings", true)->values; const std::vector &uptodate_configs = m_config.option("uptodate_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; //skip model object logic const std::vector &skip_objects = m_config.option("skip_objects", true)->values; std::map skip_maps; bool need_skip = (skip_objects.size() > 0)?true:false; long long global_begin_time = 0, global_current_time; sliced_info_t sliced_info; std::map record_key_values; 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); } } global_begin_time = (long long)Slic3r::Utils::get_current_time_utc(); BOOST_LOG_TRIVIAL(info) << boost::format("cli mode, begin at %1%")%global_begin_time; //BBS: add plate data related logic PlateDataPtrs plate_data_src; int arrange_option; int plate_to_slice = 0, filament_count = 0, duplicate_count = 0, real_duplicate_count = 0; bool first_file = true, is_bbl_3mf = false, need_arrange = true, has_thumbnails = false, up_config_to_date = false, normative_check = true, duplicate_single_object = false, use_first_fila_as_default = false, minimum_save = false; 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, new_process_system_name, new_printer_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; bool new_process_config_is_system = true, new_printer_config_is_system = true; 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; ConfigOptionBool* load_defaultfila_option = m_config.option("load_defaultfila"); if (load_defaultfila_option) use_first_fila_as_default = load_defaultfila_option->value; ConfigOptionBool* min_save_option = m_config.option("min_save"); if (min_save_option) minimum_save = min_save_option->value; ConfigOptionString* pipe_option = m_config.option("pipe"); if (pipe_option) { pipe_name = pipe_option->value; if (!pipe_name.empty()) { BOOST_LOG_TRIVIAL(info) << boost::format("Will use pipe %1%")%pipe_name; #if defined(__linux__) || defined(__LINUX__) g_cli_callback_mgr.start(pipe_name); PrintBase::SlicingStatus slicing_status{1, "Start to load files"}; cli_status_callback(slicing_status); #endif } } //skip model object map construct 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]; } } std::string custom_gcode_file; ConfigOptionString* custom_gcode_option = m_config.option("load_custom_gcodes"); if (custom_gcode_option) custom_gcode_file = custom_gcode_option->value; /*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%, use_first_fila_as_default=%3%")%plate_to_slice %normative_check %use_first_fila_as_default; //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; record_exit_reson(outfile_dir, CLI_FILE_NOTFOUND, 0, cli_errors[CLI_FILE_NOTFOUND], sliced_info); 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"; record_exit_reson(outfile_dir, CLI_FILELIST_INVALID_ORDER, 0, cli_errors[CLI_FILELIST_INVALID_ORDER], sliced_info); flush_and_exit(CLI_FILELIST_INVALID_ORDER); } BOOST_LOG_TRIVIAL(info) << boost::format("the first file is a 3mf, version %1%, got plate count %2%") %file_version.to_string() %plate_data_src.size(); 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), old_version2(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 ((file_version < old_version2) && !config.empty()) { regenerate_thumbnails = true; BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to regenerate_thumbnails for all")%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(); record_exit_reson(outfile_dir, CLI_POSTPROCESS_NOT_SUPPORTED, 0, cli_errors[CLI_POSTPROCESS_NOT_SUPPORTED], sliced_info); 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; BOOST_LOG_TRIVIAL(info) << boost::format("current_printer_name %1%, current_process_name %2%")%current_printer_name %current_process_name; 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; BOOST_LOG_TRIVIAL(info) << boost::format("inherits of printer is null, should be system preset"); } else { current_printer_system_name = current_inherits_group[size-1]; BOOST_LOG_TRIVIAL(info) << boost::format("inherits of printer valid, current_printer_system_name is %1%") %current_printer_system_name; } if (current_inherits_group[0].empty()) { current_process_system_name = current_process_name; BOOST_LOG_TRIVIAL(info) << boost::format("inherits of process is null, should be system preset"); } else { current_process_system_name = current_inherits_group[0]; BOOST_LOG_TRIVIAL(info) << boost::format("inherits of process valid, current_process_system_name is %1%") %current_process_system_name; } 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; BOOST_LOG_TRIVIAL(info) << boost::format("no inherits_group: use system name the same as current 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; //use Pointfs insteadof Points old_printable_area = config.option("printable_area", true)->values; old_exclude_area = config.option("bed_exclude_area", true)->values; if (old_printable_area.size() >= 4) { old_printable_width = (int)(old_printable_area[2].x() - old_printable_area[0].x()); old_printable_depth = (int)(old_printable_area[2].y() - old_printable_area[0].y()); } old_printable_height = (int)(config.opt_float("printable_height")); if (config.option("extruder_clearance_height_to_rod")) old_height_to_rod = config.opt_float("extruder_clearance_height_to_rod"); if (config.option("extruder_clearance_height_to_lid")) old_height_to_lid = config.opt_float("extruder_clearance_height_to_lid"); if (config.option("extruder_clearance_max_radius")) old_max_radius = config.opt_float("extruder_clearance_max_radius"); BOOST_LOG_TRIVIAL(info) << boost::format("old printable size from 3mf: {%1%, %2%, %3%}")%old_printable_width %old_printable_depth %old_printable_height; BOOST_LOG_TRIVIAL(info) << boost::format("old extruder_clearance_height_to_rod %1%, extruder_clearance_height_to_lid %2%, extruder_clearance_max_radius %3%}")%old_height_to_rod %old_height_to_lid %old_max_radius; } 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; record_exit_reson(outfile_dir, CLI_DATA_FILE_ERROR, 0, cli_errors[CLI_DATA_FILE_ERROR], sliced_info); 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)); } //} //load custom gcode file std::map custom_gcodes_map; if (!custom_gcode_file.empty()) { // parse the custom gcode json file std::string file = custom_gcode_file; if(!boost::filesystem::exists(file)) { boost::nowide::cerr << __FUNCTION__ << ": can not find custom_gcode file: " << file << std::endl; record_exit_reson(outfile_dir, CLI_FILE_NOTFOUND, 0, cli_errors[CLI_FILE_NOTFOUND], sliced_info); flush_and_exit(CLI_FILE_NOTFOUND); } try { nlohmann::json jj; boost::nowide::ifstream ifs(file); ifs >> jj; ifs.close(); int plate_id = 0; if (plate_to_slice == 0) plate_id = 0; else plate_id = plate_to_slice-1; CustomGCode::Info info; info.from_json(jj); custom_gcodes_map.emplace(plate_id, info); BOOST_LOG_TRIVIAL(info) << boost::format("load custom_gcode from file %1% success, store custom gcodes to plate %2%")%file %(plate_id+1); } catch (std::exception &ex) { boost::nowide::cerr << __FUNCTION__<< ":Loading custom-gcode file \"" << file << "\" failed: " << ex.what() << std::endl; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } } auto load_config_file = [config_substitution_rule](const std::string& file, DynamicPrintConfig& config, std::string& config_type, std::string& config_name, std::string& filament_id, std::string& config_from) { 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_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")&&(config_from != "User")&&(config_from != "user")) { 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, config_from; int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } if (config_type == "machine") { if (!new_printer_name.empty()) { boost::nowide::cerr << "duplicate machine config file: " << file << std::endl; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } new_printer_name = config_name; if (config_from == "system") { new_printer_system_name = new_printer_name; new_printer_config_is_system = true; } else { new_printer_system_name = config.option("inherits", true)->value; new_printer_config_is_system = false; } config.set("printer_settings_id", new_printer_name, true); //printer_inherits = config.option("inherits", true)->value; load_machine_config = std::move(config); BOOST_LOG_TRIVIAL(info) << boost::format("loaded machine config %1%, type %2%, name %3%, inherits %4%")%file %config_name %config_from % new_printer_system_name; } else if (config_type == "process") { if (!new_process_name.empty()) { boost::nowide::cerr << "duplicate process config file: " << file << std::endl; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } new_process_name = config_name; if (config_from == "system") { new_process_system_name = new_process_name; new_process_config_is_system = true; } else { new_process_system_name = config.option("inherits", true)->value; new_process_config_is_system = false; } 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); BOOST_LOG_TRIVIAL(info) << boost::format("loaded process config %1%, type %2%, name %3%, inherits %4%")%file %config_name %config_from % new_process_system_name; } 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, load_filaments_inherit; int current_index = 0; std::string default_load_fila_name, default_load_fila_id, default_filament_file, default_filament_inherit; DynamicPrintConfig default_load_fila_config; if (use_first_fila_as_default) { //construct default filament for (int index = 0; index < load_filament_count; index++) { const std::string& file = load_filaments[index]; if (default_filament_file.empty() && !file.empty()) { DynamicPrintConfig config; std::string config_type, config_name, filament_id, config_from; int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } if (config_type != "filament") { BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-filaments") % config_type % file; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } if ((config_from == "User")||(config_from == "user")) { default_filament_inherit = config.option("inherits", true)->value; } default_filament_file = file; default_load_fila_name = config_name; default_load_fila_id = filament_id; default_load_fila_config = std::move(config); BOOST_LOG_TRIVIAL(info) << boost::format("loaded default filament config %1%, type %2%, name %3%, inherits %4%")%file %config_from %config_name % default_filament_inherit; break; } } if ((load_filament_count > 0) && default_filament_file.empty()) { BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": load_filament_count is %1%, but can not load a default filament") % load_filament_count; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } } 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, config_from; int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } if (config_type != "filament") { BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-filaments") % config_type % file; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); 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_LOG_TRIVIAL(error) << "invalid printer_technology " <("inherits", true)->value; } load_filaments_inherit.push_back(inherits); 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); BOOST_LOG_TRIVIAL(info) << boost::format("loaded filament %1% from file %2%, type %3%, name %4%, inherits %5%")%(index+1) %file %config_from %config_name % inherits; } else { if (use_first_fila_as_default) { BOOST_LOG_TRIVIAL(info)<<__FUNCTION__ << boost::format(": load filament %1% from default, config name %2%, filament_id %3%, current_index %4%") % (index+1) % default_load_fila_name %default_load_fila_id %current_index; load_filaments_id.push_back(default_load_fila_id); load_filaments_name.push_back(default_load_fila_name); load_filaments_config.push_back(default_load_fila_config); load_filaments_index.push_back(current_index); load_filaments_inherit.push_back(default_filament_inherit); } continue; } } if (filament_count == 0) filament_count = load_filament_count; //load system config if needed bool fetch_compatible_values = false, fetch_upward_values = false; if (is_bbl_3mf && up_config_to_date) { if (uptodate_configs.size() > 0) { for (auto const &file : uptodate_configs) { DynamicPrintConfig config; std::string config_type, config_name, filament_id, config_from; int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } if (config_type == "machine") { if ( config_name != current_printer_system_name ) { BOOST_LOG_TRIVIAL(error) << boost::format("wrong machine config file %1% loaded, current machine config name %2% ")%config_name %current_printer_system_name; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } upward_compatible_printers = config.option("upward_compatible_machine", true)->values; BOOST_LOG_TRIVIAL(info) << boost::format("load a machine config %1% from file %2%, upward_compatible_printers size is %3% ")%config_name %file %upward_compatible_printers.size(); if (new_printer_name.empty() && !current_printer_system_name.empty()) { config.set("printer_settings_id", config_name, true); load_machine_config = std::move(config); } } else if (config_type == "process") { if ( config_name != current_process_system_name ) { BOOST_LOG_TRIVIAL(error) << boost::format("wrong process config file %1% loaded, current process config name %2% ")%config_name %current_process_system_name; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } current_print_compatible_printers = config.option("compatible_printers", true)->values; BOOST_LOG_TRIVIAL(info) << boost::format("load a process config %1% from file %2%, current_print_compatible_printers size is %3% ")%config_name %file %current_print_compatible_printers.size(); if (new_process_name.empty() && !current_process_system_name.empty()) { config.set("print_settings_id", config_name, true); load_process_config = std::move(config); } } else { BOOST_LOG_TRIVIAL(error) << boost::format("found invalid config type %1% from config %2% ")%config_type %file; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } } } else { if (new_printer_name.empty() && !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_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_printer_path; //use original one } else { DynamicPrintConfig config; std::string config_type, config_name, filament_id, config_from; int ret = load_config_file(system_printer_path, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } upward_compatible_printers = config.option("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_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_process_path; //use original one } else { DynamicPrintConfig config; std::string config_type, config_name, filament_id, config_from; int ret = load_config_file(system_process_path, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); 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; } //20230802 lhwei: remove below codes, don't replace filament currently /*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_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_filament_path; continue; } DynamicPrintConfig config; std::string config_type, config_name, filament_id, config_from; int ret = load_config_file(system_filament_path, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); 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); std::string inherits; if ((config_from == "User")||(config_from == "user")) { inherits = config.option("inherits", true)->value; } load_filaments_inherit.push_back(inherits); } }*/ } 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_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_printer_path; //skip } else { DynamicPrintConfig config; std::string config_type, config_name, filament_id, config_from; int ret = load_config_file(system_printer_path, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); 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_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_process_path; //use original one } else { DynamicPrintConfig config; std::string config_type, config_name, filament_id, config_from; int ret = load_config_file(system_process_path, config, config_type, config_name, filament_id, config_from); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } current_print_compatible_printers = config.option("compatible_printers", true)->values; } } } //upwards check bool process_compatible = false, machine_upwards = false; BOOST_LOG_TRIVIAL(info) << boost::format("current printer %1%, new printer %2%, current process %3%, new process %4%")%current_printer_name %new_printer_name %current_process_name %new_process_name; BOOST_LOG_TRIVIAL(info) << boost::format("current printer inherits %1%, new printer inherits %2%, current process inherits %3%, new process inherits %4%") %current_printer_system_name %new_printer_system_name %current_process_system_name %new_process_system_name; for (int index = 0; index < current_print_compatible_printers.size(); index++) { BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, current print compatible printer %2%")%index %current_print_compatible_printers[index]; } for (int index = 0; index < new_print_compatible_printers.size(); index++) { BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, new print compatible printer %2%")%index %new_print_compatible_printers[index]; } for (int index = 0; index < upward_compatible_printers.size(); index++) { BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, upward_compatible_printers %2%")%index %upward_compatible_printers[index]; } if (!new_printer_name.empty()) { if (!new_process_name.empty()) { for (int index = 0; index < new_print_compatible_printers.size(); index++) { if (new_print_compatible_printers[index] == new_printer_system_name) { process_compatible = true; break; } } BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, inherited from %2%, new process %3%, inherited from %4% ,compatible %5%") %new_printer_name %new_printer_system_name %new_process_name %new_process_system_name %process_compatible; } else { for (int index = 0; index < current_print_compatible_printers.size(); index++) { if (current_print_compatible_printers[index] == new_printer_system_name) { process_compatible = true; break; } } BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, inherited from %2%, old process %3%, inherited from %4% ,compatible %5%") %new_printer_name %new_printer_system_name %current_process_name %current_process_system_name %process_compatible; } } else if (!new_process_name.empty()) { for (int index = 0; index < new_print_compatible_printers.size(); index++) { if (new_print_compatible_printers[index] == current_printer_system_name) { process_compatible = true; break; } } BOOST_LOG_TRIVIAL(info) << boost::format("old printer %1%, inherited from %2%, new process %3%, inherited from %4% ,compatible %5%") %current_printer_name %current_printer_system_name %new_process_name %new_process_system_name %process_compatible; } else { //check the compatible of old printer&&process for (int index = 0; index < current_print_compatible_printers.size(); index++) { if (current_print_compatible_printers[index] == current_printer_system_name) { process_compatible = true; break; } } if (!process_compatible && current_print_compatible_printers.empty()) { BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf, no compatible printers, set to compatible"); process_compatible = true; } BOOST_LOG_TRIVIAL(info) << boost::format("old printer %1%, inherited from %2%, old process %3%, inherited from %4% ,compatible %5%") %current_printer_name %current_printer_system_name %current_process_name %current_process_system_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_system_name) { process_compatible = true; machine_upwards = true; break; } } if (!process_compatible) { BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(" %1% : current 3mf file not support the new printer %2%, new_printer_system_name %3%")%__LINE__%new_printer_name %new_printer_system_name; record_exit_reson(outfile_dir, CLI_3MF_NEW_MACHINE_NOT_SUPPORTED, 0, cli_errors[CLI_3MF_NEW_MACHINE_NOT_SUPPORTED], sliced_info); flush_and_exit(CLI_3MF_NEW_MACHINE_NOT_SUPPORTED); } } else { BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(" %1%: current 3mf file not support upward_compatible_printers, can not change machine preset.")%__LINE__; record_exit_reson(outfile_dir, CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE, 0, cli_errors[CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE], sliced_info); flush_and_exit(CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE); } } if (!process_compatible) { BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(" %1%: process not compatible with printer.")%__LINE__; record_exit_reson(outfile_dir, CLI_PROCESS_NOT_COMPATIBLE, 0, cli_errors[CLI_PROCESS_NOT_COMPATIBLE], sliced_info); flush_and_exit(CLI_PROCESS_NOT_COMPATIBLE); } //create project embedded preset if needed Preset *new_preset = NULL; if (is_bbl_3mf && machine_upwards) { //we need to update the compatible printer and create a new process here, or if we load the 3mf in studio, the process preset can not be loaded as not compatible 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_system_name) { need_insert = false; break; } } if (need_insert) compatible_printers.push_back(new_printer_system_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_system_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, bool update_all = false) { for (const t_config_option_key &opt_key : config.keys()) { if (!update_all && !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 { //todo: support user machine preset's different settings different_settings[filament_count+1] = ""; if (new_printer_config_is_system) inherits_group[filament_count+1] = ""; else inherits_group[filament_count+1] = new_printer_system_name; } 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; if (new_printer_name.empty()) { ret = update_full_config(m_print_config, load_machine_config, different_keys_set); BOOST_LOG_TRIVIAL(info) << boost::format("no new printer, only update the different key, ret %1%")%ret; } else { ret = update_full_config(m_print_config, load_machine_config, different_keys_set, true); BOOST_LOG_TRIVIAL(info) << boost::format("load a new printer, update all the keys, ret %1%")%ret; } if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); 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 { //todo: support system process preset different_settings[0] = ""; if (new_process_config_is_system) inherits_group[0] = ""; else inherits_group[0] = new_process_system_name; 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; if (new_process_name.empty()) { ret = update_full_config(m_print_config, load_process_config, different_keys_set); BOOST_LOG_TRIVIAL(info) << boost::format("no new process, only update the different key, ret %1%")%ret; } else { ret = update_full_config(m_print_config, load_process_config, different_keys_set, true); BOOST_LOG_TRIVIAL(info) << boost::format("load a new process, update all the keys, ret %1%")%ret; } if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } } if (machine_upwards) { print_compatible_printers.push_back(new_printer_system_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); //todo: update different settings of filaments different_settings[filament_index] = ""; inherits_group[filament_index] = load_filaments_inherit[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 ((load_filament_count == 0) && !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]; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); 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]; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); 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); } } } } //compute the flush volume ConfigOptionStrings* selected_filament_colors_option = m_extra_config.option("filament_colour"); ConfigOptionStrings *project_filament_colors_option = m_print_config.option("filament_colour"); if (project_filament_colors_option) { std::vector selected_filament_colors; if (selected_filament_colors_option) { selected_filament_colors = selected_filament_colors_option->values; //erase here m_extra_config.erase("filament_colour"); } std::vector &project_filament_colors = project_filament_colors_option->values; size_t project_filament_count = project_filament_colors.size(); BOOST_LOG_TRIVIAL(info) << boost::format("select filament color from cli, size %1%")%selected_filament_colors.size(); BOOST_LOG_TRIVIAL(info) << boost::format("project filament colors size %1%")%project_filament_colors.size(); if (project_filament_count > 0) { for ( size_t index = 0; index < project_filament_count; index++ ) { BOOST_LOG_TRIVIAL(info) << boost::format("project filament %1% original color %2%")%index %project_filament_colors[index]; if (selected_filament_colors.size() > index) { if (!selected_filament_colors[index].empty()) { BOOST_LOG_TRIVIAL(info) << boost::format("changed to new color %1%")%selected_filament_colors[index]; unsigned char ori_rgb_color[4] = {}, new_rgb_color[4] = {}; Slic3r::GUI::BitmapCache::parse_color4(project_filament_colors[index], ori_rgb_color); Slic3r::GUI::BitmapCache::parse_color4(selected_filament_colors[index], new_rgb_color); if ((ori_rgb_color[0] != new_rgb_color[0]) || (ori_rgb_color[1] != new_rgb_color[1]) || (ori_rgb_color[2] != new_rgb_color[2]) || (ori_rgb_color[3] != new_rgb_color[3])) { BOOST_LOG_TRIVIAL(info) << boost::format("found color changes, need to regenerate thumbnail"); filament_color_changed = true; } project_filament_colors[index] = selected_filament_colors[index]; } } } //computing ConfigOptionBools* filament_is_support = m_print_config.option("filament_is_support", true); std::vector& flush_vol_matrix = m_print_config.option("flush_volumes_matrix", true)->values; //std::vector& flush_vol_vector = m_print_config.option("flush_volumes_vector", true)->values; flush_vol_matrix.resize(project_filament_count*project_filament_count, 0.f); //flush_vol_vector.resize(project_filament_count); //set multiplier to 1? m_print_config.option("flush_multiplier", true)->set(new ConfigOptionFloat(1.f)); ConfigOption* extra_flush_volume_opt = m_print_config.option("nozzle_volume"); int extra_flush_volume = extra_flush_volume_opt ? (int)extra_flush_volume_opt->getFloat() : 0; if (filament_is_support->size() != project_filament_count) { BOOST_LOG_TRIVIAL(error) << boost::format("filament_is_support's count %1% not equal to filament_colour's size %2%")%filament_is_support->size() %project_filament_count; record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(CLI_CONFIG_FILE_ERROR); } BOOST_LOG_TRIVIAL(info) << boost::format("extra_flush_volume: %1%")%extra_flush_volume; BOOST_LOG_TRIVIAL(info) << boost::format("filament_is_support: %1%")%filament_is_support->serialize(); BOOST_LOG_TRIVIAL(info) << boost::format("flush_volumes_matrix before computing: %1%")%m_print_config.option("flush_volumes_matrix")->serialize(); for (int from_idx = 0; from_idx < project_filament_count; from_idx++) { const std::string& from_color = project_filament_colors[from_idx]; unsigned char from_rgb[4] = {}; Slic3r::GUI::BitmapCache::parse_color4(from_color, from_rgb); bool is_from_support = filament_is_support->get_at(from_idx); for (int to_idx = 0; to_idx < project_filament_count; to_idx++) { bool is_to_support = filament_is_support->get_at(to_idx); if (from_idx == to_idx) { flush_vol_matrix[project_filament_count*from_idx + to_idx] = 0.f; } else { int flushing_volume = 0; if (is_to_support) { flushing_volume = Slic3r::g_flush_volume_to_support; } else { const std::string& to_color = project_filament_colors[to_idx]; unsigned char to_rgb[4] = {}; Slic3r::GUI::BitmapCache::parse_color4(to_color, to_rgb); //BOOST_LOG_TRIVIAL(info) << boost::format("src_idx %1%, src color %2%, dst idex %3%, dst color %4%")%from_idx %from_color %to_idx %to_color; //BOOST_LOG_TRIVIAL(info) << boost::format("src_rgba {%1%,%2%,%3%,%4%} dst_rgba {%5%,%6%,%7%,%8%}")%(unsigned int)(from_rgb[0]) %(unsigned int)(from_rgb[1]) %(unsigned int)(from_rgb[2]) %(unsigned int)(from_rgb[3]) // %(unsigned int)(to_rgb[0]) %(unsigned int)(to_rgb[1]) %(unsigned int)(to_rgb[2]) %(unsigned int)(to_rgb[3]); Slic3r::FlushVolCalculator calculator(extra_flush_volume, Slic3r::g_max_flush_volume); flushing_volume = calculator.calc_flush_vol(from_rgb[3], from_rgb[0], from_rgb[1], from_rgb[2], to_rgb[3], to_rgb[0], to_rgb[1], to_rgb[2]); if (is_from_support) { flushing_volume = std::max(Slic3r::g_min_flush_volume_from_support, flushing_volume); } } flush_vol_matrix[project_filament_count * from_idx + to_idx] = flushing_volume; //flushing_volume = int(flushing_volume * get_flush_multiplier()); } } } BOOST_LOG_TRIVIAL(info) << boost::format("flush_volumes_matrix after computed: %1%")%m_print_config.option("flush_volumes_matrix")->serialize(); } else { BOOST_LOG_TRIVIAL(warning) << boost::format("filament colors count is 0 in projects"); } } else { BOOST_LOG_TRIVIAL(warning) << boost::format("no filament colors found in projects"); } //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)); } //load custom gcodes into model if needed if ((custom_gcodes_map.size() > 0)&&(m_models.size() > 0)) { m_models[0].plates_custom_gcodes = custom_gcodes_map; /*m_models[0].plates_custom_gcodes.clear(); for (auto& custom_gcode: custom_gcodes_map) { BOOST_LOG_TRIVIAL(info) << boost::format("insert custom_gocde %1% into plate %2%")%plate_id; m_models[0].plates_custom_gcodes.emplace(custom_gcode.first, custom_gcode.second); }*/ } // 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; record_exit_reson(outfile_dir, CLI_INVALID_PRINTER_TECH, 0, cli_errors[CLI_INVALID_PRINTER_TECH], sliced_info); 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; record_exit_reson(outfile_dir, CLI_INVALID_VALUES_IN_3MF, 0, cli_errors[CLI_INVALID_VALUES_IN_3MF], sliced_info); 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 current_printable_area = m_print_config.opt("printable_area")->values; Pointfs current_exclude_area = 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 cleareance_radius = m_print_config.opt_float("extruder_clearance_max_radius"); //double plate_stride; std::string bed_texture; current_printable_width = current_printable_area[2].x() - current_printable_area[0].x(); current_printable_depth = current_printable_area[2].y() - current_printable_area[0].y(); current_printable_height = print_height; if (old_printable_width == 0) old_printable_width = current_printable_width; if (old_printable_depth == 0) old_printable_depth = current_printable_depth; if (old_printable_height == 0) old_printable_height = current_printable_height; if ((old_printable_width > 0)&&(old_printable_depth > 0)&&(old_printable_height > 0)) { //check the printable size logic if ((old_printable_width > current_printable_width) || (old_printable_depth > current_printable_depth) || (old_printable_height > current_printable_height)) { BOOST_LOG_TRIVIAL(error) << boost::format("old printable size {%1%, %2%, %3%} is larger than new printable size {%4%, %5%, %6%}, can not print") %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height; record_exit_reson(outfile_dir, CLI_PRINTABLE_SIZE_REDUCED, 0, cli_errors[CLI_PRINTABLE_SIZE_REDUCED], sliced_info); flush_and_exit(CLI_PRINTABLE_SIZE_REDUCED); } else if ((old_printable_width < current_printable_width) || (old_printable_depth < current_printable_depth)) { BOOST_LOG_TRIVIAL(info) << boost::format("old printable size {%1%, %2%, %3%} is smaller than new printable size {%4%, %5%, %6%}, need to center the model") %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height; shrink_to_new_bed = true; } else { BOOST_LOG_TRIVIAL(info) << boost::format("old printable size {%1%, %2%, %3%}, new printable size {%4%, %5%, %6%}") %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height; } } if (m_models.size() > 0) { BOOST_LOG_TRIVIAL(info) << boost::format("translate_old %1%, shrink_to_new_bed %2%, old bed size {%3%, %4%, %5%}")%translate_old%shrink_to_new_bed %old_printable_width %old_printable_depth %old_printable_height; if (translate_old) { BOOST_LOG_TRIVIAL(info) << boost::format("translate old 3mf, switch to older bed size,{%1%, %2%, %3%}")%(old_printable_width + bed3d_ax3s_default_tip_radius)%(old_printable_depth+bed3d_ax3s_default_tip_radius) %old_printable_height; partplate_list.reset_size(old_printable_width + bed3d_ax3s_default_tip_radius, old_printable_depth + bed3d_ax3s_default_tip_radius, old_printable_height, false); } else { partplate_list.reset_size(old_printable_width, old_printable_depth, old_printable_height, false); } partplate_list.set_shapes(current_printable_area, current_exclude_area, bed_texture, height_to_lid, height_to_rod); //plate_stride = partplate_list.plate_stride_x(); } auto translate_models = [translate_old, shrink_to_new_bed, old_printable_width, old_printable_depth, old_printable_height, current_printable_width, current_printable_depth, current_printable_height] (Slic3r::GUI::PartPlateList& plate_list, DynamicPrintConfig& print_config) { //BBS: translate old 3mf to correct positions if (translate_old) { //translate the objects int plate_count = plate_list.get_plate_count(); for (int index = 1; index < plate_count; index ++) { Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)plate_list.get_plate(index); Vec3d cur_origin = cur_plate->get_origin(); Vec3d new_origin = plate_list.compute_origin_using_new_size(index, old_printable_width, old_printable_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%}")%old_printable_width %old_printable_depth %old_printable_height; plate_list.reset_size(old_printable_width, old_printable_depth, old_printable_height, true, true); } if (shrink_to_new_bed) { int plate_count = plate_list.get_plate_count(); ConfigOptionFloats *wipe_x_option = nullptr, *wipe_y_option = nullptr; Vec3d wipe_offset; if (print_config.has("wipe_tower_x")) { wipe_x_option = dynamic_cast(print_config.option("wipe_tower_x")); wipe_y_option = dynamic_cast(print_config.option("wipe_tower_y")); } for (int index = 0; index < plate_count; index ++) { Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)plate_list.get_plate(index); Vec3d cur_origin = cur_plate->get_origin(); Vec3d new_origin = plate_list.compute_origin_using_new_size(index, current_printable_width, current_printable_depth); Vec3d cur_center_offset { ((double)old_printable_width)/2, ((double)old_printable_depth)/2, 0}, new_center_offset { ((double)current_printable_width)/2, ((double)current_printable_depth)/2, 0}; Vec3d cur_center = cur_origin + cur_center_offset; Vec3d new_center = new_origin + new_center_offset; Vec3d offset = new_center - cur_center; cur_plate->translate_all_instance(offset); if (index == 0) wipe_offset = offset; if (wipe_x_option) { BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed, plate %1%: wipe tower src: {%2%, %3%}")%(index+1) %wipe_x_option->get_at(index) %wipe_y_option->get_at(index); ConfigOptionFloat wipe_tower_x(wipe_x_option->get_at(index) + wipe_offset(0)); ConfigOptionFloat wipe_tower_y(wipe_y_option->get_at(index) + wipe_offset(1)); wipe_x_option->set_at(&wipe_tower_x, index, 0); wipe_y_option->set_at(&wipe_tower_y, index, 0); BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed, plate %1% wipe tower changes to: {%2%, %3%}")%(index+1) %wipe_x_option->get_at(index) %wipe_y_option->get_at(index); } BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed, plate %1% translate offset: {%2%, %3%, %4%}")%(index+1) %offset[0] %offset[1] %offset[2]; } BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed, shrink all the models to current bed size,{%1%, %2%, %3%}")%current_printable_width %current_printable_depth %current_printable_height; plate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true); } }; if (plate_data_src.size() > 0) { partplate_list.load_from_3mf_structure(plate_data_src); translate_models(partplate_list, m_print_config); } /*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{2, "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 == "repetitions") { int repetitions_count = m_config.option("repetitions")->value; if (repetitions_count <= 1) { BOOST_LOG_TRIVIAL(info) << "invalid repetitions value " << repetitions_count << ", just skip\n"; } else { if (plate_to_slice == 0) { BOOST_LOG_TRIVIAL(error) << "Invalid params: can not set repetitions when slice all." << std::endl; record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } else if (plate_to_slice > partplate_list.get_plate_count()) { BOOST_LOG_TRIVIAL(error) << boost::format("Invalid params:invalid plate %1% to slice, total %2%")%plate_to_slice %partplate_list.get_plate_count(); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } BOOST_LOG_TRIVIAL(info) << "repetitions value " << repetitions_count << std::endl; need_arrange = true; duplicate_count = repetitions_count - 1; } } 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") { //BBS: orient 0 means disable, 1 means force orient, others means auto int orient_option = m_config.option("orient")->value; if (orient_option == 0) { orients_requirement.clear(); for (auto& model : m_models) for (ModelObject* o : model.objects) { orients_requirement.insert(std::pair(o->id().id, false)); BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << ", no need to orient when setting orient to 0\n"; } } else if (orient_option == 1) { //force orient orients_requirement.clear(); for (auto& model : m_models) for (ModelObject* o : model.objects) { orients_requirement.insert(std::pair(o->id().id, true)); BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << ", need to orient when setting orient to 1\n"; } } else { //auto arrange, keep the original logic } BOOST_LOG_TRIVIAL(info) << boost::format("orient_option %1%")%orient_option; } 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; record_exit_reson(outfile_dir, CLI_COPY_OBJECTS_ERROR, 0, cli_errors[CLI_COPY_OBJECTS_ERROR], sliced_info); 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; record_exit_reson(outfile_dir, CLI_SCALE_TO_FIT_ERROR, 0, cli_errors[CLI_SCALE_TO_FIT_ERROR], sliced_info); 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; record_exit_reson(outfile_dir, CLI_UNSUPPORTED_OPERATION, 0, cli_errors[CLI_UNSUPPORTED_OPERATION], sliced_info); 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< partplate_list.get_plate_count())) { BOOST_LOG_TRIVIAL(error) << boost::format("invalid plate id %1%, total %2%")%plate_to_slice %partplate_list.get_plate_count(); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } if ((!need_arrange) && is_bbl_3mf && !shrink_to_new_bed && (plate_to_slice > 0)) { if (((old_height_to_rod != 0.f) && (old_height_to_rod != height_to_rod)) || ((old_height_to_lid != 0.f) && (old_height_to_lid != height_to_lid)) || ((old_max_radius != 0.f) && (old_max_radius != cleareance_radius))) { Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1); PrintSequence curr_plate_seq = cur_plate->get_print_seq(); bool is_seq_print = false; 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("Check whether need to arrange by print_sequence: plate %1% print by object, set from global")%plate_to_slice; is_seq_print = true; } } else if (curr_plate_seq == PrintSequence::ByObject) { BOOST_LOG_TRIVIAL(info) << boost::format("Check whether need to arrange by print_sequence: plate %1% print by object, set from plate self")%plate_to_slice; is_seq_print = true; } if (is_seq_print) { need_arrange = true; BOOST_LOG_TRIVIAL(info) << boost::format("old_height_to_rod %1%, old_height_to_lid %2%, old_max_radius %3%, current height_to_rod %4%, height_to_lid %5%, cleareance_radius %6%, need arrange!") %old_height_to_rod %old_height_to_lid %old_max_radius %height_to_rod %height_to_lid %cleareance_radius; } } } oriented_or_arranged |= need_arrange; BOOST_LOG_TRIVIAL(info) << boost::format("before arrange, need_arrange=%1%, duplicate_count %2%, filament_color_changed %3%")%need_arrange %duplicate_count %filament_color_changed; if (need_arrange || filament_color_changed) { for (int index = 0; index < partplate_list.get_plate_count(); index ++) { if ((plate_to_slice != 0) && (plate_to_slice != (index + 1))) { continue; } 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(); } } } } if (need_arrange) { ArrangePolygons selected, unselected, unprintable, locked_aps; //for (Model &model : m_models) if (m_models.size() > 0) { Model &model = m_models[0]; Model original_model; std::set> backup_set; bool finished_arrange = false, first_run = true; Slic3r::GUI::PartPlate* cur_plate = nullptr; int low_duplicate_count = 0, up_duplicate_count = duplicate_count, arrange_count = 0; arrange_cfg.is_seq_print = false; if (duplicate_count > 0) { original_model = model; } while(!finished_arrange) { arrange_count++; //step-0: duplicate model if (duplicate_count > 0) { //copy model objects and instances on plate if (!first_run) { BOOST_LOG_TRIVIAL(info) << boost::format("restore model object and plate, new duplicate_count %1%, arrange_count=%2%")%duplicate_count%arrange_count; beds = get_bed_shape(m_print_config); model.clear_objects(); model.clear_materials(); model = original_model; partplate_list.load_from_3mf_structure(plate_data_src); selected.clear(); unselected.clear(); unprintable.clear(); locked_aps.clear(); } else first_run = false; cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1); cur_plate->duplicate_all_instance(duplicate_count, need_skip, skip_maps); } else if (plate_to_slice > 0) { cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1); } if (cur_plate) { PrintSequence curr_plate_seq = cur_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")%plate_to_slice; arrange_cfg.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")%plate_to_slice; arrange_cfg.is_seq_print = true; } } //Step-1: prepare arrange polygons if ((duplicate_count == 0) && (plate_to_slice == 0)) { //global arrange for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { ModelObject* mo = model.objects[oidx]; for (size_t inst_idx = 0; inst_idx < mo->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; } } } if (m_print_config.has("print_sequence")) { PrintSequence seq = m_print_config.option>("print_sequence")->value; arrange_cfg.is_seq_print = (seq == PrintSequence::ByObject); } //add the virtual object into unselect list if has partplate_list.preprocess_exclude_areas(unselected); } else { //only arrange current plate partplate_list.lock_plate(plate_to_slice - 1, false); partplate_list.select_plate(plate_to_slice-1); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% set to selected")%plate_to_slice; for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { ModelObject* mo = model.objects[oidx]; for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx) { ModelInstance* minst = mo->instances[inst_idx]; bool in_plate = cur_plate->contain_instance(oidx, inst_idx) || cur_plate->intersect_instance(oidx, inst_idx); ArrangePolygon ap = get_instance_arrange_poly(minst, m_print_config); ArrangePolygons& cont = mo->instances[inst_idx]->printable ? (in_plate ? selected : unselected) : unprintable; bool locked = partplate_list.preprocess_arrange_polygon_other_locked(oidx, inst_idx, ap, in_plate); BOOST_LOG_TRIVIAL(info) << boost::format("name %4% in_plate %1% printable %2%, locked %3%")%in_plate %mo->instances[inst_idx]->printable %locked % ap.name ; if (!locked) { ap.itemid = cont.size(); cont.emplace_back(std::move(ap)); } else { //skip this object due to be not in current plate, treated as locked ap.itemid = locked_aps.size(); locked_aps.emplace_back(std::move(ap)); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format("arrange: skip locked instance, obj_id %1%, name %2%") % oidx % mo->name; } } } if ((duplicate_count > 0)&&(selected.size() == (duplicate_count + 1))) { duplicate_single_object = true; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found single object mode"); } if (!arrange_cfg.is_seq_print && m_print_config.has("wipe_tower_x")) { float x = dynamic_cast(m_print_config.option("wipe_tower_x"))->get_at(plate_to_slice-1); float y = dynamic_cast(m_print_config.option("wipe_tower_y"))->get_at(plate_to_slice-1); float w = dynamic_cast(m_print_config.option("prime_tower_width"))->value; float a = dynamic_cast(m_print_config.option("wipe_tower_rotation_angle"))->value; float v = dynamic_cast(m_print_config.option("prime_volume"))->value; unsigned int filaments_cnt = plate_data_src[plate_to_slice-1]->slice_filaments_info.size(); if ((filaments_cnt == 0) || need_skip) { // slice filaments info invalid std::vector extruders = cur_plate->get_extruders_under_cli(true, m_print_config); filaments_cnt = extruders.size(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange: slice filaments info invalid or need_skip, get from partplate: filament_count %1%")%filaments_cnt; } if (filaments_cnt <= 1) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("arrange: not a multi-color object anymore, drop the wipe tower before arrange."); } else { float layer_height = 0.2; ConfigOption* layer_height_opt = m_print_config.option("layer_height"); if (layer_height_opt) layer_height = layer_height_opt->getFloat(); float depth = v * (filaments_cnt - 1) / (layer_height * w); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: x=%1%, y=%2%, width=%3%, depth=%4%, angle=%5%, prime_volume=%6%, filaments_cnt=%7%, layer_height=%8%") %x %y %w %depth %a %v %filaments_cnt %layer_height; Vec3d plate_origin = cur_plate->get_origin(); ArrangePolygon wipe_tower_ap; Polygon ap({ {scaled(x), scaled(y)}, {scaled(x + w), scaled(y)}, {scaled(x + w), scaled(y + depth)}, {scaled(x), scaled(y + depth)} }); wipe_tower_ap.bed_idx = 0; wipe_tower_ap.setter = NULL; // do not move wipe tower wipe_tower_ap.poly.contour = std::move(ap); wipe_tower_ap.translation = {scaled(0.f), scaled(0.f)}; wipe_tower_ap.rotation = a; wipe_tower_ap.name = "WipeTower"; wipe_tower_ap.is_virt_object = true; wipe_tower_ap.is_wipe_tower = true; ++wipe_tower_ap.priority; unselected.emplace_back(std::move(wipe_tower_ap)); } } // add the virtual object into unselect list if has partplate_list.preprocess_exclude_areas(unselected, plate_to_slice); } //Step-2:prepare the arrange params arrange_cfg.allow_rotations = true; arrange_cfg.allow_multi_materials_on_same_plate = true; arrange_cfg.avoid_extrusion_cali_region = false; arrange_cfg.clearance_height_to_rod = height_to_rod; arrange_cfg.clearance_height_to_lid = height_to_lid; arrange_cfg.cleareance_radius = cleareance_radius; arrange_cfg.printable_height = print_height; arrange_cfg.min_obj_distance = 0; if (auto printer_structure_opt = m_print_config.option>("printer_structure")) { arrange_cfg.align_to_y_axis = (printer_structure_opt->value == PrinterStructure::psI3); } arrangement::update_arrange_params(arrange_cfg, m_print_config, selected); arrangement::update_selected_items_inflation(selected, &m_print_config, arrange_cfg); arrangement::update_unselected_items_inflation(unselected, &m_print_config, arrange_cfg); arrangement::update_selected_items_axis_align(selected, &m_print_config, arrange_cfg); beds=get_shrink_bedpts(&m_print_config, arrange_cfg); { BOOST_LOG_TRIVIAL(debug)<< "Arrange full params: "<< arrange_cfg.to_json(); BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items selected before arranging: %1%")%selected.size(); for (auto item : selected) BOOST_LOG_TRIVIAL(trace) << item.name << ", extruder: " << item.extrude_ids.back() << ", bed: " << item.bed_idx << ", trans: " << item.translation.transpose(); BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items unselected before arranging: %1%") % unselected.size(); for (auto item : unselected) BOOST_LOG_TRIVIAL(trace) << item.name << ", bed: " << item.bed_idx << ", trans: " << item.translation.transpose(); } arrange_cfg.progressind= [](unsigned st, std::string str = "") { //boost::nowide::cout << "st=" << st << ", " << str << std::endl; }; //Step-3:do the arrange BOOST_LOG_TRIVIAL(info) << boost::format("start %1% th arranging...")%arrange_count; arrangement::arrange(selected, unselected, beds, arrange_cfg); arrangement::arrange(unprintable, {}, beds, arrange_cfg); BOOST_LOG_TRIVIAL(info) << boost::format("finished %1% th arranging...")%arrange_count; //Step-4:postprocess by partplate list&&apply the result int bed_idx_max = 0; if (duplicate_count == 0) { if (plate_to_slice > 0) { //only for partplate case partplate_list.clear(false, false, true, plate_to_slice-1); for (ArrangePolygon& ap : selected) { partplate_list.postprocess_bed_index_for_current_plate(ap); if (ap.bed_idx != (plate_to_slice-1)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":arrange failed: ap.name %1% ap.bed_idx %2%, plate index %3%")% ap.name % ap.bed_idx % (plate_to_slice-1); record_exit_reson(outfile_dir, CLI_OBJECT_ARRANGE_FAILED, 0, cli_errors[CLI_OBJECT_ARRANGE_FAILED], sliced_info); flush_and_exit(CLI_OBJECT_ARRANGE_FAILED); } bed_idx_max = std::max(ap.bed_idx, bed_idx_max); } } else { //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_LOG_TRIVIAL(trace)<< "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(); } // Apply the arrange result to unselected objects(due to the sukodu-style column changes, the position of unselected may also be modified) for (ArrangePolygon& ap : unselected) { if (ap.is_virt_object) continue; //BBS: partplate postprocess partplate_list.postprocess_arrange_polygon(ap, false); ap.apply(); } // Move the unprintable items to the last virtual bed. // Note ap.apply() moves relatively according to bed_idx, so we need to subtract the orignal bed_idx for (ArrangePolygon& ap : unprintable) { ap.bed_idx = bed_idx_max + 1; partplate_list.postprocess_arrange_polygon(ap, true); ap.apply(); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":arrange m_unprintable: name: %4%, bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale(ap.translation(X)) % unscale(ap.translation(Y)) % ap.name; } //BBS: reload all objects due to arrange if (plate_to_slice > 0) partplate_list.rebuild_plates_after_arrangement(false, true, plate_to_slice-1); else partplate_list.rebuild_plates_after_arrangement(); } else { //only for partplate case partplate_list.clear(false, false, true, plate_to_slice-1); //BBS: adjust the bed_index, create new plates, get the max bed_index bool failed_this_time = false; for (ArrangePolygon& ap : selected) { partplate_list.postprocess_bed_index_for_current_plate(ap); if (ap.bed_idx != (plate_to_slice-1)) { // BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":arrange failed: ap.name %1% ap.bed_idx %2%, plate index %3%")% ap.name % ap.bed_idx % (plate_to_slice-1); if (!duplicate_single_object) { BOOST_LOG_TRIVIAL(warning) << boost::format("arrange failed when duplicate multiple objects at count %1%, low_duplicate_count %2%, up_duplicate_count %3%")%duplicate_count %low_duplicate_count %up_duplicate_count; if (duplicate_count == 1) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": failed even on duplicate 1 copy, just print one original model"); duplicate_count = 0; } else { if (duplicate_count == low_duplicate_count) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": previous success, but currently failed count %1%!!!")%duplicate_count; up_duplicate_count = duplicate_count; low_duplicate_count --; duplicate_count --; } else { up_duplicate_count = duplicate_count; duplicate_count = (up_duplicate_count + low_duplicate_count)/2; } BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": try new count %1%, low_duplicate_count %2%, up_duplicate_count %3%")%duplicate_count %low_duplicate_count %up_duplicate_count; } //record_exit_reson(outfile_dir, CLI_OBJECT_ARRANGE_FAILED, 0, cli_errors[CLI_OBJECT_ARRANGE_FAILED], sliced_info); //flush_and_exit(CLI_OBJECT_ARRANGE_FAILED); failed_this_time = true; break; } } else { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":arrange success: ap.name %1% ap.bed_idx %2%, plate index %3%")% ap.name % ap.bed_idx % (plate_to_slice-1); real_duplicate_count ++; } bed_idx_max = std::max(ap.bed_idx, bed_idx_max); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": arrange selected %4%: bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale(ap.translation(X)) % unscale(ap.translation(Y)) % ap.name; } if (failed_this_time) { if (duplicate_count == 0) { //restore to the original BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": restore to the original model and plates"); finished_arrange = true; model = original_model; partplate_list.load_from_3mf_structure(plate_data_src); partplate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": exit arrange process"); } continue; } if (duplicate_single_object) { if (real_duplicate_count <= 1) { BOOST_LOG_TRIVIAL(warning) << "no object can be placed under single object mode, restore to the original model and plates also" << std::endl; //record_exit_reson(outfile_dir, CLI_OBJECT_ARRANGE_FAILED, 0, cli_errors[CLI_OBJECT_ARRANGE_FAILED], sliced_info); //flush_and_exit(CLI_OBJECT_ARRANGE_FAILED); finished_arrange = true; model = original_model; partplate_list.load_from_3mf_structure(plate_data_src); partplate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true); duplicate_count = 0; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": exit arrange process"); continue; } duplicate_count = real_duplicate_count - 1; } else { //multiple objects case BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": multiple objects mode, arrange success on count %1%, low_duplicate_count %2%, up_duplicate_count %3%")%duplicate_count %low_duplicate_count %up_duplicate_count; if ((duplicate_count == up_duplicate_count) || (duplicate_count == (up_duplicate_count - 1))) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found the max arrangeable count %1%")%duplicate_count; } else { low_duplicate_count = duplicate_count; duplicate_count = (up_duplicate_count + low_duplicate_count)/2; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": try new count %1%, low_duplicate_count %2%, up_duplicate_count %3%")%duplicate_count %low_duplicate_count %up_duplicate_count; continue; } } //BBS: adjust the bed_index, create new plates, get the max bed_index for (ArrangePolygon& ap : unselected) { if (ap.is_virt_object) continue; bed_idx_max = std::max(ap.bed_idx, bed_idx_max); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":arrange unselected %4%: bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale(ap.translation(X)) % unscale(ap.translation(Y)) % ap.name; } 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(); } // Apply the arrange result to unselected objects(due to the sukodu-style column changes, the position of unselected may also be modified) for (ArrangePolygon& ap : unselected) { if (ap.is_virt_object) continue; //BBS: partplate postprocess partplate_list.postprocess_arrange_polygon(ap, false); ap.apply(); } // Move the unprintable items to the last virtual bed. // Note ap.apply() moves relatively according to bed_idx, so we need to subtract the orignal bed_idx for (ArrangePolygon& ap : unprintable) { ap.bed_idx = bed_idx_max + 1; partplate_list.postprocess_arrange_polygon(ap, true); ap.apply(); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":arrange m_unprintable: name: %4%, bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale(ap.translation(X)) % unscale(ap.translation(Y)) % ap.name; } partplate_list.rebuild_plates_after_arrangement(false, true, plate_to_slice-1); } finished_arrange = true; } original_model.clear_objects(); original_model.clear_materials(); } } // 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, 0407 sinking supported if (m_config.opt_bool("ensure_on_bed")) { BOOST_LOG_TRIVIAL(info) << "ensure_on_bed: need to ensure each object on beds"; 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::vector calibration_thumbnails; int max_slicing_time_per_plate = 0, max_triangle_count_per_plate = 0; std::vector plate_has_skips(partplate_list.get_plate_count(), false); std::vector> plate_skipped_objects(partplate_list.get_plate_count()); global_current_time = (long long)Slic3r::Utils::get_current_time_utc(); sliced_info.prepare_time = (size_t) (global_current_time - global_begin_time); global_begin_time = global_current_time; 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; record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } else if (duplicate_count > 0) { BOOST_LOG_TRIVIAL(error) << "should not set load_slicedata when set repetitions." << std::endl; record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } else if (shrink_to_new_bed) { BOOST_LOG_TRIVIAL(warning) << "use load_slicedata when shrink_to_new_bed(switch printer from small to bigger." << std::endl; //record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); //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 == "min_save") { //already processed before } else if (opt_key == "load_defaultfila") { //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)) { record_exit_reson(outfile_dir, CLI_EXPORT_STL_ERROR, 0, cli_errors[CLI_EXPORT_STL_ERROR], sliced_info); 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)) { record_exit_reson(outfile_dir, CLI_EXPORT_OBJ_ERROR, 0, cli_errors[CLI_EXPORT_OBJ_ERROR], sliced_info); 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; record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); 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; bool finished = false; /*if (opt_key == "export_gcode" && printer_technology == ptSLA) { boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl; record_exit_reson(outfile_dir, 1, 0, cli_errors[1], sliced_info); 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; record_exit_reson(outfile_dir, 1, 0, cli_errors[1], sliced_info); flush_and_exit(1); }*/ BOOST_LOG_TRIVIAL(info) << "Need to slice for plate "< plate_triangle_counts(partplate_list.get_plate_count(), 0); while(!finished) { //BBS: slice every partplate one by one PrintBase *print=NULL; Print *print_fff = NULL; Slic3r::GUI::GCodeResult *gcode_result = NULL; int print_index; for (int index = 0; index < partplate_list.get_plate_count(); index ++) { if ((plate_to_slice != 0) && (plate_to_slice != (index + 1))) { BOOST_LOG_TRIVIAL(info) << "Skip plate " << index+1 << std::endl; continue; } sliced_plate_info_t sliced_plate_info; sliced_plate_info.plate_id = index+1; model.curr_plate_index = index; BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: pre_check %2%, start")%(index+1)%pre_check; long long start_time = 0, end_time = 0; start_time = (long long)Slic3r::Utils::get_current_time_utc(); //get the current partplate Slic3r::GUI::PartPlate* part_plate = partplate_list.get_plate(index); part_plate->get_print(&print, &gcode_result, &print_index); print_fff = dynamic_cast(print); /*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; record_exit_reson(outfile_dir, CLI_NO_SUITABLE_OBJECTS, index+1, cli_errors[CLI_NO_SUITABLE_OBJECTS], sliced_info); 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) { i->use_loaded_id_for_label = true; 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++; plate_has_skips[index] = true; plate_skipped_objects[index].emplace_back(i->loaded_id); 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; record_exit_reson(outfile_dir, CLI_OBJECTS_PARTLY_INSIDE, index+1, cli_errors[CLI_OBJECTS_PARTLY_INSIDE], sliced_info); flush_and_exit(CLI_OBJECTS_PARTLY_INSIDE); } else if (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 ((max_triangle_count_per_plate != 0) && (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; record_exit_reson(outfile_dir, CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT, index+1, cli_errors[CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT], sliced_info); 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(error) << "got error when validate: "<< err.string << std::endl; boost::nowide::cerr << err.string << std::endl; int validate_error; switch (err.type) { case STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE: validate_error = CLI_FILAMENT_NOT_MATCH_BED_TYPE; break; case STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP: validate_error = CLI_FILAMENTS_DIFFERENT_TEMP; break; case STRING_EXCEPT_OBJECT_COLLISION_IN_SEQ_PRINT: validate_error = CLI_OBJECT_COLLISION_IN_SEQ_PRINT; break; case STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT: validate_error = CLI_OBJECT_COLLISION_IN_LAYER_PRINT; break; default: validate_error = CLI_VALIDATE_ERROR; break; } record_exit_reson(outfile_dir, validate_error, index+1, cli_errors[validate_error], sliced_info); flush_and_exit(validate_error); } else if (!warning.string.empty()) { BOOST_LOG_TRIVIAL(warning) << "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; record_exit_reson(outfile_dir, CLI_NO_SUITABLE_OBJECTS, index+1, cli_errors[CLI_NO_SUITABLE_OBJECTS], sliced_info); flush_and_exit(CLI_NO_SUITABLE_OBJECTS); } else { if (pre_check && (partplate_list.get_plate_count() > 1)) //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); } } else { BOOST_LOG_TRIVIAL(info) << "set print's callback to default_status_callback."; print->set_status_callback(default_status_callback); } #else BOOST_LOG_TRIVIAL(info) << "set print's callback to default_status_callback."; print->set_status_callback(default_status_callback); #endif //check whether it is bbl printer std::string& printer_model_string = new_print_config.opt_string("printer_model", true); bool is_bbl_vendor_preset = false; if (!printer_model_string.empty()) { is_bbl_vendor_preset = (printer_model_string.compare(0, 9, "Bambu Lab") == 0); BOOST_LOG_TRIVIAL(info) << boost::format("printer_model_string: %1%, is_bbl_vendor_preset %2%")%printer_model_string %is_bbl_vendor_preset; } else { if (!new_printer_name.empty()) is_bbl_vendor_preset = (new_printer_name.compare(0, 9, "Bambu Lab") == 0); else if (!current_printer_system_name.empty()) is_bbl_vendor_preset = (current_printer_system_name.compare(0, 9, "Bambu Lab") == 0); BOOST_LOG_TRIVIAL(info) << boost::format("new_printer_name: %1%, current_printer_system_name %2%, is_bbl_vendor_preset %3%")%new_printer_name %current_printer_system_name %is_bbl_vendor_preset; } (dynamic_cast(print))->set_BBL_Printer(is_bbl_vendor_preset); //update information for brim const PrintConfig& print_config = print_fff->config(); Model::setExtruderParams(m_print_config, filament_count); Model::setPrintSpeedTable(m_print_config, print_config); 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) { std::string conflict_result = print_fff->get_conflict_string(); if (!conflict_result.empty()) { BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": found slicing result conflict!"<< std::endl; record_exit_reson(outfile_dir, CLI_GCODE_PATH_CONFLICTS, index+1, cli_errors[CLI_GCODE_PATH_CONFLICTS], sliced_info); flush_and_exit(CLI_GCODE_PATH_CONFLICTS); } //check the warnings if (!g_slicing_warnings.empty()) { for (unsigned int i = 0; i < g_slicing_warnings.size(); i++) { PrintBase::SlicingStatus& status = g_slicing_warnings[i]; if ((status.warning_step != -1) && (status.message_type != PrintStateBase::SlicingDefaultNotification)) { sliced_plate_info.warning_message = status.text; if (status.warning_level == PrintStateBase::WarningLevel::NON_CRITICAL) { BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": found slicing warnings: "<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 = print_fff->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; record_exit_reson(outfile_dir, 1, index+1, cli_errors[1], sliced_info); 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); record_exit_reson(outfile_dir, ret, index+1, cli_errors[ret], sliced_info); flush_and_exit(ret); } } end_time = (long long)Slic3r::Utils::get_current_time_utc(); sliced_plate_info.sliced_time = end_time - start_time; if (max_slicing_time_per_plate != 0) { long long time_cost = end_time - start_time; if (time_cost > max_slicing_time_per_plate) { sliced_plate_info.warning_message = (boost::format("plate %1%'s slice time %2% exceeds the limit %3%, return error.")%(index+1) %time_cost %max_slicing_time_per_plate).str(); BOOST_LOG_TRIVIAL(error) << sliced_plate_info.warning_message; sliced_info.sliced_plates.push_back(sliced_plate_info); record_exit_reson(outfile_dir, CLI_SLICING_TIME_EXCEEDS_LIMIT, index+1, cli_errors[CLI_SLICING_TIME_EXCEEDS_LIMIT], sliced_info); flush_and_exit(CLI_SLICING_TIME_EXCEEDS_LIMIT); } } sliced_info.sliced_plates.push_back(sliced_plate_info); } catch (const std::exception &ex) { BOOST_LOG_TRIVIAL(error) << "found slicing or export error for partplate "< 1)) pre_check = false; else finished = true; }//end for partplate #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { int plate_count = (plate_to_slice== 0)?partplate_list.get_plate_count():1; g_cli_callback_mgr.set_plate_info(0, plate_count); } #endif /* print.center = ! m_config.has("center") && ! m_config.has("align_xy") && ! m_config.opt_bool("dont_arrange"); print.set_model(model); // start chronometer typedef std::chrono::high_resolution_clock clock_; typedef std::chrono::duration > 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; record_exit_reson(outfile_dir, CLI_UNSUPPORTED_OPERATION, 0, cli_errors[CLI_UNSUPPORTED_OPERATION], sliced_info); flush_and_exit(CLI_UNSUPPORTED_OPERATION); } } global_begin_time = (long long)Slic3r::Utils::get_current_time_utc(); if (export_to_3mf) { //BBS: export as bbl 3mf 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 || regenerate_thumbnails; bool need_regenerate_top_thumbnail = oriented_or_arranged || regenerate_thumbnails; 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; plate_data->skipped_objects = plate_skipped_objects[i]; 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 && !regenerate_thumbnails && 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 { if (regenerate_thumbnails) { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file %2% cleared, need to regenerate")%(i+1) %plate_data->thumbnail_file; plate_data->thumbnail_file.clear(); } else BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file exists, no need to regenerate")%(i+1); } } else { if (regenerate_thumbnails) plate_data->plate_thumbnail.reset(); if (!skip_this_plate) { need_create_thumbnail_group = true; } } if (plate_data->top_file.empty() || plate_data->pick_file.empty()) { if (!regenerate_thumbnails && (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 { if (regenerate_thumbnails) { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_thumbnail file %2% cleared, need to regenerate")%(i+1) %plate_data->top_file; plate_data->top_file.clear(); plate_data->pick_file.clear(); } 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("#FFFFFFFF"); std::vector> colors_out(colors.size()); unsigned char rgb_color[4] = {}; for (const std::string& color : colors) { Slic3r::GUI::BitmapCache::parse_color4(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, float(rgb_color[3]) / 255.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(); else obj_extruder_id = 1; 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) volume_extruder_id = (dynamic_cast(option))->getInt(); else volume_extruder_id = obj_extruder_id; BOOST_LOG_TRIVIAL(debug) << boost::format("volume %1%'s extruder_id %2%")%volume_idx %volume_extruder_id; //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(volume_extruder_id - 1):"#00FF00FF"; BOOST_LOG_TRIVIAL(debug) << boost::format("volume %1%'s color %2%")%volume_idx %color; unsigned char rgb_color[4] = {}; Slic3r::GUI::BitmapCache::parse_color4(color, rgb_color); 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] = float(rgb_color[3]) / 255.f; glvolume_collection.volumes.back()->set_render_color( new_color[0], new_color[1], new_color[2], new_color[3]); 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("thumbnail"); 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; int dec_ret = decode_png_to_thumbnail(plate_data->thumbnail_file, plate_data->plate_thumbnail); if (!dec_ret) { BOOST_LOG_TRIVIAL(info) << boost::format("decode png to mem sucess."); } else { BOOST_LOG_TRIVIAL(warning) << boost::format("decode png to mem failed."); } } } 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("Line %1%: use previous thumbnails, no need to regenerate")%__LINE__; 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(); } else if (!plate_data->plate_thumbnail.is_valid() && !plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))) { BOOST_LOG_TRIVIAL(info) << boost::format("no need to generate: plate %1% has a valid thumbnail %2% extracted from 3mf, convert to data")%(i+1) %plate_data->thumbnail_file; int dec_ret = decode_png_to_thumbnail(plate_data->thumbnail_file, plate_data->plate_thumbnail); if (!dec_ret) { BOOST_LOG_TRIVIAL(info) << boost::format("decode png to mem sucess."); need_create_thumbnail_group = true; } else { BOOST_LOG_TRIVIAL(warning) << boost::format("decode png to mem failed."); } } } for (int i = 0; i < partplate_list.get_plate_count(); i++) { PlateData *plate_data = plate_data_list[i]; Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); 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); 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, minimum_save, plate_to_slice - 1)) { release_PlateData_list(plate_data_list); record_exit_reson(outfile_dir, CLI_EXPORT_3MF_ERROR, 0, cli_errors[CLI_EXPORT_3MF_ERROR], sliced_info); 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; global_current_time = (long long)Slic3r::Utils::get_current_time_utc(); sliced_info.export_time = (size_t) (global_current_time - global_begin_time); //record the duplicate here if (duplicate_count > 0) { record_key_values["sliced_count"] = std::to_string(duplicate_count+1); record_exit_reson(outfile_dir, 0, plate_to_slice, cli_errors[0], sliced_info, record_key_values); } else { record_exit_reson(outfile_dir, 0, plate_to_slice, cli_errors[0], sliced_info); } 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 = true; 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); if (success) BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl; else { boost::nowide::cerr << "Model export to " << path << " failed" << std::endl; return false; } break; case IO::STL: { unsigned int index = 1; for (ModelObject* model_object : model.objects) { const std::string path = this->output_filepath(*model_object, index++, format); success = Slic3r::store_stl(path.c_str(), model_object, true); if (success) BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl; else { boost::nowide::cerr << "Model export to " << path << " failed" << std::endl; return false; } } 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; } } 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, bool minimum_save, int plate_to_export) { //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|SaveStrategy::ShareMesh; store_params.export_plate_idx = plate_to_export; if (minimum_save) store_params.strategy = store_params.strategy | SaveStrategy::SkipModel; 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("outputdir"); 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(); } std::string CLI::output_filepath(const ModelObject &object, unsigned int index, IO::ExportFormat format) const { std::string ext, subdir, file_name, output_path; switch (format) { case IO::AMF: ext = ".zip.amf"; subdir = "amf"; break; case IO::OBJ: ext = ".obj"; subdir = "obj"; break; case IO::STL: ext = ".stl"; subdir = "stl"; break; case IO::TMF: ext = ".3mf"; subdir = "3mf"; break; default: assert(false); break; }; // use --outputdir when available file_name = object.name.empty()?object.input_file:object.name; file_name = "obj_"+std::to_string(index)+"_"+file_name; size_t pos = file_name.find_last_of(ext), ext_pos = file_name.size() - 1; if (pos != ext_pos) file_name += ext; BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ": file_name="< //#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); SET_DEFULTER_HANDLER(); #endif std::set_new_handler([]() { int smallSize = 10 * 1024 * 1024; int *test_apply = (int *) malloc(smallSize); if (test_apply == NULL) { throw std::bad_alloc(); } else { free(test_apply); int *a = nullptr; *a = 0; } }); // 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 */