mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	 a1e4975c81
			
		
	
	
		a1e4975c81
		
	
	
	
	
		
			
			JIRA: MAK-xxx Change-Id: I6d6af58c7106df4dacb51e98dd607ff77c058c13 (cherry picked from commit 080c6ced15bf9eb6e490fbd3616667d66ecbff86)
		
			
				
	
	
		
			4163 lines
		
	
	
	
		
			224 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4163 lines
		
	
	
	
		
			224 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifdef WIN32
 | ||
|     // Why?
 | ||
|     #define _WIN32_WINNT 0x0502
 | ||
|     // The standard Windows includes.
 | ||
|     #define WIN32_LEAN_AND_MEAN
 | ||
|     #define NOMINMAX
 | ||
|     #include <Windows.h>
 | ||
|     #include <wchar.h>
 | ||
|     #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 <cstdio>
 | ||
| #include <string>
 | ||
| #include <cstring>
 | ||
| #include <iostream>
 | ||
| #include <math.h>
 | ||
| 
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
| #include <condition_variable>
 | ||
| #include <mutex>
 | ||
| #include <boost/thread.hpp>
 | ||
| //add json logic
 | ||
| #include "nlohmann/json.hpp"
 | ||
| 
 | ||
| using namespace nlohmann;
 | ||
| #endif
 | ||
| 
 | ||
| #include <boost/algorithm/string/predicate.hpp>
 | ||
| #include <boost/filesystem.hpp>
 | ||
| #include <boost/nowide/args.hpp>
 | ||
| #include <boost/nowide/cenv.hpp>
 | ||
| #include <boost/nowide/iostream.hpp>
 | ||
| #include <boost/nowide/fstream.hpp>
 | ||
| #include <boost/nowide/integration/filesystem.hpp>
 | ||
| #include <boost/dll/runtime_symbol_info.hpp>
 | ||
| #include <boost/log/trivial.hpp>
 | ||
| 
 | ||
| #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 "BambuStudio.hpp"
 | ||
| //BBS: add exception handler for win32
 | ||
| #include <wx/stdpaths.h>
 | ||
| #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 <GLFW/glfw3.h>
 | ||
| 
 | ||
| #ifdef __WXGTK__
 | ||
| #include <X11/Xlib.h>
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef SLIC3R_GUI
 | ||
|     #include "slic3r/GUI/GUI_Init.hpp"
 | ||
| #endif /* SLIC3R_GUI */
 | ||
| 
 | ||
| using namespace Slic3r;
 | ||
| 
 | ||
| /*typedef struct _error_message{
 | ||
|     int code;
 | ||
|     std::string message;
 | ||
| }error_message;*/
 | ||
| 
 | ||
| 
 | ||
| std::map<int, std::string> cli_errors = {
 | ||
|     {CLI_SUCCESS, "Success."},
 | ||
|     {CLI_ENVIRONMENT_ERROR, "Failed setting up server environment."},
 | ||
|     {CLI_INVALID_PARAMS, "Invalid parameters to the slicer."},
 | ||
|     {CLI_FILE_NOTFOUND, "The input files to the slicer are not found."},
 | ||
|     {CLI_FILELIST_INVALID_ORDER, "File list order to the slicer is invalid. Please make sure the 3mf in the first place."},
 | ||
|     {CLI_CONFIG_FILE_ERROR, "The input preset file is invalid and can not be parsed."},
 | ||
|     {CLI_DATA_FILE_ERROR, "The input model file to the slicer can not be parsed."},
 | ||
|     {CLI_INVALID_PRINTER_TECH, "Unsupported printer technology (not FDM)."},
 | ||
|     {CLI_UNSUPPORTED_OPERATION, "Unsupported CLI instruction."},
 | ||
|     {CLI_COPY_OBJECTS_ERROR, "Failed copying objects."},
 | ||
|     {CLI_SCALE_TO_FIT_ERROR, "Failed scaling an object to fit the plate."},
 | ||
|     {CLI_EXPORT_STL_ERROR, "Failed exporting STL files."},
 | ||
|     {CLI_EXPORT_OBJ_ERROR, "Failed exporting OBJ files."},
 | ||
|     {CLI_EXPORT_3MF_ERROR, "Failed exporting 3mf files."},
 | ||
|     {CLI_OUT_OF_MEMORY, "Out of memory during slicing. Please upload a model with lower geometry resolution and try again."},
 | ||
|     {CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE, "The selected printer is not supported."},
 | ||
|     {CLI_3MF_NEW_MACHINE_NOT_SUPPORTED, "The selected printer is not compatible with the 3mf."},
 | ||
|     {CLI_PROCESS_NOT_COMPATIBLE, "The selected printer is not compatible with the process preset in the 3mf."},
 | ||
|     {CLI_INVALID_VALUES_IN_3MF, "Invalid parameter value(s) included in the 3mf file."},
 | ||
|     {CLI_POSTPROCESS_NOT_SUPPORTED, "post_process is not supported under CLI."},
 | ||
|     {CLI_PRINTABLE_SIZE_REDUCED, "The selected printer's bed size is smaller than the bed size used in the print profile."},
 | ||
|     {CLI_OBJECT_ARRANGE_FAILED, "An error occurred when auto-arranging object(s)."},
 | ||
|     {CLI_OBJECT_ORIENT_FAILED, "An error occurred when auto-orienting object(s)."},
 | ||
|     {CLI_NO_SUITABLE_OBJECTS, "An empty plate was found. Please check that all plates are not empty in Bambu Studio before uploading."},
 | ||
|     {CLI_VALIDATE_ERROR, "There are some incorrect slicing parameters in the 3mf. Please verify the slicing of all plates in Bambu Studio before uploading."},
 | ||
|     {CLI_OBJECTS_PARTLY_INSIDE, "Some objects are located over the boundary of the heated bed."},
 | ||
|     {CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED, "Failed creating directory when exporting cache data."},
 | ||
|     {CLI_EXPORT_CACHE_WRITE_FAILED, "Failed exporting cache data."},
 | ||
|     {CLI_IMPORT_CACHE_NOT_FOUND, "Cache data not found."},
 | ||
|     {CLI_IMPORT_CACHE_DATA_CAN_NOT_USE, "Cache data can not be parsed."},
 | ||
|     {CLI_IMPORT_CACHE_LOAD_FAILED, "Failed importing cache data."},
 | ||
|     {CLI_SLICING_TIME_EXCEEDS_LIMIT, "Slicing time of a certain plate exceeds the limit. Please simplify the model or use a larger slicing layer height."},
 | ||
|     {CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT, "Triangle count of single plate exceeds the limit. Please simplify the model and try to upload again."},
 | ||
|     {CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP, "No printable objects to slice after skipping."},
 | ||
|     {CLI_FILAMENT_NOT_MATCH_BED_TYPE, "Filaments are not compatible with the plate type. Please verify the slicing of all plates in Bambu Studio before uploading."},
 | ||
|     {CLI_FILAMENTS_DIFFERENT_TEMP, "The temperature difference of the filaments used is too large. Please verify the slicing of all plates in Bambu Studio before uploading."},
 | ||
|     {CLI_OBJECT_COLLISION_IN_SEQ_PRINT, "Object conflicts were detected when using print-by-object mode. Please verify the slicing of all plates in Bambu Studio before uploading."},
 | ||
|     {CLI_OBJECT_COLLISION_IN_LAYER_PRINT, "Object conflicts were detected. Please verify the slicing of all plates in Bambu Studio before uploading."},
 | ||
|     {CLI_SLICING_ERROR, "Failed slicing the model. Please verify the slicing of all plates on Bambu Studio before uploading."},
 | ||
|     {CLI_GCODE_PATH_CONFLICTS, " G-code conflicts detected after slicing. Please make sure the 3mf file can be successfully sliced in the latest Bambu Studio."}
 | ||
| };
 | ||
| 
 | ||
| #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<std::mutex> lck(m_mutex);
 | ||
|         result = m_started;
 | ||
|         lck.unlock();
 | ||
| 
 | ||
|         return result;
 | ||
|     }
 | ||
| 
 | ||
|     void set_plate_info(int index, int count)
 | ||
|     {
 | ||
|         BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": index="<<index<< ", count = "<< count;
 | ||
|         std::unique_lock<std::mutex> lck(m_mutex);
 | ||
|         m_plate_count = count;
 | ||
|         m_plate_index = index;
 | ||
|         m_progress = 0;
 | ||
|         lck.unlock();
 | ||
| 
 | ||
|         return;
 | ||
|     }
 | ||
| 
 | ||
|     void    notify()
 | ||
|     {
 | ||
|         if (m_pipe_fd < 0)
 | ||
|             return;
 | ||
| 
 | ||
|         json j;
 | ||
|         //record the headers
 | ||
|         j["plate_index"] = m_plate_index;
 | ||
|         j["plate_count"] = m_plate_count;
 | ||
|         j["plate_percent"] = m_progress;
 | ||
|         j["total_percent"] = m_total_progress;
 | ||
|         if (m_warning_step >= 0)
 | ||
|             j["warning"] = m_message;
 | ||
|         else
 | ||
|             j["message"] = m_message;
 | ||
| 
 | ||
|         std::string notify_message = j.dump();
 | ||
|         //notify_message = "Plate "+ std::to_string(m_plate_index) + "/" +std::to_string(m_plate_count)+  ": Percent " + std::to_string(m_progress) + ": "+m_message;
 | ||
| 
 | ||
|         char pipe_message[PIPE_BUFFER_SIZE] = {0};
 | ||
|         sprintf(pipe_message, "%s\n", notify_message.c_str());
 | ||
| 
 | ||
|         int ret = write(m_pipe_fd, pipe_message, strlen(pipe_message));
 | ||
|         BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": write returns "<<ret;
 | ||
| 
 | ||
|         return;
 | ||
|     }
 | ||
| 
 | ||
|     void    thread_proc()
 | ||
|     {
 | ||
|         std::unique_lock<std::mutex> 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<std::mutex> 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="<<percent<< ", warning_step=" << warning_step << ", plate_index = "<< m_plate_index<<", plate_count="<< m_plate_count<<", message="<<message;
 | ||
|         if (warning_step == -1) {
 | ||
|             m_progress = percent;
 | ||
|             if ((m_plate_count <= 1) && (m_plate_index >= 1))
 | ||
|                 m_total_progress = 3 + 0.9*m_progress;
 | ||
|             else if ((m_plate_count > 1) && (m_plate_index >= 1)) {
 | ||
|                 m_total_progress = 3 + ((float)(m_plate_index - 1)*90)/m_plate_count + ((float)m_progress*0.9)/m_plate_count;
 | ||
|             }
 | ||
|             else
 | ||
|                 m_total_progress = m_progress;
 | ||
|         }
 | ||
|         if (m_total_progress < old_total_progress)
 | ||
|             m_total_progress = old_total_progress;
 | ||
|         m_message = message;
 | ||
|         m_warning_step = warning_step;
 | ||
|         m_data_ready = true;
 | ||
|         lck.unlock();
 | ||
|         m_condition.notify_one();
 | ||
|         BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": m_total_progress="<<m_total_progress;
 | ||
|         return;
 | ||
|     }
 | ||
| 
 | ||
|     bool start(std::string pipe_name)
 | ||
|     {
 | ||
|         BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::start enter.";
 | ||
|         m_pipe_fd = open(pipe_name.c_str(),O_WRONLY|O_NONBLOCK);
 | ||
|         if (m_pipe_fd < 0) {
 | ||
|             BOOST_LOG_TRIVIAL(warning) << "could not create pipe for "<<pipe_name;
 | ||
|             return false;
 | ||
|         }
 | ||
|         std::unique_lock<std::mutex> 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<std::mutex> lck(m_mutex);
 | ||
|         if (!m_started) {
 | ||
|             lck.unlock();
 | ||
| 	    BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::stop not started before, return directly.";
 | ||
|             return;
 | ||
|         }
 | ||
|         m_exit = true;
 | ||
|         lck.unlock();
 | ||
|         m_condition.notify_one();
 | ||
|         // Wait until the worker thread exits.
 | ||
|         m_thread.join();
 | ||
|         if (m_pipe_fd > 0) {
 | ||
|             close(m_pipe_fd);
 | ||
|             m_pipe_fd = -1;
 | ||
|         }
 | ||
|         BOOST_LOG_TRIVIAL(info) << "cli_callback_mgr_t::stop successfully.";
 | ||
|     }
 | ||
| }cli_callback_mgr_t;
 | ||
| 
 | ||
| cli_callback_mgr_t g_cli_callback_mgr;
 | ||
| 
 | ||
| void cli_status_callback(const PrintBase::SlicingStatus& slicing_status)
 | ||
