#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 "libslic3r/ObjColorUtils.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 "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GuiColor.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;*/ #define MAX_CLONEABLE_SIZE 512 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_MODIFIED_PARAMS_TO_PRINTER, "You cannot change the Printable Area, Printable Height, and Exclude Area in Printer Settings."}, {CLI_FILE_VERSION_NOT_SUPPORTED, "Unsupported 3MF version. Please make sure the 3MF file was created with the official version of Bambu Studio, not a beta version."}, {CLI_NO_SUITABLE_OBJECTS, "One of the plate is empty or has no object fully inside it. Please check that the 3mf contains no empty plate 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_SPIRAL_MODE_INVALID_PARAMS, "Some slicing parameters cannot work with Spiral Vase mode. Please solve the issue 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 sliced_time_with_cache {0}; size_t make_perimeters_time {0}; size_t infill_time {0}; size_t generate_support_material_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; std::vector upward_machines; std::vector downward_machines; }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}; snprintf(pipe_message, PIPE_BUFFER_SIZE, "%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="<= 50) { BOOST_LOG_TRIVIAL(warning) << boost::format("reach max retry_count, failed to open pipe"); return false; } boost::this_thread::sleep(boost::posix_time::milliseconds(20)); m_pipe_fd = open(pipe_name.c_str(),O_WRONLY|O_NONBLOCK); } std::unique_lock 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 if (sliced_info.downward_machines.size() > 0) j["downward_compatible_machine"] = sliced_info.downward_machines; if (sliced_info.upward_machines.size() > 0) j["upward_compatible_machine"] = sliced_info.upward_machines; 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["sliced_time_with_cache"] = sliced_info.sliced_plates[index].sliced_time_with_cache; plate_json["make_perimeters_time"] = sliced_info.sliced_plates[index].make_perimeters_time; plate_json["infill_time"] = sliced_info.sliced_plates[index].infill_time; plate_json["generate_support_material_time"] = sliced_info.sliced_plates[index].generate_support_material_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 " <& key_values) { json j; CNumericLocalesSetter locales_setter; BOOST_LOG_TRIVIAL(debug) << __FUNCTION__<< ": begin to parse "<> j; ifs.close(); //parse the json elements for (auto it = j.begin(); it != j.end(); it++) { if (boost::iequals(it.key(),BBL_JSON_KEY_MODEL_ID)) { key_values.emplace(BBL_JSON_KEY_MODEL_ID, it.value()); } else if (boost::iequals(it.key(), BBL_JSON_KEY_NAME)) { key_values.emplace(BBL_JSON_KEY_NAME, it.value()); } } } catch (const std::ifstream::failure &err) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "< gcodes_key_set = {"filament_end_gcode", "filament_start_gcode", "change_filament_gcode", "layer_change_gcode", "machine_end_gcode", "machine_pause_gcode", "machine_start_gcode", "template_custom_gcode", "printing_by_object_gcode", "before_layer_change_gcode", "time_lapse_gcode"}; static void load_default_gcodes_to_config(DynamicPrintConfig& config, Preset::Type type) { if (config.size() == 0) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", empty config, return directly"; return; } //add those empty gcodes by default if (type == Preset::TYPE_PRINTER) { std::string change_filament_gcode = config.option("change_filament_gcode", true)->value; BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", change_filament_gcode: "<< change_filament_gcode; ConfigOptionString* layer_change_gcode_opt = config.option("layer_change_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", layer_change_gcode: "<value; ConfigOptionString* machine_end_gcode_opt = config.option("machine_end_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", machine_end_gcode: "<value; ConfigOptionString* machine_pause_gcode_opt = config.option("machine_pause_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", machine_pause_gcode: "<value; ConfigOptionString* machine_start_gcode_opt = config.option("machine_start_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", machine_start_gcode: "<value; ConfigOptionString* template_custom_gcode_opt = config.option("template_custom_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", template_custom_gcode: "<value; ConfigOptionString* printing_by_object_gcode_opt = config.option("printing_by_object_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", printing_by_object_gcode: "<value; ConfigOptionString* before_layer_change_gcode_opt = config.option("before_layer_change_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", before_layer_change_gcode: "<value; ConfigOptionString* timeplase_gcode_opt = config.option("time_lapse_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", time_lapse_gcode: "<value; } else if (type == Preset::TYPE_FILAMENT) { std::vector& filament_start_gcodes = config.option("filament_start_gcode", true)->values; if (filament_start_gcodes.empty()) { filament_start_gcodes.resize(1, std::string()); BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", set filament_start_gcodes to empty"; } else { BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", filament_start_gcodes: "<& filament_end_gcodes = config.option("filament_end_gcode", true)->values; if (filament_end_gcodes.empty()) { filament_end_gcodes.resize(1, std::string()); BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ", set filament_end_gcode to empty"; } else { BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", filament_end_gcode: "< &assemble_plate_info_list) { int ret = 0; boost::filesystem::path directory_path(config_file); BOOST_LOG_TRIVIAL(info) << boost::format("%1% enter, file %2%")%__FUNCTION__ % config_file; if (!fs::exists(directory_path)) { BOOST_LOG_TRIVIAL(error) << boost::format("directory %1% not exist.")%config_file; return CLI_FILE_NOTFOUND; } try { json root_json; boost::nowide::ifstream ifs(config_file); ifs >> root_json; ifs.close(); int plate_count = root_json[JSON_ASSEMPLE_PLATES].size(); if ((plate_count <= 0) || (plate_count > MAX_PLATE_COUNT)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": invalid plate count %1%")%plate_count; return CLI_CONFIG_FILE_ERROR; } assemble_plate_info_list.resize(plate_count); for (int plate_index = 0; plate_index < plate_count; plate_index++) { assemble_plate_info_t &assemble_plate = assemble_plate_info_list[plate_index]; const json& plate_json = root_json[JSON_ASSEMPLE_PLATES][plate_index]; assemble_plate.plate_name = plate_json[JSON_ASSEMPLE_PLATE_NAME]; assemble_plate.need_arrange = plate_json[JSON_ASSEMPLE_PLATE_NEED_ARRANGE]; if (plate_json.contains(JSON_ASSEMPLE_PLATE_PARAMS)) { assemble_plate.plate_params = plate_json[JSON_ASSEMPLE_PLATE_PARAMS].get>(); BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, has %2% plate params") % (plate_index + 1) % assemble_plate.plate_params.size(); } int object_count = plate_json[JSON_ASSEMPLE_OBJECTS].size(); if (object_count <= 0) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": invalid object count %1% in plate %2%")%object_count %(plate_index+1); return CLI_CONFIG_FILE_ERROR; } assemble_plate.assemble_obj_list.resize(object_count); for (int object_index = 0; object_index < object_count; object_index++) { assemble_object_info_t& assemble_object = assemble_plate.assemble_obj_list[object_index]; const json& object_json = plate_json[JSON_ASSEMPLE_OBJECTS][object_index]; assemble_object.path = object_json[JSON_ASSEMPLE_OBJECT_PATH]; assemble_object.count = object_json[JSON_ASSEMPLE_OBJECT_COUNT]; if (assemble_object.count <= 0) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": invalid object clone count %1% in plate %2% Object %3%") % assemble_object.count % (plate_index + 1) % assemble_object.path; return CLI_CONFIG_FILE_ERROR; } assemble_object.filaments = object_json.at(JSON_ASSEMPLE_OBJECT_FILAMENTS).get>(); if ((assemble_object.filaments.size() > 0) && (assemble_object.filaments.size() != assemble_object.count) && (assemble_object.filaments.size() != 1)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s filaments count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.filaments.size() % assemble_object.count; return CLI_CONFIG_FILE_ERROR; } if (object_json.contains(JSON_ASSEMPLE_OBJECT_ASSEMBLE_INDEX)) { assemble_object.assemble_index = object_json[JSON_ASSEMPLE_OBJECT_ASSEMBLE_INDEX].get>(); if ((assemble_object.assemble_index.size() > 0) && (assemble_object.assemble_index.size() != assemble_object.count) && (assemble_object.assemble_index.size() != 1)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s assemble_index count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.assemble_index.size() % assemble_object.count; return CLI_CONFIG_FILE_ERROR; } } if (object_json.contains(JSON_ASSEMPLE_OBJECT_POS_X)) { assemble_object.pos_x = object_json[JSON_ASSEMPLE_OBJECT_POS_X].get>(); if ((assemble_object.pos_x.size() > 0) && (assemble_object.pos_x.size() != assemble_object.count) && (assemble_object.pos_x.size() != 1)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s pos_x count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.pos_x.size() % assemble_object.count; return CLI_CONFIG_FILE_ERROR; } } if (object_json.contains(JSON_ASSEMPLE_OBJECT_POS_Y)) { assemble_object.pos_y = object_json[JSON_ASSEMPLE_OBJECT_POS_Y].get>(); if ((assemble_object.pos_y.size() > 0) && (assemble_object.pos_y.size() != assemble_object.count) && (assemble_object.pos_y.size() != 1)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s pos_y count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.pos_y.size() % assemble_object.count; return CLI_CONFIG_FILE_ERROR; } } if (object_json.contains(JSON_ASSEMPLE_OBJECT_POS_Z)) { assemble_object.pos_z = object_json[JSON_ASSEMPLE_OBJECT_POS_Z].get>(); if ((assemble_object.pos_z.size() > 0) && (assemble_object.pos_z.size() != assemble_object.count) && (assemble_object.pos_z.size() != 1)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s pos_z count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.pos_z.size() % assemble_object.count; return CLI_CONFIG_FILE_ERROR; } } if (object_json.contains(JSON_ASSEMPLE_OBJECT_PRINT_PARAMS)) { assemble_object.print_params = object_json[JSON_ASSEMPLE_OBJECT_PRINT_PARAMS].get>(); BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, object %2% has %3% print params") % (plate_index + 1) %assemble_object.path % assemble_object.print_params.size(); } if (object_json.contains(JSON_ASSEMPLE_OBJECT_HEIGHT_RANGES)) { json height_range_json = object_json[JSON_ASSEMPLE_OBJECT_HEIGHT_RANGES]; int range_count = height_range_json.size(); BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, object %2% has %3% height ranges") % (plate_index + 1) %assemble_object.path % range_count; assemble_object.height_ranges.resize(range_count); for (int range_index = 0; range_index < range_count; range_index++) { height_range_info_t& height_range = assemble_object.height_ranges[range_index]; height_range.min_z = height_range_json[range_index][JSON_ASSEMPLE_OBJECT_MIN_Z]; height_range.max_z = height_range_json[range_index][JSON_ASSEMPLE_OBJECT_MAX_Z]; height_range.range_params = height_range_json[range_index][JSON_ASSEMPLE_OBJECT_RANGE_PARAMS].get>(); } } } if (plate_json.contains(JSON_ASSEMPLE_ASSEMBLE_PARAMS)) { json assemble_params_json = plate_json[JSON_ASSEMPLE_ASSEMBLE_PARAMS]; int assemble_count = assemble_params_json.size(); for (int i = 0; i < assemble_count; i++) { assembled_param_info_t assembled_param; int assemble_index = assemble_params_json[i][JSON_ASSEMPLE_OBJECT_ASSEMBLE_INDEX]; if (assemble_params_json[i].contains(JSON_ASSEMPLE_OBJECT_PRINT_PARAMS)) { assembled_param.print_params = assemble_params_json[i][JSON_ASSEMPLE_OBJECT_PRINT_PARAMS].get>(); BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, assemble object %2% has %3% print params") % (plate_index + 1) %i % assembled_param.print_params.size(); } if (assemble_params_json[i].contains(JSON_ASSEMPLE_OBJECT_HEIGHT_RANGES)) { json height_range_json = assemble_params_json[i][JSON_ASSEMPLE_OBJECT_HEIGHT_RANGES]; int range_count = height_range_json.size(); BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, assemble object %2% has %3% height ranges") % (plate_index + 1) %i % range_count; assembled_param.height_ranges.resize(range_count); for (int range_index = 0; range_index < range_count; range_index++) { height_range_info_t& height_range = assembled_param.height_ranges[range_index]; height_range.min_z = height_range_json[range_index][JSON_ASSEMPLE_OBJECT_MIN_Z]; height_range.max_z = height_range_json[range_index][JSON_ASSEMPLE_OBJECT_MAX_Z]; height_range.range_params = height_range_json[range_index][JSON_ASSEMPLE_OBJECT_RANGE_PARAMS].get>(); } } assemble_plate.assembled_param_list.emplace(assemble_index, std::move(assembled_param)); } BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, has %2% plate params") % (plate_index + 1) % assemble_plate.plate_params.size(); } } } catch(std::exception &err) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse file "<config.assign_config(ori_object->config.get()); } else new_object = iter->second; for (auto volume : ori_object->volumes) { ModelVolume* new_volume = new_object->add_volume(*volume); // set extruder id new_volume->config.set_key_value("extruder", new ConfigOptionInt(ori_object->config.extruder())); } BOOST_LOG_TRIVIAL(debug) << boost::format("assemble_index %1%, name %2%, merged to new model %3%") % assemble_index % ori_object->name % new_object->name; } else { ModelObject* new_object = model.add_object(*ori_object); assemble_plate_info.loaded_obj_list.emplace_back(new_object); BOOST_LOG_TRIVIAL(debug) << boost::format("assemble_index %1%, name %2%, no need to merge, copy to new model") % assemble_index % ori_object->name; } } bool convert_obj_cluster_colors(std::vector& input_colors, std::vector& all_colours, int max_filament_count, std::vector& output_filament_ids, int& first_filament_id) { using namespace Slic3r::GUI; BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, got original input obj colors %3%")%__FUNCTION__ %__LINE__ %input_colors.size(); if (input_colors.size() > 0) { std::vector cluster_colors; std::vector cluster_labels; char cluster_number = -1; obj_color_deal_algo(input_colors, cluster_colors, cluster_labels, cluster_number); std::vector cluster_color_maps; BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, after obj_color_deal_algo, cluster_colors size %3%, all_colours size %4%, max_filament_count=%5%")%__FUNCTION__ %__LINE__%cluster_colors.size() %all_colours.size() %max_filament_count; cluster_color_maps.resize(cluster_colors.size(), 1); int init_size = all_colours.size(); first_filament_id = max_filament_count; for (size_t i = 0; i < cluster_colors.size(); i++) { auto previous_color = std::find(all_colours.begin(), all_colours.end(), cluster_colors[i]); if (previous_color != all_colours.end()) { cluster_color_maps[i] = previous_color - all_colours.begin() + 1; BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, cluster color index %3% RGBA {%4%,%5%,%6%,%7%} found same color before, id %8%") %__FUNCTION__ %__LINE__%(i+1) %cluster_colors[i][0] %cluster_colors[i][1] %cluster_colors[i][2] %cluster_colors[i][3] %cluster_color_maps[i] ; } else { if ((init_size + i + 1) <= max_filament_count) { all_colours.push_back(cluster_colors[i]); cluster_color_maps[i] = all_colours.size(); BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, cluster color index %3% RGBA {%4%,%5%,%6%,%7%} directly inserted, id %8%") %__FUNCTION__ %__LINE__%(i+1) %cluster_colors[i][0] %cluster_colors[i][1] %cluster_colors[i][2] %cluster_colors[i][3] %cluster_color_maps[i] ; } else { std::vector color_dists; color_dists.resize(max_filament_count); for (size_t j = 0; j < max_filament_count; j++) { color_dists[j].distance = calc_color_distance(cluster_colors[i], all_colours[j]); color_dists[j].id = j + 1; } std::sort(color_dists.begin(), color_dists.end(), [](ColorDistValue &a, ColorDistValue &b) { return a.distance < b.distance; }); cluster_color_maps[i] = color_dists[0].id; BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, color size reaches to max, cluster color index %3% RGBA {%4%,%5%,%6%,%7%} mapped to id %8%") %__FUNCTION__ %__LINE__%(i+1) %cluster_colors[i][0] %cluster_colors[i][1] %cluster_colors[i][2] %cluster_colors[i][3] %cluster_color_maps[i] ; } } if (cluster_color_maps[i] < first_filament_id) first_filament_id = cluster_color_maps[i]; } //3.generate filament_ids auto input_colors_size = input_colors.size(); output_filament_ids.resize(input_colors_size); for (size_t i = 0; i < input_colors_size; i++) { int label = cluster_labels[i]; output_filament_ids[i] = cluster_color_maps[label]; } BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, all_colours size changes to %3%, first_filament_id = %4%")%__FUNCTION__ %__LINE__%all_colours.size() %first_filament_id; return true; } return false; } #ifdef _WIN32 #define DIR_SEPARATOR '\\' #else #define DIR_SEPARATOR '/' #endif static int construct_assemble_list(std::vector &assemble_plate_info_list, Model &model, PlateDataPtrs &plate_list, std::vector& all_colours) { int ret = 0; int plate_count = assemble_plate_info_list.size(); ConfigSubstitutionContext config_substitutions(ForwardCompatibilitySubstitutionRule::Enable); Model temp_model; const int max_filament_count = size_t(EnforcerBlockerType::ExtruderMax); plate_list.resize(plate_count); for (int index = 0; index < plate_count; index++) { //each plate has its dependent assemble list std::map merged_objects; std::set used_filaments; //std::map to_merge_objects; assemble_plate_info_t& assemble_plate_info = assemble_plate_info_list[index]; int object_count = assemble_plate_info.assemble_obj_list.size(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Plate %1%, name %2%, obj count %3%, plate params count %4%") % (index + 1) %assemble_plate_info.plate_name %object_count %assemble_plate_info.plate_params.size(); PlateData* plate_data = new PlateData(); plate_list[index] = plate_data; plate_data->plate_name = assemble_plate_info.plate_name; plate_data->plate_index = index; if (!assemble_plate_info.plate_params.empty()) { for (auto plate_iter = assemble_plate_info.plate_params.begin(); plate_iter != assemble_plate_info.plate_params.end(); plate_iter++) { plate_data->config.set_deserialize(plate_iter->first, plate_iter->second, config_substitutions); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": Plate %1%, key %2%, value %3%") % (index + 1) % plate_iter->first % plate_iter->second; } } //construct the object list for (size_t obj_index = 0; obj_index < object_count; obj_index++) { assemble_object_info_t& assemble_object = assemble_plate_info.assemble_obj_list[obj_index]; std::string object_name; std::string object_1_name; ModelObject* object = nullptr; TriangleMesh mesh; bool skip_filament = false; boost::filesystem::path object_path(assemble_object.path); if (!fs::exists(object_path)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": directory %1% not exist in plate %2%") % assemble_object.path % (index + 1); return CLI_FILE_NOTFOUND; } const char* path_str = assemble_object.path.c_str(); const char* last_slash = strrchr(path_str, DIR_SEPARATOR); object_name.assign((last_slash == nullptr) ? path_str : last_slash + 1); if (boost::algorithm::iends_with(assemble_object.path, ".stl")) { if (!mesh.ReadSTLFile(path_str, true, nullptr)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": failed to read stl file from %1%, plate index %2%, object index %3%") % assemble_object.path % (index+1) % (obj_index+1); return CLI_DATA_FILE_ERROR; } if (mesh.empty()) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found no mesh data from stl file %1%, plate index %2%, object index %3%") % assemble_object.path % (index + 1) % (obj_index + 1); return CLI_DATA_FILE_ERROR; } object_name.erase(object_name.end() - 4, object_name.end()); object_1_name = object_name + "_1"; object = temp_model.add_object(object_1_name.c_str(), path_str, std::move(mesh)); if (!object) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": add_object %1% for stl failed, plate index %2%, object index %3%") % object_1_name % (index + 1) % (obj_index + 1); return CLI_DATA_FILE_ERROR; } } else if (boost::algorithm::iends_with(assemble_object.path, ".obj")) { std::string message; ObjInfo obj_info; bool result = load_obj(path_str, &mesh, obj_info, message); if (!result) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": failed to read a valid mesh from obj file %1%, plate index %2%, object index %3%, error %4%") % assemble_object.path % (index + 1) % (obj_index + 1) % message; return CLI_DATA_FILE_ERROR; } object_name.erase(object_name.end() - 4, object_name.end()); object_1_name = object_name + "_1"; //process colors Model obj_temp_model; ModelObject* temp_object = obj_temp_model.add_object(object_1_name.c_str(), path_str, std::move(mesh)); if (!temp_object) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": add_object %1% for obj failed, plate index %2%, object index %3%") % object_1_name % (index + 1) % (obj_index + 1); return CLI_DATA_FILE_ERROR; } std::vector output_filament_ids; int first_filament_id; if (obj_info.vertex_colors.size() > 0) { convert_obj_cluster_colors(obj_info.vertex_colors, all_colours, max_filament_count, output_filament_ids, first_filament_id); if (output_filament_ids.size() > 0) { unsigned char first_eid = (unsigned char)first_filament_id; result = Model::obj_import_vertex_color_deal(output_filament_ids, first_eid, & obj_temp_model); } skip_filament = true; } else if (obj_info.face_colors.size() > 0 && obj_info.has_uv_png == false) { // mtl file convert_obj_cluster_colors(obj_info.face_colors, all_colours, max_filament_count, output_filament_ids, first_filament_id); if (output_filament_ids.size() > 0) { unsigned char first_eid = (unsigned char)first_filament_id; result = Model::obj_import_face_color_deal(output_filament_ids, first_eid, & obj_temp_model); } skip_filament = true; } if (!result) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": failed to convert colors for %1%, plate index %2%, object index %3%, error %4%") % assemble_object.path % (index + 1) % (obj_index + 1) % message; return CLI_DATA_FILE_ERROR; } object = temp_model.add_object(*temp_object); if (!object) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": add_object %1% for stl failed, plate index %2%, object index %3%") % object_1_name % (index + 1) % (obj_index + 1); return CLI_DATA_FILE_ERROR; } obj_temp_model.clear_objects(); obj_temp_model.clear_materials(); } else { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": unsupported file %1%, plate index %2%, object index %3%") % assemble_object.path % (index + 1) % (obj_index + 1); return CLI_INVALID_PARAMS; } if (!skip_filament) { object->config.set_key_value("extruder", new ConfigOptionInt(assemble_object.filaments[0])); used_filaments.emplace(assemble_object.filaments[0]); } else { assemble_object.filaments[0] = 0; for (const ModelVolume* mv : object->volumes) { std::vector volume_extruders = mv->get_extruders(); used_filaments.insert(volume_extruders.begin(), volume_extruders.end()); } } if (!assemble_object.print_params.empty()) { for (auto param_iter = assemble_object.print_params.begin(); param_iter != assemble_object.print_params.end(); param_iter++) { object->config.set_deserialize(param_iter->first, param_iter->second, config_substitutions); BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, object %2% key %3%, value %4%") % (index + 1) % object_1_name % param_iter->first % param_iter->second; } } if (!assemble_object.height_ranges.empty()) { for (int range_index = 0; range_index < assemble_object.height_ranges.size(); range_index++) { height_range_info_t& range = assemble_object.height_ranges[range_index]; DynamicPrintConfig range_config; for (auto range_config_iter = range.range_params.begin(); range_config_iter != range.range_params.end(); range_config_iter++) { range_config.set_deserialize(range_config_iter->first, range_config_iter->second, config_substitutions); BOOST_LOG_TRIVIAL(debug) << boost::format("object %1%, height range %2% key %3%, value %4%") % object_1_name % range_index % range_config_iter->first % range_config_iter->second; } object->layer_config_ranges[{ range.min_z, range.max_z }].assign_config(std::move(range_config)); } } if (assemble_object.pos_x.empty()) assemble_object.pos_x.resize(1, 0.f); if (assemble_object.pos_y.empty()) assemble_object.pos_y.resize(1, 0.f); if (assemble_object.pos_z.empty()) assemble_object.pos_z.resize(1, 0.f); if (assemble_object.assemble_index.empty()) assemble_object.assemble_index.resize(1, 0); object->translate(assemble_object.pos_x[0], assemble_object.pos_y[0], assemble_object.pos_z[0]); merge_or_add_object(assemble_plate_info, model, assemble_object.assemble_index[0], merged_objects, object); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": object %1%, name %2%, pos_x %3% pos_y %4%, pos_z %5%, filament %6%, assemble_index %7%") %obj_index %object->name %assemble_object.pos_x[0] %assemble_object.pos_y[0] %assemble_object.pos_z[0] %assemble_object.filaments[0] %assemble_object.assemble_index[0]; for (size_t copy_index = 1; copy_index < assemble_object.count; copy_index++) { int array_index = copy_index; ModelObject* copy_obj = temp_model.add_object(*object); copy_obj->name = object_name + "_" + std::to_string(copy_index + 1); if (copy_index >= assemble_object.pos_x.size()) array_index = 0; copy_obj->translate(assemble_object.pos_x[array_index], assemble_object.pos_y[array_index], assemble_object.pos_z[array_index]); if (copy_index < assemble_object.filaments.size()) array_index = copy_index; else array_index = 0; if (!skip_filament) { copy_obj->config.set_key_value("extruder", new ConfigOptionInt(assemble_object.filaments[array_index])); used_filaments.emplace(assemble_object.filaments[array_index]); } else { assemble_object.filaments[array_index] = 0; } if (copy_index < assemble_object.assemble_index.size()) array_index = copy_index; else array_index = 0; merge_or_add_object(assemble_plate_info, model, assemble_object.assemble_index[array_index], merged_objects, copy_obj); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": cloned object %1%, name %2%, pos_x %3% pos_y %4%, pos_z %5%") %copy_index %object->name %assemble_object.pos_x[array_index] %assemble_object.pos_y[array_index] %assemble_object.pos_z[array_index]; } } size_t assemble_count = merged_objects.size(); if ((assemble_count > 0) && (assemble_plate_info.assembled_param_list.size() > 0)) { for (auto& iter : merged_objects) { ModelObject* assemble_obj = iter.second; int assemble_index = iter.first; auto assemble_iter = assemble_plate_info.assembled_param_list.find(assemble_index); if (assemble_iter != assemble_plate_info.assembled_param_list.end()) { assembled_param_info_t& assembled_param = assemble_iter->second; if (!assembled_param.print_params.empty()) { for (auto param_iter = assembled_param.print_params.begin(); param_iter != assembled_param.print_params.end(); param_iter++) { assemble_obj->config.set_deserialize(param_iter->first, param_iter->second, config_substitutions); BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, assemble object %2% key %3%, value %4%") % (index + 1) % assemble_obj->name % param_iter->first % param_iter->second; } } if (!assembled_param.height_ranges.empty()) { for (int range_index = 0; range_index < assembled_param.height_ranges.size(); range_index++) { height_range_info_t& range = assembled_param.height_ranges[range_index]; DynamicPrintConfig range_config; for (auto range_config_iter = range.range_params.begin(); range_config_iter != range.range_params.end(); range_config_iter++) { range_config.set_deserialize(range_config_iter->first, range_config_iter->second, config_substitutions); BOOST_LOG_TRIVIAL(debug) << boost::format("assenble object %1%, height range %2% key %3%, value %4%") % assemble_obj->name % range_index % range_config_iter->first % range_config_iter->second; } assemble_obj->layer_config_ranges[{ range.min_z, range.max_z }].assign_config(std::move(range_config)); } } } } } assemble_plate_info.filaments_count = used_filaments.size(); assemble_plate_info.assemble_obj_list.clear(); assemble_plate_info.assemble_obj_list.shrink_to_fit(); assemble_plate_info.plate_params.clear(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Plate %1%, used filaments %2%") % (index + 1) % assemble_plate_info.filaments_count; } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": has objects need to be merged, total plates %1%, total objects %2%") % plate_count % model.objects.size(); temp_model.clear_objects(); temp_model.clear_materials(); return ret; } static void load_downward_settings_list_from_config(std::string config_file, std::string printer_name, std::string printer_model, std::vector& downward_settings) { std::map printer_params; boost::filesystem::path directory_path(config_file); BOOST_LOG_TRIVIAL(info) << boost::format("%1%, will parse file %2% for printer mode %3%, printer name %4%")%__FUNCTION__ % config_file %printer_model %printer_name; if (!fs::exists(directory_path)) { BOOST_LOG_TRIVIAL(warning) << boost::format("file %1% not exist.")%config_file; } else { try { json root_json; boost::nowide::ifstream ifs(config_file); ifs >> root_json; ifs.close(); if (root_json.contains("printer")) { json printer_json = root_json["printer"]; if (!printer_model.empty() && printer_json.contains(printer_model)) { json printer_model_json = printer_json[printer_model]; if (printer_model_json.contains("downward_check")) { json downward_check_json = printer_model_json["downward_check"]; if (downward_check_json.contains(printer_name)) { downward_settings = downward_check_json[printer_name].get>(); BOOST_LOG_TRIVIAL(info) << boost::format("got %1% downward settings of %2% in cli_config.json")%downward_settings.size() %printer_name; } else { BOOST_LOG_TRIVIAL(info) << boost::format("can not find %1% in downward_check of %2% in cli_config.json")%printer_name %printer_model; } } else { BOOST_LOG_TRIVIAL(info) << boost::format("can not find downward_check for %1% in cli_config.json")%printer_model; } } else { BOOST_LOG_TRIVIAL(info) << boost::format("can not find printer_model %1% in the file")%printer_model; } } else { BOOST_LOG_TRIVIAL(warning) << boost::format("can not find key printer in the file"); } } catch (std::exception &err) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse file "<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); //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, filament_color_changed = false, downward_check = false; int current_printable_width, current_printable_depth, current_printable_height, shrink_to_new_bed = 0; 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::vector old_max_layer_height, old_min_layer_height; 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; const std::vector &uptodate_filaments = m_config.option("uptodate_filaments", true)->values; std::vector downward_settings = m_config.option("downward_settings", true)->values; std::vector downward_compatible_machines; //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; ConfigOptionBool* downward_check_option = m_config.option("downward_check"); if (downward_check_option) downward_check = downward_check_option->value; bool start_gui = m_actions.empty() && !downward_check; 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_milliseconds_time_utc(); BOOST_LOG_TRIVIAL(warning) << boost::format("cli mode, Current BambuStudio Version %1%")%SLIC3R_VERSION; //BBS: add plate data related logic PlateDataPtrs plate_data_src; std::vector plate_obj_size_infos; 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, enable_timelapse = false; bool allow_rotations = true, skip_modified_gcodes = false, avoid_extrusion_cali_region = false, skip_useless_pick = false, allow_newer_file = 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_model_id, current_printer_model, printer_model;//, 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, makerlab_name, makerlab_version, different_process_setting; const std::vector &metadata_name = m_config.option("metadata_name", true)->values; const std::vector &metadata_value = m_config.option("metadata_value", true)->values; if (metadata_name.size() != metadata_value.size()) { BOOST_LOG_TRIVIAL(error) << boost::format("metadata_name should be the same size with metadata_value"); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } // 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; ConfigOptionBool* enable_timelapse_option = m_config.option("enable_timelapse"); if (enable_timelapse_option) enable_timelapse = enable_timelapse_option->value; ConfigOptionBool* allow_rotations_option = m_config.option("allow_rotations"); if (allow_rotations_option) allow_rotations = allow_rotations_option->value; ConfigOptionBool* skip_modified_gcodes_option = m_config.option("skip_modified_gcodes"); if (skip_modified_gcodes_option) skip_modified_gcodes = skip_modified_gcodes_option->value; ConfigOptionBool* skip_useless_picks_option = m_config.option("skip_useless_pick"); if (skip_useless_picks_option) skip_useless_pick = skip_useless_picks_option->value; ConfigOptionBool* allow_newer_file_option = m_config.option("allow_newer_file"); if (allow_newer_file_option) allow_newer_file = allow_newer_file_option->value; ConfigOptionBool* avoid_extrusion_cali_region_option = m_config.option("avoid_extrusion_cali_region"); if (avoid_extrusion_cali_region_option) avoid_extrusion_cali_region = avoid_extrusion_cali_region_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 } } ConfigOptionString* makerlab_name_option = m_config.option("makerlab_name"); if (makerlab_name_option) makerlab_name = makerlab_name_option->value; ConfigOptionString* makerlab_version_option = m_config.option("makerlab_version"); if (makerlab_version_option) makerlab_version = makerlab_version_option->value; //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; std::string load_assemble_list; std::vector assemble_plate_info_list; ConfigOptionString* load_assemble_list_option = m_config.option("load_assemble_list"); if (load_assemble_list_option) load_assemble_list = load_assemble_list_option->value; bool allow_multicolor_oneplate = m_config.option("allow_multicolor_oneplate", true)->value; const std::vector loaded_filament_ids = m_config.option("load_filament_ids", true)->values; const std::vector clone_objects = m_config.option("clone_objects", true)->values; //when load objects from stl/obj, the total used filaments set std::set used_filament_set; BOOST_LOG_TRIVIAL(info) << boost::format("allow_multicolor_oneplate %1%, allow_rotations %2% skip_modified_gcodes %3% avoid_extrusion_cali_region %4% loaded_filament_ids size %5%, clone_objects size %6%, skip_useless_pick %7%, allow_newer_file %8%") %allow_multicolor_oneplate %allow_rotations %skip_modified_gcodes %avoid_extrusion_cali_region %loaded_filament_ids.size() %clone_objects.size() %skip_useless_pick %allow_newer_file; if (clone_objects.size() > 0) { if (clone_objects.size() != m_input_files.size()) { BOOST_LOG_TRIVIAL(error) << boost::format("clone_objects size %1% should be the same with input files size %2%")%clone_objects.size() %m_input_files.size(); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } else if (load_filaments.size() == 0) { BOOST_LOG_TRIVIAL(error) << boost::format("clone_objects should be used with load_filaments together"); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } } if (loaded_filament_ids.size() > 0) { if (loaded_filament_ids.size() != m_input_files.size()) { BOOST_LOG_TRIVIAL(error) << boost::format("loaded_filament_ids size %1% should be the same with input files size %2%")%loaded_filament_ids.size() %m_input_files.size(); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } else if (load_filaments.size() == 0) { BOOST_LOG_TRIVIAL(error) << boost::format("loaded_filament_ids should be used with load_filaments together"); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } } /*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; unsigned int input_index = 0; std::vector input_obj_colours; if (!load_assemble_list.empty() && (m_input_files.size() > 0)) { BOOST_LOG_TRIVIAL(error) << boost::format("load_assemble_list should not be used with input model files to load and should not be sued with transforms"); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } if (load_assemble_list.empty()) { 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) { if ((clone_objects.size() > 0) || (loaded_filament_ids.size() > 0)) { BOOST_LOG_TRIVIAL(error) << boost::format("can not load 3mf when set loaded_filament_ids or clone_objects"); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } 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 cli_ver = *Semver::parse(SLIC3R_VERSION); if (!allow_newer_file && ((cli_ver.maj() != file_version.maj()) || (cli_ver.min() < file_version.min()))){ BOOST_LOG_TRIVIAL(error) << boost::format("Version Check: File Version %1% not supported by current cli version %2%")%file_version.to_string() %SLIC3R_VERSION; record_exit_reson(outfile_dir, CLI_FILE_VERSION_NOT_SUPPORTED, 0, cli_errors[CLI_FILE_VERSION_NOT_SUPPORTED], sliced_info); flush_and_exit(CLI_FILE_VERSION_NOT_SUPPORTED); } 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_printer_model = config.option("printer_model", true)->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"); if (config.option("max_layer_height")) old_max_layer_height = config.option("max_layer_height")->values; if (config.option("min_layer_height")) old_min_layer_height = config.option("min_layer_height")->values; 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; int object_extruder_id = 0, clone_count = 1; if (loaded_filament_ids.size() > input_index) { if (loaded_filament_ids[input_index] > 0) { if (loaded_filament_ids[input_index] > load_filaments.size()) { BOOST_LOG_TRIVIAL(error) << boost::format("invalid filament_id %1% at index %2%, max %3%")%loaded_filament_ids[input_index] % (input_index + 1) %load_filaments.size(); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } object_extruder_id = loaded_filament_ids[input_index]; used_filament_set.emplace(object_extruder_id); } } if (clone_objects.size() > input_index) { if (clone_objects[input_index] > 0) { if (clone_objects[input_index] > MAX_CLONEABLE_SIZE) { BOOST_LOG_TRIVIAL(error) << boost::format("invalid clone count %1% at index %2%, max %3%")%clone_objects[input_index] % (input_index + 1) %MAX_CLONEABLE_SIZE; record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } clone_count = clone_objects[input_index]; } } //clone objects process if (clone_count > 1) { unsigned int object_count = model.objects.size(); for (unsigned int obj_index = 0; obj_index < object_count; obj_index++) { ModelObject* object = model.objects[obj_index]; for (unsigned int clone_index = 1; clone_index < clone_count; clone_index++) { ModelObject* newObj = model.add_object(*object); newObj->name = object->name +"_"+ std::to_string(clone_index+1); } object->name = object->name +"_"+ std::to_string(1); } } for (ModelObject* o : model.objects) { if (object_extruder_id != 0) { o->config.set_key_value("extruder", new ConfigOptionInt(object_extruder_id)); } //default not orient for all, if need to orient use the action //orients_requirement.insert(std::pair(o->id().id, false)); 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); input_index++; } 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)); } } else { //parse the json and assemble object here Model model; int ret = load_assemble_plate_list(load_assemble_list, assemble_plate_info_list); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } try { ret = construct_assemble_list(assemble_plate_info_list, model, plate_data_src, input_obj_colours); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } } catch (std::exception& e) { boost::nowide::cerr << construct_assemble_list << ": " << 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); } model.add_default_instances(); m_models.push_back(std::move(model)); } if (!is_bbl_3mf && plate_to_slice > 0) { BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: not support to slice plate %2%, reset to 0")%__LINE__ %plate_to_slice; plate_to_slice = 0; } //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); //get printer_model_id printer_model = config.option("printer_model", true)->value; if (!printer_model.empty()) { std::string printer_model_path = resources_dir() + "/profiles/BBL/machine_full/"+printer_model+".json"; if (boost::filesystem::exists(printer_model_path)) { std::map key_values; load_key_values_from_json(printer_model_path, key_values); if (key_values.find("model_id") != key_values.end()) { printer_model_id = key_values["model_id"]; BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, load printer_model_id %2% from current printer model %3%")%__LINE__ %printer_model_id %printer_model; } } } //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%, printer_model_id %5%")%file %config_name %config_from % new_printer_system_name %printer_model_id; } 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; if (!is_bbl_3mf && config.option("different_settings_to_system")) { std::vector diff_settings = config.option("different_settings_to_system")->values; different_process_setting = diff_settings[0]; } 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::set load_filaments_set; bool disable_wipe_tower_after_mapping = false; 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); load_filaments_set.emplace(config_name); 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); load_filaments_set.emplace(default_load_fila_name); } continue; } } //add logic for obj auto colors if (input_obj_colours.size() > 0) { BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, got input obj colors %3%")%__FUNCTION__ %__LINE__ %input_obj_colours.size(); int input_color_count = input_obj_colours.size(); if (load_filament_count == 0) { BOOST_LOG_TRIVIAL(error) << boost::format("filament config not loaded when loading colored obj"); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } //check filament_color from extra config ConfigOptionStrings *selected_filament_colors_option = m_extra_config.option("filament_colour"); if (selected_filament_colors_option) { BOOST_LOG_TRIVIAL(error) << boost::format("filament_colour should not be set when loading colored obj"); record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } if (load_filament_count < input_color_count) { int delta = input_color_count - load_filament_count; for (int index = 0; index < delta; index++) { load_filaments_id.push_back(load_filaments_id[0]); load_filaments_name.push_back(load_filaments_name[0]); load_filaments_config.push_back(load_filaments_config[0]); load_filaments_index.push_back(index+1+load_filament_count); load_filaments_inherit.push_back(load_filaments_inherit[0]); } load_filament_count = input_color_count; } selected_filament_colors_option = m_extra_config.option("filament_colour", true); std::vector& filament_colors = selected_filament_colors_option->values; filament_colors.resize(input_color_count); for (int index = 0; index < input_color_count; index++) { std::string color_string; int color[4]; color[0] = std::clamp((int) (input_obj_colours[index][0] * 255.f), 0, 255); color[1] = std::clamp((int) (input_obj_colours[index][1] * 255.f), 0, 255); color[2] = std::clamp((int) (input_obj_colours[index][2] * 255.f), 0, 255); color[3] = std::clamp((int) (input_obj_colours[index][3] * 255.f), 0, 255); std::stringstream stream; stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[0]; stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[1]; stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[2]; stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[3]; std::string result(stream.str()); filament_colors[index] = "#" + result; BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, index %3%, argb {%4%,%5%,%6%,%7%} to string %8%")%__FUNCTION__ %__LINE__ %(index+1) %color[0] %color[1] %color[2] %color[3] %filament_colors[index]; } } if (filament_count == 0) filament_count = load_filament_count; if (is_bbl_3mf && (load_filament_count > 0) && (load_filaments_set.size() == 1)) { disable_wipe_tower_after_mapping = true; BOOST_LOG_TRIVIAL(info) << boost::format("map all the filaments to the same one, load_filament_count %1%")%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); //get printer_model_id printer_model = config.option("printer_model", true)->value; if (!printer_model.empty()) { std::string printer_model_path = resources_dir() + "/profiles/BBL/machine_full/"+printer_model+".json"; if (boost::filesystem::exists(printer_model_path)) { std::map key_values; load_key_values_from_json(printer_model_path, key_values); if (key_values.find("model_id") != key_values.end()) { printer_model_id = key_values["model_id"]; BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, load printer_model_id %2% from current printer model %3%")%__LINE__ %printer_model_id %printer_model; } } } int orig_printable_width = 0, orig_printable_depth = 0, orig_printable_height = 0; Pointfs orig_printable_area; orig_printable_area = config.option("printable_area", true)->values; if (orig_printable_area.size() >= 4) { orig_printable_width = (int)(orig_printable_area[2].x() - orig_printable_area[0].x()); orig_printable_depth = (int)(orig_printable_area[2].y() - orig_printable_area[0].y()); } orig_printable_height = (int)(config.opt_float("printable_height")); BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, check printable size: old_printable_width=%2%, orig_printable_width=%3%, old_printable_depth=%4%, orig_printable_depth=%5%, old_printable_height=%6%, orig_printable_height=%7%") %__LINE__ %old_printable_width %orig_printable_width %old_printable_depth %orig_printable_depth %old_printable_height %orig_printable_height; if ((orig_printable_width > 0) && (orig_printable_depth > 0) && (orig_printable_height > 0)) { if ((old_printable_width > orig_printable_width) || (old_printable_depth > orig_printable_depth) || (old_printable_height > orig_printable_height)) { std::string error_str = (boost::format("Printer Settings: the printable size {%1%, %2%, %3%} exceeds the default size.")%old_printable_width %old_printable_depth %old_printable_height).str(); BOOST_LOG_TRIVIAL(error) << error_str; record_exit_reson(outfile_dir, CLI_MODIFIED_PARAMS_TO_PRINTER, 0, error_str, sliced_info); flush_and_exit(CLI_MODIFIED_PARAMS_TO_PRINTER); } } 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); //get printer_model_id printer_model = config.option("printer_model", true)->value; if (!printer_model.empty()) { std::string printer_model_path = resources_dir() + "/profiles/BBL/machine_full/"+printer_model+".json"; if (boost::filesystem::exists(printer_model_path)) { std::map key_values; load_key_values_from_json(printer_model_path, key_values); if (key_values.find("model_id") != key_values.end()) { printer_model_id = key_values["model_id"]; BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, load printer_model_id %2% from current printer model %3%")%__LINE__ %printer_model_id %printer_model; } } } 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; } //filament processes if (load_filament_count == 0) { if (uptodate_filaments.size() > 0) { if (uptodate_filaments.size() != (size_t)filament_count) { BOOST_LOG_TRIVIAL(error) << boost::format("uptodate_filaments size %1% not equal to filament_count %2% ")%uptodate_filaments.size() %filament_count; record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } for (int index = 0; index < filament_count; index ++) { std::string file = uptodate_filaments[index]; 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) { BOOST_LOG_TRIVIAL(error) << boost::format("load uptodate_filaments %1% fail, index %2%!")%file %index; 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_name != current_filaments_system_name[index]) { BOOST_LOG_TRIVIAL(error) << boost::format("wrong filament config file %1% loaded, current filament config name %2%, index %3%")%config_name %current_filaments_system_name[index] %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); } 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(index+1); load_filaments_inherit.push_back(config_name); BOOST_LOG_TRIVIAL(info) << boost::format("load a filament config %1% from file %2%, using uptodate_filaments index %3%")%config_name %file %index; } } else { current_index = 0; 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); } if (config_type != "filament") { BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-filaments") % config_type % system_filament_path; 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); } 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); load_filaments_inherit.push_back(config_name); BOOST_LOG_TRIVIAL(info) << boost::format("LINE %4%: load a filament config %1% from file %2%, index %3%")%config_name %system_filament_path %index %__LINE__; } } } } 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, machine_switch = 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)) { //set all printer to compatible process_compatible = true; machine_switch = true; BOOST_LOG_TRIVIAL(info) << boost::format("switch to new printers, set to compatible"); 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; BOOST_LOG_TRIVIAL(info) << boost::format("new printer is upward_compatible"); 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); } sliced_info.upward_machines = upward_compatible_printers; //create project embedded preset if needed Preset *new_preset = NULL; if (is_bbl_3mf && machine_switch) { //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, bool skip_gcodes = false) { const t_config_option_keys& config_keys = config.keys(); BOOST_LOG_TRIVIAL(info) << boost::format("update_full_config: config keys count %1%")%config_keys.size(); for (const t_config_option_key &opt_key : config_keys) { if (!update_all && !diff_key_sets.empty()) { std::set::iterator iter = diff_key_sets.find(opt_key); if ( iter != diff_key_sets.end()) { if (skip_gcodes && (gcodes_key_set.find(opt_key) != gcodes_key_set.end())) { diff_key_sets.erase(iter); BOOST_LOG_TRIVIAL(info) << boost::format("%1%, gcodes %2% modified, reset to default.")%__LINE__ %opt_key; } else { //uptodate, diff keys, continue BOOST_LOG_TRIVIAL(info) << boost::format("%1%, keep key %2%")%__LINE__ %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()); if (!is_bbl_3mf && !different_process_setting.empty()) { different_settings[0] = different_process_setting; } //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_settings: %2%")%different_keys_set.size() %different_settings[filament_count+1]; int ret; load_default_gcodes_to_config(load_machine_config, Preset::TYPE_PRINTER); if (new_printer_name.empty()) { int diff_keys_size = different_keys_set.size(); ret = update_full_config(m_print_config, load_machine_config, different_keys_set, false, skip_modified_gcodes); if (diff_keys_size != different_keys_set.size()) { //changed BOOST_LOG_TRIVIAL(info) << boost::format("new different key size %1%")%different_keys_set.size(); different_keys.clear(); for (std::set::iterator iter=different_keys_set.begin(); iter !=different_keys_set.end(); ++iter) different_keys.emplace_back(*iter); different_settings[filament_count+1] = Slic3r::escape_strings_cstyle(different_keys); } BOOST_LOG_TRIVIAL(info) << boost::format("no new printer, only update the different key, new different_settings: %1%")%different_settings[filament_count+1]; } 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, different_settings: %1%")%different_settings[filament_count+1]; if (new_printer_name != current_printer_name) { //printer safe check BOOST_LOG_TRIVIAL(info) << boost::format("check printer cli safe params, current_printer_name %1%, new_printer_name %2%, printer_model %3%")%current_printer_name %new_printer_name %printer_model; std::map printer_params; std::string cli_config_file = resources_dir() + "/profiles/BBL/cli_config.json"; boost::filesystem::path directory_path(cli_config_file); BOOST_LOG_TRIVIAL(info) << boost::format("line %1% , will parse file %2%")%__LINE__ % cli_config_file; if (!fs::exists(directory_path)) { BOOST_LOG_TRIVIAL(warning) << boost::format("file %1% not exist.")%cli_config_file; } else { try { json root_json; boost::nowide::ifstream ifs(cli_config_file); ifs >> root_json; ifs.close(); if (root_json.contains("printer")) { json printer_json = root_json["printer"]; if (!printer_model.empty() && printer_json.contains(printer_model)) { json printer_model_json = printer_json[printer_model]; if (printer_model_json.contains("machine_limits")) { json machine_limits_json = printer_model_json["machine_limits"]; printer_params = machine_limits_json.get>(); for (auto param_iter = printer_params.begin(); param_iter != printer_params.end(); param_iter++) { std::string key = param_iter->first; //replace "cli_safe" with "machine_max" key.replace(0, 8, "machine_max"); ConfigOptionFloats* option = m_print_config.option(key); if (option) { //de-serialize the values from param_iter->second, and do the compare here unsigned int array_count = option->size(); ConfigOptionFloats new_option; new_option.deserialize(param_iter->second); unsigned int new_array_count = new_option.size(); for (unsigned int index = 0; index < array_count; index++) { if ((index < new_array_count) && new_option.values[index] != 0.f && (new_option.values[index] < option->values[index])) { BOOST_LOG_TRIVIAL(info) << boost::format("set key %1% index %2%, from %3% to %4%") % key %index %option->values[index] % new_option.values[index]; option->values[index] = new_option.values[index]; } } } else BOOST_LOG_TRIVIAL(warning) << boost::format("can not find key %1% in config") %key; } } else { BOOST_LOG_TRIVIAL(info) << boost::format("can not find machine_limits for printer %1% in cli_config.json")%printer_model; } } else { BOOST_LOG_TRIVIAL(info) << boost::format("can not find key %1% in the file")%printer_model; } } else { BOOST_LOG_TRIVIAL(warning) << boost::format("can not find key printer in the file"); } } catch (std::exception &err) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse file "<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_settings: %2%")%different_keys_set.size() %different_settings[0]; int ret; load_default_gcodes_to_config(load_machine_config, Preset::TYPE_PRINT); if (new_process_name.empty()) { int diff_keys_size = different_keys_set.size(); ret = update_full_config(m_print_config, load_process_config, different_keys_set, false, skip_modified_gcodes); if (diff_keys_size != different_keys_set.size()) { //changed BOOST_LOG_TRIVIAL(info) << boost::format("new different key size %1%")%different_keys_set.size(); different_keys.clear(); for (std::set::iterator iter=different_keys_set.begin(); iter !=different_keys_set.end(); ++iter) different_keys.emplace_back(*iter); different_settings[0] = Slic3r::escape_strings_cstyle(different_keys); } BOOST_LOG_TRIVIAL(info) << boost::format("no new process, only update the different key, new different_settings: %1%")%different_settings[0]; } 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, different_settings: %1%")%different_settings[0]; } if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); } } if (machine_switch) { 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; load_default_gcodes_to_config(config, Preset::TYPE_FILAMENT); 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()); int diff_keys_size = different_keys_set.size(); BOOST_LOG_TRIVIAL(info) << boost::format("update filament %1%'s config to newest, different size %2%, name %3%, different_settings %4%") %filament_index%different_keys_set.size()%load_filaments_name[index] % different_settings[filament_index]; for (const t_config_option_key &opt_key : config.keys()) { if ((load_filament_count == 0) && !different_keys_set.empty()) { std::set::iterator iter = different_keys_set.find(opt_key); if ( iter != different_keys_set.end()) { if (skip_modified_gcodes && (gcodes_key_set.find(opt_key) != gcodes_key_set.end())) { different_keys_set.erase(iter); BOOST_LOG_TRIVIAL(info) << boost::format("%1%, filament %2%'s gcodes %3% modified, reset to default.")%__LINE__ %filament_index %opt_key; } else { //uptodate, diff keys, continue BOOST_LOG_TRIVIAL(info) << boost::format("%1%, keep key %2%")%__LINE__ %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); } } if (diff_keys_size != different_keys_set.size()) { //changed different_keys.clear(); for (std::set::iterator iter=different_keys_set.begin(); iter !=different_keys_set.end(); ++iter) different_keys.emplace_back(*iter); different_settings[filament_index] = Slic3r::escape_strings_cstyle(different_keys); BOOST_LOG_TRIVIAL(info) << boost::format("filament %1% new different key size %2%, different_settings %3%")%filament_index %different_keys_set.size() %different_settings[filament_index]; } } } //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 || (project_filament_colors_option->values.size() == 0)) && selected_filament_colors_option) { BOOST_LOG_TRIVIAL(info) << boost::format("initial project_filament_colors is null, create it due to filament_colour set in cli"); project_filament_colors_option = m_print_config.option("filament_colour", true); std::vector& project_filament_colors = project_filament_colors_option->values; project_filament_colors.resize(filament_count, "#FFFFFF"); } if (project_filament_colors_option && (selected_filament_colors_option || !m_print_config.option("flush_volumes_matrix"))) { 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"); if (disable_wipe_tower_after_mapping) { std::set filament_color_set; for (unsigned int color_index = 0; color_index 1) { disable_wipe_tower_after_mapping = false; BOOST_LOG_TRIVIAL(info) << boost::format("different filament colours, switch disable_wipe_tower_after_mapping back to false"); } else { BOOST_LOG_TRIVIAL(warning) << boost::format("only %1% filament colour, finally set disable_wipe_tower_after_mapping to true")%filament_color_set.size(); } } } 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)); const std::vector& min_flush_volumes = Slic3r::GUI::get_min_flush_volumes(m_print_config); 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); } { std::ostringstream volumes_str; std::copy(min_flush_volumes.begin(), min_flush_volumes.end(), std::ostream_iterator(volumes_str, ",")); BOOST_LOG_TRIVIAL(info) << boost::format("extra_flush_volume: %1%") % volumes_str.str(); 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(min_flush_volumes[from_idx], 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); }*/ } if (skip_modified_gcodes) { std::map::iterator gcodes_iter; for (gcodes_iter = m_models[0].plates_custom_gcodes.begin(); gcodes_iter != m_models[0].plates_custom_gcodes.end(); gcodes_iter++) { CustomGCode::Info &gcode_info = gcodes_iter->second; std::vector::iterator item_iter = gcode_info.gcodes.begin(); while ( item_iter != gcode_info.gcodes.end() ) { if (item_iter->type == CustomGCode::Custom) { BOOST_LOG_TRIVIAL(warning) << boost::format("skip_modified_gcodes: remove custom gcodes %1% in plate %2%") %item_iter->extra %gcodes_iter->first; item_iter = gcode_info.gcodes.erase(item_iter); } else item_iter++; } } } // 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); } auto timelapse_type_opt = m_print_config.option("timelapse_type"); bool is_smooth_timelapse = false; if (enable_timelapse && timelapse_type_opt && (timelapse_type_opt->getInt() == TimelapseType::tlSmooth)) is_smooth_timelapse = true; if (disable_wipe_tower_after_mapping) { if (is_smooth_timelapse) { disable_wipe_tower_after_mapping = false; BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: disable_wipe_tower_after_mapping: set back to false due to smooth timelapse!")%__LINE__; } else { ConfigOptionBool* enable_wipe_op = m_print_config.option("enable_prime_tower", true); enable_wipe_op->value = false; BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: disable_wipe_tower_after_mapping: disable prime tower for only one filament!")%__LINE__; std::string diff_settings = different_settings[0]; if (diff_settings.empty()) different_settings[0] = "enable_prime_tower"; else { std::vector different_keys; Slic3r::unescape_strings_cstyle(diff_settings, different_keys); bool need_insert = true; for (int index = 0; index < different_keys.size(); index++) { if (different_keys[index] == "enable_prime_tower") { need_insert = false; break; } } if (need_insert) different_settings[0] = diff_settings + ";enable_prime_tower"; } } } //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 (is_bbl_3mf && (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)) 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%}, the object size should be limited") %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);*/ shrink_to_new_bed = 2; } 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 = 1; } else { if ((current_exclude_area.size() > 0)&&(current_exclude_area != old_exclude_area)) { BOOST_LOG_TRIVIAL(info) << boost::format("old printable size {%1%, %2%, %3%}, new printable size {%4%, %5%, %6%}, exclude_area different, need to shrink") %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height; shrink_to_new_bed = 2; } else { BOOST_LOG_TRIVIAL(info) << boost::format("old printable size {%1%, %2%, %3%}, new printable size {%4%, %5%, %6%}, extract the same, no need shrink") %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 get_print_sequence = [](Slic3r::GUI::PartPlate* plate, DynamicPrintConfig& print_config, bool &is_seq_print) { PrintSequence curr_plate_seq = plate->get_print_seq(); if (curr_plate_seq == PrintSequence::ByDefault) { auto seq_print = print_config.option>("print_sequence"); if (seq_print && (seq_print->value == PrintSequence::ByObject)) { BOOST_LOG_TRIVIAL(info) << boost::format("plate print by object, set from global"); is_seq_print = true; } } else if (curr_plate_seq == PrintSequence::ByObject) { BOOST_LOG_TRIVIAL(info) << boost::format("plate print by object, set from plate self"); is_seq_print = true; } }; auto check_plate_wipe_tower = [get_print_sequence, is_smooth_timelapse](Slic3r::GUI::PartPlate* plate, int plate_index, DynamicPrintConfig& print_config, plate_obj_size_info_t &plate_obj_size_info) { plate_obj_size_info.obj_bbox= plate->get_objects_bounding_box(); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, object bbox: min {%2%, %3%, %4%} - max {%5%, %6%, %7%}") %(plate_index+1) %plate_obj_size_info.obj_bbox.min.x() % plate_obj_size_info.obj_bbox.min.y() % plate_obj_size_info.obj_bbox.min.z() %plate_obj_size_info.obj_bbox.max.x() % plate_obj_size_info.obj_bbox.max.y() % plate_obj_size_info.obj_bbox.max.z(); if (!print_config.has("wipe_tower_x")) { plate_obj_size_info.has_wipe_tower = false; BOOST_LOG_TRIVIAL(info) << boost::format("can not found wipe_tower_x in config, set to no wipe tower"); return; } bool wipe_tower_enabled = false; if (print_config.has("enable_prime_tower")) { wipe_tower_enabled = print_config.option("enable_prime_tower")->value; } if (!wipe_tower_enabled) { plate_obj_size_info.has_wipe_tower = false; BOOST_LOG_TRIVIAL(info) << boost::format("enable_prime_tower set to false"); return; } int valid_count = plate->printable_instance_size(); if (valid_count <= 0){ plate_obj_size_info.has_wipe_tower = false; BOOST_LOG_TRIVIAL(info) << boost::format("no printable object found, set to no wipe tower"); return; } bool is_sequence = false; get_print_sequence(plate, print_config, is_sequence); if (is_sequence && valid_count > 1) { plate_obj_size_info.has_wipe_tower = false; BOOST_LOG_TRIVIAL(info) << boost::format("sequence print, valid_count=%1%, set to no wipe tower")%valid_count; return; } std::vector extruders = plate->get_extruders_under_cli(true, print_config); unsigned int filaments_cnt = extruders.size(); if ((filaments_cnt <= 1) && !is_smooth_timelapse){ plate_obj_size_info.has_wipe_tower = false; BOOST_LOG_TRIVIAL(info) << boost::format("filaments_cnt=%1%, set to no wipe tower")%filaments_cnt; return; } ConfigOptionFloats *wipe_x_option = dynamic_cast(print_config.option("wipe_tower_x")); ConfigOptionFloats *wipe_y_option = dynamic_cast(print_config.option("wipe_tower_y")); plate_obj_size_info.wipe_x = wipe_x_option->get_at(plate_index); plate_obj_size_info.wipe_y = wipe_y_option->get_at(plate_index); ConfigOptionFloat* width_option = print_config.option("prime_tower_width", true); plate_obj_size_info.wipe_width = width_option->value; ConfigOptionFloat* brim_width_option = print_config.option("prime_tower_brim_width", true); float brim_width = brim_width_option->value; ConfigOptionFloat* volume_option = print_config.option("prime_volume", true); float wipe_volume = volume_option->value; Vec3d wipe_tower_size = plate->estimate_wipe_tower_size(print_config, plate_obj_size_info.wipe_width, wipe_volume, filaments_cnt); plate_obj_size_info.wipe_depth = wipe_tower_size(1); Vec3d origin = plate->get_origin(); Vec3d start(origin(0) + plate_obj_size_info.wipe_x - brim_width, origin(1) + plate_obj_size_info.wipe_y, 0.f); plate_obj_size_info.obj_bbox.merge(start); Vec3d end(origin(0) + plate_obj_size_info.wipe_x + plate_obj_size_info.wipe_width + brim_width, origin(1) + plate_obj_size_info.wipe_y + plate_obj_size_info.wipe_depth, 0.f); plate_obj_size_info.obj_bbox.merge(end); plate_obj_size_info.has_wipe_tower = true; BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, has wipe tower, wipe bbox: min {%2%, %3%, %4%} - max {%5%, %6%, %7%}") %(plate_index+1) %start.x() % start.y() % start.z() %end.x() % end.y() % end.z(); }; 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, current_exclude_area, &plate_obj_size_infos] (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 > 0) { 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")); } double exclude_width = 0.f, exclude_depth = 0.f; if (current_exclude_area.size() >= 4) { exclude_width = current_exclude_area[2].x() - current_exclude_area[0].x(); exclude_depth = current_exclude_area[2].y() - current_exclude_area[0].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 offset; if (shrink_to_new_bed == 1) { 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; offset = new_center - cur_center; if (index == 0) wipe_offset = offset; BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed 1, plate %1%, cur_origin: {%2%, %3%}, new_origin: {%4%, %5%}, cur_center {%6%, %7%} new_center {%8%, %9%}") %(index+1) %cur_origin(0) %cur_origin(1) %new_origin(0) %new_origin(1) %cur_center(0) %cur_center(1) %new_center(0) %new_center(1); } else { //center the object Vec3d new_center_offset { ((double)current_printable_width + exclude_width)/2, ((double)current_printable_depth + exclude_depth)/2, 0}; BoundingBoxf3& bbox = plate_obj_size_infos[index].obj_bbox; Vec3d size = bbox.size(); if (size.x() > (current_printable_width - exclude_width)) new_center_offset(0) = ((double)current_printable_width)/2; if (size.y() > (current_printable_depth - exclude_depth)) new_center_offset(1) = ((double)current_printable_depth)/2; Vec3d new_center = new_origin + new_center_offset; offset = new_center - bbox.center(); wipe_offset = offset + cur_origin - new_origin; BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed 2, plate %1%, new_origin: {%2%, %3%}, new_center: {%4%, %5%}, obj bbox(including wipe tower) min {%6%, %7%} max {%8%, %9%}") %(index+1) %new_origin(0) %new_origin(1) %new_center(0) %new_center(1) %bbox.min(0) %bbox.min(1) %bbox.max(0) %bbox.max(1); } offset(2) = 0.f; BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed %1%, plate %2% translate offset: {%3%, %4%} wipe_offset {%5%, %6%}") %shrink_to_new_bed %(index+1) %offset[0] %offset[1] %wipe_offset[0] %wipe_offset[1]; cur_plate->translate_all_instance(offset); if (wipe_x_option) { BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed %4%, plate %1%: wipe tower src: {%2%, %3%}")%(index+1) %wipe_x_option->get_at(index) %wipe_y_option->get_at(index)%shrink_to_new_bed; 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 %4%, plate %1% wipe tower changes to: {%2%, %3%}")%(index+1) %wipe_x_option->get_at(index) %wipe_y_option->get_at(index) %shrink_to_new_bed; } } 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); int plate_count = partplate_list.get_plate_count(); plate_obj_size_infos.resize(plate_count, plate_obj_size_info_t()); for (int index = 0; index < plate_count; index ++) { Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(index); check_plate_wipe_tower(cur_plate, index, m_print_config, plate_obj_size_infos[index]); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, has_wipe_tower %2%, wipe_x %3%, wipe_y %4%, width %5%, depth %6%") %(index+1) %plate_obj_size_infos[index].has_wipe_tower %plate_obj_size_infos[index].wipe_x %plate_obj_size_infos[index].wipe_y %plate_obj_size_infos[index].wipe_width %plate_obj_size_infos[index].wipe_depth; } 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; }*/ //doing downward_check std::vector downward_check_printers; std::vector downward_check_status; if (downward_check) { bool use_default = false; std::string default_path; if (downward_settings.size() == 0) { //parse from internal std::string cli_config_file = resources_dir() + "/profiles/BBL/cli_config.json"; load_downward_settings_list_from_config(cli_config_file, current_printer_name, current_printer_model, downward_settings); use_default = true; default_path = resources_dir() + "/profiles/BBL/machine_full/"; } for (auto const &file : downward_settings) { DynamicPrintConfig config; std::string config_type, config_name, filament_id, config_from, downward_printer; std::string file_path = use_default?(default_path+file+".json"):file; int ret = load_config_file(file_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); } if ((config_type != "machine") || (config_from != "system")) { BOOST_LOG_TRIVIAL(info) << boost::format("found invalid config type %1% or from %2% in file %3% when downward_check")%config_type %config_from %file_path; record_exit_reson(outfile_dir, ret, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info); flush_and_exit(ret); } BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: loaded machine config %1%, from %2%")%config_name %file_path ; printer_plate_info_t printer_plate; Pointfs temp_printable_area, temp_exclude_area; printer_plate.printer_name = config_name; temp_printable_area = config.option("printable_area", true)->values; temp_exclude_area = config.option("bed_exclude_area", true)->values; if (temp_printable_area.size() >= 4) { printer_plate.printable_width = (int)(temp_printable_area[2].x() - temp_printable_area[0].x()); printer_plate.printable_depth = (int)(temp_printable_area[2].y() - temp_printable_area[0].y()); printer_plate.printable_height = (int)(config.opt_float("printable_height")); } if (temp_exclude_area.size() >= 4) { printer_plate.exclude_width = (int)(temp_exclude_area[2].x() - temp_exclude_area[0].x()); printer_plate.exclude_depth = (int)(temp_exclude_area[2].y() - temp_exclude_area[0].y()); printer_plate.exclude_x = (int)temp_exclude_area[0].x(); printer_plate.exclude_y = (int)temp_exclude_area[0].y(); } BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: printable size{%1%,%2%, %3%}, exclude area{%4%, %5%: %6% x %7%}") %printer_plate.printable_width %printer_plate.printable_depth %printer_plate.printable_height %printer_plate.exclude_x %printer_plate.exclude_y %printer_plate.exclude_width %printer_plate.exclude_depth; if (config.option("extruder_clearance_height_to_lid")) printer_plate.height_to_lid = config.opt_float("extruder_clearance_height_to_lid"); if (config.option("extruder_clearance_height_to_rod")) printer_plate.height_to_rod = config.opt_float("extruder_clearance_height_to_rod"); if (config.option("extruder_clearance_max_radius")) printer_plate.cleareance_radius = config.opt_float("extruder_clearance_max_radius"); downward_check_printers.push_back(std::move(printer_plate)); } } int downward_check_size = downward_check_printers.size(); if (downward_check_size > 0) { downward_check_status.resize(downward_check_size, false); int plate_count = partplate_list.get_plate_count(); int failed_count = 0; for (int index = 0; index < plate_count; index ++) { if (failed_count == downward_check_size) { BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: all failed, size %1%")%downward_check_size; break; } Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(index); Vec3d size = plate_obj_size_infos[index].obj_bbox.size(); bool is_sequence = false; get_print_sequence(cur_plate, m_print_config, is_sequence); for (int index2 = 0; index2 < downward_check_size; index2 ++) { if (failed_count == downward_check_size) { break; } if (downward_check_status[index2]) continue; printer_plate_info_t& plate_info = downward_check_printers[index2]; if (is_sequence) { if ((plate_info.cleareance_radius > 0.f) && (plate_info.height_to_rod > 0.f) && (plate_info.height_to_lid > 0.f)) { if ((cleareance_radius < plate_info.cleareance_radius) || (height_to_rod > plate_info.height_to_rod) || (height_to_lid > plate_info.height_to_lid)) { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, downward_check index %2%, name %3%, sequence print, original clearance{%4%, %5%, %6%} exceeds new {%7%, %8%, %9%}") %(index+1) %(index2+1) %plate_info.printer_name %cleareance_radius %height_to_rod %height_to_lid %plate_info.cleareance_radius %plate_info.height_to_rod %plate_info.height_to_lid; downward_check_status[index2] = true; failed_count ++; continue; } } else { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, downward_check index %2%, name %3%, sequence print, can not get cleareance params, set to false") %(index+1) %(index2+1) %plate_info.printer_name; downward_check_status[index2] = true; failed_count ++; continue; } } if ((size.z() > plate_info.printable_height) || (size.y() > plate_info.printable_depth) || (size.x() > plate_info.printable_width)) { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, downward_check index %2%, name %3%, bbox {%4%, %5%, %6%} exceeds printer size {%7%, %8%, %9%}") %(index+1) %(index2+1) %plate_info.printer_name %size.x() % size.y() % size.z() %plate_info.printable_width %plate_info.printable_depth %plate_info.printable_height; downward_check_status[index2] = true; failed_count ++; continue; } if (plate_info.exclude_width > 0) { int real_width = plate_info.printable_width - plate_info.exclude_width; int real_depth = plate_info.printable_depth - plate_info.exclude_depth; if ((size.x() > real_width) && (size.y() > real_depth)) { BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, downward_check index %2%, name %3%, bbox {%4%, %5%} exceeds real size without exclude_area {%6%, %7%}") %(index+1) %(index2+1) %plate_info.printer_name %size.x() % size.y() %real_width %real_depth; downward_check_status[index2] = true; failed_count ++; continue; } } } } if (failed_count < downward_check_size) { //has success ones BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: downward_check_size %1%, failed_count %2%")%downward_check_size %failed_count; for (int index2 = 0; index2 < downward_check_size; index2 ++) { if (downward_check_status[index2]) continue; printer_plate_info_t& plate_info = downward_check_printers[index2]; BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: found compatible printer %1%")%plate_info.printer_name; downward_compatible_machines.push_back(plate_info.printer_name); } sliced_info.downward_machines = downward_compatible_machines; } } // Loop through transform options. bool user_center_specified = false; Points beds = get_bed_shape(m_print_config); ArrangeParams arrange_cfg; 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 == "assemble") { if (clone_objects.size() > 0) { BOOST_LOG_TRIVIAL(error) << "Invalid params: can not set assemble and clone_objects 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); } Model m; ModelObject* new_object = m.add_object(); new_object->name = _u8L("Assembly"); new_object->add_instance(); int idx = 0; for (auto& model : m_models) for (ModelObject* o : model.objects) { for (auto volume : o->volumes) { ModelVolume* new_volume = new_object->add_volume(*volume); // set extruder id new_volume->config.set_key_value("extruder", new ConfigOptionInt(o->config.extruder())); } } 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; 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") { float ratio = m_config.opt_float(opt_key); if (ratio <= 0.f) { BOOST_LOG_TRIVIAL(error) << boost::format("Invalid params:invalid scale ratio %1%")%ratio; record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } for (auto &model : m_models) for (auto &o : model.objects) // this affects volumes: o->scale(ratio); } 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); } else if (plate_to_slice > 0){ 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(); 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_for_curr_plate = 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_for_curr_plate = true; } if (duplicate_count > 0) { const ConfigOptionBool* spiral_vase = m_print_config.option("spiral_mode"); if ((spiral_vase != nullptr) && spiral_vase->value) { //spiral mode can only be duplicated with by-object if (!is_seq_print_for_curr_plate) { BOOST_LOG_TRIVIAL(warning) << boost::format("Spiral mode can not be duplicated under by-object print, skip duplicate"); duplicate_count = 0; } } } if (duplicate_count > 0) need_arrange = true; } if ((!need_arrange) && is_bbl_3mf && !shrink_to_new_bed && (plate_to_slice > 0) && !new_printer_system_name.empty() && (new_printer_system_name!= current_printer_system_name)) { 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))) { if (is_seq_print_for_curr_plate) { 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]->no_light_thumbnail_file.empty()) { BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded no_light_thumbnail %2%.")%(index+1)%plate_data_src[index]->no_light_thumbnail_file; plate_data_src[index]->no_light_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 (!assemble_plate_info_list.empty()) { //need to arrange for assemble cases int plate_count = assemble_plate_info_list.size(); if (plate_count != partplate_list.get_plate_count()) { BOOST_LOG_TRIVIAL(error) << boost::format("mismatch plate count, to_assemble %1%, generated %2%") % plate_count % 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); } for (size_t i = 0; i < plate_count; i++) { Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i); //lock those plates no need to arrange if (!assemble_plate_info_list[i].need_arrange) cur_plate->lock(true); } for (size_t i = 0; i < plate_count; i++) { assemble_plate_info_t& assemble_plate = assemble_plate_info_list[i]; Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, need arrange %2%, filaments_count %3%") % (i+1) % assemble_plate.need_arrange % assemble_plate.filaments_count; if (assemble_plate.need_arrange) { //do arrange for plate ArrangePolygons selected, unselected; Model& model = m_models[0]; arrange_cfg = ArrangeParams(); // reset all params get_print_sequence(cur_plate, m_print_config, arrange_cfg.is_seq_print); //Step-1: prepare the arranged data partplate_list.lock_plate(i, false); partplate_list.select_plate(i); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% set to selected") % i; size_t plate_obj_count = assemble_plate.loaded_obj_list.size(); for (size_t oidx = 0; oidx < plate_obj_count; ++oidx) { ModelObject* mo = assemble_plate.loaded_obj_list[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); ap.itemid = selected.size(); selected.emplace_back(std::move(ap)); BOOST_LOG_TRIVIAL(debug) << boost::format("plate %1%: add object %2% object index %3%, into selected") % (i+1) % ap.name %(oidx+1); } } if (!arrange_cfg.is_seq_print && assemble_plate.filaments_count > 1) { //prepare the wipe tower int plate_count = partplate_list.get_plate_count(); auto printer_structure_opt = m_print_config.option>("printer_structure"); // set the default position, the same with print config(left top) float x = WIPE_TOWER_DEFAULT_X_POS; float y = WIPE_TOWER_DEFAULT_Y_POS; if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) { x = I3_WIPE_TOWER_DEFAULT_X_POS; y = I3_WIPE_TOWER_DEFAULT_Y_POS; } if (x < WIPE_TOWER_MARGIN) { x = WIPE_TOWER_MARGIN; } if (y < WIPE_TOWER_MARGIN) { y = WIPE_TOWER_MARGIN; } ConfigOptionFloat wt_x_opt(x); ConfigOptionFloat wt_y_opt(y); //create the options using default if neccessary ConfigOptionFloats* wipe_x_option = m_print_config.option("wipe_tower_x", true); ConfigOptionFloats* wipe_y_option = m_print_config.option("wipe_tower_y", true); ConfigOptionFloat* width_option = m_print_config.option("prime_tower_width", true); ConfigOptionFloat* rotation_angle_option = m_print_config.option("wipe_tower_rotation_angle", true); ConfigOptionFloat* volume_option = m_print_config.option("prime_volume", true); BOOST_LOG_TRIVIAL(info) << boost::format("prime_tower_width %1% wipe_tower_rotation_angle %2% prime_volume %3%") % width_option->value % rotation_angle_option->value % volume_option->value; wipe_x_option->set_at(&wt_x_opt, i, 0); wipe_y_option->set_at(&wt_y_opt, i, 0); ArrangePolygon wipe_tower_ap = cur_plate->estimate_wipe_tower_polygon(m_print_config, i, assemble_plate.filaments_count, true); wipe_tower_ap.bed_idx = i; unselected.emplace_back(wipe_tower_ap); } // add the virtual object into unselect list if has partplate_list.preprocess_exclude_areas(unselected, i + 1); if (avoid_extrusion_cali_region) partplate_list.preprocess_nonprefered_areas(unselected, i + 1); //Step-2:prepare the arrange params arrange_cfg.allow_rotations = allow_rotations; arrange_cfg.allow_multi_materials_on_same_plate = allow_multicolor_oneplate; arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region; 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 (arrange_cfg.is_seq_print) { arrange_cfg.bed_shrink_x = BED_SHRINK_SEQ_PRINT; arrange_cfg.bed_shrink_y = BED_SHRINK_SEQ_PRINT; } 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); partplate_list.preprocess_exclude_areas(arrange_cfg.excluded_regions, 1, scale_(1)); { BOOST_LOG_TRIVIAL(debug) << "arrange bedpts:" << beds[0].transpose() << ", " << beds[1].transpose() << ", " << beds[2].transpose() << ", " << beds[3].transpose(); BOOST_LOG_TRIVIAL(info) << "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 plate %1%'s arranging...") % (i + 1); arrangement::arrange(selected, unselected, beds, arrange_cfg); //arrangement::arrange(unprintable, {}, beds, arrange_cfg); BOOST_LOG_TRIVIAL(info) << boost::format("finished plate %1%'s arranging") % (i + 1); //step-4: postprocess the bed index and result partplate_list.clear(false, false, true, i); for (ArrangePolygon& ap : selected) { partplate_list.postprocess_bed_index_for_current_plate(ap); if (ap.bed_idx != i) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":arrange failed: ap.name %1% ap.bed_idx %2%, plate index %3%") % ap.name % ap.bed_idx % i; 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); } } // Apply the arrange result to all selected objects for (ArrangePolygon& ap : selected) { //BBS: partplate postprocess partplate_list.postprocess_arrange_polygon(ap, true); ap.apply(); } //lock here cur_plate->lock(true); } else { size_t plate_obj_count = assemble_plate.loaded_obj_list.size(); Vec3d plate_origin = cur_plate->get_origin(); for (size_t oidx = 0; oidx < plate_obj_count; ++oidx) { ModelObject* mo = assemble_plate.loaded_obj_list[oidx]; mo->translate_instances(plate_origin); BOOST_LOG_TRIVIAL(debug) << boost::format("plate %1%: no arrange, directly translate object %2% by {%3%, %4%}") % (i+1) % mo->name %plate_origin(0) %plate_origin(1); } } } for (size_t i = 0; i < plate_count; i++) { //unlock all the plates Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i); cur_plate->lock(false); } partplate_list.reload_all_objects(false, -1); } else 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; float orig_wipe_x = 0.f, orig_wipe_y = 0.f; if (duplicate_count > 0) { original_model = model; } while(!finished_arrange) { arrange_cfg = ArrangeParams(); // reset all params 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; if (plate_to_slice > 0) { ConfigOptionFloats* wipe_x_option = m_print_config.option("wipe_tower_x"); ConfigOptionFloats* wipe_y_option = m_print_config.option("wipe_tower_y"); if (wipe_x_option && (wipe_x_option->size() > (plate_to_slice-1))) { orig_wipe_x = wipe_x_option->get_at(plate_to_slice-1); BOOST_LOG_TRIVIAL(info) << boost::format("%1%, plate_to_slice %2%, orig_wipe_x=%3%")%__LINE__%plate_to_slice%orig_wipe_x; } if (wipe_y_option && (wipe_y_option->size() > (plate_to_slice-1))) { orig_wipe_y = wipe_y_option->get_at(plate_to_slice-1); BOOST_LOG_TRIVIAL(info) << boost::format("%1%, plate_to_slice %2%, orig_wipe_y=%3%")%__LINE__%plate_to_slice%orig_wipe_y; } } } 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) { get_print_sequence(cur_plate, m_print_config, arrange_cfg.is_seq_print); } //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); if (used_filament_set.size() > 0) { //prepare the wipe tower int plate_count = partplate_list.get_plate_count(); int extruder_size = used_filament_set.size(); auto printer_structure_opt = m_print_config.option>("printer_structure"); // set the default position, the same with print config(left top) float x = WIPE_TOWER_DEFAULT_X_POS; float y = WIPE_TOWER_DEFAULT_Y_POS; if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) { x = I3_WIPE_TOWER_DEFAULT_X_POS; y = I3_WIPE_TOWER_DEFAULT_Y_POS; } if (x < WIPE_TOWER_MARGIN) { x = WIPE_TOWER_MARGIN; } if (y < WIPE_TOWER_MARGIN) { y = WIPE_TOWER_MARGIN; } ConfigOptionFloat wt_x_opt(x); ConfigOptionFloat wt_y_opt(y); //create the options using default if neccessary ConfigOptionFloats* wipe_x_option = m_print_config.option("wipe_tower_x", true); ConfigOptionFloats* wipe_y_option = m_print_config.option("wipe_tower_y", true); ConfigOptionFloat* width_option = m_print_config.option("prime_tower_width", true); ConfigOptionFloat* rotation_angle_option = m_print_config.option("wipe_tower_rotation_angle", true); ConfigOptionFloat* volume_option = m_print_config.option("prime_volume", true); BOOST_LOG_TRIVIAL(info) << boost::format("prime_tower_width %1% wipe_tower_rotation_angle %2% prime_volume %3%")%width_option->value %rotation_angle_option->value %volume_option->value ; for (int bedid = 0; bedid < MAX_PLATE_COUNT; bedid++) { int plate_index_valid = std::min(bedid, plate_count - 1); if (bedid < plate_count) { wipe_x_option->set_at(&wt_x_opt, plate_index_valid, 0); wipe_y_option->set_at(&wt_y_opt, plate_index_valid, 0); } ArrangePolygon wipe_tower_ap = partplate_list.get_plate(plate_index_valid)->estimate_wipe_tower_polygon(m_print_config, plate_index_valid, extruder_size, true); wipe_tower_ap.bed_idx = bedid; unselected.emplace_back(wipe_tower_ap); } } } 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 (m_print_config.has("wipe_tower_x") && (is_smooth_timelapse || !arrange_cfg.is_seq_print || (selected.size() <= 1))) { float x; float y; if (duplicate_count > 0) { auto printer_structure_opt = m_print_config.option>("printer_structure"); x = WIPE_TOWER_DEFAULT_X_POS; y = WIPE_TOWER_DEFAULT_Y_POS; if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) { x = I3_WIPE_TOWER_DEFAULT_X_POS; y = I3_WIPE_TOWER_DEFAULT_Y_POS; } } else { //keep the original x = dynamic_cast(m_print_config.option("wipe_tower_x"))->get_at(plate_to_slice-1); 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) && !is_smooth_timelapse) { 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); Vec3d wipe_tower_size = cur_plate->estimate_wipe_tower_size(m_print_config, w, v, filaments_cnt); Vec3d plate_origin = cur_plate->get_origin(); int plate_width, plate_depth, plate_height; partplate_list.get_plate_size(plate_width, plate_depth, plate_height); float depth = wipe_tower_size(1); float margin = 15.f, wp_brim_width = 0.f; ConfigOption *wipe_tower_brim_width_opt = m_print_config.option("prime_tower_brim_width"); if (wipe_tower_brim_width_opt ) { wp_brim_width = wipe_tower_brim_width_opt->getFloat(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: wp_brim_width %1%")%wp_brim_width; } 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%, plate_width=%9%, plate_depth=%10%") %x %y %w %depth %a %v %filaments_cnt %layer_height %plate_width %plate_depth; if ((y + depth + margin + wp_brim_width) > (float)plate_depth) { y = (float)plate_depth - depth - margin - wp_brim_width; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: exceeds the border, change y to %1%, plate_depth=%2%")%y %plate_depth; } if ((x + w + margin + wp_brim_width) > (float)plate_width) { x = (float)plate_width - w - margin - wp_brim_width; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: exceeds the border, change x to %1%, plate_width=%2%")%y %plate_width; } if (x < margin) { x = margin; } if (y < margin) { y = margin; } //update wipe_tower_x and wipe_tower_y ConfigOptionFloat wt_x_opt(x); ConfigOptionFloat wt_y_opt(y); ConfigOptionFloats* wipe_x_option = m_print_config.option("wipe_tower_x", true); ConfigOptionFloats* wipe_y_option = m_print_config.option("wipe_tower_y", true); wipe_x_option->set_at(&wt_x_opt, plate_to_slice-1, 0); wipe_y_option->set_at(&wt_y_opt, plate_to_slice-1, 0); ArrangePolygon wipe_tower_ap; Polygon ap({ {scaled(x - wp_brim_width), scaled(y - wp_brim_width)}, {scaled(x + w + wp_brim_width), scaled(y - wp_brim_width)}, {scaled(x + w + wp_brim_width), scaled(y + depth + wp_brim_width)}, {scaled(x - wp_brim_width), scaled(y + depth + wp_brim_width)} }); 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 = allow_rotations; arrange_cfg.allow_multi_materials_on_same_plate = allow_multicolor_oneplate; arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region; 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 (arrange_cfg.is_seq_print) { arrange_cfg.bed_shrink_x = BED_SHRINK_SEQ_PRINT; arrange_cfg.bed_shrink_y = BED_SHRINK_SEQ_PRINT; } 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); partplate_list.preprocess_exclude_areas(arrange_cfg.excluded_regions, 1, scale_(1)); { BOOST_LOG_TRIVIAL(debug) << "arrange bedpts:" << beds[0].transpose() << ", " << beds[1].transpose() << ", " << beds[2].transpose() << ", " << beds[3].transpose(); BOOST_LOG_TRIVIAL(info)<< "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, orig_wipe_x %1%, orig_wipe_y %2%")%orig_wipe_x %orig_wipe_y; 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); if ((orig_wipe_x > 0.f) && (orig_wipe_y > 0.f)) { ConfigOptionFloat wt_x_opt(orig_wipe_x); ConfigOptionFloat wt_y_opt(orig_wipe_y); ConfigOptionFloats* wipe_x_option = m_print_config.option("wipe_tower_x", true); ConfigOptionFloats* wipe_y_option = m_print_config.option("wipe_tower_y", true); wipe_x_option->set_at(&wt_x_opt, plate_to_slice-1, 0); wipe_y_option->set_at(&wt_y_opt, plate_to_slice-1, 0); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": restore wipe_tower position to {%1%, %2%}")%orig_wipe_x %orig_wipe_y; } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": exit arrange process"); } continue; } if (duplicate_single_object) { if (real_duplicate_count <= 1) { BOOST_LOG_TRIVIAL(warning) << boost::format("no object can be placed under single object mode, restore to the original model and plates also, orig_wipe_x %1%, orig_wipe_y %2%")%orig_wipe_x %orig_wipe_y; //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; if ((orig_wipe_x > 0.f) && (orig_wipe_y > 0.f)) { ConfigOptionFloat wt_x_opt(orig_wipe_x); ConfigOptionFloat wt_y_opt(orig_wipe_y); ConfigOptionFloats* wipe_x_option = m_print_config.option("wipe_tower_x", true); ConfigOptionFloats* wipe_y_option = m_print_config.option("wipe_tower_y", true); wipe_x_option->set_at(&wt_x_opt, plate_to_slice-1, 0); wipe_y_option->set_at(&wt_y_opt, plate_to_slice-1, 0); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": restore wipe_tower position to {%1%, %2%}")%orig_wipe_x %orig_wipe_y; } 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, export_stls_dir; std::vector calibration_thumbnails; std::vector plate_object_count(partplate_list.get_plate_count(), 0); int max_slicing_time_per_plate = 0, max_triangle_count_per_plate = 0, sliced_plate = -1; 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_milliseconds_time_utc(); sliced_info.prepare_time = (size_t) (global_current_time - global_begin_time); global_begin_time = global_current_time; //opengl related Slic3r::GUI::OpenGLManager opengl_mgr; GLShaderProgram* shader = nullptr; GLVolumeCollection glvolume_collection; bool opengl_valid = false; const ConfigOptionStrings* filament_color = dynamic_cast(m_print_config.option("filament_colour")); std::vector colors; if (filament_color) { colors= filament_color->vserialize(); } else colors.push_back("#FFFFFFFF"); std::vector> colors_out(colors.size()); auto init_opengl_and_colors = [&opengl_mgr, &colors_out, &glvolume_collection, &shader, &filament_color](Model &model, std::vector& f_colors) -> bool { unsigned char rgb_color[4] = {}; for (const std::string& color : f_colors) { Slic3r::GUI::BitmapCache::parse_color4(color, rgb_color); size_t color_idx = &color - &f_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; } } } } } BOOST_LOG_TRIVIAL(info) << boost::format("init_opengl_and_colors finished, gl_valid=%1%")%gl_valid; return gl_valid; }; 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 > 0) { BOOST_LOG_TRIVIAL(warning) << "use load_slicedata when shrink_to_new_bed." << 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_stls") { export_stls_dir = m_config.opt_string(opt_key); for (auto &model : m_models) model.add_default_instances(); if (! this->export_models(IO::STL, export_stls_dir)) { 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; sliced_plate = plate_to_slice; 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, temp_time = 0; std::unordered_map slice_time; slice_time[TIME_USING_CACHE] = 0; slice_time[TIME_MAKE_PERIMETERS] = 0; slice_time[TIME_INFILL] = 0; slice_time[TIME_GENERATE_SUPPORT] = 0; start_time = (long long)Slic3r::Utils::get_current_milliseconds_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]->no_light_thumbnail_file.empty()) { BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded no_light_thumbnail %2%.")%(index+1)%plate_data_src[index]->no_light_thumbnail_file; plate_data_src[index]->no_light_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(debug) << 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()) { if ((STRING_EXCEPT_LAYER_HEIGHT_EXCEEDS_LIMIT == err.type) && no_check) { BOOST_LOG_TRIVIAL(warning) << "got warnings: "<< err.string << std::endl; } else { 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; } if (no_check) record_exit_reson(outfile_dir, validate_error, index+1, err.string, sliced_info); else 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(nullptr, true); BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": finished print::process."; } } else { print->process(&slice_time); BOOST_LOG_TRIVIAL(info) << "print::process: first time_using_cache is " << slice_time[TIME_USING_CACHE] << " secs."; } 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 NON_CRITICAL slicing warnings: "< ThumbnailsList{ ThumbnailsList thumbnails; for (const Vec2d& size : params.sizes) { thumbnails.push_back(ThumbnailData()); Point isize(size); // round to ints ThumbnailData& thumbnail_data = thumbnails.back(); 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, isize.x(), isize.y(), 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, isize.x(), isize.y(), 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; } if (!thumbnails.back().is_valid()) thumbnails.pop_back(); } return thumbnails; }; // The outfile is processed by a PlaceholderParser. //outfile = part_plate->get_tmp_gcode_path(); if (outfile_dir.empty()) { outfile = part_plate->get_tmp_gcode_path(); } else { outfile = outfile_dir + "/plate_" + std::to_string(index + 1) + ".gcode"; part_plate->set_tmp_gcode_path(outfile); } BOOST_LOG_TRIVIAL(info) << "process finished, will export gcode temporily to " << outfile << std::endl; temp_time = (long long)Slic3r::Utils::get_current_milliseconds_time_utc(); if (is_bbl_vendor_preset) { outfile = print_fff->export_gcode(outfile, gcode_result, nullptr); } else { if (!opengl_valid) opengl_valid = init_opengl_and_colors(model, colors); outfile = print_fff->export_gcode(outfile, gcode_result, cli_generate_thumbnails); } slice_time[TIME_USING_CACHE] = slice_time[TIME_USING_CACHE] + ((long long)Slic3r::Utils::get_current_milliseconds_time_utc() - temp_time); BOOST_LOG_TRIVIAL(info) << "export_gcode finished: time_using_cache update to " << slice_time[TIME_USING_CACHE] << " secs."; //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_milliseconds_time_utc(); sliced_plate_info.sliced_time = end_time - start_time; sliced_plate_info.sliced_time_with_cache = slice_time[TIME_USING_CACHE]; sliced_plate_info.make_perimeters_time = slice_time[TIME_MAKE_PERIMETERS]; sliced_plate_info.infill_time = slice_time[TIME_INFILL]; sliced_plate_info.generate_support_material_time = slice_time[TIME_GENERATE_SUPPORT]; if (max_slicing_time_per_plate != 0) { long long time_cost = end_time - start_time; if (time_cost > max_slicing_time_per_plate * 1000) { 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 * 1000)).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_milliseconds_time_utc(); if (export_to_3mf) { //BBS: export as bbl 3mf std::vector thumbnails, no_light_thumbnails, top_thumbnails, pick_thumbnails; std::vector plate_bboxes; PlateDataPtrs plate_data_list; partplate_list.store_to_3mf_structure(plate_data_list); if (sliced_plate == -1) { for (int i = 0; i < plate_data_list.size(); i++) { Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); plate_object_count[i] = part_plate->printable_instance_size(); } } else if (sliced_plate == 0){ //slicing all for (int i = 0; i < plate_data_list.size(); i++) { if (skip_useless_pick && (plate_object_count[i] == 1)) { BOOST_LOG_TRIVIAL(info) << boost::format("only has 1 object, set plate %1%'s is_label_object_enabled from %2% to false")%(i+1) % (plate_data_list[i]->is_label_object_enabled); plate_data_list[i]->is_label_object_enabled = false; } } } else { if (skip_useless_pick && (plate_object_count[sliced_plate - 1] == 1)) { BOOST_LOG_TRIVIAL(info) << boost::format("only has 1 object, set plate %1%'s is_label_object_enabled from %2% to false")%sliced_plate % (plate_data_list[sliced_plate - 1]->is_label_object_enabled); plate_data_list[sliced_plate - 1]->is_label_object_enabled = false; } } 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_no_light_thumbnail = oriented_or_arranged || regenerate_thumbnails; bool need_regenerate_top_thumbnail = oriented_or_arranged || regenerate_thumbnails; bool need_create_thumbnail_group = false, need_create_no_light_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")); const ConfigOptionFloats* nozzle_diameter_option = dynamic_cast(m_print_config.option("nozzle_diameter")); std::string nozzle_diameter_str; if (nozzle_diameter_option) nozzle_diameter_str = nozzle_diameter_option->serialize(); 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]; if (!printer_model_id.empty()) plate_data->printer_model_id = printer_model_id; if (!nozzle_diameter_str.empty()) plate_data->nozzle_diameters = nozzle_diameter_str; for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) { 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"; it->filament_id = filament_id?filament_id->get_at(it->id):""; } 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->no_light_thumbnail_file.empty()) { if (!regenerate_thumbnails && (plate_data_src.size() > i)) { plate_data->no_light_thumbnail_file = plate_data_src[i]->no_light_thumbnail_file; } if (plate_data->no_light_thumbnail_file.empty() || (!boost::filesystem::exists(plate_data->no_light_thumbnail_file))) { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s no_light_thumbnail_file %2% also not there, need to regenerate")%(i+1)%plate_data->no_light_thumbnail_file; if (!skip_this_plate) { need_regenerate_no_light_thumbnail = true; need_create_no_light_group = true; } } else { if (regenerate_thumbnails) { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s no_light_thumbnail file %2% cleared, need to regenerate")%(i+1) %plate_data->no_light_thumbnail_file; plate_data->no_light_thumbnail_file.clear(); } else BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s no_light_thumbnail file exists, no need to regenerate")%(i+1); } } 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_no_light_thumbnail || need_regenerate_top_thumbnail) { if (!opengl_valid) opengl_valid = init_opengl_and_colors(m_models[0], colors); /*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 {*/ if (opengl_valid) { Model &model = m_models[0]; 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); } //no light thumbnail if (!plate_data->no_light_thumbnail_file.empty() && (boost::filesystem::exists(plate_data->no_light_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 no_light_thumbnail_file path to empty.")%__LINE__%(i+1); plate_data->no_light_thumbnail_file.clear(); } else BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has valid no_light_thumbnail_file extracted from 3mf, directly using it")%(i+1); } else{ ThumbnailData *no_light_thumbnail = &part_plate->no_light_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->no_light_thumbnail_data.reset(); plate_data->no_light_thumbnail_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 no_light_thumbnail_file 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(*no_light_thumbnail, thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, false, false, true); break; } case Slic3r::GUI::OpenGLManager::EFramebufferType::Ext: { BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT"); Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*no_light_thumbnail, thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, false, false, true); break; } default: BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown"); break; } plate_data->no_light_thumbnail_file = "valid_no_light"; BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s no_light thumbnail,finished rendering")%(i+1); } } if (need_create_no_light_group) { no_light_thumbnails.push_back(&part_plate->no_light_thumbnail_data); BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for no_light 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, objects count %2%, skip_useless_pick %3%")%(i+1) %plate_object_count[i] %skip_useless_pick; if (skip_useless_pick && ((plate_object_count[i] <= 1) || (plate_object_count[i] > 64))) { //don't render pick and top part_plate->top_thumbnail_data.reset(); part_plate->pick_thumbnail_data.reset(); plate_data->top_file.clear(); plate_data->pick_file.clear(); BOOST_LOG_TRIVIAL(info) << boost::format("skip rendering for top&&pick"); } else { 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->no_light_thumbnail_data.reset(); plate_data->no_light_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_no_light_group) { no_light_thumbnails.push_back(&part_plate->no_light_thumbnail_data); 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 (!makerlab_name.empty()) { Model &model = m_models[0]; model.mk_name = makerlab_name; model.mk_version = makerlab_version; BOOST_LOG_TRIVIAL(info) << boost::format("mk_name %1%, mk_version %2%")%makerlab_name %makerlab_version; } if (!metadata_name.empty()) { Model &model = m_models[0]; model.md_value = metadata_value; model.md_name = metadata_name; for (unsigned int i = 0; i < metadata_name.size(); i++) { BOOST_LOG_TRIVIAL(info) << boost::format("index %1% metadata_name %2%, metadata_value %3%")%i %metadata_name[i] %metadata_value[i]; } } if (!this->export_project(&m_models[0], export_3mf_file, plate_data_list, project_presets, thumbnails, no_light_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); } for (unsigned int i = 0; i < thumbnails.size(); i++) thumbnails[i]->reset(); for (unsigned int i = 0; i < no_light_thumbnails.size(); i++) no_light_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(); release_PlateData_list(plate_data_list); 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_milliseconds_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, std::string path_dir) { 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, path_dir); 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 &no_light_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.no_light_thumbnail_data = no_light_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, std::string path_dir) 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__ << ": dir = "<< path_dir<<", 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 *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 */