| {
 | ||
|     g_cli_callback_mgr.update(slicing_status.percent, slicing_status.text, slicing_status.warning_step);
 | ||
|     return;
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| static PrinterTechnology get_printer_technology(const DynamicConfig &config)
 | ||
| {
 | ||
|     const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("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 "<<ret<<", exit..." << std::endl;\
 | ||
|     g_cli_callback_mgr.stop();\
 | ||
|     boost::nowide::cout.flush();\
 | ||
|     boost::nowide::cerr.flush();\
 | ||
|     for (Model &model : m_models) {\
 | ||
|        model.remove_backup_path_if_exist();\
 | ||
|     }\
 | ||
|     return(ret);}
 | ||
| #else
 | ||
| #define flush_and_exit(ret)     { boost::nowide::cout << __FUNCTION__ << " found error, exit" << std::endl;\
 | ||
|     boost::nowide::cout.flush();\
 | ||
|     boost::nowide::cerr.flush();\
 | ||
|     for (Model &model : m_models) {\
 | ||
|        model.remove_backup_path_if_exist();\
 | ||
|     }\
 | ||
|     return(ret);}
 | ||
| #endif
 | ||
| 
 | ||
| void record_exit_reson(std::string outputdir, int code, int plate_id, std::string error_message,
 | ||
|     size_t prepare_time = 0, std::vector<size_t> sliced_time = std::vector<size_t>(), size_t export_time = 0, std::map<std::string, std::string> key_values = std::map<std::string, std::string>())
 | ||
| {
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|     std::string result_file;
 | ||
| 
 | ||
|     if (!outputdir.empty())
 | ||
|         result_file = outputdir + "/result.json";
 | ||
|     else
 | ||
|         result_file = "result.json";
 | ||
| 
 | ||
|     try {
 | ||
|         json j;
 | ||
|         //record the headers
 | ||
|         j["plate_index"] = plate_id;
 | ||
|         j["return_code"] = code;
 | ||
|         j["error_string"] = error_message;
 | ||
|         j["prepare_time"] = prepare_time;
 | ||
|         j["slice_time"] = sliced_time;
 | ||
|         j["export_time"] = export_time;
 | ||
|         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 void glfw_callback(int error_code, const char* description)
 | ||
| {
 | ||
|     BOOST_LOG_TRIVIAL(error) << "error_code " <<error_code <<", description: " <<description<< std::endl;
 | ||
| }
 | ||
| 
 | ||
| const float bed3d_ax3s_default_stem_radius = 0.5f;
 | ||
| const float bed3d_ax3s_default_stem_length = 25.0f;
 | ||
| const float bed3d_ax3s_default_tip_radius = 2.5f * bed3d_ax3s_default_stem_radius;
 | ||
| const float bed3d_ax3s_default_tip_length = 5.0f;
 | ||
| 
 | ||
| int CLI::run(int argc, char **argv)
 | ||
| {
 | ||
|     // Mark the main thread for the debugger and for runtime checks.
 | ||
|     set_current_thread_name("bambustu_main");
 | ||
|     // Save the thread ID of the main thread.
 | ||
|     save_main_thread_id();
 | ||
| 
 | ||
| #ifdef __WXGTK__
 | ||
|     // On Linux, wxGTK has no support for Wayland, and the app crashes on
 | ||
|     // startup if gtk3 is used. This env var has to be set explicitly to
 | ||
|     // instruct the window manager to fall back to X server mode.
 | ||
|     ::setenv("GDK_BACKEND", "x11", /* replace */ true);
 | ||
| 
 | ||
|     // Also on Linux, we need to tell Xlib that we will be using threads,
 | ||
|     // lest we crash when we fire up GStreamer.
 | ||
|     XInitThreads();
 | ||
| #endif
 | ||
| 
 | ||
| 	// Switch boost::filesystem to utf8.
 | ||
|     try {
 | ||
|         boost::nowide::nowide_filesystem();
 | ||
|     } catch (const std::runtime_error& ex) {
 | ||
|         std::string caption = std::string(SLIC3R_APP_FULL_NAME) + " Error";
 | ||
|         std::string text = std::string("boost::nowide::nowide_filesystem Failed!\n") + (
 | ||
|         	SLIC3R_APP_FULL_NAME " will now terminate.\n\n") + ex.what();
 | ||
|     #if defined(_WIN32) && defined(SLIC3R_GUI)
 | ||
|         if (m_actions.empty())
 | ||
|         	// Empty actions means Slicer is executed in the GUI mode. Show a GUI message.
 | ||
|             MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);
 | ||
|     #endif
 | ||
|         boost::nowide::cerr << text.c_str() << std::endl;
 | ||
|         return CLI_ENVIRONMENT_ERROR;
 | ||
|     }
 | ||
|     BOOST_LOG_TRIVIAL(info) << "Current BambuStudio Version "<< SLIC3R_VERSION << std::endl;
 | ||
| 
 | ||
|     /*BOOST_LOG_TRIVIAL(info) << "begin to setup params, argc=" << argc << std::endl;
 | ||
|     for (int index=0; index < argc; index++)
 | ||
|         BOOST_LOG_TRIVIAL(info) << "index="<< index <<", arg is "<< argv[index] <<std::endl;
 | ||
|     int debug_argc = 7;
 | ||
|     char *debug_argv[] = {
 | ||
|         "F:\work\projects\bambu_debug\bamboo_slicer\build_debug\src\Debug\bambu-studio.exe",
 | ||
|         //"--uptodate",
 | ||
|         //"--load-settings",
 | ||
|         //"machine.json;process.json",
 | ||
|         "--repetitions",
 | ||
|         "3",
 | ||
|         "--export-3mf=output.3mf",
 | ||
|         "--slice",
 | ||
|         "1",
 | ||
|         "test_repetitions.3mf"
 | ||
|         };
 | ||
|     if (! this->setup(debug_argc, debug_argv))*/
 | ||
|     if (!this->setup(argc, argv))
 | ||
|     {
 | ||
|         boost::nowide::cerr << "setup params error" << std::endl;
 | ||
|         return CLI_INVALID_PARAMS;
 | ||
|     }
 | ||
|     BOOST_LOG_TRIVIAL(info) << "finished setup params, argc="<< argc << std::endl;
 | ||
|     std::string temp_path = wxFileName::GetTempDir().utf8_str().data();
 | ||
|     set_temporary_dir(temp_path);
 | ||
| 
 | ||
|     m_extra_config.apply(m_config, true);
 | ||
|     m_extra_config.normalize_fdm();
 | ||
| 
 | ||
|     PrinterTechnology printer_technology = get_printer_technology(m_config);
 | ||
| 
 | ||
|     bool							start_gui			= m_actions.empty();
 | ||
| 
 | ||
|     //BBS: remove GCodeViewer as seperate APP logic
 | ||
|     /*bool 							start_as_gcodeviewer =
 | ||
| #ifdef _WIN32
 | ||
|             false;
 | ||
| #else
 | ||
|             // On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning.
 | ||
|             boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer");
 | ||
| #endif // _WIN32*/
 | ||
| 
 | ||
|     bool translate_old = false, regenerate_thumbnails = false, shrink_to_new_bed = false, filament_color_changed = false;
 | ||
|     int current_printable_width, current_printable_depth, current_printable_height;
 | ||
|     int old_printable_height = 0, old_printable_width = 0, old_printable_depth = 0;
 | ||
|     Pointfs old_printable_area, old_exclude_area;
 | ||
|     float old_max_radius = 0.f, old_height_to_rod = 0.f, old_height_to_lid = 0.f;
 | ||
|     std::string outfile_dir              =  m_config.opt_string("outputdir", true);
 | ||
|     const std::vector<std::string>              &load_configs               = m_config.option<ConfigOptionStrings>("load_settings", true)->values;
 | ||
|     const std::vector<std::string>              &uptodate_configs          = m_config.option<ConfigOptionStrings>("uptodate_settings", true)->values;
 | ||
|     //BBS: always use ForwardCompatibilitySubstitutionRule::Enable
 | ||
|     //const ForwardCompatibilitySubstitutionRule   config_substitution_rule = m_config.option<ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>>("config_compatibility", true)->value;
 | ||
|     const ForwardCompatibilitySubstitutionRule   config_substitution_rule = ForwardCompatibilitySubstitutionRule::Enable;
 | ||
|     const std::vector<std::string>              &load_filaments           = m_config.option<ConfigOptionStrings>("load_filaments", true)->values;
 | ||
|     //skip model object logic
 | ||
|     const std::vector<int>                      &skip_objects             = m_config.option<ConfigOptionInts>("skip_objects", true)->values;
 | ||
|     std::map<int, bool>     skip_maps;
 | ||
|     bool   need_skip      = (skip_objects.size() > 0)?true:false;
 | ||
|     long long global_begin_time = 0, global_current_time;
 | ||
|     std::vector<size_t> sliced_time;
 | ||
|     size_t prepare_time, export_time;
 | ||
| 
 | ||
|     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<std::string>    gcode_files;
 | ||
|         std::vector<std::string>    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 = "<<params.input_files.size();
 | ||
|         }
 | ||
|         else {
 | ||
|             params.input_files  = std::move(m_input_files);
 | ||
|             BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", normal mode, input_files size = "<<params.input_files.size();
 | ||
|         }
 | ||
|         //BBS: remove GCodeViewer as seperate APP logic
 | ||
|         //params.start_as_gcodeviewer = start_as_gcodeviewer;
 | ||
| 
 | ||
|         BOOST_LOG_TRIVIAL(info) << "begin to launch BambuStudio GUI soon";
 | ||
|         return Slic3r::GUI::GUI_Run(params);
 | ||
| #else // SLIC3R_GUI
 | ||
|         // No GUI support. Just print out a help.
 | ||
|         this->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<ConfigOptionInt>("debug");
 | ||
|         if (opt_loglevel) {
 | ||
|             set_logging_level(opt_loglevel->value);
 | ||
|         }
 | ||
|         else {
 | ||
|             set_logging_level(2);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     global_begin_time = (long long)Slic3r::Utils::get_current_time_utc();
 | ||
|     BOOST_LOG_TRIVIAL(info) << boost::format("cli mode, begin at %1%")%global_begin_time;
 | ||
| 
 | ||
|     //BBS: add plate data related logic
 | ||
|     PlateDataPtrs plate_data_src;
 | ||
|     int arrange_option;
 | ||
|     int plate_to_slice = 0, filament_count = 0, duplicate_count = 0, real_duplicate_count = 0;
 | ||
|     bool first_file = true, is_bbl_3mf = false, need_arrange = true, has_thumbnails = false, up_config_to_date = false, normative_check = true, duplicate_single_object = false, use_first_fila_as_default = false, minimum_save = false;
 | ||
|     Semver file_version;
 | ||
|     std::map<size_t, bool> orients_requirement;
 | ||
|     std::vector<Preset*> project_presets;
 | ||
|     std::string new_printer_name, current_printer_name, new_process_name, current_process_name, current_printer_system_name, current_process_system_name, new_process_system_name, new_printer_system_name;//, printer_inherits, print_inherits;
 | ||
|     std::vector<std::string> upward_compatible_printers, new_print_compatible_printers, current_print_compatible_printers, current_different_settings;
 | ||
|     std::vector<std::string> current_filaments_name, current_filaments_system_name, current_inherits_group;
 | ||
|     DynamicPrintConfig load_process_config, load_machine_config;
 | ||
|     bool new_process_config_is_system = true, new_printer_config_is_system = true;
 | ||
|     std::string pipe_name;
 | ||
| 
 | ||
|     // Read input file(s) if any.
 | ||
|     BOOST_LOG_TRIVIAL(info) << "Will start to read model file now, file count :" << m_input_files.size() << "\n";
 | ||
|     ConfigOptionInt* slice_option = m_config.option<ConfigOptionInt>("slice");
 | ||
|     if (slice_option)
 | ||
|         plate_to_slice = slice_option->value;
 | ||
| 
 | ||
|     ConfigOptionBool* normative_check_option = m_config.option<ConfigOptionBool>("normative_check");
 | ||
|     if (normative_check_option)
 | ||
|         normative_check = normative_check_option->value;
 | ||
| 
 | ||
|     ConfigOptionBool* uptodate_option = m_config.option<ConfigOptionBool>("uptodate");
 | ||
|     if (uptodate_option)
 | ||
|         up_config_to_date = uptodate_option->value;
 | ||
| 
 | ||
|     ConfigOptionBool* load_defaultfila_option = m_config.option<ConfigOptionBool>("load_defaultfila");
 | ||
|     if (load_defaultfila_option)
 | ||
|         use_first_fila_as_default = load_defaultfila_option->value;
 | ||
| 
 | ||
|     ConfigOptionBool* min_save_option = m_config.option<ConfigOptionBool>("min_save");
 | ||
|     if (min_save_option)
 | ||
|         minimum_save = min_save_option->value;
 | ||
| 
 | ||
|     ConfigOptionString* pipe_option = m_config.option<ConfigOptionString>("pipe");
 | ||
|     if (pipe_option) {
 | ||
|         pipe_name = pipe_option->value;
 | ||
|         if (!pipe_name.empty()) {
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("Will use pipe %1%")%pipe_name;
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|             g_cli_callback_mgr.start(pipe_name);
 | ||
|             PrintBase::SlicingStatus slicing_status{1, "Start to load files"};
 | ||
|             cli_status_callback(slicing_status);
 | ||
| #endif
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //skip model object map construct
 | ||
|     if (need_skip) {
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("need to skip objects, size %1%:")%skip_objects.size();
 | ||
|         for (int index = 0; index < skip_objects.size(); index++)
 | ||
|         {
 | ||
|             skip_maps[skip_objects[index]] = false;
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("object %1%, id %2%")%index %skip_objects[index];
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     /*for (const std::string& file : m_input_files)
 | ||
|         if (is_gcode_file(file) && boost::filesystem::exists(file)) {
 | ||
|             start_as_gcodeviewer = true;
 | ||
|             BOOST_LOG_TRIVIAL(info) << "found a gcode file:" << file << ", will start as gcode viewer\n";
 | ||
|             break;
 | ||
|         }*/
 | ||
|     BOOST_LOG_TRIVIAL(info) << boost::format("plate_to_slice=%1%, normative_check=%2%, use_first_fila_as_default=%3%")%plate_to_slice %normative_check %use_first_fila_as_default;
 | ||
|     //if (!start_as_gcodeviewer) {
 | ||
|         for (const std::string& file : m_input_files) {
 | ||
|             if (!boost::filesystem::exists(file)) {
 | ||
|                 boost::nowide::cerr << "No such file: " << file << std::endl;
 | ||
|                 record_exit_reson(outfile_dir, CLI_FILE_NOTFOUND, 0, cli_errors[CLI_FILE_NOTFOUND]);
 | ||
|                 flush_and_exit(CLI_FILE_NOTFOUND);
 | ||
|             }
 | ||
|             Model model;
 | ||
|             //BBS: add plate related logic
 | ||
|             //bool load_aux = false;
 | ||
|             BOOST_LOG_TRIVIAL(info) << "read model file:" << file << "\n";
 | ||
|             try {
 | ||
|                 // When loading an AMF or 3MF, config is imported as well, including the printer technology.
 | ||
|                 DynamicPrintConfig config;
 | ||
|                 ConfigSubstitutionContext config_substitutions(config_substitution_rule);
 | ||
| 
 | ||
|                 //FIXME should we check the version here? // | LoadStrategy::CheckVersion ?
 | ||
|                 is_bbl_3mf = false;
 | ||
|                 LoadStrategy strategy;
 | ||
|                 if (boost::algorithm::iends_with(file, ".3mf") && first_file) {
 | ||
|                     strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances | LoadStrategy::LoadAuxiliary;
 | ||
|                     //load_aux = true;
 | ||
|                 }
 | ||
|                 else
 | ||
|                     strategy = LoadStrategy::LoadModel | LoadStrategy::AddDefaultInstances;
 | ||
|                 // BBS: adjust whebackup
 | ||
|                 //LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances;
 | ||
|                 //if (load_aux) strategy = strategy | LoadStrategy::LoadAuxiliary;
 | ||
|                 model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, nullptr, nullptr, plate_to_slice);
 | ||
|                 if (is_bbl_3mf)
 | ||
|                 {
 | ||
|                     if (!first_file)
 | ||
|                     {
 | ||
|                         BOOST_LOG_TRIVIAL(info) << "The BBL 3mf file should be placed at the first position, filename=" << file << "\n";
 | ||
|                         record_exit_reson(outfile_dir, CLI_FILELIST_INVALID_ORDER, 0, cli_errors[CLI_FILELIST_INVALID_ORDER]);
 | ||
|                         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<size_t, bool>(o->id().id, false));
 | ||
|                         BOOST_LOG_TRIVIAL(info) << "object "<<o->name <<", id :" << o->id().id << ", from bbl 3mf\n";
 | ||
|                     }
 | ||
| 
 | ||
|                     Semver old_version(1, 5, 9), old_version2(1, 5, 9);
 | ||
|                     if ((file_version < old_version) && !config.empty()) {
 | ||
|                         translate_old = true;
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to translate")%file_version.to_string();
 | ||
|                     }
 | ||
|                     if ((file_version < old_version2) && !config.empty()) {
 | ||
|                         regenerate_thumbnails = true;
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to regenerate_thumbnails for all")%file_version.to_string();
 | ||
|                     }
 | ||
| 
 | ||
|                     if (normative_check) {
 | ||
|                         ConfigOptionStrings* postprocess_scripts = config.option<ConfigOptionStrings>("post_process");
 | ||
|                         if (postprocess_scripts) {
 | ||
|                             std::vector<std::string> 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]);
 | ||
|                                 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<ConfigOptionString>("printer_settings_id")->value;
 | ||
|                     current_process_name = config.option<ConfigOptionString>("print_settings_id")->value;
 | ||
|                     current_filaments_name = config.option<ConfigOptionStrings>("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<ConfigOptionStrings>("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<ConfigOptionStrings>("upward_compatible_machine", true)->values;
 | ||
|                     current_print_compatible_printers  = config.option<ConfigOptionStrings>("print_compatible_printers", true)->values;
 | ||
|                     current_different_settings = config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
 | ||
| 
 | ||
|                     //use Pointfs insteadof Points
 | ||
|                     old_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values;
 | ||
|                     old_exclude_area = config.option<ConfigOptionPoints>("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<ConfigOptionFloat>("extruder_clearance_height_to_rod"))
 | ||
|                         old_height_to_rod = config.opt_float("extruder_clearance_height_to_rod");
 | ||
|                     if (config.option<ConfigOptionFloat>("extruder_clearance_height_to_lid"))
 | ||
|                         old_height_to_lid = config.opt_float("extruder_clearance_height_to_lid");
 | ||
|                     if (config.option<ConfigOptionFloat>("extruder_clearance_max_radius"))
 | ||
|                         old_max_radius = config.opt_float("extruder_clearance_max_radius");
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("old printable size from 3mf: {%1%, %2%, %3%}")%old_printable_width %old_printable_depth %old_printable_height;
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("old extruder_clearance_height_to_rod %1%, extruder_clearance_height_to_lid %2%, extruder_clearance_max_radius %3%}")%old_height_to_rod %old_height_to_lid %old_max_radius;
 | ||
|                 }
 | ||
|                 else
 | ||
|                 {
 | ||
|                     need_arrange = true;
 | ||
|                     for (ModelObject* o : model.objects)
 | ||
|                     {
 | ||
|                         orients_requirement.insert(std::pair<size_t, bool>(o->id().id, true));
 | ||
|                         BOOST_LOG_TRIVIAL(info) << "object "<<o->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 " <<printer_technology<<", from source file "<< file <<std::endl;
 | ||
|                     record_exit_reson(outfile_dir, CLI_INVALID_PRINTER_TECH, 0, cli_errors[CLI_INVALID_PRINTER_TECH]);
 | ||
|                     flush_and_exit(CLI_INVALID_PRINTER_TECH);
 | ||
|                 }
 | ||
|                 if (!config_substitutions.substitutions.empty()) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << "Found legacy configuration values, substituted when loading " << file << ":\n";
 | ||
|                     for (const ConfigSubstitution &subst : config_substitutions.substitutions)
 | ||
|                         BOOST_LOG_TRIVIAL(info) << "\tkey = \"" << subst.opt_def->opt_key << "\"\t old_value = \"" << subst.old_value << "\tnew_value = \"" << subst.new_value->serialize() << "\"\n";
 | ||
|                 }
 | ||
| 
 | ||
|                 // config is applied to m_print_config before the current m_config values.
 | ||
|                 config += std::move(m_print_config);
 | ||
|                 m_print_config = std::move(config);
 | ||
|             }
 | ||
|             catch (std::exception& e) {
 | ||
|                 boost::nowide::cerr << file << ": " << e.what() << std::endl;
 | ||
|                 record_exit_reson(outfile_dir, CLI_DATA_FILE_ERROR, 0, cli_errors[CLI_DATA_FILE_ERROR]);
 | ||
|                 flush_and_exit(CLI_DATA_FILE_ERROR);
 | ||
|             }
 | ||
|             if (model.objects.empty()) {
 | ||
|                 boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
 | ||
|                 continue;
 | ||
|             }
 | ||
|             m_models.push_back(std::move(model));
 | ||
|         }
 | ||
|     //}
 | ||
| 
 | ||
|     auto load_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<std::string, std::string> 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 "<<file<<"\n";
 | ||
|                 return CLI_CONFIG_FILE_ERROR;
 | ||
|             }
 | ||
| 
 | ||
|             config_name = key_values[BBL_JSON_KEY_NAME];
 | ||
|             auto from_iter = key_values.find(BBL_JSON_KEY_FROM);
 | ||
|             if (from_iter != key_values.end()) {
 | ||
|                 config_from = from_iter->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<ConfigOptionString>("inherits", true)->value;
 | ||
|             }
 | ||
|             else if (config_type == "process") {
 | ||
|                 //config.set("print_settings_id", config_name, true);
 | ||
|                 //print_inherits = config.option<ConfigOptionString>("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<std::string> 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]);
 | ||
|             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]);
 | ||
|                 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<ConfigOptionString>("inherits", true)->value;
 | ||
|                 new_printer_config_is_system = false;
 | ||
|             }
 | ||
|             config.set("printer_settings_id", new_printer_name, true);
 | ||
|             //printer_inherits = config.option<ConfigOptionString>("inherits", true)->value;
 | ||
|             load_machine_config = std::move(config);
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("loaded machine config %1%, type %2%, name %3%, inherits %4%")%file %config_name %config_from % new_printer_system_name;
 | ||
|         }
 | ||
|         else if (config_type == "process") {
 | ||
|             if (!new_process_name.empty()) {
 | ||
|                 boost::nowide::cerr << "duplicate process config file: " << file << std::endl;
 | ||
|                 record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR]);
 | ||
|                 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<ConfigOptionString>("inherits", true)->value;
 | ||
|                 new_process_config_is_system = false;
 | ||
|             }
 | ||
|             config.set("print_settings_id", new_process_name, true);
 | ||
|             //print_inherits = config.option<ConfigOptionString>("inherits", true)->value;
 | ||
|             new_print_compatible_printers = config.option<ConfigOptionStrings>("compatible_printers", true)->values;
 | ||
|             load_process_config = std::move(config);
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("loaded process config %1%, type %2%, name %3%, inherits %4%")%file %config_name %config_from % new_process_system_name;
 | ||
|         }
 | ||
| 
 | ||
|         PrinterTechnology other_printer_technology = get_printer_technology(config);
 | ||
|         if (printer_technology == ptUnknown) {
 | ||
|             printer_technology = other_printer_technology;
 | ||
|         }
 | ||
| 
 | ||
|         if ((printer_technology != other_printer_technology)&&(other_printer_technology != ptUnknown)){
 | ||
|             boost::nowide::cerr << "invalid printer_technology " <<printer_technology<<", from config "<< file <<std::endl;
 | ||
|             record_exit_reson(outfile_dir, CLI_INVALID_PRINTER_TECH, 0, cli_errors[CLI_INVALID_PRINTER_TECH]);
 | ||
|             flush_and_exit(CLI_INVALID_PRINTER_TECH);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //load filaments files
 | ||
|     int load_filament_count = load_filaments.size();
 | ||
|     std::vector<int> load_filaments_index;
 | ||
|     std::vector<DynamicPrintConfig> load_filaments_config;
 | ||
|     std::vector<std::string> load_filaments_id;
 | ||
|     std::vector<std::string> 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]);
 | ||
|                     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]);
 | ||
|                     flush_and_exit(CLI_CONFIG_FILE_ERROR);
 | ||
|                 }
 | ||
| 
 | ||
|                 if ((config_from == "User")||(config_from == "user")) {
 | ||
|                     default_filament_inherit = config.option<ConfigOptionString>("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]);
 | ||
|             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]);
 | ||
|                 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]);
 | ||
|                 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 " <<printer_technology<<", from filament file "<< file;
 | ||
|                 record_exit_reson(outfile_dir, CLI_INVALID_PRINTER_TECH, 0, cli_errors[CLI_INVALID_PRINTER_TECH]);
 | ||
|                 flush_and_exit(CLI_INVALID_PRINTER_TECH);
 | ||
|             }
 | ||
|             std::string inherits;
 | ||
|             if ((config_from == "User")||(config_from == "user")) {
 | ||
|                 inherits = config.option<ConfigOptionString>("inherits", true)->value;
 | ||
|             }
 | ||
|             load_filaments_inherit.push_back(inherits);
 | ||
|             load_filaments_id.push_back(filament_id);
 | ||
|             load_filaments_name.push_back(config_name);
 | ||
|             load_filaments_config.push_back(std::move(config));
 | ||
|             load_filaments_index.push_back(current_index);
 | ||
| 
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("loaded filament %1% from file %2%, type %3%, name %4%, inherits %5%")%(index+1) %file %config_from %config_name % inherits;
 | ||
|         }
 | ||
|         else {
 | ||
|             if (use_first_fila_as_default) {
 | ||
|                 BOOST_LOG_TRIVIAL(info)<<__FUNCTION__ << boost::format(": load filament %1% from default, config name %2%, filament_id %3%, current_index %4%") % (index+1) % default_load_fila_name %default_load_fila_id %current_index;
 | ||
|                 load_filaments_id.push_back(default_load_fila_id);
 | ||
|                 load_filaments_name.push_back(default_load_fila_name);
 | ||
|                 load_filaments_config.push_back(default_load_fila_config);
 | ||
|                 load_filaments_index.push_back(current_index);
 | ||
|                 load_filaments_inherit.push_back(default_filament_inherit);
 | ||
|             }
 | ||
|             continue;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     if (filament_count == 0)
 | ||
|         filament_count = load_filament_count;
 | ||
| 
 | ||
|     //load system config if needed
 | ||
|     bool fetch_compatible_values = false, fetch_upward_values = false;
 | ||
|     if (is_bbl_3mf && up_config_to_date) {
 | ||
|         if (uptodate_configs.size() > 0)
 | ||
|         {
 | ||
|             for (auto const &file : uptodate_configs) {
 | ||
|                 DynamicPrintConfig  config;
 | ||
|                 std::string config_type, config_name, filament_id, config_from;
 | ||
|                 int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from);
 | ||
|                 if (ret) {
 | ||
|                     record_exit_reson(outfile_dir, ret, 0, cli_errors[ret]);
 | ||
|                     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]);
 | ||
|                         flush_and_exit(CLI_CONFIG_FILE_ERROR);
 | ||
|                     }
 | ||
|                     upward_compatible_printers = config.option<ConfigOptionStrings>("upward_compatible_machine", true)->values;
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("load a machine config %1% from file %2%, upward_compatible_printers size is %3% ")%config_name %file %upward_compatible_printers.size();
 | ||
|                     if (new_printer_name.empty() && !current_printer_system_name.empty())
 | ||
|                     {
 | ||
|                         config.set("printer_settings_id", config_name, true);
 | ||
|                         load_machine_config = std::move(config);
 | ||
|                     }
 | ||
|                 }
 | ||
|                 else if (config_type == "process") {
 | ||
|                     if ( config_name != current_process_system_name ) {
 | ||
|                         BOOST_LOG_TRIVIAL(error) << boost::format("wrong process config file %1% loaded, current process config name %2% ")%config_name %current_process_system_name;
 | ||
|                         record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR]);
 | ||
|                         flush_and_exit(CLI_CONFIG_FILE_ERROR);
 | ||
|                     }
 | ||
|                     current_print_compatible_printers  = config.option<ConfigOptionStrings>("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]);
 | ||
|                     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]);
 | ||
|                         flush_and_exit(ret);
 | ||
|                     }
 | ||
|                     upward_compatible_printers = config.option<ConfigOptionStrings>("upward_compatible_machine", true)->values;
 | ||
|                     config.set("printer_settings_id", config_name, true);
 | ||
|                     load_machine_config = std::move(config);
 | ||
|                 }
 | ||
|             }
 | ||
|             else
 | ||
|                 fetch_upward_values = true;
 | ||
| 
 | ||
|             if (new_process_name.empty() && !current_process_system_name.empty()) {
 | ||
|                 //use the original printer name in 3mf
 | ||
|                 std::string system_process_path = resources_dir() + "/profiles/BBL/process_full/"+current_process_system_name+".json";
 | ||
|                 if (! boost::filesystem::exists(system_process_path)) {
 | ||
|                     BOOST_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_process_path;
 | ||
|                     //use original one
 | ||
|                 }
 | ||
|                 else {
 | ||
|                     DynamicPrintConfig  config;
 | ||
|                     std::string config_type, config_name, filament_id, config_from;
 | ||
|                     int ret = load_config_file(system_process_path, config, config_type, config_name, filament_id, config_from);
 | ||
|                     if (ret) {
 | ||
|                         record_exit_reson(outfile_dir, ret, 0, cli_errors[ret]);
 | ||
|                         flush_and_exit(ret);
 | ||
|                     }
 | ||
|                     current_print_compatible_printers  = config.option<ConfigOptionStrings>("compatible_printers", true)->values;
 | ||
|                     config.set("print_settings_id", config_name, true);
 | ||
|                     load_process_config = std::move(config);
 | ||
|                 }
 | ||
|             }
 | ||
|             else
 | ||
|                 fetch_compatible_values = true;
 | ||
|         }
 | ||
| 
 | ||
|         //20230802 lhwei: remove below codes, don't replace filament currently
 | ||
|         /*if (load_filaments_config.empty() && !current_filaments_system_name.empty()) {
 | ||
|             for (int index = 0; index < current_filaments_system_name.size(); index++) {
 | ||
|                 std::string system_filament_path = resources_dir() + "/profiles/BBL/filament_full/"+current_filaments_system_name[index]+".json";
 | ||
|                 current_index++;
 | ||
|                 if (! boost::filesystem::exists(system_filament_path)) {
 | ||
|                     BOOST_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_filament_path;
 | ||
|                     continue;
 | ||
|                 }
 | ||
|                 DynamicPrintConfig  config;
 | ||
|                 std::string config_type, config_name, filament_id, config_from;
 | ||
|                 int ret = load_config_file(system_filament_path, config, config_type, config_name, filament_id, config_from);
 | ||
|                 if (ret) {
 | ||
|                     record_exit_reson(outfile_dir, ret, 0, cli_errors[ret]);
 | ||
|                     flush_and_exit(ret);
 | ||
|                 }
 | ||
| 
 | ||
|                 load_filaments_id.push_back(filament_id);
 | ||
|                 load_filaments_name.push_back(config_name);
 | ||
|                 load_filaments_config.push_back(std::move(config));
 | ||
|                 load_filaments_index.push_back(current_index);
 | ||
|                 std::string inherits;
 | ||
|                 if ((config_from == "User")||(config_from == "user")) {
 | ||
|                     inherits = config.option<ConfigOptionString>("inherits", true)->value;
 | ||
|                 }
 | ||
|                 load_filaments_inherit.push_back(inherits);
 | ||
|             }
 | ||
|         }*/
 | ||
|     }
 | ||
|     else if (is_bbl_3mf){
 | ||
|         fetch_upward_values = true;
 | ||
|         fetch_compatible_values = true;
 | ||
|     }
 | ||
| 
 | ||
|     //fetch upward_compatible_machine
 | ||
|     if (fetch_upward_values) {
 | ||
|         if (!current_printer_system_name.empty()) {
 | ||
|             //use the original printer name in 3mf
 | ||
|             std::string system_printer_path = resources_dir() + "/profiles/BBL/machine_full/"+current_printer_system_name+".json";
 | ||
|             if (! boost::filesystem::exists(system_printer_path)) {
 | ||
|                 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_printer_path;
 | ||
|                 //skip
 | ||
|             }
 | ||
|             else {
 | ||
|                 DynamicPrintConfig  config;
 | ||
|                 std::string config_type, config_name, filament_id, config_from;
 | ||
|                 int ret = load_config_file(system_printer_path, config, config_type, config_name, filament_id, config_from);
 | ||
|                 if (ret) {
 | ||
|                     record_exit_reson(outfile_dir, ret, 0, cli_errors[ret]);
 | ||
|                     flush_and_exit(ret);
 | ||
|                 }
 | ||
|                 upward_compatible_printers = config.option<ConfigOptionStrings>("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]);
 | ||
|                     flush_and_exit(ret);
 | ||
|                 }
 | ||
|                 current_print_compatible_printers  = config.option<ConfigOptionStrings>("compatible_printers", true)->values;
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //upwards check
 | ||
|     bool process_compatible = false, machine_upwards = false;
 | ||
|     BOOST_LOG_TRIVIAL(info) << boost::format("current printer %1%, new printer %2%, current process %3%, new process %4%")%current_printer_name %new_printer_name %current_process_name %new_process_name;
 | ||
|     BOOST_LOG_TRIVIAL(info) << boost::format("current printer inherits %1%, new printer inherits %2%, current process inherits %3%, new process inherits %4%")
 | ||
|         %current_printer_system_name %new_printer_system_name %current_process_system_name %new_process_system_name;
 | ||
|     for (int index = 0; index < current_print_compatible_printers.size(); index++) {
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, current print compatible printer %2%")%index %current_print_compatible_printers[index];
 | ||
|     }
 | ||
|     for (int index = 0; index < new_print_compatible_printers.size(); index++) {
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, new print compatible printer %2%")%index %new_print_compatible_printers[index];
 | ||
|     }
 | ||
|     for (int index = 0; index < upward_compatible_printers.size(); index++) {
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, upward_compatible_printers %2%")%index %upward_compatible_printers[index];
 | ||
|     }
 | ||
|     if (!new_printer_name.empty()) {
 | ||
|         if (!new_process_name.empty()) {
 | ||
|             for (int index = 0; index < new_print_compatible_printers.size(); index++) {
 | ||
|                 if (new_print_compatible_printers[index] == new_printer_system_name) {
 | ||
|                     process_compatible = true;
 | ||
|                     break;
 | ||
|                 }
 | ||
|             }
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, inherited from %2%, new process %3%, inherited from %4% ,compatible %5%")
 | ||
|                 %new_printer_name %new_printer_system_name %new_process_name %new_process_system_name %process_compatible;
 | ||
|         }
 | ||
|         else {
 | ||
|             for (int index = 0; index < current_print_compatible_printers.size(); index++) {
 | ||
|                 if (current_print_compatible_printers[index] == new_printer_system_name) {
 | ||
|                     process_compatible = true;
 | ||
|                     break;
 | ||
|                 }
 | ||
|             }
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, inherited from %2%, old process %3%, inherited from %4% ,compatible %5%")
 | ||
|                 %new_printer_name %new_printer_system_name %current_process_name %current_process_system_name %process_compatible;
 | ||
|         }
 | ||
|     }
 | ||
|     else if (!new_process_name.empty()) {
 | ||
|         for (int index = 0; index < new_print_compatible_printers.size(); index++) {
 | ||
|             if (new_print_compatible_printers[index] == current_printer_system_name) {
 | ||
|                 process_compatible = true;
 | ||
|                 break;
 | ||
|             }
 | ||
|         }
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("old printer %1%, inherited from %2%, new process %3%, inherited from %4% ,compatible %5%")
 | ||
|             %current_printer_name %current_printer_system_name %new_process_name %new_process_system_name %process_compatible;
 | ||
|     }
 | ||
|     else {
 | ||
|         //check the compatible of old printer&&process
 | ||
|         for (int index = 0; index < current_print_compatible_printers.size(); index++) {
 | ||
|             if (current_print_compatible_printers[index] == current_printer_system_name) {
 | ||
|                 process_compatible = true;
 | ||
|                 break;
 | ||
|             }
 | ||
|         }
 | ||
|         if (!process_compatible && current_print_compatible_printers.empty())
 | ||
|         {
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf, no compatible printers, set to compatible");
 | ||
|             process_compatible = true;
 | ||
|         }
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("old printer %1%, inherited from %2%, old process %3%, inherited from %4% ,compatible %5%")
 | ||
|             %current_printer_name %current_printer_system_name %current_process_name %current_process_system_name %process_compatible;
 | ||
|     }
 | ||
|     if (!process_compatible && !new_printer_name.empty() && !current_printer_name.empty() && (new_printer_name != current_printer_name)) {
 | ||
|         if (upward_compatible_printers.size() > 0) {
 | ||
|             for (int index = 0; index < upward_compatible_printers.size(); index++) {
 | ||
|                 if (upward_compatible_printers[index] == new_printer_system_name) {
 | ||
|                     process_compatible = true;
 | ||
|                     machine_upwards = true;
 | ||
|                     break;
 | ||
|                 }
 | ||
|             }
 | ||
|             if (!process_compatible) {
 | ||
|                 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(" %1% : current 3mf file not support the new printer %2%, new_printer_system_name %3%")%__LINE__%new_printer_name %new_printer_system_name;
 | ||
|                 record_exit_reson(outfile_dir, CLI_3MF_NEW_MACHINE_NOT_SUPPORTED, 0, cli_errors[CLI_3MF_NEW_MACHINE_NOT_SUPPORTED]);
 | ||
|                 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]);
 | ||
|             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]);
 | ||
|         flush_and_exit(CLI_PROCESS_NOT_COMPATIBLE);
 | ||
|     }
 | ||
| 
 | ||
|     //create project embedded preset if needed
 | ||
|     Preset *new_preset = NULL;
 | ||
|     if (is_bbl_3mf && machine_upwards) {
 | ||
|         //we need to update the compatible printer and create a new process here, or if we load the 3mf in studio, the process preset can not be loaded as not compatible
 | ||
|         Preset *current_preset = NULL;
 | ||
|         size_t project_presets_count = project_presets.size();
 | ||
|         for (int index = 0; index < project_presets_count; index++)
 | ||
|         {
 | ||
|             if (project_presets[index]->name == current_process_name) {
 | ||
|                 current_preset = project_presets[index];
 | ||
|                 break;
 | ||
|             }
 | ||
|         }
 | ||
|         new_preset = new Preset(Preset::TYPE_PRINT, current_process_name);
 | ||
|         if (current_preset) {
 | ||
|             *new_preset = *current_preset;
 | ||
|             std::vector<std::string>& compatible_printers = new_preset->config.option<ConfigOptionStrings>("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<std::string>& process_keys = Preset::print_options();
 | ||
|             new_preset->config.apply_only(m_print_config, process_keys);
 | ||
|             std::vector<std::string>& compatible_printers = new_preset->config.option<ConfigOptionStrings>("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<ConfigOptionString>("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<std::string>& diff_key_sets, bool update_all = false) {
 | ||
|         for (const t_config_option_key &opt_key : config.keys()) {
 | ||
|             if (!update_all && !diff_key_sets.empty() && (diff_key_sets.find(opt_key) != diff_key_sets.end())) {
 | ||
|                 //uptodate, diff keys, continue
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("keep key %1%")%opt_key;
 | ||
|                 continue;
 | ||
|             }
 | ||
|             const ConfigOption *source_opt = config.option(opt_key);
 | ||
|             if (source_opt == nullptr) {
 | ||
|                 // The key was not found in the source config, therefore it will not be initialized!
 | ||
|                 boost::nowide::cerr << __FUNCTION__<<": can not found option " <<opt_key<<"from config." <<std::endl;
 | ||
|                 return CLI_CONFIG_FILE_ERROR;
 | ||
|             }
 | ||
|             if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "model_id" || opt_key == "inherits" ||opt_key == "dev_model_name"
 | ||
|                 || opt_key == "name" || opt_key == "from" || opt_key == "type" || opt_key == "version" || opt_key == "setting_id" || opt_key == "instantiation" )
 | ||
|                 continue;
 | ||
|             else {
 | ||
|                 ConfigOption *dest_opt = full_config.option(opt_key, true);
 | ||
|                 if (dest_opt == nullptr) {
 | ||
|                     boost::nowide::cerr << __FUNCTION__<<":can not create option " <<opt_key<<" to full_config "<<std::endl;
 | ||
|                     return CLI_CONFIG_FILE_ERROR;
 | ||
|                 }
 | ||
|                 dest_opt->set(source_opt);
 | ||
|                 //*dest_opt = *source_opt;
 | ||
|             }
 | ||
|         }
 | ||
|         return 0;
 | ||
|     };
 | ||
| 
 | ||
|     std::vector<std::string>& different_settings = m_print_config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
 | ||
|     std::vector<std::string>& inherits_group = m_print_config.option<ConfigOptionStrings>("inherits_group", true)->values;
 | ||
|     inherits_group.resize(filament_count + 2, std::string());
 | ||
|     different_settings.resize(filament_count + 2, std::string());
 | ||
|     //set the machine settings into print config
 | ||
|     if (!new_printer_name.empty() || up_config_to_date) {
 | ||
|         std::vector<std::string> 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<std::string> different_keys_set(different_keys.begin(), different_keys.end());
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("update printer config to newest, different size %1%")%different_keys_set.size();
 | ||
| 
 | ||
|         int ret;
 | ||
|         if (new_printer_name.empty()) {
 | ||
|             ret = update_full_config(m_print_config, load_machine_config, different_keys_set);
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("no new printer, only update the different key, ret %1%")%ret;
 | ||
|         }
 | ||
|         else {
 | ||
|             ret = update_full_config(m_print_config, load_machine_config, different_keys_set, true);
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("load a new printer, update all the keys, ret %1%")%ret;
 | ||
|         }
 | ||
| 
 | ||
|         if (ret) {
 | ||
|             record_exit_reson(outfile_dir, ret, 0, cli_errors[ret]);
 | ||
|             flush_and_exit(ret);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //set the process settings into print config
 | ||
|     std::vector<std::string>& print_compatible_printers = m_print_config.option<ConfigOptionStrings>("print_compatible_printers", true)->values;
 | ||
|     if (!new_process_name.empty() || up_config_to_date) {
 | ||
|         std::vector<std::string> 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<std::string> different_keys_set(different_keys.begin(), different_keys.end());
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("update process config to newest, different size %1%")%different_keys_set.size();
 | ||
| 
 | ||
|         int ret;
 | ||
|         if (new_process_name.empty()) {
 | ||
|             ret = update_full_config(m_print_config, load_process_config, different_keys_set);
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("no new process, only update the different key, ret %1%")%ret;
 | ||
|         }
 | ||
|         else {
 | ||
|             ret = update_full_config(m_print_config, load_process_config, different_keys_set, true);
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("load a new process, update all the keys, ret %1%")%ret;
 | ||
|         }
 | ||
| 
 | ||
|         if (ret) {
 | ||
|             record_exit_reson(outfile_dir, ret, 0, cli_errors[ret]);
 | ||
|             flush_and_exit(ret);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     if (machine_upwards) {
 | ||
|         print_compatible_printers.push_back(new_printer_system_name);
 | ||
| 
 | ||
|         std::string old_setting = different_settings[0];
 | ||
|         if (old_setting.empty())
 | ||
|             different_settings[0] = "compatible_printers";
 | ||
|         else {
 | ||
|             std::vector<std::string> 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<std::string> different_keys;
 | ||
| 
 | ||
|             if (load_filament_count > 0) {
 | ||
|                 ConfigOptionStrings *opt_filament_settings = static_cast<ConfigOptionStrings *> (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<ConfigOptionStrings *> (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<std::string> different_keys_set(different_keys.begin(), different_keys.end());
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("update filament %1%'s config to newest, different size %2%")%filament_index%different_keys_set.size();
 | ||
|             for (const t_config_option_key &opt_key : config.keys()) {
 | ||
|                 if ((load_filament_count == 0) && !different_keys_set.empty() && (different_keys_set.find(opt_key) != different_keys_set.end())) {
 | ||
|                     //uptodate, diff keys, continue
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("keep key %1%")%opt_key;
 | ||
|                     continue;
 | ||
|                 }
 | ||
|                 // Create a new option with default value for the key.
 | ||
|                 // If the key is not in the parameter definition, or this ConfigBase is a static type and it does not support the parameter,
 | ||
|                 // an exception is thrown if not ignore_nonexistent.
 | ||
| 
 | ||
|                 const ConfigOption *source_opt = config.option(opt_key);
 | ||
|                 if (source_opt == nullptr) {
 | ||
|                     // The key was not found in the source config, therefore it will not be initialized!
 | ||
|                     BOOST_LOG_TRIVIAL(error) << boost::format("can not find %1% from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index];
 | ||
|                     record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR]);
 | ||
|                     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<ConfigOptionStrings*>(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<ConfigOptionStrings*>(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]);
 | ||
|                         flush_and_exit(CLI_CONFIG_FILE_ERROR);
 | ||
|                     }
 | ||
|                     ConfigOptionVectorBase* opt_vec_dst = static_cast<ConfigOptionVectorBase*>(opt);
 | ||
|                     const ConfigOptionVectorBase* opt_vec_src = static_cast<const ConfigOptionVectorBase*>(source_opt);
 | ||
|                     opt_vec_dst->set_at(opt_vec_src, filament_index-1, 0);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //compute the flush volume
 | ||
|     ConfigOptionStrings* selected_filament_colors_option = m_extra_config.option<ConfigOptionStrings>("filament_colour");
 | ||
|     ConfigOptionStrings *project_filament_colors_option = m_print_config.option<ConfigOptionStrings>("filament_colour");
 | ||
|     if (project_filament_colors_option)
 | ||
|     {
 | ||
|         std::vector<std::string>  selected_filament_colors;
 | ||
|         if (selected_filament_colors_option) {
 | ||
|             selected_filament_colors = selected_filament_colors_option->values;
 | ||
|             //erase here
 | ||
|             m_extra_config.erase("filament_colour");
 | ||
|         }
 | ||
| 
 | ||
|         std::vector<std::string>  &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<ConfigOptionBools>("filament_is_support", true);
 | ||
|             std::vector<double>& flush_vol_matrix = m_print_config.option<ConfigOptionFloats>("flush_volumes_matrix", true)->values;
 | ||
|             //std::vector<float>& flush_vol_vector = m_print_config.option<ConfigOptionFloats>("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<ConfigOptionFloat>("flush_multiplier", true)->set(new ConfigOptionFloat(1.f));
 | ||
|             ConfigOption* extra_flush_volume_opt = m_print_config.option("nozzle_volume");
 | ||
|             int extra_flush_volume = extra_flush_volume_opt ? (int)extra_flush_volume_opt->getFloat() : 0;
 | ||
| 
 | ||
|             if (filament_is_support->size() != project_filament_count)
 | ||
|             {
 | ||
|                 BOOST_LOG_TRIVIAL(error) << boost::format("filament_is_support's count %1% not equal to filament_colour's size %2%")%filament_is_support->size() %project_filament_count;
 | ||
|                 record_exit_reson(outfile_dir, CLI_CONFIG_FILE_ERROR, 0, cli_errors[CLI_CONFIG_FILE_ERROR]);
 | ||
|                 flush_and_exit(CLI_CONFIG_FILE_ERROR);
 | ||
|             }
 | ||
| 
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("extra_flush_volume: %1%")%extra_flush_volume;
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("filament_is_support: %1%")%filament_is_support->serialize();
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("flush_volumes_matrix before computing: %1%")%m_print_config.option<ConfigOptionFloats>("flush_volumes_matrix")->serialize();
 | ||
|             for (int from_idx = 0; from_idx < project_filament_count; from_idx++) {
 | ||
|                 const std::string& from_color = project_filament_colors[from_idx];
 | ||
|                 unsigned char from_rgb[4] = {};
 | ||
|                 Slic3r::GUI::BitmapCache::parse_color4(from_color, from_rgb);
 | ||
|                 bool is_from_support = filament_is_support->get_at(from_idx);
 | ||
|                 for (int to_idx = 0; to_idx < project_filament_count; to_idx++) {
 | ||
|                     bool is_to_support = filament_is_support->get_at(to_idx);
 | ||
|                     if (from_idx == to_idx) {
 | ||
|                         flush_vol_matrix[project_filament_count*from_idx + to_idx] = 0.f;
 | ||
|                     }
 | ||
|                     else {
 | ||
|                         int flushing_volume = 0;
 | ||
|                         if (is_to_support) {
 | ||
|                             flushing_volume = Slic3r::g_flush_volume_to_support;
 | ||
|                         }
 | ||
|                         else {
 | ||
|                             const std::string& to_color = project_filament_colors[to_idx];
 | ||
|                             unsigned char to_rgb[4] = {};
 | ||
|                             Slic3r::GUI::BitmapCache::parse_color4(to_color, to_rgb);
 | ||
|                             //BOOST_LOG_TRIVIAL(info) << boost::format("src_idx %1%, src color %2%, dst idex %3%, dst color %4%")%from_idx %from_color %to_idx %to_color;
 | ||
|                             //BOOST_LOG_TRIVIAL(info) << boost::format("src_rgba {%1%,%2%,%3%,%4%} dst_rgba {%5%,%6%,%7%,%8%}")%(unsigned int)(from_rgb[0]) %(unsigned int)(from_rgb[1]) %(unsigned int)(from_rgb[2]) %(unsigned int)(from_rgb[3])
 | ||
|                             //       %(unsigned int)(to_rgb[0]) %(unsigned int)(to_rgb[1]) %(unsigned int)(to_rgb[2]) %(unsigned int)(to_rgb[3]);
 | ||
| 
 | ||
|                             Slic3r::FlushVolCalculator calculator(extra_flush_volume, Slic3r::g_max_flush_volume);
 | ||
| 
 | ||
|                             flushing_volume = calculator.calc_flush_vol(from_rgb[3], from_rgb[0], from_rgb[1], from_rgb[2], to_rgb[3], to_rgb[0], to_rgb[1], to_rgb[2]);
 | ||
|                             if (is_from_support) {
 | ||
|                                 flushing_volume = std::max(Slic3r::g_min_flush_volume_from_support, flushing_volume);
 | ||
|                             }
 | ||
|                         }
 | ||
| 
 | ||
|                         flush_vol_matrix[project_filament_count * from_idx + to_idx] = flushing_volume;
 | ||
|                         //flushing_volume = int(flushing_volume * get_flush_multiplier());
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("flush_volumes_matrix after computed: %1%")%m_print_config.option<ConfigOptionFloats>("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, "<<orients_requirement.size()<<" objects"<<std::endl;
 | ||
|     if (m_models.size() > 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 "<<o->name <<", id :" << o->id().id << "\n";
 | ||
|                 orients_requirement.emplace(new_object->id().id, orients_requirement[o->id().id]);
 | ||
|                 orients_requirement.erase(o->id().id);
 | ||
|             }
 | ||
|         m.add_default_instances();
 | ||
|         m_models.clear();
 | ||
|         m_models.emplace_back(std::move(m));
 | ||
|     }
 | ||
| 
 | ||
|     // Apply command line options to a more specific DynamicPrintConfig which provides normalize()
 | ||
|     // (command line options override --load files)
 | ||
|     m_print_config.apply(m_extra_config, true);
 | ||
|     // Normalizing after importing the 3MFs / AMFs
 | ||
|     m_print_config.normalize_fdm();
 | ||
| 
 | ||
|     m_print_config.option<ConfigOptionEnum<PrinterTechnology>>("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]);
 | ||
|         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<std::string, std::string> validity = m_print_config.validate(true);
 | ||
|     if (!validity.empty()) {
 | ||
|         boost::nowide::cerr << "Param values in 3mf/config error: "<< std::endl;
 | ||
|         for (std::map<std::string, std::string>::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]);
 | ||
|         flush_and_exit(CLI_INVALID_VALUES_IN_3MF);
 | ||
|     }
 | ||
| 
 | ||
|     //BBS: partplate list
 | ||
|     Slic3r::GUI::PartPlateList partplate_list(NULL, m_models.data(), printer_technology);
 | ||
|     //use Pointfs insteadof Points
 | ||
|     Pointfs current_printable_area = m_print_config.opt<ConfigOptionPoints>("printable_area")->values;
 | ||
|     Pointfs current_exclude_area = m_print_config.opt<ConfigOptionPoints>("bed_exclude_area")->values;
 | ||
|     //update part plate's size
 | ||
|     double print_height = m_print_config.opt_float("printable_height");
 | ||
|     double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid");
 | ||
|     double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod");
 | ||
|     double cleareance_radius = m_print_config.opt_float("extruder_clearance_max_radius");
 | ||
|     //double plate_stride;
 | ||
|     std::string bed_texture;
 | ||
| 
 | ||
|     current_printable_width = current_printable_area[2].x() - current_printable_area[0].x();
 | ||
|     current_printable_depth = current_printable_area[2].y() - current_printable_area[0].y();
 | ||
|     current_printable_height = print_height;
 | ||
|     if (old_printable_width == 0)
 | ||
|         old_printable_width = current_printable_width;
 | ||
|     if (old_printable_depth == 0)
 | ||
|         old_printable_depth = current_printable_depth;
 | ||
|     if (old_printable_height == 0)
 | ||
|         old_printable_height = current_printable_height;
 | ||
|     if ((old_printable_width > 0)&&(old_printable_depth > 0)&&(old_printable_height > 0))
 | ||
|     {
 | ||
|         //check the printable size logic
 | ||
|         if ((old_printable_width > current_printable_width) || (old_printable_depth > current_printable_depth) || (old_printable_height > current_printable_height))
 | ||
|         {
 | ||
|             BOOST_LOG_TRIVIAL(error) << boost::format("old printable size {%1%, %2%, %3%} is larger than new printable size {%4%, %5%, %6%}, can not print")
 | ||
|                 %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height;
 | ||
|             record_exit_reson(outfile_dir, CLI_PRINTABLE_SIZE_REDUCED, 0, cli_errors[CLI_PRINTABLE_SIZE_REDUCED]);
 | ||
|             flush_and_exit(CLI_PRINTABLE_SIZE_REDUCED);
 | ||
|         }
 | ||
|         else if ((old_printable_width < current_printable_width) || (old_printable_depth < current_printable_depth))
 | ||
|         {
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("old printable size {%1%, %2%, %3%} is smaller than new printable size {%4%, %5%, %6%}, need to center the model")
 | ||
|                 %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height;
 | ||
|             shrink_to_new_bed = true;
 | ||
|         }
 | ||
|         else {
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("old printable size {%1%, %2%, %3%}, new printable size {%4%, %5%, %6%}")
 | ||
|                 %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     if (m_models.size() > 0)
 | ||
|     {
 | ||
|         BOOST_LOG_TRIVIAL(info) << boost::format("translate_old %1%, shrink_to_new_bed %2%, old bed size {%3%, %4%, %5%}")%translate_old%shrink_to_new_bed %old_printable_width %old_printable_depth %old_printable_height;
 | ||
|         if (translate_old) {
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("translate old 3mf, switch to older bed size,{%1%, %2%, %3%}")%(old_printable_width + bed3d_ax3s_default_tip_radius)%(old_printable_depth+bed3d_ax3s_default_tip_radius) %old_printable_height;
 | ||
|             partplate_list.reset_size(old_printable_width + bed3d_ax3s_default_tip_radius, old_printable_depth + bed3d_ax3s_default_tip_radius, old_printable_height, false);
 | ||
|         }
 | ||
|         else {
 | ||
|             partplate_list.reset_size(old_printable_width, old_printable_depth, old_printable_height, false);
 | ||
|         }
 | ||
|         partplate_list.set_shapes(current_printable_area, current_exclude_area, bed_texture, height_to_lid, height_to_rod);
 | ||
|         //plate_stride = partplate_list.plate_stride_x();
 | ||
|     }
 | ||
| 
 | ||
|     auto translate_models = [translate_old, shrink_to_new_bed, old_printable_width, old_printable_depth, old_printable_height, current_printable_width, current_printable_depth, current_printable_height] (Slic3r::GUI::PartPlateList& plate_list, DynamicPrintConfig& print_config) {
 | ||
|         //BBS: translate old 3mf to correct positions
 | ||
|         if (translate_old) {
 | ||
|             //translate the objects
 | ||
|             int plate_count = plate_list.get_plate_count();
 | ||
|             for (int index = 1; index < plate_count; index ++) {
 | ||
|                 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)plate_list.get_plate(index);
 | ||
| 
 | ||
|                 Vec3d cur_origin = cur_plate->get_origin();
 | ||
|                 Vec3d new_origin = plate_list.compute_origin_using_new_size(index, old_printable_width, old_printable_depth);
 | ||
| 
 | ||
|                 cur_plate->translate_all_instance(new_origin - cur_origin);
 | ||
|             }
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("translate old 3mf, switch back to current bed size,{%1%, %2%, %3%}")%old_printable_width %old_printable_depth %old_printable_height;
 | ||
|             plate_list.reset_size(old_printable_width, old_printable_depth, old_printable_height, true, true);
 | ||
|         }
 | ||
| 
 | ||
|         if (shrink_to_new_bed)
 | ||
|         {
 | ||
|             int plate_count = plate_list.get_plate_count();
 | ||
|             ConfigOptionFloats *wipe_x_option = nullptr, *wipe_y_option = nullptr;
 | ||
|             Vec3d wipe_offset;
 | ||
|             if (print_config.has("wipe_tower_x")) {
 | ||
|                 wipe_x_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_x"));
 | ||
|                 wipe_y_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_y"));
 | ||
|             }
 | ||
|             for (int index = 0; index < plate_count; index ++) {
 | ||
|                 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)plate_list.get_plate(index);
 | ||
| 
 | ||
|                 Vec3d cur_origin = cur_plate->get_origin();
 | ||
|                 Vec3d new_origin = plate_list.compute_origin_using_new_size(index, current_printable_width, current_printable_depth);
 | ||
|                 Vec3d cur_center_offset { ((double)old_printable_width)/2, ((double)old_printable_depth)/2, 0}, new_center_offset { ((double)current_printable_width)/2, ((double)current_printable_depth)/2, 0};
 | ||
|                 Vec3d cur_center = cur_origin + cur_center_offset;
 | ||
|                 Vec3d new_center = new_origin + new_center_offset;
 | ||
|                 Vec3d offset  = new_center - cur_center;
 | ||
| 
 | ||
|                 cur_plate->translate_all_instance(offset);
 | ||
|                 if (index == 0)
 | ||
|                     wipe_offset = offset;
 | ||
|                 if (wipe_x_option) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed, plate %1%: wipe tower src: {%2%, %3%}")%(index+1) %wipe_x_option->get_at(index) %wipe_y_option->get_at(index);
 | ||
|                     ConfigOptionFloat wipe_tower_x(wipe_x_option->get_at(index) + wipe_offset(0));
 | ||
|                     ConfigOptionFloat wipe_tower_y(wipe_y_option->get_at(index) + wipe_offset(1));
 | ||
| 
 | ||
|                     wipe_x_option->set_at(&wipe_tower_x, index, 0);
 | ||
|                     wipe_y_option->set_at(&wipe_tower_y, index, 0);
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed, plate %1% wipe tower changes to: {%2%, %3%}")%(index+1) %wipe_x_option->get_at(index) %wipe_y_option->get_at(index);
 | ||
|                 }
 | ||
| 
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed, plate %1% translate offset: {%2%, %3%, %4%}")%(index+1) %offset[0] %offset[1] %offset[2];
 | ||
|             }
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed, shrink all the models to current bed size,{%1%, %2%, %3%}")%current_printable_width %current_printable_depth %current_printable_height;
 | ||
|             plate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true);
 | ||
|         }
 | ||
|     };
 | ||
|     if (plate_data_src.size() > 0)
 | ||
|     {
 | ||
|         partplate_list.load_from_3mf_structure(plate_data_src);
 | ||
| 
 | ||
|         translate_models(partplate_list, m_print_config);
 | ||
|     }
 | ||
| 
 | ||
|     /*for (ModelObject *model_object : m_models[0].objects)
 | ||
|         for (ModelInstance *model_instance : model_object->instances)
 | ||
|         {
 | ||
|             const Vec3d &instance_offset = model_instance->get_offset();
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("instance %1% transform {%2%,%3%,%4%} at %5%:%6%")% model_object->name % instance_offset.x() % instance_offset.y() %instance_offset.z() % __FUNCTION__ % __LINE__<< std::endl;
 | ||
|         }*/
 | ||
| 
 | ||
|     // Loop through transform options.
 | ||
|     bool user_center_specified = false;
 | ||
|     Points beds = get_bed_shape(m_print_config);
 | ||
|     ArrangeParams arrange_cfg;
 | ||
|     arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config));
 | ||
| 
 | ||
|     BOOST_LOG_TRIVIAL(info) << "will start transforms, commands count " << m_transforms.size() << "\n";
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|     if (g_cli_callback_mgr.is_started()) {
 | ||
|         PrintBase::SlicingStatus slicing_status{2, "Loading files finished"};
 | ||
|         cli_status_callback(slicing_status);
 | ||
|     }
 | ||
| #endif
 | ||
| 
 | ||
|     for (auto const &opt_key : m_transforms) {
 | ||
|         BOOST_LOG_TRIVIAL(info) << "process transform " << opt_key << "\n";
 | ||
|         if (opt_key == "merge") {
 | ||
|             //BBS: always merge, do nothing here
 | ||
|             /*Model m;
 | ||
|             for (auto& model : m_models)
 | ||
|                 for (ModelObject* o : model.objects)
 | ||
|                     m.add_object(*o);
 | ||
|             // Rearrange instances unless --dont-arrange is supplied
 | ||
|             if (!m_config.opt_bool("dont_arrange")) {
 | ||
|                 m.add_default_instances();
 | ||
|                 if (this->has_print_action())
 | ||
|                     arrange_objects(m, bed, arrange_cfg);
 | ||
|                 else
 | ||
|                     arrange_objects(m, InfiniteBed{}, arrange_cfg);
 | ||
|             }
 | ||
|             m_models.clear();
 | ||
|             m_models.emplace_back(std::move(m));*/
 | ||
|         }
 | ||
|         else if (opt_key == "repetitions") {
 | ||
|             int repetitions_count = m_config.option<ConfigOptionInt>("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]);
 | ||
|                     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]);
 | ||
|                     flush_and_exit(CLI_INVALID_PARAMS);
 | ||
|                 }
 | ||
|                 BOOST_LOG_TRIVIAL(info) << "repetitions value " << repetitions_count << std::endl;
 | ||
| 
 | ||
|                 need_arrange = true;
 | ||
|                 duplicate_count = repetitions_count - 1;
 | ||
|             }
 | ||
|         }
 | ||
|         else if (opt_key == "convert_unit") {
 | ||
|             for (auto& model : m_models) {
 | ||
|                 if (model.looks_like_saved_in_meters()) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << "convert from meter to millimeter\n";
 | ||
|                     model.convert_from_meters(true);
 | ||
|                 }
 | ||
|                 else if (model.looks_like_imperial_units()) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << "convert from inch to millimeter\n";
 | ||
|                     model.convert_from_imperial_units(true);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
|         else if (opt_key == "orient") {
 | ||
|             for (auto& model : m_models)
 | ||
|                 for (ModelObject* o : model.objects)
 | ||
|                 {
 | ||
|                     // coconut: always orient instance instead of object
 | ||
|                     for (ModelInstance* mi : o->instances)
 | ||
|                     {
 | ||
|                         orientation::orient(mi);
 | ||
|                     }
 | ||
|                     BOOST_LOG_TRIVIAL(info) << "orient object, name=" << o->name <<",id="<<o->id().id<<std::endl;
 | ||
|                     //BBS: clear the orient objects lists
 | ||
|                     orients_requirement[o->id().id] = false;
 | ||
|                 }
 | ||
|         }
 | ||
|         else if (opt_key == "copy") {
 | ||
|             for (auto &model : m_models) {
 | ||
|                 const bool all_objects_have_instances = std::none_of(
 | ||
|                     model.objects.begin(), model.objects.end(),
 | ||
|                     [](ModelObject* o){ return o->instances.empty(); }
 | ||
|                 );
 | ||
| 
 | ||
|                 int dups = m_config.opt_int("copy");
 | ||
|                 if (!all_objects_have_instances) model.add_default_instances();
 | ||
| 
 | ||
|                 try {
 | ||
|                     if (dups > 1) {
 | ||
|                         // if all input objects have defined position(s) apply duplication to the whole model
 | ||
|                         duplicate(model, size_t(dups), beds, arrange_cfg);
 | ||
|                     } else {
 | ||
|                         arrange_objects(model, beds, arrange_cfg);
 | ||
|                     }
 | ||
|                 } catch (std::exception &ex) {
 | ||
|                     boost::nowide::cerr << "error: " << ex.what() << std::endl;
 | ||
|                     record_exit_reson(outfile_dir, CLI_COPY_OBJECTS_ERROR, 0, cli_errors[CLI_COPY_OBJECTS_ERROR]);
 | ||
|                     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<ConfigOptionPoint>("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<ConfigOptionPoint>("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<ConfigOptionInt>("arrange")->value;
 | ||
| 
 | ||
|             if (arrange_option == 0)
 | ||
|             {
 | ||
|                 need_arrange = false;
 | ||
|             }
 | ||
|             else if (arrange_option == 1)
 | ||
|             {
 | ||
|                 need_arrange = true;
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 //auto arrange, keep the original logic
 | ||
|             }
 | ||
|         } else if (opt_key == "ensure_on_bed") {
 | ||
|             // do nothing, the value is used later
 | ||
|         } else if (opt_key == "rotate") {
 | ||
|             for (auto &model : m_models)
 | ||
|                 for (auto &o : model.objects)
 | ||
|                     // this affects volumes:
 | ||
|                     o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Z);
 | ||
|         } else if (opt_key == "rotate_x") {
 | ||
|             for (auto &model : m_models)
 | ||
|                 for (auto &o : model.objects)
 | ||
|                     // this affects volumes:
 | ||
|                     o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), X);
 | ||
|         } else if (opt_key == "rotate_y") {
 | ||
|             for (auto &model : m_models)
 | ||
|                 for (auto &o : model.objects)
 | ||
|                     // this affects volumes:
 | ||
|                     o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Y);
 | ||
|         } else if (opt_key == "scale") {
 | ||
|             for (auto &model : m_models)
 | ||
|                 for (auto &o : model.objects)
 | ||
|                     // this affects volumes:
 | ||
|                     o->scale(m_config.get_abs_value(opt_key, 1));
 | ||
|         } else if (opt_key == "scale_to_fit") {
 | ||
|             const Vec3d &opt = m_config.opt<ConfigOptionPoint3>(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]);
 | ||
|                 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<Model> 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<Vec3d, 4> 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<Model> new_models;
 | ||
|             for (auto &model : m_models) {
 | ||
|                 TriangleMesh mesh = model.mesh();
 | ||
|                 mesh.repair();
 | ||
| 
 | ||
|                 std::vector<TriangleMesh> meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("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]);
 | ||
|             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="<<o->id().id<<std::endl;
 | ||
|                 orientation::orient(o);
 | ||
|                 oriented_or_arranged = true;
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 BOOST_LOG_TRIVIAL(info) << "Before process command, no need to orient, object id :" << o->id().id<<std::endl;
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
|     //BBS: clear the orient objects lists
 | ||
|     orients_requirement.clear();
 | ||
| 
 | ||
|     if ((plate_to_slice < 0) || (plate_to_slice > 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]);
 | ||
|         flush_and_exit(CLI_INVALID_PARAMS);
 | ||
|     }
 | ||
| 
 | ||
|     if ((!need_arrange) && is_bbl_3mf && !shrink_to_new_bed && (plate_to_slice > 0))
 | ||
|     {
 | ||
|         if (((old_height_to_rod != 0.f) && (old_height_to_rod != height_to_rod))
 | ||
|             || ((old_height_to_lid != 0.f) && (old_height_to_lid != height_to_lid))
 | ||
|             || ((old_max_radius != 0.f) && (old_max_radius != cleareance_radius)))
 | ||
|         {
 | ||
|             Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1);
 | ||
|             PrintSequence curr_plate_seq = cur_plate->get_print_seq();
 | ||
|             bool is_seq_print = false;
 | ||
|             if (curr_plate_seq == PrintSequence::ByDefault) {
 | ||
|                 auto seq_print  = m_print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence");
 | ||
|                 if (seq_print && (seq_print->value == PrintSequence::ByObject)) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("Check whether need to arrange by print_sequence: plate %1% print by object, set from global")%plate_to_slice;
 | ||
|                     is_seq_print = true;
 | ||
|                 }
 | ||
|             }
 | ||
|             else if (curr_plate_seq == PrintSequence::ByObject) {
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("Check whether need to arrange by print_sequence: plate %1% print by object, set from plate self")%plate_to_slice;
 | ||
|                 is_seq_print = true;
 | ||
|             }
 | ||
|             if (is_seq_print) {
 | ||
|                 need_arrange = true;
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("old_height_to_rod %1%, old_height_to_lid %2%,  old_max_radius %3%, current height_to_rod %4%, height_to_lid %5%, cleareance_radius %6%, need arrange!")
 | ||
|                     %old_height_to_rod %old_height_to_lid %old_max_radius %height_to_rod %height_to_lid %cleareance_radius;
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     oriented_or_arranged |= need_arrange;
 | ||
| 
 | ||
|     BOOST_LOG_TRIVIAL(info) << boost::format("before arrange, need_arrange=%1%, duplicate_count %2%, filament_color_changed %3%")%need_arrange %duplicate_count %filament_color_changed;
 | ||
|     if (need_arrange || filament_color_changed)
 | ||
|     {
 | ||
|         for (int index = 0; index < partplate_list.get_plate_count(); index ++)
 | ||
|         {
 | ||
|             if ((plate_to_slice != 0) && (plate_to_slice != (index + 1))) {
 | ||
|                 continue;
 | ||
|             }
 | ||
| 
 | ||
|             if (plate_data_src.size() > index) {
 | ||
|                 if (!plate_data_src[index]->thumbnail_file.empty()) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded thumbnail %2%.")%(index+1)%plate_data_src[index]->thumbnail_file;
 | ||
|                     plate_data_src[index]->thumbnail_file.clear();
 | ||
|                 }
 | ||
|                 if (!plate_data_src[index]->top_file.empty()) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded top_thumbnail %2%.")%(index+1)%plate_data_src[index]->top_file;
 | ||
|                     plate_data_src[index]->top_file.clear();
 | ||
|                 }
 | ||
|                 if (!plate_data_src[index]->pick_file.empty()) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded pick_thumbnail %2%.")%(index+1)%plate_data_src[index]->pick_file;
 | ||
|                     plate_data_src[index]->pick_file.clear();
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     if (need_arrange)
 | ||
|     {
 | ||
|         ArrangePolygons selected, unselected, unprintable, locked_aps;
 | ||
| 
 | ||
|         //for (Model &model : m_models)
 | ||
|         if (m_models.size() > 0)
 | ||
|         {
 | ||
|             Model &model = m_models[0];
 | ||
|             Model original_model;
 | ||
|             std::set<std::pair<int, int>> backup_set;
 | ||
|             bool finished_arrange = false, first_run = true;
 | ||
|             Slic3r::GUI::PartPlate* cur_plate;
 | ||
|             int low_duplicate_count = 0, up_duplicate_count = duplicate_count, arrange_count = 0;
 | ||
| 
 | ||
|             arrange_cfg.is_seq_print = false;
 | ||
|             if (duplicate_count > 0) {
 | ||
|                 original_model = model;
 | ||
|             }
 | ||
| 
 | ||
|             while(!finished_arrange)
 | ||
|             {
 | ||
|                 arrange_count++;
 | ||
|                 //step-0: duplicate model
 | ||
|                 if (duplicate_count > 0)
 | ||
|                 {
 | ||
|                     //copy model objects and instances on plate
 | ||
|                     if (!first_run) {
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("restore model object and plate, new duplicate_count %1%, arrange_count=%2%")%duplicate_count%arrange_count;
 | ||
|                         beds = get_bed_shape(m_print_config);
 | ||
|                         model.clear_objects();
 | ||
|                         model.clear_materials();
 | ||
|                         model = original_model;
 | ||
|                         partplate_list.load_from_3mf_structure(plate_data_src);
 | ||
| 
 | ||
|                         selected.clear();
 | ||
|                         unselected.clear();
 | ||
|                         unprintable.clear();
 | ||
|                         locked_aps.clear();
 | ||
|                     }
 | ||
|                     else
 | ||
|                         first_run = false;
 | ||
| 
 | ||
|                     cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1);
 | ||
|                     cur_plate->duplicate_all_instance(duplicate_count, need_skip, skip_maps);
 | ||
|                 }
 | ||
|                 else if (plate_to_slice > 0)
 | ||
|                 {
 | ||
|                     cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1);
 | ||
|                 }
 | ||
| 
 | ||
|                 if (cur_plate) {
 | ||
|                     PrintSequence curr_plate_seq = cur_plate->get_print_seq();
 | ||
|                     if (curr_plate_seq == PrintSequence::ByDefault) {
 | ||
|                         auto seq_print  = m_print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence");
 | ||
|                         if (seq_print && (seq_print->value == PrintSequence::ByObject)) {
 | ||
|                             BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from global")%plate_to_slice;
 | ||
|                             arrange_cfg.is_seq_print = true;
 | ||
|                         }
 | ||
|                     }
 | ||
|                     else if (curr_plate_seq == PrintSequence::ByObject) {
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from plate self")%plate_to_slice;
 | ||
|                         arrange_cfg.is_seq_print = true;
 | ||
|                     }
 | ||
|                 }
 | ||
| 
 | ||
|                 //Step-1: prepare arrange polygons
 | ||
|                 if ((duplicate_count == 0) && (plate_to_slice == 0))
 | ||
|                 {
 | ||
|                     //global arrange
 | ||
|                     for (size_t oidx = 0; oidx < model.objects.size(); ++oidx)
 | ||
|                     {
 | ||
|                         ModelObject* mo = model.objects[oidx];
 | ||
|                         for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx)
 | ||
|                         {
 | ||
|                             ModelInstance* minst = mo->instances[inst_idx];
 | ||
|                             ArrangePolygon ap = get_instance_arrange_poly(minst, m_print_config);
 | ||
| 
 | ||
|                             //preprocess by partplate list
 | ||
|                             //remove the locked plate's instances, neither in selected, nor in un-selected
 | ||
|                             bool locked = partplate_list.preprocess_arrange_polygon(oidx, inst_idx, ap, true);
 | ||
|                             if (!locked)
 | ||
|                             {
 | ||
|                                 ap.itemid = selected.size();
 | ||
|                                 if (minst->printable)
 | ||
|                                     selected.emplace_back(ap);
 | ||
|                                 else
 | ||
|                                     unprintable.emplace_back(ap);
 | ||
|                             }
 | ||
|                             else
 | ||
|                             {
 | ||
|                                 //skip this object due to be locked in plate
 | ||
|                                 ap.itemid = locked_aps.size();
 | ||
|                                 locked_aps.emplace_back(ap);
 | ||
|                                 boost::nowide::cout <<__FUNCTION__ << boost::format(": skip locked instance, obj_id %1%, instance_id %2%") % oidx % inst_idx;
 | ||
|                             }
 | ||
|                         }
 | ||
|                     }
 | ||
| 
 | ||
|                     if (m_print_config.has("print_sequence")) {
 | ||
|                         PrintSequence seq = m_print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence")->value;
 | ||
|                         arrange_cfg.is_seq_print = (seq == PrintSequence::ByObject);
 | ||
|                     }
 | ||
| 
 | ||
|                     //add the virtual object into unselect list if has
 | ||
|                     partplate_list.preprocess_exclude_areas(unselected);
 | ||
|                 }
 | ||
|                 else {
 | ||
|                     //only arrange current plate
 | ||
|                     partplate_list.lock_plate(plate_to_slice - 1, false);
 | ||
|                     partplate_list.select_plate(plate_to_slice-1);
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% set to selected")%plate_to_slice;
 | ||
| 
 | ||
|                     for (size_t oidx = 0; oidx < model.objects.size(); ++oidx)
 | ||
|                     {
 | ||
|                         ModelObject* mo = model.objects[oidx];
 | ||
| 
 | ||
|                         for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx)
 | ||
|                         {
 | ||
|                             ModelInstance*   minst = mo->instances[inst_idx];
 | ||
|                             bool             in_plate = cur_plate->contain_instance(oidx, inst_idx) || cur_plate->intersect_instance(oidx, inst_idx);
 | ||
|                             ArrangePolygon   ap = get_instance_arrange_poly(minst, m_print_config);
 | ||
| 
 | ||
|                             ArrangePolygons& cont = mo->instances[inst_idx]->printable ?
 | ||
|                                 (in_plate ? selected : unselected) :
 | ||
|                                 unprintable;
 | ||
|                             bool locked = partplate_list.preprocess_arrange_polygon_other_locked(oidx, inst_idx, ap, in_plate);
 | ||
|                             BOOST_LOG_TRIVIAL(info) << boost::format("name %4% in_plate %1% printable %2%, locked %3%")%in_plate %mo->instances[inst_idx]->printable %locked % ap.name ;
 | ||
|                             if (!locked)
 | ||
|                             {
 | ||
|                                 ap.itemid = cont.size();
 | ||
|                                 cont.emplace_back(std::move(ap));
 | ||
|                             }
 | ||
|                             else
 | ||
|                             {
 | ||
|                                 //skip this object due to be not in current plate, treated as locked
 | ||
|                                 ap.itemid = locked_aps.size();
 | ||
|                                 locked_aps.emplace_back(std::move(ap));
 | ||
|                                 BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format("arrange: skip locked instance, obj_id %1%, name %2%") % oidx % mo->name;
 | ||
|                             }
 | ||
|                         }
 | ||
|                     }
 | ||
|                     if ((duplicate_count > 0)&&(selected.size() == (duplicate_count + 1)))
 | ||
|                     {
 | ||
|                         duplicate_single_object = true;
 | ||
|                         BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found single object mode");
 | ||
|                     }
 | ||
| 
 | ||
|                     if (m_print_config.has("wipe_tower_x")) {
 | ||
|                         float x = dynamic_cast<const ConfigOptionFloats *>(m_print_config.option("wipe_tower_x"))->get_at(plate_to_slice-1);
 | ||
|                         float y = dynamic_cast<const ConfigOptionFloats *>(m_print_config.option("wipe_tower_y"))->get_at(plate_to_slice-1);
 | ||
|                         float w = dynamic_cast<const ConfigOptionFloat *>(m_print_config.option("prime_tower_width"))->value;
 | ||
|                         float a = dynamic_cast<const ConfigOptionFloat *>(m_print_config.option("wipe_tower_rotation_angle"))->value;
 | ||
|                         float v = dynamic_cast<const ConfigOptionFloat *>(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<int> extruders = cur_plate->get_extruders_under_cli(true, m_print_config);
 | ||
|                             filaments_cnt = extruders.size();
 | ||
|                             BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange: slice filaments info invalid or need_skip, get from partplate: filament_count %1%")%filaments_cnt;
 | ||
|                         }
 | ||
| 
 | ||
|                         if (filaments_cnt <= 1)
 | ||
|                         {
 | ||
|                             BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("arrange: not a multi-color object anymore, drop the wipe tower before arrange.");
 | ||
|                         }
 | ||
|                         else
 | ||
|                         {
 | ||
|                             float layer_height = 0.2;
 | ||
|                             ConfigOption* layer_height_opt = m_print_config.option("layer_height");
 | ||
|                             if (layer_height_opt)
 | ||
|                                 layer_height = layer_height_opt->getFloat();
 | ||
| 
 | ||
|                             float depth = v * (filaments_cnt - 1) / (layer_height * w);
 | ||
| 
 | ||
|                             BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: x=%1%, y=%2%, width=%3%, depth=%4%, angle=%5%, prime_volume=%6%, filaments_cnt=%7%, layer_height=%8%")
 | ||
|                                 %x %y %w %depth %a %v %filaments_cnt %layer_height;
 | ||
| 
 | ||
|                             Vec3d plate_origin = cur_plate->get_origin();
 | ||
| 
 | ||
|                             ArrangePolygon wipe_tower_ap;
 | ||
| 
 | ||
|                             Polygon ap({
 | ||
|                                 {scaled(x), scaled(y)},
 | ||
|                                 {scaled(x + w), scaled(y)},
 | ||
|                                 {scaled(x + w), scaled(y + depth)},
 | ||
|                                 {scaled(x), scaled(y + depth)}
 | ||
|                                 });
 | ||
|                             wipe_tower_ap.bed_idx = 0;
 | ||
|                             wipe_tower_ap.setter = NULL; // do not move wipe tower
 | ||
| 
 | ||
|                             wipe_tower_ap.poly.contour = std::move(ap);
 | ||
|                             wipe_tower_ap.translation  = {scaled(0.f), scaled(0.f)};
 | ||
|                             wipe_tower_ap.rotation     = a;
 | ||
|                             wipe_tower_ap.name = "WipeTower";
 | ||
|                             wipe_tower_ap.is_virt_object = true;
 | ||
|                             wipe_tower_ap.is_wipe_tower = true;
 | ||
|                             ++wipe_tower_ap.priority;
 | ||
|                             unselected.emplace_back(std::move(wipe_tower_ap));
 | ||
|                         }
 | ||
|                     }
 | ||
| 
 | ||
|                     // add the virtual object into unselect list if has
 | ||
|                     partplate_list.preprocess_exclude_areas(unselected, plate_to_slice);
 | ||
|                 }
 | ||
| 
 | ||
| 
 | ||
|                 //Step-2:prepare the arrange params
 | ||
|                 arrange_cfg.allow_rotations  = true;
 | ||
|                 arrange_cfg.allow_multi_materials_on_same_plate = true;
 | ||
|                 arrange_cfg.avoid_extrusion_cali_region         = false;
 | ||
|                 arrange_cfg.clearance_height_to_rod             = height_to_rod;
 | ||
|                 arrange_cfg.clearance_height_to_lid             = height_to_lid;
 | ||
|                 arrange_cfg.cleareance_radius                   = cleareance_radius;
 | ||
|                 arrange_cfg.printable_height                    = print_height;
 | ||
|                 if (arrange_cfg.is_seq_print)
 | ||
|                     arrange_cfg.min_obj_distance = std::max(arrange_cfg.min_obj_distance, scaled(arrange_cfg.cleareance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
 | ||
|                 else
 | ||
|                     arrange_cfg.min_obj_distance = scaled(22.0);
 | ||
| 
 | ||
|                 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);
 | ||
|                 beds=get_shrink_bedpts(&m_print_config, arrange_cfg);
 | ||
| 
 | ||
|                 {
 | ||
|                     BOOST_LOG_TRIVIAL(debug)<< "Arrange full params: "<< arrange_cfg.to_json();
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items selected before arranging: %1%")%selected.size();
 | ||
|                     for (auto item : selected)
 | ||
|                         BOOST_LOG_TRIVIAL(trace) << item.name << ", extruder: " << item.extrude_ids.back() << ", bed: " << item.bed_idx
 | ||
|                                                 << ", trans: " << item.translation.transpose();
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items unselected before arranging: %1%") % unselected.size();
 | ||
|                     for (auto item : unselected)
 | ||
|                         BOOST_LOG_TRIVIAL(trace) << item.name << ", bed: " << item.bed_idx << ", trans: " << item.translation.transpose();
 | ||
|                 }
 | ||
|                 arrange_cfg.progressind= [](unsigned st, std::string str = "") {
 | ||
|                     //boost::nowide::cout << "st=" << st << ", " << str << std::endl;
 | ||
|                 };
 | ||
| 
 | ||
|                 //Step-3:do the arrange
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("start %1% th arranging...")%arrange_count;
 | ||
|                 arrangement::arrange(selected, unselected, beds, arrange_cfg);
 | ||
|                 arrangement::arrange(unprintable, {}, beds, arrange_cfg);
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("finished %1% th arranging...")%arrange_count;
 | ||
| 
 | ||
|                 //Step-4:postprocess by partplate list&&apply the result
 | ||
|                 int bed_idx_max = 0;
 | ||
|                 if (duplicate_count == 0)
 | ||
|                 {
 | ||
|                     if (plate_to_slice > 0)
 | ||
|                     {
 | ||
|                         //only for partplate case
 | ||
|                         partplate_list.clear(false, false, true, plate_to_slice-1);
 | ||
| 
 | ||
|                         for (ArrangePolygon& ap : selected) {
 | ||
|                             partplate_list.postprocess_bed_index_for_current_plate(ap);
 | ||
|                             if (ap.bed_idx != (plate_to_slice-1))
 | ||
|                             {
 | ||
|                                 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":arrange failed: ap.name %1% ap.bed_idx %2%, plate index %3%")% ap.name % ap.bed_idx % (plate_to_slice-1);
 | ||
|                                 record_exit_reson(outfile_dir, CLI_OBJECT_ARRANGE_FAILED, 0, cli_errors[CLI_OBJECT_ARRANGE_FAILED]);
 | ||
|                                 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<double>(ap.translation(X)) % unscale<double>(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<double>(ap.translation(X)) % unscale<double>(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]);
 | ||
|                                 //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<double>(ap.translation(X)) % unscale<double>(ap.translation(Y)) % ap.name;
 | ||
|                     }
 | ||
| 
 | ||
|                     if (failed_this_time) {
 | ||
|                         if (duplicate_count == 0)
 | ||
|                         {
 | ||
|                             //restore to the original
 | ||
|                             BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": restore to the original model and plates");
 | ||
|                             finished_arrange = true;
 | ||
|                             model = original_model;
 | ||
|                             partplate_list.load_from_3mf_structure(plate_data_src);
 | ||
|                             partplate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true);
 | ||
|                             BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": exit arrange process");
 | ||
|                         }
 | ||
|                         continue;
 | ||
|                     }
 | ||
| 
 | ||
|                     if (duplicate_single_object)
 | ||
|                     {
 | ||
|                         if (real_duplicate_count <= 1) {
 | ||
|                             BOOST_LOG_TRIVIAL(warning) << "no object can be placed under single object mode, restore to the original model and plates also" << std::endl;
 | ||
|                             //record_exit_reson(outfile_dir, CLI_OBJECT_ARRANGE_FAILED, 0, cli_errors[CLI_OBJECT_ARRANGE_FAILED]);
 | ||
|                             //flush_and_exit(CLI_OBJECT_ARRANGE_FAILED);
 | ||
|                             finished_arrange = true;
 | ||
|                             model = original_model;
 | ||
|                             partplate_list.load_from_3mf_structure(plate_data_src);
 | ||
|                             partplate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true);
 | ||
|                             duplicate_count = 0;
 | ||
|                             BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": exit arrange process");
 | ||
|                             continue;
 | ||
|                         }
 | ||
|                         duplicate_count = real_duplicate_count - 1;
 | ||
|                     }
 | ||
|                     else {
 | ||
|                         //multiple objects case
 | ||
|                         BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": multiple objects mode, arrange success on count %1%, low_duplicate_count %2%, up_duplicate_count %3%")%duplicate_count %low_duplicate_count %up_duplicate_count;
 | ||
|                         if ((duplicate_count == up_duplicate_count) || (duplicate_count == (up_duplicate_count - 1)))
 | ||
|                         {
 | ||
|                             BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found the max arrangeable count %1%")%duplicate_count;
 | ||
|                         }
 | ||
|                         else {
 | ||
|                             low_duplicate_count = duplicate_count;
 | ||
|                             duplicate_count = (up_duplicate_count + low_duplicate_count)/2;
 | ||
|                             BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": try new count %1%, low_duplicate_count %2%, up_duplicate_count %3%")%duplicate_count %low_duplicate_count %up_duplicate_count;
 | ||
|                             continue;
 | ||
|                         }
 | ||
|                     }
 | ||
| 
 | ||
|                     //BBS: adjust the bed_index, create new plates, get the max bed_index
 | ||
|                     for (ArrangePolygon& ap : unselected)
 | ||
|                     {
 | ||
|                         if (ap.is_virt_object)
 | ||
|                             continue;
 | ||
| 
 | ||
|                         bed_idx_max = std::max(ap.bed_idx, bed_idx_max);
 | ||
|                         BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":arrange unselected %4%: bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale<double>(ap.translation(X)) % unscale<double>(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<double>(ap.translation(X)) % unscale<double>(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"))
 | ||
|     //    for (auto &model : m_models)
 | ||
|     //        for (auto &o : model.objects)
 | ||
|     //            o->ensure_on_bed();
 | ||
| 
 | ||
|     // loop through action options
 | ||
|     bool export_to_3mf = false, load_slicedata = false, export_slicedata = false, export_slicedata_error = false;
 | ||
|     bool no_check = false;
 | ||
|     std::string export_3mf_file, load_slice_data_dir, export_slice_data_dir;
 | ||
|     std::vector<ThumbnailData*> calibration_thumbnails;
 | ||
|     int max_slicing_time_per_plate = 0, max_triangle_count_per_plate = 0;
 | ||
|     std::vector<bool> plate_has_skips(partplate_list.get_plate_count(), false);
 | ||
|     std::vector<std::vector<size_t>> plate_skipped_objects(partplate_list.get_plate_count());
 | ||
| 
 | ||
|     global_current_time = (long long)Slic3r::Utils::get_current_time_utc();
 | ||
|     prepare_time = (size_t) (global_current_time - global_begin_time);
 | ||
|     global_begin_time = global_current_time;
 | ||
|     sliced_time.resize(partplate_list.get_plate_count(), 0);
 | ||
| 
 | ||
|     for (auto const &opt_key : m_actions) {
 | ||
|         if (opt_key == "help") {
 | ||
|             this->print_help();
 | ||
|         } else if (opt_key == "help_fff") {
 | ||
|             this->print_help(true, ptFFF);
 | ||
|         } else if (opt_key == "help_sla") {
 | ||
|             this->print_help(true, ptSLA);
 | ||
|         } else if (opt_key == "pipe") {
 | ||
|             //already processed before
 | ||
|         } else if (opt_key == "load_slicedata") {
 | ||
|             load_slicedata = true;
 | ||
|             load_slice_data_dir = m_config.opt_string(opt_key);
 | ||
|             if (export_slicedata) {
 | ||
|                 BOOST_LOG_TRIVIAL(error) << "should not set load_slicedata and export_slicedata together." << std::endl;
 | ||
|                 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS]);
 | ||
|                 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]);
 | ||
|                 flush_and_exit(CLI_INVALID_PARAMS);
 | ||
|             }
 | ||
|             else if (shrink_to_new_bed)
 | ||
|             {
 | ||
|                 BOOST_LOG_TRIVIAL(warning) << "use load_slicedata when shrink_to_new_bed(switch printer from small to bigger." << std::endl;
 | ||
|                 //record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS]);
 | ||
|                 //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<ConfigOptionInt>("mtcpp")->value;
 | ||
|         } else if (opt_key == "mstpp") {
 | ||
|             max_slicing_time_per_plate = m_config.option<ConfigOptionInt>("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]);
 | ||
|                 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]);
 | ||
|                 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]);
 | ||
|                 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<ConfigOptionInt>("slice")->value;
 | ||
|             bool pre_check = (plate_to_slice == 0)?true:false;
 | ||
|             bool finished = false;
 | ||
| 
 | ||
|             /*if (opt_key == "export_gcode" && printer_technology == ptSLA) {
 | ||
|                 boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl;
 | ||
|                 record_exit_reson(outfile_dir, 1, 0, cli_errors[1]);
 | ||
|                 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]);
 | ||
|                 flush_and_exit(1);
 | ||
|             }*/
 | ||
|             BOOST_LOG_TRIVIAL(info) << "Need to slice for plate "<<plate_to_slice <<", total plate count "<<partplate_list.get_plate_count()<<" partplates!" << std::endl;
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|             if (g_cli_callback_mgr.is_started()) {
 | ||
|                 PrintBase::SlicingStatus slicing_status{3, "Prepare slicing"};
 | ||
|                 cli_status_callback(slicing_status);
 | ||
|             }
 | ||
| #endif
 | ||
|             // Make a copy of the model if the current action is not the last action, as the model may be
 | ||
|             // modified by the centering and such.
 | ||
|             Model model_copy;
 | ||
|             bool  make_copy = &opt_key != &m_actions.back();
 | ||
|             for (Model &model_in : m_models) {
 | ||
|                 if (make_copy)
 | ||
|                     model_copy = model_in;
 | ||
|                 Model &model = make_copy ? model_copy : model_in;
 | ||
|                 // If all objects have defined instances, their relative positions will be
 | ||
|                 // honored when printing (they will be only centered, unless --dont-arrange
 | ||
|                 // is supplied); if any object has no instances, it will get a default one
 | ||
|                 // and all instances will be rearranged (unless --dont-arrange is supplied).
 | ||
|                 std::string outfile;
 | ||
|                 //Print       fff_print;
 | ||
| 
 | ||
|                 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;
 | ||
|                         }
 | ||
|                         model.curr_plate_index = index;
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: pre_check %2%, start")%(index+1)%pre_check;
 | ||
|                         long long start_time = 0, end_time = 0;
 | ||
|                         start_time = (long long)Slic3r::Utils::get_current_time_utc();
 | ||
|                         //get the current partplate
 | ||
|                         Slic3r::GUI::PartPlate* part_plate = partplate_list.get_plate(index);
 | ||
|                         part_plate->get_print(&print, &gcode_result, &print_index);
 | ||
| 
 | ||
|                         print_fff = dynamic_cast<Print *>(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]);
 | ||
|                             flush_and_exit(CLI_NO_SUITABLE_OBJECTS);
 | ||
|                         }
 | ||
|                         else if ((plate_to_slice != 0) || pre_check) {
 | ||
|                             long long triangle_count = 0;
 | ||
|                             int printable_instances = 0;
 | ||
|                             int skipped_count = 0;
 | ||
|                             for (ModelObject* model_object : model.objects)
 | ||
|                                 for (ModelInstance *i : model_object->instances)
 | ||
|                                 {
 | ||
|                                     i->use_loaded_id_for_label = true;
 | ||
|                                     if (skip_maps.find(i->loaded_id) != skip_maps.end()) {
 | ||
|                                         skip_maps[i->loaded_id] = true;
 | ||
|                                         i->printable = false;
 | ||
|                                         if (i->print_volume_state == ModelInstancePVS_Inside) {
 | ||
|                                             skipped_count++;
 | ||
|                                             plate_has_skips[index] = true;
 | ||
|                                             plate_skipped_objects[index].emplace_back(i->loaded_id);
 | ||
|                                             BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: skip object %2%.")%(index+1)%i->loaded_id;
 | ||
|                                             //need to regenerate the thumbnail
 | ||
|                                             if (plate_data_src.size() > index) {
 | ||
|                                                 if (!plate_data_src[index]->thumbnail_file.empty()) {
 | ||
|                                                     BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded thumbnail %2%.")%(index+1)%plate_data_src[index]->thumbnail_file;
 | ||
|                                                     plate_data_src[index]->thumbnail_file.clear();
 | ||
|                                                 }
 | ||
|                                                 if (!plate_data_src[index]->top_file.empty()) {
 | ||
|                                                     BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded top_thumbnail %2%.")%(index+1)%plate_data_src[index]->top_file;
 | ||
|                                                     plate_data_src[index]->top_file.clear();
 | ||
|                                                 }
 | ||
|                                                 if (!plate_data_src[index]->pick_file.empty()) {
 | ||
|                                                     BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded pick_thumbnail %2%.")%(index+1)%plate_data_src[index]->pick_file;
 | ||
|                                                     plate_data_src[index]->pick_file.clear();
 | ||
|                                                 }
 | ||
|                                             }
 | ||
|                                         }
 | ||
|                                         continue;
 | ||
|                                     }
 | ||
| 
 | ||
|                                     if (i->print_volume_state == ModelInstancePVS_Partly_Outside)
 | ||
|                                     {
 | ||
|                                         BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Found Object " << model_object->name <<" partly inside, can not be sliced." << std::endl;
 | ||
|                                         record_exit_reson(outfile_dir, CLI_OBJECTS_PARTLY_INSIDE, index+1, cli_errors[CLI_OBJECTS_PARTLY_INSIDE]);
 | ||
|                                         flush_and_exit(CLI_OBJECTS_PARTLY_INSIDE);
 | ||
|                                     }
 | ||
|                                     else if ((max_triangle_count_per_plate != 0) && (i->print_volume_state == ModelInstancePVS_Inside))
 | ||
|                                     {
 | ||
|                                         for (const ModelVolume* vol : model_object->volumes)
 | ||
|                                         {
 | ||
|                                             if (vol->is_model_part()) {
 | ||
|                                                 size_t volume_triangle_count = vol->mesh().facets_count();
 | ||
|                                                 triangle_count += volume_triangle_count;
 | ||
|                                                 BOOST_LOG_TRIVIAL(info) << boost::format("volume triangle count %1%, total %2%")%volume_triangle_count %triangle_count;
 | ||
|                                                 if (triangle_count > max_triangle_count_per_plate)
 | ||
|                                                 {
 | ||
|                                                     BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": triangle count " << triangle_count <<" exceeds the limit:" << max_triangle_count_per_plate;
 | ||
|                                                     record_exit_reson(outfile_dir, CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT, index+1, cli_errors[CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT]);
 | ||
|                                                     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 "<<skipped_count<<" objects."<< std::endl;
 | ||
|                                 record_exit_reson(outfile_dir, CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP, index+1, cli_errors[CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP]);
 | ||
|                                 flush_and_exit(CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP);
 | ||
|                             }
 | ||
|                         }
 | ||
|                         // BBS: TODO
 | ||
|                         //BOOST_LOG_TRIVIAL(info) << boost::format("print_volume {%1%,%2%,%3%}->{%4%, %5%, %6%}, has %7% printables") % print_volume.min(0) % print_volume.min(1)
 | ||
|                         //    % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) % count << std::endl;
 | ||
| #endif
 | ||
|                         DynamicPrintConfig new_print_config = m_print_config;
 | ||
|                         new_print_config.apply(*part_plate->config());
 | ||
|                         new_print_config.apply(m_extra_config, true);
 | ||
|                         print->apply(model, new_print_config);
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("set no_check to %1%:")%no_check;
 | ||
|                         print->set_no_check_flag(no_check);//BBS
 | ||
|                         StringObjectException warning;
 | ||
|                         auto err = print->validate(&warning);
 | ||
|                         if (!err.string.empty()) {
 | ||
|                             BOOST_LOG_TRIVIAL(error) << "got error when validate: "<< err.string << std::endl;
 | ||
|                             boost::nowide::cerr << err.string << std::endl;
 | ||
|                             int validate_error;
 | ||
|                             switch (err.type)
 | ||
|                             {
 | ||
|                                 case STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE:
 | ||
|                                     validate_error = CLI_FILAMENT_NOT_MATCH_BED_TYPE;
 | ||
|                                     break;
 | ||
|                                 case STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP:
 | ||
|                                     validate_error = CLI_FILAMENTS_DIFFERENT_TEMP;
 | ||
|                                     break;
 | ||
|                                 case STRING_EXCEPT_OBJECT_COLLISION_IN_SEQ_PRINT:
 | ||
|                                     validate_error = CLI_OBJECT_COLLISION_IN_SEQ_PRINT;
 | ||
|                                     break;
 | ||
|                                 case STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT:
 | ||
|                                     validate_error = CLI_OBJECT_COLLISION_IN_LAYER_PRINT;
 | ||
|                                     break;
 | ||
|                                 default:
 | ||
|                                     validate_error = CLI_VALIDATE_ERROR;
 | ||
|                                     break;
 | ||
|                             }
 | ||
|                             record_exit_reson(outfile_dir, validate_error, index+1, cli_errors[validate_error]);
 | ||
|                             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]);
 | ||
|                             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 "<<index+1 << std::endl;
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|                                 BOOST_LOG_TRIVIAL(info) << "cli callback mgr started:  "<<g_cli_callback_mgr.m_started << std::endl;
 | ||
|                                 if (g_cli_callback_mgr.is_started()) {
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << "set print's callback to cli_status_callback.";
 | ||
|                                     print->set_status_callback(cli_status_callback);
 | ||
|                                     g_cli_callback_mgr.set_plate_info(index+1, (plate_to_slice== 0)?partplate_list.get_plate_count():1);
 | ||
|                                     if (!warning.string.empty()) {
 | ||
|                                         PrintBase::SlicingStatus slicing_status{4, warning.string, 0, 0};
 | ||
|                                         cli_status_callback(slicing_status);
 | ||
|                                     }
 | ||
|                                     else {
 | ||
|                                         PrintBase::SlicingStatus slicing_status{4, "Slicing begins"};
 | ||
|                                         cli_status_callback(slicing_status);
 | ||
|                                     }
 | ||
|                                 }
 | ||
| #endif
 | ||
|                                 //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*>(print))->set_BBL_Printer(is_bbl_vendor_preset);
 | ||
| 
 | ||
|                                 //update information for brim
 | ||
|                                 const PrintConfig& print_config = print_fff->config();
 | ||
|                                 Model::setExtruderParams(m_print_config, filament_count);
 | ||
|                                 Model::setPrintSpeedTable(m_print_config, print_config);
 | ||
|                                 if (load_slicedata) {
 | ||
|                                     std::string plate_dir = load_slice_data_dir+"/"+std::to_string(index+1);
 | ||
|                                     int ret = print->load_cached_data(plate_dir);
 | ||
|                                     if (ret) {
 | ||
|                                         BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": load Slicing data error, ret=" << ret;
 | ||
|                                         BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": switch normal slicing";
 | ||
|                                         print->process();
 | ||
|                                     }
 | ||
|                                     else {
 | ||
|                                         BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": load cached data success, go on.";
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|                                         if (g_cli_callback_mgr.is_started()) {
 | ||
|                                             PrintBase::SlicingStatus slicing_status{69, "Cache data loaded"};
 | ||
|                                             cli_status_callback(slicing_status);
 | ||
|                                         }
 | ||
| #endif
 | ||
|                                         print->process(true);
 | ||
|                                         BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": finished print::process.";
 | ||
|                                     }
 | ||
|                                 }
 | ||
|                                 else {
 | ||
|                                     print->process();
 | ||
|                                 }
 | ||
|                                 if (printer_technology == ptFFF) {
 | ||
|                                     std::string conflict_result = print_fff->get_conflict_string();
 | ||
|                                     if (!conflict_result.empty()) {
 | ||
|                                        BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": found slicing result conflict!"<< std::endl;
 | ||
|                                        record_exit_reson(outfile_dir, CLI_GCODE_PATH_CONFLICTS, index+1, cli_errors[CLI_GCODE_PATH_CONFLICTS]);
 | ||
|                                        flush_and_exit(CLI_GCODE_PATH_CONFLICTS);
 | ||
|                                     }
 | ||
| 
 | ||
|                                     // The outfile is processed by a PlaceholderParser.
 | ||
|                                     //outfile = part_plate->get_tmp_gcode_path();
 | ||
|                                     if (outfile_dir.empty()) {
 | ||
|                                         outfile = part_plate->get_tmp_gcode_path();
 | ||
|                                     }
 | ||
|                                     else {
 | ||
|                                         outfile = outfile_dir + "/plate_" + std::to_string(index + 1) + ".gcode";
 | ||
|                                         part_plate->set_tmp_gcode_path(outfile);
 | ||
|                                     }
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << "process finished, will export gcode temporily to " << outfile << std::endl;
 | ||
|                                     outfile = print_fff->export_gcode(outfile, gcode_result, nullptr);
 | ||
|                                     //outfile_final = (dynamic_cast<Print*>(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]);
 | ||
|                                         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]);
 | ||
|                                         flush_and_exit(ret);
 | ||
|                                     }
 | ||
|                                 }
 | ||
|                                 end_time = (long long)Slic3r::Utils::get_current_time_utc();
 | ||
|                                 sliced_time[index] = end_time - start_time;
 | ||
|                                 if (max_slicing_time_per_plate != 0) {
 | ||
|                                     long long time_cost = end_time - start_time;
 | ||
|                                     if (time_cost > max_slicing_time_per_plate) {
 | ||
|                                         BOOST_LOG_TRIVIAL(error) << boost::format("plate %1%'s slice time %2% exceeds the limit %3%, return error.")
 | ||
|                                             %(index+1) %time_cost %max_slicing_time_per_plate;
 | ||
|                                         record_exit_reson(outfile_dir, CLI_SLICING_TIME_EXCEEDS_LIMIT, index+1, cli_errors[CLI_SLICING_TIME_EXCEEDS_LIMIT]);
 | ||
|                                         flush_and_exit(CLI_SLICING_TIME_EXCEEDS_LIMIT);
 | ||
|                                     }
 | ||
|                                 }
 | ||
|                             } catch (const std::exception &ex) {
 | ||
|                                 BOOST_LOG_TRIVIAL(error) << "found slicing or export error for partplate "<<index+1 << std::endl;
 | ||
|                                 boost::nowide::cerr << ex.what() << std::endl;
 | ||
|                                 //continue;
 | ||
|                                 record_exit_reson(outfile_dir, CLI_SLICING_ERROR, index+1, cli_errors[CLI_SLICING_ERROR]);
 | ||
|                                 flush_and_exit(CLI_SLICING_ERROR);
 | ||
|                             }
 | ||
|                         }
 | ||
|                     }
 | ||
|                     if (pre_check&& (partplate_list.get_plate_count() > 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<double, std::ratio<1> > second_;
 | ||
|                 std::chrono::time_point<clock_> 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<second_>(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]);
 | ||
|             flush_and_exit(CLI_UNSUPPORTED_OPERATION);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     global_begin_time = (long long)Slic3r::Utils::get_current_time_utc();
 | ||
|     if (export_to_3mf) {
 | ||
|         //BBS: export as bbl 3mf
 | ||
|         std::vector<ThumbnailData *> thumbnails, top_thumbnails, pick_thumbnails;
 | ||
|         std::vector<PlateBBoxData*> plate_bboxes;
 | ||
|         PlateDataPtrs plate_data_list;
 | ||
|         partplate_list.store_to_3mf_structure(plate_data_list);
 | ||
| 
 | ||
|         if (!outfile_dir.empty()) {
 | ||
|             export_3mf_file = outfile_dir + "/"+export_3mf_file;
 | ||
|         }
 | ||
| 
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|         if (g_cli_callback_mgr.is_started()) {
 | ||
|             PrintBase::SlicingStatus slicing_status{94, "Generate thumbnails"};
 | ||
|             cli_status_callback(slicing_status);
 | ||
|         }
 | ||
| #endif
 | ||
| 
 | ||
|         bool need_regenerate_thumbnail = oriented_or_arranged || regenerate_thumbnails;
 | ||
|         bool need_regenerate_top_thumbnail = oriented_or_arranged || regenerate_thumbnails;
 | ||
|         bool need_create_thumbnail_group = false,  need_create_top_group = false;
 | ||
| 
 | ||
|         // get type and color for platedata
 | ||
|         auto* filament_types = dynamic_cast<const ConfigOptionStrings*>(m_print_config.option("filament_type"));
 | ||
|         const ConfigOptionStrings* filament_color = dynamic_cast<const ConfigOptionStrings *>(m_print_config.option("filament_colour"));
 | ||
|         //auto* filament_id = dynamic_cast<const ConfigOptionStrings*>(m_print_config.option("filament_ids"));
 | ||
| 
 | ||
|         for (int i = 0; i < plate_data_list.size(); i++) {
 | ||
|             PlateData *plate_data = plate_data_list[i];
 | ||
|             bool skip_this_plate = ((plate_to_slice != 0) && (plate_to_slice != (i + 1)))?true:false;
 | ||
| 
 | ||
|             plate_data->skipped_objects = plate_skipped_objects[i];
 | ||
| 
 | ||
|             for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) {
 | ||
|                 //it->filament_id = filament_id?filament_id->get_at(it->id):"unknown";
 | ||
|                 std::string display_filament_type;
 | ||
|                 it->type  = m_print_config.get_filament_type(display_filament_type, it->id);
 | ||
|                 it->color = filament_color ? filament_color->get_at(it->id) : "#FFFFFF";
 | ||
|             }
 | ||
| 
 | ||
|             if (!plate_data->plate_thumbnail.is_valid()) {
 | ||
|                 if (!oriented_or_arranged && !regenerate_thumbnails && plate_data_src.size() > i)
 | ||
|                     plate_data->thumbnail_file = plate_data_src[i]->thumbnail_file;
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail data is invalid, check the file %2% exist or not")%(i+1) %plate_data->thumbnail_file;
 | ||
|                 if (plate_data->thumbnail_file.empty() || (!boost::filesystem::exists(plate_data->thumbnail_file))) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file also not there, need to regenerate")%(i+1);
 | ||
|                     if (!skip_this_plate) {
 | ||
|                         need_regenerate_thumbnail = true;
 | ||
|                         need_create_thumbnail_group = true;
 | ||
|                     }
 | ||
|                 }
 | ||
|                 else {
 | ||
|                     if (regenerate_thumbnails) {
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file %2% cleared, need to regenerate")%(i+1) %plate_data->thumbnail_file;
 | ||
|                         plate_data->thumbnail_file.clear();
 | ||
|                     }
 | ||
|                     else
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file exists, no need to regenerate")%(i+1);
 | ||
|                 }
 | ||
|             }
 | ||
|             else {
 | ||
|                 if (regenerate_thumbnails)
 | ||
|                     plate_data->plate_thumbnail.reset();
 | ||
| 
 | ||
|                 if (!skip_this_plate) {
 | ||
|                     need_create_thumbnail_group = true;
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             if (plate_data->top_file.empty() || plate_data->pick_file.empty()) {
 | ||
|                 if (!regenerate_thumbnails && (plate_data_src.size() > i)) {
 | ||
|                     plate_data->top_file = plate_data_src[i]->top_file;
 | ||
|                     plate_data->pick_file = plate_data_src[i]->pick_file;
 | ||
|                 }
 | ||
|                 if (plate_data->top_file.empty()|| plate_data->pick_file.empty()
 | ||
|                     || (!boost::filesystem::exists(plate_data->top_file)) || (!boost::filesystem::exists(plate_data->pick_file))) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_file %2% also not there, need to regenerate")%(i+1)%plate_data->top_file;
 | ||
|                     if (!skip_this_plate) {
 | ||
|                         need_regenerate_top_thumbnail = true;
 | ||
|                         need_create_top_group = true;
 | ||
|                     }
 | ||
|                 }
 | ||
|                 else {
 | ||
|                     if (regenerate_thumbnails) {
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_thumbnail file %2% cleared, need to regenerate")%(i+1) %plate_data->top_file;
 | ||
|                         plate_data->top_file.clear();
 | ||
|                         plate_data->pick_file.clear();
 | ||
|                     }
 | ||
|                     else
 | ||
|                         BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_thumbnail file exists, no need to regenerate")%(i+1);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         if (need_regenerate_thumbnail || need_regenerate_top_thumbnail) {
 | ||
|             std::vector<std::string> colors;
 | ||
|             if (filament_color) {
 | ||
|                 colors= filament_color->vserialize();
 | ||
|             }
 | ||
|             else
 | ||
|                 colors.push_back("#FFFFFFFF");
 | ||
| 
 | ||
|             std::vector<std::array<float, 4>> 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 " <<code<< std::endl;
 | ||
|             }
 | ||
|             else {
 | ||
|                 BOOST_LOG_TRIVIAL(info) << "glfwInit Success."<< std::endl;
 | ||
|                 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, gl_major);
 | ||
|                 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, gl_minor);
 | ||
|                 glfwWindowHint(GLFW_RED_BITS, 8);
 | ||
|                 glfwWindowHint(GLFW_GREEN_BITS, 8);
 | ||
|                 glfwWindowHint(GLFW_BLUE_BITS, 8);
 | ||
|                 glfwWindowHint(GLFW_ALPHA_BITS, 8);
 | ||
|                 glfwWindowHint(GLFW_VISIBLE, false);
 | ||
|                 //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 | ||
|                 //glfwDisable(GLFW_AUTO_POLL_EVENTS);
 | ||
| #ifdef __WXMAC__
 | ||
|                 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 | ||
|                 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
 | ||
| #else
 | ||
|                 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef __linux__
 | ||
|                 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_OSMESA_CONTEXT_API);
 | ||
| #endif
 | ||
| 
 | ||
|                 GLFWwindow* window = glfwCreateWindow(640, 480, "base_window", NULL, NULL);
 | ||
|                 if (window == NULL)
 | ||
|                 {
 | ||
|                     BOOST_LOG_TRIVIAL(error) << "Failed to create GLFW window" << std::endl;
 | ||
|                 }
 | ||
|                 else
 | ||
|                     glfwMakeContextCurrent(window);
 | ||
|             }
 | ||
| 
 | ||
|             //opengl manager related logic
 | ||
|             {
 | ||
|                 Slic3r::GUI::OpenGLManager opengl_mgr;
 | ||
|                 bool opengl_valid = opengl_mgr.init_gl(false);
 | ||
|                 if (!opengl_valid) {
 | ||
|                     BOOST_LOG_TRIVIAL(error) << "init opengl failed! skip thumbnail generating" << std::endl;
 | ||
|                 }
 | ||
|                 else {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << "glewInit Sucess." << std::endl;
 | ||
|                     GLVolumeCollection glvolume_collection;
 | ||
|                     Model &model = m_models[0];
 | ||
|                     int extruder_id = 1;
 | ||
|                     for (unsigned int obj_idx = 0; obj_idx < (unsigned int)model.objects.size(); ++ obj_idx) {
 | ||
|                         const ModelObject &model_object = *model.objects[obj_idx];
 | ||
|                         const ConfigOption* option = model_object.config.option("extruder");
 | ||
|                         if (option)
 | ||
|                             extruder_id = (dynamic_cast<const ConfigOptionInt *>(option))->getInt();
 | ||
|                         for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) {
 | ||
|                             const ModelVolume &model_volume = *model_object.volumes[volume_idx];
 | ||
|                             option = model_volume.config.option("extruder");
 | ||
|                             if (option) extruder_id = (dynamic_cast<const ConfigOptionInt *>(option))->getInt();
 | ||
|                             //if (!model_volume.is_model_part())
 | ||
|                             //    continue;
 | ||
|                             for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) {
 | ||
|                                 const ModelInstance &model_instance = *model_object.instances[instance_idx];
 | ||
|                                 glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, "volume", true, false, true);
 | ||
|                                 //glvolume_collection.volumes.back()->geometry_id = key.geometry_id;
 | ||
|                                 std::string color = filament_color?filament_color->get_at(extruder_id - 1):"#00FF00FF";
 | ||
| 
 | ||
|                                 unsigned char  rgb_color[4] = {};
 | ||
|                                 Slic3r::GUI::BitmapCache::parse_color4(color, rgb_color);
 | ||
|                                 glvolume_collection.volumes.back()->set_render_color( float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, float(rgb_color[3]) / 255.f);
 | ||
| 
 | ||
|                                 std::array<float, 4> 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_color(new_color);
 | ||
|                                 glvolume_collection.volumes.back()->printable = model_instance.printable;
 | ||
|                             }
 | ||
|                         }
 | ||
|                     }
 | ||
| 
 | ||
|                     ThumbnailsParams thumbnail_params;
 | ||
|                     GLShaderProgram* shader = opengl_mgr.get_shader("thumbnail");
 | ||
|                     if (!shader) {
 | ||
|                         BOOST_LOG_TRIVIAL(error) << boost::format("can not get shader for rendering thumbnail");
 | ||
|                     }
 | ||
|                     else {
 | ||
|                         for (int i = 0; i < partplate_list.get_plate_count(); i++) {
 | ||
|                             Slic3r::GUI::PartPlate *part_plate      = partplate_list.get_plate(i);
 | ||
|                             PlateData *plate_data = plate_data_list[i];
 | ||
|                             if (plate_data->plate_thumbnail.is_valid()) {
 | ||
|                                 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, reset plate %2%'s thumbnail.")%__LINE__%(i+1);
 | ||
|                                     plate_data->plate_thumbnail.reset();
 | ||
|                                 }
 | ||
|                                 else
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid thumbnail, width %2%, height %3%, directly using it")%(i+1) %plate_data->plate_thumbnail.width %plate_data->plate_thumbnail.height;
 | ||
|                             }
 | ||
|                             else if (!plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file)))
 | ||
|                             {
 | ||
|                                 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s thumbnail file path to empty.")%__LINE__%(i+1);
 | ||
|                                     plate_data->thumbnail_file.clear();
 | ||
|                                 }
 | ||
|                                 else
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid thumbnail %2% extracted from 3mf, directly using it")%(i+1) %plate_data->thumbnail_file;
 | ||
|                             }
 | ||
|                             else {
 | ||
|                                 ThumbnailData* thumbnail_data = &plate_data->plate_thumbnail;
 | ||
| 
 | ||
|                                 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1);
 | ||
|                                 }
 | ||
|                                 else {
 | ||
|                                     unsigned int thumbnail_width = 512, thumbnail_height = 512;
 | ||
|                                     const ThumbnailsParams thumbnail_params = {{}, false, true, true, true, i};
 | ||
| 
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail, need to regenerate")%(i+1);
 | ||
|                                     switch (Slic3r::GUI::OpenGLManager::get_framebuffers_type())
 | ||
|                                     {
 | ||
|                                     case Slic3r::GUI::OpenGLManager::EFramebufferType::Arb:
 | ||
|                                             {
 | ||
|                                                 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB");
 | ||
|                                                 Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*thumbnail_data,
 | ||
|                                                    thumbnail_width, thumbnail_height, thumbnail_params,
 | ||
|                                                    partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho);
 | ||
|                                                 break;
 | ||
|                                             }
 | ||
|                                     case Slic3r::GUI::OpenGLManager::EFramebufferType::Ext:
 | ||
|                                             {
 | ||
|                                                 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT");
 | ||
|                                                 Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*thumbnail_data,
 | ||
|                                                    thumbnail_width, thumbnail_height, thumbnail_params,
 | ||
|                                                    partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho);
 | ||
|                                                 break;
 | ||
|                                             }
 | ||
|                                     default:
 | ||
|                                             BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown");
 | ||
|                                             break;
 | ||
|                                     }
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail,finished rendering")%(i+1);
 | ||
|                                 }
 | ||
|                             }
 | ||
|                             if (need_create_thumbnail_group) {
 | ||
|                                 thumbnails.push_back(&plate_data->plate_thumbnail);
 | ||
|                                 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1);
 | ||
|                             }
 | ||
| 
 | ||
|                             //top thumbnails
 | ||
|                             /*if (part_plate->top_thumbnail_data.is_valid() && part_plate->pick_thumbnail_data.is_valid()) {
 | ||
|                                 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, reset plate %2%'s top/pick thumbnail.")%__LINE__%(i+1);
 | ||
|                                     part_plate->top_thumbnail_data.reset();
 | ||
|                                     part_plate->pick_thumbnail_data.reset();
 | ||
|                                     plate_data->top_file.clear();
 | ||
|                                     plate_data->pick_file.clear();
 | ||
|                                 }
 | ||
|                                 else {
 | ||
|                                     plate_data->top_file = "valid_top";
 | ||
|                                     plate_data->pick_file = "valid_pick";
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid top/pick thumbnail data, directly using it")%(i+1);
 | ||
|                                 }
 | ||
|                             }
 | ||
|                             else*/
 | ||
|                             if ((!plate_data->top_file.empty() && (boost::filesystem::exists(plate_data->top_file)))
 | ||
|                                 &&(!plate_data->pick_file.empty() && (boost::filesystem::exists(plate_data->pick_file))))
 | ||
|                             {
 | ||
|                                 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s top/pick thumbnail file path to empty.")%__LINE__%(i+1);
 | ||
|                                     plate_data->top_file.clear();
 | ||
|                                     plate_data->pick_file.clear();
 | ||
|                                 }
 | ||
|                                 else
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has valid top/pick thumbnail extracted from 3mf, directly using it")%(i+1);
 | ||
|                             }
 | ||
|                             else{
 | ||
|                                 ThumbnailData* top_thumbnail = &part_plate->top_thumbnail_data;
 | ||
|                                 ThumbnailData* picking_thumbnail = &part_plate->pick_thumbnail_data;
 | ||
|                                 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1);
 | ||
|                                     part_plate->top_thumbnail_data.reset();
 | ||
|                                     part_plate->pick_thumbnail_data.reset();
 | ||
|                                     plate_data->top_file.clear();
 | ||
|                                     plate_data->pick_file.clear();
 | ||
|                                 }
 | ||
|                                 else {
 | ||
|                                     unsigned int thumbnail_width = 512, thumbnail_height = 512;
 | ||
|                                     const ThumbnailsParams thumbnail_params = { {}, false, true, false, true, i };
 | ||
| 
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s top/pick thumbnail missed, need to regenerate")%(i+1);
 | ||
| 
 | ||
|                                     switch (Slic3r::GUI::OpenGLManager::get_framebuffers_type())
 | ||
|                                     {
 | ||
|                                     case Slic3r::GUI::OpenGLManager::EFramebufferType::Arb:
 | ||
|                                             {
 | ||
|                                                 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB");
 | ||
|                                                 Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*top_thumbnail,
 | ||
|                                                    thumbnail_width, thumbnail_height, thumbnail_params,
 | ||
|                                                    partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, false);
 | ||
|                                                 Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*picking_thumbnail,
 | ||
|                                                    thumbnail_width, thumbnail_height, thumbnail_params,
 | ||
|                                                    partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, true);
 | ||
|                                                 break;
 | ||
|                                             }
 | ||
|                                     case Slic3r::GUI::OpenGLManager::EFramebufferType::Ext:
 | ||
|                                             {
 | ||
|                                                 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT");
 | ||
|                                                 Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*top_thumbnail,
 | ||
|                                                    thumbnail_width, thumbnail_height, thumbnail_params,
 | ||
|                                                    partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, false);
 | ||
|                                                 Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*picking_thumbnail,
 | ||
|                                                    thumbnail_width, thumbnail_height, thumbnail_params,
 | ||
|                                                    partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, true);
 | ||
|                                                 break;
 | ||
|                                             }
 | ||
|                                     default:
 | ||
|                                             BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown");
 | ||
|                                             break;
 | ||
|                                     }
 | ||
|                                     plate_data->top_file = "valid_top";
 | ||
|                                     plate_data->pick_file = "valid_pick";
 | ||
|                                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s top_thumbnail,finished rendering")%(i+1);
 | ||
|                                 }
 | ||
|                             }
 | ||
| 
 | ||
|                             if (need_create_top_group) {
 | ||
|                                 top_thumbnails.push_back(&part_plate->top_thumbnail_data);
 | ||
|                                 pick_thumbnails.push_back(&part_plate->pick_thumbnail_data);
 | ||
|                                 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for top and pick into group")%(i+1);
 | ||
|                             }
 | ||
|                         }
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
|             //BBS: release glfw
 | ||
|             glfwTerminate();
 | ||
|         }
 | ||
|         else {
 | ||
|             BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: use previous thumbnails, no need to regenerate")%__LINE__;
 | ||
|             for (int i = 0; i < partplate_list.get_plate_count(); i++) {
 | ||
|                 PlateData *plate_data = plate_data_list[i];
 | ||
|                 bool skip_this_plate = ((plate_to_slice != 0) && (plate_to_slice != (i + 1)))?true:false;
 | ||
|                 Slic3r::GUI::PartPlate *part_plate      = partplate_list.get_plate(i);
 | ||
| 
 | ||
|                 if (skip_this_plate) {
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s all the thumbnails skipped, reset here")%(i+1);
 | ||
|                     plate_data->plate_thumbnail.reset();
 | ||
|                     plate_data->thumbnail_file.clear();
 | ||
|                     part_plate->top_thumbnail_data.reset();
 | ||
|                     part_plate->pick_thumbnail_data.reset();
 | ||
|                     plate_data->top_file.clear();
 | ||
|                     plate_data->pick_file.clear();
 | ||
|                 }
 | ||
| 
 | ||
|                 if (need_create_thumbnail_group) {
 | ||
|                     thumbnails.push_back(&plate_data->plate_thumbnail);
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1);
 | ||
|                 }
 | ||
| 
 | ||
|                 if (need_create_top_group) {
 | ||
|                     top_thumbnails.push_back(&part_plate->top_thumbnail_data);
 | ||
|                     pick_thumbnails.push_back(&part_plate->pick_thumbnail_data);
 | ||
|                     BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for top and pick into group")%(i+1);
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         //generate first layer bboxes
 | ||
|         for (int i = 0; i < partplate_list.get_plate_count(); i++) {
 | ||
|             if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: generate bbox, Skip plate %2%.")%__LINE__%(i+1);
 | ||
|                 plate_bboxes.push_back(new PlateBBoxData());
 | ||
|                 continue;
 | ||
|             }
 | ||
|             Slic3r::GUI::PartPlate *part_plate      = partplate_list.get_plate(i);
 | ||
|             //render calibration thumbnail
 | ||
|             if (!part_plate->get_slice_result() || !part_plate->is_slice_result_valid()) {
 | ||
|                 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% doesn't have a valid sliced result, skip it")%(i+1);
 | ||
|                 //calibration_thumbnails.push_back(new ThumbnailData());
 | ||
|                 plate_bboxes.push_back(new PlateBBoxData());
 | ||
|                 continue;
 | ||
|             }
 | ||
|             PrintBase  *print_base=NULL;
 | ||
|             Slic3r::GUI::GCodeResult *gcode_result = NULL;
 | ||
|             int print_index;
 | ||
|             part_plate->get_print(&print_base, &gcode_result, &print_index);
 | ||
| 
 | ||
|             Print *print = dynamic_cast<Print *>(print_base);
 | ||
| 
 | ||
|              //don't render calibration picture
 | ||
|             /*BuildVolume build_volume(part_plate->get_shape(), print_height);
 | ||
|             const std::vector<BoundingBoxf3>& 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<std::string> 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<BBoxData>& 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<ConfigOptionEnum<PrintSequence>>("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<ConfigOptionEnum<BedType>>("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<ConfigOptionFloats>("nozzle_diameter");
 | ||
|             if (opt_nozzle_diameters != nullptr)
 | ||
|                 plate_bbox->nozzle_diameter = float(opt_nozzle_diameters->get_at(plate_bbox->first_extruder));
 | ||
| 
 | ||
|             auto objects = print->objects();
 | ||
|             auto orig = part_plate->get_origin();
 | ||
|             Vec2d orig2d = { orig[0], orig[1] };
 | ||
| 
 | ||
|             for (auto obj : objects)
 | ||
|             {
 | ||
|                 BBoxData data;
 | ||
|                 auto bb_scaled = obj->get_first_layer_bbox(data.area, data.layer_height, data.name);
 | ||
|                 auto bb = unscaled(bb_scaled);
 | ||
|                 bbox_all.merge(bb);
 | ||
|                 data.area *= (SCALING_FACTOR * SCALING_FACTOR); // unscale area
 | ||
|                 data.id = obj->id().id;
 | ||
|                 data.bbox = { bb.min.x(),bb.min.y(),bb.max.x(),bb.max.y() };
 | ||
|                 id_bboxes.emplace_back(std::move(data));
 | ||
|             }
 | ||
|             // add wipe tower bounding box
 | ||
|             if (print->has_wipe_tower()) {
 | ||
|                 BBoxData data;
 | ||
|                 auto   wt_corners = print->first_layer_wipe_tower_corners();
 | ||
|                 // when loading gcode.3mf, wipe tower info may not be correct
 | ||
|                 if (!wt_corners.empty()) {
 | ||
|                     BoundingBox bb_scaled = {wt_corners[0], wt_corners[2]};
 | ||
|                     auto        bb        = unscaled(bb_scaled);
 | ||
|                     bb.min -= orig2d;
 | ||
|                     bb.max -= orig2d;
 | ||
|                     bbox_all.merge(bb);
 | ||
|                     data.name = "wipe_tower";
 | ||
|                     data.id   = partplate_list.get_curr_plate()->get_index() + 1000;
 | ||
|                     data.bbox = {bb.min.x(), bb.min.y(), bb.max.x(), bb.max.y()};
 | ||
|                     id_bboxes.emplace_back(std::move(data));
 | ||
|                 }
 | ||
|             }
 | ||
|             plate_bbox->bbox_all = { bbox_all.min.x(),bbox_all.min.y(),bbox_all.max.x(),bbox_all.max.y() };
 | ||
| 
 | ||
|             PlateData *plate_data = plate_data_list[i];
 | ||
|             for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) {
 | ||
|                 plate_bbox->filament_ids.push_back(it->id);
 | ||
|                 plate_bbox->filament_colors.push_back(it->color);
 | ||
|             }
 | ||
|             plate_bboxes.push_back(plate_bbox);
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|         if (g_cli_callback_mgr.is_started()) {
 | ||
|             PrintBase::SlicingStatus slicing_status{97, "Exporting 3mf"};
 | ||
|             cli_status_callback(slicing_status);
 | ||
|         }
 | ||
| #endif
 | ||
| 
 | ||
|         BOOST_LOG_TRIVIAL(info) << "will export 3mf to " << export_3mf_file << std::endl;
 | ||
|         if (! this->export_project(&m_models[0], export_3mf_file, plate_data_list, project_presets, thumbnails, top_thumbnails, pick_thumbnails,
 | ||
|                                 calibration_thumbnails, plate_bboxes, &m_print_config, minimum_save))
 | ||
|         {
 | ||
|             release_PlateData_list(plate_data_list);
 | ||
|             record_exit_reson(outfile_dir, CLI_EXPORT_3MF_ERROR, 0, cli_errors[CLI_EXPORT_3MF_ERROR]);
 | ||
|             flush_and_exit(CLI_EXPORT_3MF_ERROR);
 | ||
|         }
 | ||
|         release_PlateData_list(plate_data_list);
 | ||
|         for (unsigned int i = 0; i < thumbnails.size(); i++)
 | ||
|             thumbnails[i]->reset();
 | ||
|         for (unsigned int i = 0; i < top_thumbnails.size(); i++)
 | ||
|             top_thumbnails[i]->reset();
 | ||
|         for (unsigned int i = 0; i < pick_thumbnails.size(); i++)
 | ||
|             pick_thumbnails[i]->reset();
 | ||
| 
 | ||
|         for (unsigned int i = 0; i < calibration_thumbnails.size(); i++)
 | ||
|             delete calibration_thumbnails[i];
 | ||
| 
 | ||
|         for (int i = 0; i < plate_bboxes.size(); i++)
 | ||
|             delete plate_bboxes[i];
 | ||
|     }
 | ||
| 
 | ||
|     if (plate_data_src.size() > 0)
 | ||
|     {
 | ||
|         release_PlateData_list(plate_data_src);
 | ||
|     }
 | ||
| 
 | ||
| #if defined(__linux__) || defined(__LINUX__)
 | ||
|     if (g_cli_callback_mgr.is_started()) {
 | ||
|         PrintBase::SlicingStatus slicing_status{100, "All done, Success"};
 | ||
|         cli_status_callback(slicing_status);
 | ||
|     }
 | ||
| 
 | ||
|     g_cli_callback_mgr.stop();
 | ||
| #endif
 | ||
| 
 | ||
|     for (Model &model : m_models) {
 | ||
| 	model.remove_backup_path_if_exist();
 | ||
|     }
 | ||
|     //BBS: flush logs
 | ||
|     BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", Finished" << std::endl;
 | ||
|     global_current_time = (long long)Slic3r::Utils::get_current_time_utc();
 | ||
|     export_time = (size_t) (global_current_time - global_begin_time);
 | ||
| 
 | ||
|     //record the duplicate here
 | ||
|     if (duplicate_count > 0)
 | ||
|     {
 | ||
|         std::map<std::string, std::string> key_values;
 | ||
|         key_values["sliced_count"] = std::to_string(duplicate_count+1);
 | ||
|         record_exit_reson(outfile_dir, 0, plate_to_slice, cli_errors[0], prepare_time, sliced_time, export_time, key_values);
 | ||
|     }
 | ||
|     else {
 | ||
|         record_exit_reson(outfile_dir, 0, plate_to_slice, cli_errors[0], prepare_time, sliced_time, export_time);
 | ||
|     }
 | ||
| 
 | ||
|     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<std::string, std::string> 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<std::string, std::string>::iterator it=validity.begin(); it!=validity.end(); ++it)
 | ||
|             boost::nowide::cerr << it->first <<": "<< it->second << std::endl;
 | ||
|         return false;
 | ||
|     }
 | ||
| 
 | ||
|     return true;
 | ||
| }
 | ||
| 
 | ||
| void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const
 | ||
| {
 | ||
|     boost::nowide::cout
 | ||
|         << SLIC3R_APP_KEY <<"-"<< SLIC3R_VERSION << ":"
 | ||
|         << std::endl
 | ||
|         << "Usage: bambu-studio [ OPTIONS ] [ file.3mf/file.stl ... ]" << std::endl
 | ||
|         << std::endl
 | ||
|         << "OPTIONS:" << std::endl;
 | ||
|     cli_misc_config_def.print_cli_help(boost::nowide::cout, false);
 | ||
|     cli_transform_config_def.print_cli_help(boost::nowide::cout, false);
 | ||
|     cli_actions_config_def.print_cli_help(boost::nowide::cout, false);
 | ||
| 
 | ||
|     boost::nowide::cout
 | ||
|         << std::endl
 | ||
|         << "Print settings priorites:" << std::endl
 | ||
|         << "\t1) setting values from the command line (highest priority)"<< std::endl
 | ||
|         << "\t2) setting values loaded with --load_settings and --load_filaments" << std::endl
 | ||
| 	    << "\t3) setting values loaded from 3mf(lowest priority)" << std::endl;
 | ||
| 
 | ||
|     /*if (include_print_options) {
 | ||
|         boost::nowide::cout << std::endl;
 | ||
|         print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def)
 | ||
|             { return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; });
 | ||
|     } else {
 | ||
|         boost::nowide::cout
 | ||
|             << std::endl
 | ||
|             << "Run --help-fff / --help-sla to see the full listing of print options." << std::endl;
 | ||
|     }*/
 | ||
| }
 | ||
| 
 | ||
| bool CLI::export_models(IO::ExportFormat format)
 | ||
| {
 | ||
|     for (Model &model : m_models) {
 | ||
|         const std::string path = this->output_filepath(model, format);
 | ||
|         bool success = true;
 | ||
|         switch (format) {
 | ||
|             //case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr, false); break;
 | ||
|             case IO::OBJ:
 | ||
|                 success = Slic3r::store_obj(path.c_str(), &model);
 | ||
|                 if (success)
 | ||
|                     BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl;
 | ||
|                 else {
 | ||
|                     boost::nowide::cerr << "Model export to " << path << " failed" << std::endl;
 | ||
|                     return false;
 | ||
|                 }
 | ||
|                 break;
 | ||
|             case IO::STL:
 | ||
|             {
 | ||
|                 unsigned int index = 1;
 | ||
|                 for (ModelObject* model_object : model.objects)
 | ||
|                 {
 | ||
|                     const std::string path = this->output_filepath(*model_object, index++, format);
 | ||
|                     success = Slic3r::store_stl(path.c_str(), model_object, true);
 | ||
|                     if (success)
 | ||
|                         BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl;
 | ||
|                     else {
 | ||
|                         boost::nowide::cerr << "Model export to " << path << " failed" << std::endl;
 | ||
|                         return false;
 | ||
|                     }
 | ||
|                 }
 | ||
|                 break;
 | ||
|             }
 | ||
|             //BBS: use bbs 3mf instead of original
 | ||
|             //case IO::TMF: success = Slic3r::store_bbs_3mf(path.c_str(), &model, nullptr, false); break;
 | ||
|             default: assert(false); break;
 | ||
|         }
 | ||
|     }
 | ||
|     return true;
 | ||
| }
 | ||
| 
 | ||
| //BBS: add export_project function
 | ||
| bool CLI::export_project(Model *model, std::string& path, PlateDataPtrs &partplate_data,
 | ||
|     std::vector<Preset*>& project_presets, std::vector<ThumbnailData*>& thumbnails, std::vector<ThumbnailData*>& top_thumbnails, std::vector<ThumbnailData*>& pick_thumbnails,
 | ||
|     std::vector<ThumbnailData*>& calibration_thumbnails, std::vector<PlateBBoxData*>& plate_bboxes, const DynamicPrintConfig* config, bool minimum_save)
 | ||
| {
 | ||
|     //const std::string path = this->output_filepath(*model, IO::TMF);
 | ||
|     bool success = false;
 | ||
| 
 | ||
|     StoreParams store_params;
 | ||
|     store_params.path = path.c_str();
 | ||
|     store_params.model = model;
 | ||
|     store_params.plate_data_list = partplate_data;
 | ||
|     store_params.project_presets = project_presets;
 | ||
|     store_params.config = (DynamicPrintConfig*)config;
 | ||
|     store_params.thumbnail_data = thumbnails;
 | ||
|     store_params.top_thumbnail_data = top_thumbnails;
 | ||
|     store_params.pick_thumbnail_data = pick_thumbnails;
 | ||
|     store_params.calibration_thumbnail_data = calibration_thumbnails;
 | ||
|     store_params.id_bboxes = plate_bboxes;
 | ||
|     store_params.strategy = SaveStrategy::Silence|SaveStrategy::WithGcode|SaveStrategy::SplitModel|SaveStrategy::UseLoadedId|SaveStrategy::ShareMesh;
 | ||
|     if (minimum_save)
 | ||
|         store_params.strategy = store_params.strategy | SaveStrategy::SkipModel;
 | ||
| 
 | ||
|     success = Slic3r::store_bbs_3mf(store_params);
 | ||
| 
 | ||
|     if (success)
 | ||
|         BOOST_LOG_TRIVIAL(info) << "Project exported to " << path << std::endl;
 | ||
|     else {
 | ||
|         boost::nowide::cerr << "Project export to " << path << " failed" << std::endl;
 | ||
|         return false;
 | ||
|     }
 | ||
|     return true;
 | ||
| }
 | ||
| 
 | ||
| std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) const
 | ||
| {
 | ||
|     std::string ext;
 | ||
|     switch (format) {
 | ||
|         case IO::AMF: ext = ".zip.amf"; break;
 | ||
|         case IO::OBJ: ext = ".obj"; break;
 | ||
|         case IO::STL: ext = ".stl"; break;
 | ||
|         case IO::TMF: ext = ".3mf"; break;
 | ||
|         default: assert(false); break;
 | ||
|     };
 | ||
|     auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext));
 | ||
|     // use --output when available
 | ||
|     std::string cmdline_param = m_config.opt_string("outputdir");
 | ||
|     if (! cmdline_param.empty()) {
 | ||
|         // if we were supplied a directory, use it and append our automatically generated filename
 | ||
|         boost::filesystem::path cmdline_path(cmdline_param);
 | ||
|         if (boost::filesystem::is_directory(cmdline_path))
 | ||
|             proposed_path = cmdline_path / proposed_path.filename();
 | ||
|         else
 | ||
|             proposed_path = cmdline_param + ext;
 | ||
|     }
 | ||
|     return proposed_path.string();
 | ||
| }
 | ||
| 
 | ||
| std::string CLI::output_filepath(const ModelObject &object, unsigned int index, IO::ExportFormat format) const
 | ||
| {
 | ||
|     std::string ext, subdir, file_name, output_path;
 | ||
|     switch (format) {
 | ||
|         case IO::AMF:
 | ||
|             ext = ".zip.amf";
 | ||
|             subdir = "amf";
 | ||
|             break;
 | ||
|         case IO::OBJ:
 | ||
|             ext = ".obj";
 | ||
|             subdir = "obj";
 | ||
|             break;
 | ||
|         case IO::STL:
 | ||
|             ext = ".stl";
 | ||
|             subdir = "stl";
 | ||
|             break;
 | ||
|         case IO::TMF:
 | ||
|             ext = ".3mf";
 | ||
|             subdir = "3mf";
 | ||
|             break;
 | ||
|         default: assert(false); break;
 | ||
|     };
 | ||
|     // use --outputdir when available
 | ||
|     file_name = object.name.empty()?object.input_file:object.name;
 | ||
|     file_name = "obj_"+std::to_string(index)+"_"+file_name;
 | ||
|     size_t pos = file_name.find_last_of(ext), ext_pos = file_name.size() - 1;
 | ||
|     if (pos != ext_pos)
 | ||
|         file_name += ext;
 | ||
| 
 | ||
|     BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ": file_name="<<file_name<< ", pos = "<<pos<<", ext_pos="<<ext_pos;
 | ||
|     std::string cmdline_param = m_config.opt_string("outputdir");
 | ||
|     if (! cmdline_param.empty()) {
 | ||
|         subdir = cmdline_param + "/" + subdir;
 | ||
|     }
 | ||
| 
 | ||
|     output_path = subdir + "/"+file_name;
 | ||
| 
 | ||
|     boost::filesystem::path subdir_path(subdir);
 | ||
|     if (!boost::filesystem::exists(subdir_path))
 | ||
|         boost::filesystem::create_directory(subdir_path);
 | ||
|     return output_path;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| //BBS: dump stack debug codes, don't delete currently
 | ||
| //#include <dbghelp.h>
 | ||
| //#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="<<main_thread_id<<", current thread_id="<< cur_thread_id << std::endl;
 | ||
|     f << std::hex << pExceptionInfo->ExceptionRecord->ExceptionCode << std::endl;
 | ||
|     f << TraceStack();
 | ||
|     f.flush();
 | ||
|     f.close();
 | ||
| 
 | ||
|     return EXCEPTION_CONTINUE_SEARCH;
 | ||
| }*/
 | ||
| 
 | ||
| #if defined(_MSC_VER) || defined(__MINGW32__)
 | ||
| extern "C" {
 | ||
|     __declspec(dllexport) int __stdcall bambustu_main(int argc, wchar_t **argv)
 | ||
|     {
 | ||
|         // Convert wchar_t arguments to UTF8.
 | ||
|         std::vector<std::string> 	argv_narrow;
 | ||
|         std::vector<char*>			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 */
 |