OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::CLI Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/OrcaSlicer.hpp>

Collaboration diagram for Slic3r::CLI:

Public Member Functions

int run (int argc, char **argv)

Private Member Functions

bool setup (int argc, char **argv)
void print_help (bool include_print_options=false, PrinterTechnology printer_technology=ptAny) const
 Prints usage of the CLI.
bool export_models (IO::ExportFormat format, std::string path=std::string())
 Exports loaded models to a file of the specified format, according to the options affecting output filename.
bool export_project (Model *model, std::string &path, PlateDataPtrs &partplate_data, std::vector< Preset * > &project_presets, std::vector< ThumbnailData * > &thumbnails, std::vector< ThumbnailData * > &no_light_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, int plate_to_export=-1)
bool has_print_action () const
std::string output_filepath (const Model &model, IO::ExportFormat format) const
std::string output_filepath (const ModelObject &object, unsigned int index, IO::ExportFormat format, std::string path_dir) const

Private Attributes

DynamicPrintAndCLIConfig m_config
DynamicPrintConfig m_print_config
DynamicPrintConfig m_extra_config
std::vector< std::string > m_input_files
std::vector< std::string > m_actions
std::vector< std::string > m_transforms
std::set< std::string > m_given_option_keys
std::vector< Model > m_models

Detailed Description

Member Function Documentation

◆ export_models()

bool CLI::export_models ( IO::ExportFormat format,
std::string path = std::string() )
private

Exports loaded models to a file of the specified format, according to the options affecting output filename.

8285{
8286 for (Model &model : m_models) {
8287 const std::string path = this->output_filepath(model, format);
8288 bool success = true;
8289 switch (format) {
8290 //case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr, false); break;
8291 case IO::OBJ:
8292 success = Slic3r::store_obj(path.c_str(), &model);
8293 if (success)
8294 BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl;
8295 else {
8296 boost::nowide::cerr << "Model export to " << path << " failed" << std::endl;
8297 return false;
8298 }
8299 break;
8300 case IO::STL:
8301 {
8302 unsigned int index = 1;
8303 for (ModelObject* model_object : model.objects)
8304 {
8305 const std::string path = this->output_filepath(*model_object, index++, format, path_dir);
8306 if (path.empty()) {
8307 boost::nowide::cerr << "Could not create output directory for STL export" << std::endl;
8308 return false;
8309 }
8310 success = Slic3r::store_stl(path.c_str(), model_object, true);
8311 if (success)
8312 BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl;
8313 else {
8314 boost::nowide::cerr << "Model export to " << path << " failed" << std::endl;
8315 return false;
8316 }
8317 }
8318 break;
8319 }
8320 //BBS: use bbs 3mf instead of original
8321 //case IO::TMF: success = Slic3r::store_bbs_3mf(path.c_str(), &model, nullptr, false); break;
8322 default: assert(false); break;
8323 }
8324 }
8325 return true;
8326}
std::vector< Model > m_models
Definition OrcaSlicer.hpp:73
std::string output_filepath(const Model &model, IO::ExportFormat format) const
Definition OrcaSlicer.cpp:8368
@ OBJ
Definition OrcaSlicer.hpp:25
@ STL
Definition OrcaSlicer.hpp:26
bool store_stl(const char *path, TriangleMesh *mesh, bool binary)
Definition STL.cpp:43
bool store_obj(const char *path, TriangleMesh *mesh)
Definition OBJ.cpp:400
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:45

References Slic3r::format(), m_models, Slic3r::IO::OBJ, output_filepath(), Slic3r::IO::STL, Slic3r::store_obj(), and Slic3r::store_stl().

Referenced by run().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ export_project()

bool CLI::export_project ( Model * model,
std::string & path,
PlateDataPtrs & partplate_data,
std::vector< Preset * > & project_presets,
std::vector< ThumbnailData * > & thumbnails,
std::vector< ThumbnailData * > & no_light_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,
int plate_to_export = -1 )
private
8336{
8337 //const std::string path = this->output_filepath(*model, IO::TMF);
8338 bool success = false;
8339
8340 StoreParams store_params;
8341 store_params.path = path;
8342 store_params.model = model;
8343 store_params.plate_data_list = partplate_data;
8344 store_params.project_presets = project_presets;
8345 store_params.config = (DynamicPrintConfig*)config;
8346 store_params.thumbnail_data = thumbnails;
8347 store_params.no_light_thumbnail_data = no_light_thumbnails;
8348 store_params.top_thumbnail_data = top_thumbnails;
8349 store_params.pick_thumbnail_data = pick_thumbnails;
8350 store_params.calibration_thumbnail_data = calibration_thumbnails;
8351 store_params.id_bboxes = plate_bboxes;
8353 store_params.export_plate_idx = plate_to_export;
8354 if (minimum_save)
8355 store_params.strategy = store_params.strategy | SaveStrategy::SkipModel;
8356
8357 success = Slic3r::store_bbs_3mf(store_params);
8358
8359 if (success)
8360 BOOST_LOG_TRIVIAL(info) << "Project exported to " << path << std::endl;
8361 else {
8362 boost::nowide::cerr << "Project export to " << path << " failed" << std::endl;
8363 return false;
8364 }
8365 return true;
8366}
bool store_bbs_3mf(StoreParams &store_params)
Definition bbs_3mf.cpp:9723
@ WithGcode
Definition bbs_3mf.hpp:169
@ ShareMesh
Definition bbs_3mf.hpp:176
@ Silence
Definition bbs_3mf.hpp:170
@ SkipModel
Definition bbs_3mf.hpp:172
@ UseLoadedId
Definition bbs_3mf.hpp:175
@ SplitModel
Definition bbs_3mf.hpp:183
std::vector< ThumbnailData * > no_light_thumbnail_data
Definition bbs_3mf.hpp:291
std::vector< ThumbnailData * > thumbnail_data
Definition bbs_3mf.hpp:290
std::vector< ThumbnailData * > calibration_thumbnail_data
Definition bbs_3mf.hpp:294
std::string path
Definition bbs_3mf.hpp:284
DynamicPrintConfig * config
Definition bbs_3mf.hpp:289
int export_plate_idx
Definition bbs_3mf.hpp:287
Model * model
Definition bbs_3mf.hpp:285
std::vector< ThumbnailData * > top_thumbnail_data
Definition bbs_3mf.hpp:292
std::vector< Preset * > project_presets
Definition bbs_3mf.hpp:288
std::vector< ThumbnailData * > pick_thumbnail_data
Definition bbs_3mf.hpp:293
SaveStrategy strategy
Definition bbs_3mf.hpp:295
std::vector< PlateBBoxData * > id_bboxes
Definition bbs_3mf.hpp:297
PlateDataPtrs plate_data_list
Definition bbs_3mf.hpp:286

References Slic3r::StoreParams::calibration_thumbnail_data, Slic3r::StoreParams::config, Slic3r::StoreParams::export_plate_idx, Slic3r::StoreParams::id_bboxes, Slic3r::StoreParams::model, Slic3r::StoreParams::no_light_thumbnail_data, Slic3r::StoreParams::path, Slic3r::StoreParams::pick_thumbnail_data, Slic3r::StoreParams::plate_data_list, Slic3r::StoreParams::project_presets, Slic3r::ShareMesh, Slic3r::Silence, Slic3r::SkipModel, Slic3r::SplitModel, Slic3r::store_bbs_3mf(), Slic3r::StoreParams::strategy, Slic3r::StoreParams::thumbnail_data, Slic3r::StoreParams::top_thumbnail_data, Slic3r::UseLoadedId, and Slic3r::WithGcode.

Referenced by run().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_print_action()

bool Slic3r::CLI::has_print_action ( ) const
inlineprivate
91{ return m_config.opt_bool("export_gcode") || m_config.opt_bool("export_sla"); }
DynamicPrintAndCLIConfig m_config
Definition OrcaSlicer.hpp:65

References m_config.

◆ output_filepath() [1/2]

std::string CLI::output_filepath ( const Model & model,
IO::ExportFormat format ) const
private
8369{
8370 std::string ext;
8371 switch (format) {
8372 case IO::AMF: ext = ".zip.amf"; break;
8373 case IO::OBJ: ext = ".obj"; break;
8374 case IO::STL: ext = ".stl"; break;
8375 case IO::TMF: ext = ".3mf"; break;
8376 default: assert(false); break;
8377 };
8378 auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext));
8379 // use --output when available
8380 std::string cmdline_param = m_config.opt_string("outputdir");
8381 if (! cmdline_param.empty()) {
8382 // if we were supplied a directory, use it and append our automatically generated filename
8383 boost::filesystem::path cmdline_path(cmdline_param);
8384 if (boost::filesystem::is_directory(cmdline_path))
8385 proposed_path = cmdline_path / proposed_path.filename();
8386 else
8387 proposed_path = cmdline_param + ext;
8388 }
8389 return proposed_path.string();
8390}
std::string propose_export_file_name_and_path() const
Definition Model.cpp:1080
@ TMF
Definition OrcaSlicer.hpp:28
@ AMF
Definition OrcaSlicer.hpp:24

References Slic3r::IO::AMF, Slic3r::format(), m_config, Slic3r::IO::OBJ, Slic3r::Model::propose_export_file_name_and_path(), Slic3r::IO::STL, and Slic3r::IO::TMF.

Referenced by export_models().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ output_filepath() [2/2]

std::string CLI::output_filepath ( const ModelObject & object,
unsigned int index,
IO::ExportFormat format,
std::string path_dir ) const
private
8393{
8394 std::string ext, subdir, file_name, output_path;
8395 switch (format) {
8396 case IO::AMF:
8397 ext = ".zip.amf";
8398 subdir = "amf";
8399 break;
8400 case IO::OBJ:
8401 ext = ".obj";
8402 subdir = "obj";
8403 break;
8404 case IO::STL:
8405 ext = ".stl";
8406 subdir = "stl";
8407 break;
8408 case IO::TMF:
8409 ext = ".3mf";
8410 subdir = "3mf";
8411 break;
8412 default: assert(false); break;
8413 };
8414 // use --outputdir when available
8415 file_name = object.name.empty()?object.input_file:object.name;
8416 file_name = "obj_"+std::to_string(index)+"_"+file_name;
8417 size_t pos = file_name.rfind(ext), ext_pos = file_name.size() - ext.size();
8418 if ((pos == std::string::npos) || (pos != ext_pos))
8419 file_name += ext;
8420
8421 BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ": dir = "<< path_dir<<", file_name="<<file_name<< ", pos = "<<pos<<", ext_pos="<<ext_pos;
8422 if (path_dir.empty()) {
8423 std::string cmdline_param = m_config.opt_string("outputdir");
8424 if (! cmdline_param.empty()) {
8425 subdir = cmdline_param + "/" + subdir;
8426 }
8427 }
8428 else {
8429 subdir = path_dir;
8430 }
8431
8432 output_path = subdir + "/"+file_name;
8433
8434 boost::filesystem::path subdir_path(subdir);
8435 if (!boost::filesystem::exists(subdir_path)) {
8436 try {
8437 boost::filesystem::create_directories(subdir_path);
8438 } catch (const boost::filesystem::filesystem_error &ex) {
8439 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create output directory " << subdir_path.string() << ": " << ex.what();
8440 }
8441 if (!boost::filesystem::exists(subdir_path)) {
8442 // Directory creation failed and won't succeed on a retry (same path, same cause) -
8443 // signal failure now instead of letting every object in the model repeat the same
8444 // doomed attempt and fail with a less specific "export failed" error later.
8445 return std::string();
8446 }
8447 }
8448 return output_path;
8449}
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16
void trace(unsigned int level, const char *message)
Definition utils.cpp:247

References Slic3r::IO::AMF, Slic3r::format(), m_config, Slic3r::IO::OBJ, Slic3r::IO::STL, Slic3r::IO::TMF, and Slic3r::trace().

Here is the call graph for this function:

◆ print_help()

void CLI::print_help ( bool include_print_options = false,
PrinterTechnology printer_technology = ptAny ) const
private

Prints usage of the CLI.

8250{
8252
8253 boost::nowide::cout
8254 << SLIC3R_APP_KEY <<"-"<< SoftFever_VERSION << ":"
8255 << std::endl
8256 << "Usage: orca-slicer [ OPTIONS ] [ file.3mf/file.stl ... ]" << std::endl
8257 << std::endl
8258 << "OPTIONS:" << std::endl;
8259 cli_misc_config_def.print_cli_help(boost::nowide::cout, false);
8260 cli_transform_config_def.print_cli_help(boost::nowide::cout, false);
8261 cli_actions_config_def.print_cli_help(boost::nowide::cout, false);
8262
8263 boost::nowide::cout
8264 << std::endl
8265 << "Print setting priorities:" << std::endl
8266 << "\t1) setting values from the command line (highest priority)"<< std::endl
8267 << "\t2) setting values loaded with --load-settings and --load-filaments" << std::endl
8268 << "\t3) setting values loaded from 3mf(lowest priority)" << std::endl;
8269
8270 /*if (include_print_options) {
8271 boost::nowide::cout << std::endl;
8272 print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def)
8273 { return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; });
8274 } else {
8275 boost::nowide::cout
8276 << std::endl
8277 << "Run --help-fff / --help-sla to see the full listing of print options." << std::endl;
8278 }*/
8279 // flush the output buffer
8280 boost::nowide::cout.flush();
8281 boost::nowide::cerr.flush();
8282}
void attach_console_on_demand()
Definition OrcaSlicer.cpp:8206
std::ostream & print_cli_help(std::ostream &out, bool show_defaults, std::function< bool(const ConfigOptionDef &)> filter=[](const ConfigOptionDef &){ return true;}) const
Definition Config.cpp:381
const CLIActionsConfigDef cli_actions_config_def
Definition PrintConfig.cpp:13288
const CLITransformConfigDef cli_transform_config_def
Definition PrintConfig.cpp:13289
const CLIMiscConfigDef cli_misc_config_def
Definition PrintConfig.cpp:13290

References attach_console_on_demand(), Slic3r::cli_actions_config_def, Slic3r::cli_misc_config_def, Slic3r::cli_transform_config_def, and Slic3r::printer_technology().

Referenced by run(), and setup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run()

int CLI::run ( int argc,
char ** argv )
1150{
1151 // Mark the main thread for the debugger and for runtime checks.
1152 set_current_thread_name("orcaslicer_main");
1153 // Save the thread ID of the main thread.
1155
1156#ifdef __WXGTK__
1157 // ------------------------------------------------------------------
1158 // Linux backend selection — runtime, based on GDK_BACKEND env var.
1159 //
1160 // GDK_BACKEND unset (DEFAULT) Wayland path.
1161 // GTK auto-picks Wayland on Wayland
1162 // sessions and X11 otherwise. WebKit
1163 // XWayland-compositing workaround
1164 // applied only when actually on
1165 // XWayland. XInitThreads gated on
1166 // DISPLAY presence.
1167 //
1168 // GDK_BACKEND=x11 (OPT-IN) X11/XWayland mode. Applies the
1169 // v2.3.2 workarounds that the X11
1170 // path benefits from: DRI_PRIME for
1171 // AMD/nouveau PRIME, NVIDIA PRIME
1172 // render offload (when nvidia.ko is
1173 // loaded), XInitThreads.
1174 // Multi monitor is compromised
1175 //
1176 // WEBKIT_DISABLE_COMPOSITING_MODE is deliberately NOT set on the X11
1177 // opt-in path. The ~2020-era WebKit2GTK XWayland compositor bug it
1178 // worked around appears fixed in WebKit2GTK >= 2.42; leaving it
1179 // unset preserves WebKit hardware acceleration on Device / Setup
1180 // Wizard / login / store. The default path applies it only where it
1181 // forces GDK_BACKEND=x11 below (a build without EGL, so the app really
1182 // runs through XWayland), and only when the linked WebKitGTK predates
1183 // the fix (< 2.42), as a conservative fallback for older WebKit.
1184 // ------------------------------------------------------------------
1185 {
1186 const char* gdk_backend = ::getenv("GDK_BACKEND");
1187 // Match "x11" and comma-prefixed forms like "x11,wayland" (GTK
1188 // honours the first backend in the comma-separated list).
1189 const bool x11_opt_in = gdk_backend && boost::starts_with(gdk_backend, "x11");
1190
1191 // NVIDIA's proprietary driver cannot back WebKitGTK's DMABUF renderer.
1192 // Force the shared-memory transport, which keeps compositing and threaded
1193 // scrolling; the second variable clears Debian/Ubuntu/openSUSE's isNVIDIA().
1194 if (::access("/proc/driver/nvidia/version", F_OK) == 0 && WebView::SupportsForceSharedMemory()) {
1195 ::setenv("WEBKIT_DMABUF_RENDERER_FORCE_SHM", "1", /* replace */ false);
1196 ::setenv("WEBKIT_FORCE_DMABUF_RENDERER", "1", /* replace */ false);
1197 }
1198
1199 if (is_hypervisor_guest() && WebView::SupportsForceSharedMemory())
1200 ::setenv("WEBKIT_DMABUF_RENDERER_FORCE_SHM", "1", /* replace */ false);
1201
1202 if (x11_opt_in) {
1203 // ===== X11 / XWayland (opted in via GDK_BACKEND=x11) =====
1204 BOOST_LOG_TRIVIAL(info) << "GDK_BACKEND=x11 detected; applying v2.3.2 X11/XWayland workarounds.";
1205
1206 // On Linux dual-GPU systems, request the high-performance
1207 // discrete GPU. DRI_PRIME=1 handles AMD and nouveau PRIME
1208 // setups; harmless on single-GPU systems (Mesa ignores it).
1209 ::setenv("DRI_PRIME", "1", /* replace */ false);
1210
1211 // For NVIDIA proprietary driver PRIME render offload, set
1212 // additional variables. Only set if the NVIDIA kernel module
1213 // is loaded to avoid breaking systems without NVIDIA.
1214 if (::access("/proc/driver/nvidia/version", F_OK) == 0) {
1215 ::setenv("__NV_PRIME_RENDER_OFFLOAD", "1", /* replace */ false);
1216 ::setenv("__GLX_VENDOR_LIBRARY_NAME", "nvidia", /* replace */ false);
1217 }
1218
1219 // Tell Xlib we will be using threads, lest we crash when
1220 // GStreamer fires up. Safe to call here since the user has
1221 // explicitly opted into X11.
1222 #if __has_include(<X11/Xlib.h>)
1223 XInitThreads();
1224 #endif
1225 } else {
1226 // ===== Wayland default =====
1227
1228 // Safety fallback: if wxWidgets was not built with EGL
1229 // support, native Wayland will crash in
1230 // wxGLCanvas::IsDisplaySupported() because the GLX backend
1231 // cannot access an X11 display. Force X11 mode in that case.
1232 // NOTE: Do NOT remove this block even after enabling
1233 // wxHAS_EGL in the build — it protects against builds where
1234 // deps were not rebuilt.
1235 #if !defined(wxHAS_EGL) || !wxHAS_EGL
1236 {
1237 const char* wayland_env = ::getenv("WAYLAND_DISPLAY");
1238 if (wayland_env && *wayland_env) {
1239 BOOST_LOG_TRIVIAL(warning) << "Wayland detected but wxWidgets has no EGL support (wxHAS_EGL is OFF). Forcing X11 backend.";
1240 ::setenv("GDK_BACKEND", "x11", true);
1241
1242 // GTK will use X11 in a Wayland session here, which is the
1243 // one default-path case that really runs through XWayland.
1244 // WebKit2GTK accelerated compositing could fail silently
1245 // there on releases before 2.42, so force it off. Disabling
1246 // it also turns off threaded scrolling, hence the gate.
1248 ::setenv("WEBKIT_DISABLE_COMPOSITING_MODE", "1", /* replace */ false);
1249 }
1250 }
1251 #endif
1252
1253 // XInitThreads is needed before GStreamer may use Xlib. On
1254 // native Wayland without DISPLAY, GStreamer uses waylandsink
1255 // (no Xlib involved), so the call is skipped.
1256 #if __has_include(<X11/Xlib.h>)
1257 {
1258 const char* display_env = ::getenv("DISPLAY");
1259 if (display_env && *display_env) {
1260 XInitThreads();
1261 }
1262 }
1263 #endif
1264 }
1265 }
1266#endif // __WXGTK__
1267
1268 // Switch boost::filesystem to utf8.
1269 try {
1270 boost::nowide::nowide_filesystem();
1271 } catch (const std::runtime_error& ex) {
1272 std::string caption = std::string(SLIC3R_APP_FULL_NAME) + " Error";
1273 std::string text = std::string("boost::nowide::nowide_filesystem Failed!\n") + (
1274 SLIC3R_APP_FULL_NAME " will now terminate.\n\n") + ex.what();
1275 #if defined(_WIN32) && defined(SLIC3R_GUI)
1276 if (m_actions.empty())
1277 // Empty actions means Slicer is executed in the GUI mode. Show a GUI message.
1278 MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);
1279 #endif
1280 boost::nowide::cerr << text.c_str() << std::endl;
1281 return CLI_ENVIRONMENT_ERROR;
1282 }
1283
1284 if (!this->setup(argc, argv))
1285 {
1286 boost::nowide::cerr << "setup params error" << std::endl;
1287 return CLI_INVALID_PARAMS;
1288 }
1289 BOOST_LOG_TRIVIAL(info) << "finished setup params, argc="<< argc << std::endl;
1290 std::string temp_path = per_user_temp_dir(wxFileName::GetTempDir().utf8_str().data(), per_user_temp_id());
1291 // Some consumers write into the temp root directly, so create it up front.
1292 try {
1293 boost::filesystem::create_directories(temp_path);
1294 } catch (const std::exception &ex) {
1295 BOOST_LOG_TRIVIAL(warning) << "failed to create per-user temp dir " << temp_path << ": " << ex.what();
1296 }
1297 set_temporary_dir(temp_path);
1298
1299 // The Filament Track Switch flags are live-device state with no meaning in headless slicing;
1300 // default both off unless explicitly provided on the command line, so an old 3MF that had
1301 // them enabled still slices without the switch behavior.
1302 if (!m_extra_config.has("has_filament_switcher"))
1303 m_extra_config.set_key_value("has_filament_switcher", new ConfigOptionBool(false));
1304 if (!m_extra_config.has("enable_filament_dynamic_map"))
1305 m_extra_config.set_key_value("enable_filament_dynamic_map", new ConfigOptionBool(false));
1306
1307 m_extra_config.apply(m_config, true);
1308 m_extra_config.normalize_fdm();
1309
1311
1312 //BBS: remove GCodeViewer as separate APP logic
1313 /*bool start_as_gcodeviewer =
1314#ifdef _WIN32
1315 false;
1316#else
1317 // On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning.
1318 boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer");
1319#endif // _WIN32*/
1320
1321 bool translate_old = false, regenerate_thumbnails = false, keep_old_params = false, remove_wrapping_detect = false, filament_color_changed = false, downward_check = false;
1322 int current_printable_width, current_printable_depth, current_printable_height, shrink_to_new_bed = 0;
1323 int old_printable_height = 0, old_printable_width = 0, old_printable_depth = 0;
1324 Pointfs old_printable_area, old_exclude_area;
1325 float old_max_radius = 0.f, old_height_to_rod = 0.f, old_height_to_lid = 0.f;
1326 std::vector<double> old_max_layer_height, old_min_layer_height;
1327 std::string outfile_dir = m_config.opt_string("outputdir", true);
1328 const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load_settings", true)->values;
1329 const std::vector<std::string> &uptodate_configs = m_config.option<ConfigOptionStrings>("uptodate_settings", true)->values;
1330 const std::vector<std::string> &uptodate_filaments = m_config.option<ConfigOptionStrings>("uptodate_filaments", true)->values;
1331 std::vector<std::string> downward_settings = m_config.option<ConfigOptionStrings>("downward_settings", true)->values;
1332 std::vector<std::string> downward_compatible_machines;
1333 //BBS: always use ForwardCompatibilitySubstitutionRule::Enable
1334 //const ForwardCompatibilitySubstitutionRule config_substitution_rule = m_config.option<ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>>("config_compatibility", true)->value;
1336 const std::vector<std::string> &load_filaments = m_config.option<ConfigOptionStrings>("load_filaments", true)->values;
1337 //skip model object logic
1338 const std::vector<int> &skip_objects = m_config.option<ConfigOptionInts>("skip_objects", true)->values;
1339 std::map<int, bool> skip_maps;
1340 bool need_skip = (skip_objects.size() > 0)?true:false;
1341 long long global_begin_time = 0, global_current_time;
1342 sliced_info_t sliced_info;
1343 // Read up front so result.json reports it for early failures too.
1344 sliced_info.strict_mode = m_config.opt_bool("strict");
1345 // --no-check skips the check behind the only NON_CRITICAL warning --strict acts on
1346 // (support needed but disabled), from the point it appears among the actions. The pair
1347 // would make --strict a no-op or depend on argument order, so refuse it.
1348 if (sliced_info.strict_mode && m_config.opt_bool("no_check")) {
1349 boost::nowide::cerr << "--strict cannot be combined with --no-check" << std::endl;
1350 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1352 }
1353 std::map<std::string, std::string> record_key_values;
1354
1355 ConfigOptionBool* downward_check_option = m_config.option<ConfigOptionBool>("downward_check");
1356 if (downward_check_option)
1357 downward_check = downward_check_option->value;
1358
1359 // --inspect-mesh prints its JSON and exits, so any action that does work of its
1360 // own (slicing, exporting) would be skipped without notice. Reject those up front;
1361 // only options that merely tune how the input is loaded may come along.
1362 if (std::find(m_actions.begin(), m_actions.end(), "inspect_mesh") != m_actions.end()) {
1363 static const std::set<std::string> inspect_compatible = { "inspect_mesh", "uptodate", "load_defaultfila", "min_save",
1364 "mtcpp", "mstpp", "no_check", "normative_check", "pipe" };
1365 for (const std::string &action : m_actions) {
1366 if (inspect_compatible.count(action) == 0) {
1367 std::string flag = action;
1368 std::replace(flag.begin(), flag.end(), '_', '-');
1369 boost::nowide::cerr << "--inspect-mesh cannot be combined with --" << flag << std::endl;
1370 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1372 }
1373 }
1374 // Without input there is nothing to inspect; fail rather than print nothing and exit 0.
1375 if (m_input_files.empty() && m_config.opt_string("load_assemble_list").empty()) {
1376 boost::nowide::cerr << "--inspect-mesh needs an input file or --load-assemble-list" << std::endl;
1377 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1379 }
1380 }
1381
1382 // --inspect-paint prints its JSON and exits, so any action that does work of its
1383 // own (slicing, exporting) would be skipped without notice. Reject those up front;
1384 // only options that merely tune how the input is loaded may come along.
1385 if (std::find(m_actions.begin(), m_actions.end(), "inspect_paint") != m_actions.end()) {
1386 static const std::set<std::string> inspect_compatible = { "inspect_paint", "uptodate", "load_defaultfila", "min_save",
1387 "mtcpp", "mstpp", "no_check", "normative_check", "pipe" };
1388 for (const std::string &action : m_actions) {
1389 if (inspect_compatible.count(action) == 0) {
1390 std::string flag = action;
1391 std::replace(flag.begin(), flag.end(), '_', '-');
1392 boost::nowide::cerr << "--inspect-paint cannot be combined with --" << flag << std::endl;
1393 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1395 }
1396 }
1397 // Without input there is nothing to inspect; fail rather than print nothing and exit 0.
1398 if (m_input_files.empty() && m_config.opt_string("load_assemble_list").empty()) {
1399 boost::nowide::cerr << "--inspect-paint needs an input file or --load-assemble-list" << std::endl;
1400 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1402 }
1403 }
1404
1405 // --export-settings - writes its JSON to stdout, so reject every action or transform that may write there
1406 // too (--info, --help, --orient, slicing and exporting). The allowed ones do nothing when nothing is
1407 // sliced or exported.
1408 if (std::find(m_actions.begin(), m_actions.end(), "export_settings") != m_actions.end() && m_config.opt_string("export_settings") == "-") {
1409 static const std::set<std::string> stdout_compatible = { "export_settings", "uptodate", "load_defaultfila", "min_save",
1410 "mtcpp", "mstpp", "no_check", "normative_check", "pipe" };
1411 for (const std::vector<std::string> *opt_keys : { &m_actions, &m_transforms }) {
1412 for (const std::string &opt_key : *opt_keys) {
1413 if (stdout_compatible.count(opt_key) == 0) {
1414 std::string flag = opt_key;
1415 std::replace(flag.begin(), flag.end(), '_', '-');
1416 boost::nowide::cerr << "--export-settings - cannot be combined with --" << flag << std::endl;
1417 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1419 }
1420 }
1421 }
1422 }
1423
1424 bool start_gui = m_actions.empty() && !downward_check;
1425 if (start_gui) {
1426 BOOST_LOG_TRIVIAL(info) << "no action, start gui directly" << std::endl;
1427#ifdef SLIC3R_GUI
1428 #if !defined(_WIN32) && !defined(__APPLE__)
1429 // likely some linux / unix system
1430 const char *display = boost::nowide::getenv("DISPLAY");
1431 const char *wayland_display = boost::nowide::getenv("WAYLAND_DISPLAY");
1432 if (! ((display && *display) || (wayland_display && *wayland_display))) {
1433 boost::nowide::cerr << "Neither DISPLAY nor WAYLAND_DISPLAY set, GUI mode not available." << std::endl << std::endl;
1434 this->print_help(false);
1435 // Indicate an error.
1436 return 1;
1437 }
1438 #endif // some linux / unix system
1439 Slic3r::GUI::GUI_InitParams params;
1440 params.argc = argc;
1441 params.argv = argv;
1442 params.load_configs = load_configs;
1443 params.extra_config = std::move(m_extra_config);
1444
1445 std::vector<std::string> gcode_files;
1446 std::vector<std::string> non_gcode_files;
1447 for (const auto& filename : m_input_files) {
1448 if (is_gcode_file(filename))
1449 gcode_files.emplace_back(filename);
1450 else {
1451 non_gcode_files.emplace_back(filename);
1452 }
1453 }
1454 if (non_gcode_files.empty() && !gcode_files.empty()) {
1455 params.input_gcode = true;
1456 params.input_files = std::move(gcode_files);
1457 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", gcode only, gcode_files size = "<<params.input_files.size();
1458 }
1459 else {
1460 params.input_files = std::move(m_input_files);
1461 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", normal mode, input_files size = "<<params.input_files.size();
1462 }
1463 //BBS: remove GCodeViewer as separate APP logic
1464 //params.start_as_gcodeviewer = start_as_gcodeviewer;
1465
1466 BOOST_LOG_TRIVIAL(info) << "begin to launch OrcaSlicer GUI soon";
1467 return Slic3r::GUI::GUI_Run(params);
1468#else // SLIC3R_GUI
1469 // No GUI support. Just print out a help.
1470 this->print_help(false);
1471 // If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
1472 return (argc == 0) ? 0 : 1;
1473#endif // SLIC3R_GUI
1474 }
1475
1476 // Setup logging for CLI
1477 const ConfigOptionInt* opt_loglevel = m_config.opt<ConfigOptionInt>("debug");
1478 if (opt_loglevel) {
1479 set_logging_level(opt_loglevel->value);
1480 }
1481 else {
1483 }
1484 const ConfigOptionString* opt_logfile = m_config.opt<ConfigOptionString>("logfile");
1485 if (opt_logfile) {
1486 set_logging_file(opt_logfile->value);
1487 }
1488
1489 global_begin_time = (long long)Slic3r::Utils::get_current_time_utc();
1490 BOOST_LOG_TRIVIAL(warning) << boost::format("cli mode, Current OrcaSlicer Version %1%")%SoftFever_VERSION;
1491
1492 //BBS: add plate data related logic
1493 PlateDataPtrs plate_data_src;
1494 std::vector<plate_obj_size_info_t> plate_obj_size_infos;
1495 //int arrange_option;
1496 int plate_to_slice = 0, filament_count = 0, duplicate_count = 0, real_duplicate_count = 0, current_extruder_count = 1, new_extruder_count = 1, current_printer_variant_count = 1, current_print_variant_count = 1, new_printer_variant_count = 1;
1497 bool first_file = true, is_bbl_3mf = false, need_arrange = true, has_thumbnails = false, up_config_to_date = false, normative_check = true, duplicate_single_object = false, use_first_fila_as_default = false, minimum_save = false, enable_timelapse = false;
1498 bool allow_rotations = false, skip_modified_gcodes = false, avoid_extrusion_cali_region = false, skip_useless_pick = false, allow_newer_file = false, current_is_multi_extruder = false, new_is_multi_extruder = false, allow_mix_temp = false, enable_wrapping_detect = false;
1499 Semver file_version;
1500 std::map<size_t, bool> orients_requirement;
1501 std::vector<Preset*> project_presets;
1502 std::vector<NozzleVolumeType> current_nozzle_volume_type, new_nozzle_volume_type;
1503 std::string new_printer_name, current_printer_name, new_process_name, current_process_name, current_printer_system_name, current_process_system_name, new_process_system_name, new_printer_system_name, printer_model_id, current_printer_model, printer_model, new_default_process_name;
1504 std::vector<std::string> upward_compatible_printers, new_print_compatible_printers, current_print_compatible_printers, current_different_settings;
1505 std::vector<std::string> current_filaments_name, current_filaments_system_name, current_inherits_group, current_extruder_variants, new_extruder_variants, current_print_extruder_variants, new_printer_extruder_variants;
1506 DynamicPrintConfig load_process_config, load_machine_config;
1507 //ORCA: full configs of the "current" (3MF-embedded) process/printer presets, kept so that
1508 // compatible_printers_condition can be evaluated for them below. Previously only the
1509 // literal compatible_printers list was extracted.
1510 DynamicPrintConfig current_process_full_config, current_printer_full_config;
1511 bool new_process_config_is_system = true, new_printer_config_is_system = true;
1512 std::string pipe_name, makerlab_name, makerlab_version, different_process_setting;
1513 const std::vector<std::string> &metadata_name = m_config.option<ConfigOptionStrings>("metadata_name", true)->values;
1514 const std::vector<std::string> &metadata_value = m_config.option<ConfigOptionStrings>("metadata_value", true)->values;
1515
1516 if (metadata_name.size() != metadata_value.size())
1517 {
1518 BOOST_LOG_TRIVIAL(error) << boost::format("metadata_name should be the same size with metadata_value");
1519 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1521 }
1522
1523 // Read input file(s) if any.
1524 BOOST_LOG_TRIVIAL(info) << "Will start to read model file now, file count :" << m_input_files.size() << "\n";
1525 ConfigOptionInt* slice_option = m_config.option<ConfigOptionInt>("slice");
1526 if (slice_option)
1527 plate_to_slice = slice_option->value;
1528
1529 ConfigOptionBool* normative_check_option = m_config.option<ConfigOptionBool>("normative_check");
1530 if (normative_check_option)
1531 normative_check = normative_check_option->value;
1532
1533 ConfigOptionBool* uptodate_option = m_config.option<ConfigOptionBool>("uptodate");
1534 if (uptodate_option)
1535 up_config_to_date = uptodate_option->value;
1536
1537 ConfigOptionBool* load_defaultfila_option = m_config.option<ConfigOptionBool>("load_defaultfila");
1538 if (load_defaultfila_option)
1539 use_first_fila_as_default = load_defaultfila_option->value;
1540
1541 ConfigOptionBool* min_save_option = m_config.option<ConfigOptionBool>("min_save");
1542 if (min_save_option)
1543 minimum_save = min_save_option->value;
1544
1545 ConfigOptionBool* enable_timelapse_option = m_config.option<ConfigOptionBool>("enable_timelapse");
1546 if (enable_timelapse_option)
1547 enable_timelapse = enable_timelapse_option->value;
1548
1549 ConfigOptionBool* allow_rotations_option = m_config.option<ConfigOptionBool>("allow_rotations");
1550 if (allow_rotations_option)
1551 allow_rotations = allow_rotations_option->value;
1552 // Only an explicit --align-to-y-axis overrides the printer-structure default.
1553 std::optional<bool> align_to_y_axis;
1554 if (m_given_option_keys.count("align_to_y_axis") > 0)
1555 align_to_y_axis = m_config.opt_bool("align_to_y_axis");
1556
1557 ConfigOptionBool* skip_modified_gcodes_option = m_config.option<ConfigOptionBool>("skip_modified_gcodes");
1558 if (skip_modified_gcodes_option)
1559 skip_modified_gcodes = skip_modified_gcodes_option->value;
1560
1561 ConfigOptionBool* skip_useless_picks_option = m_config.option<ConfigOptionBool>("skip_useless_pick");
1562 if (skip_useless_picks_option)
1563 skip_useless_pick = skip_useless_picks_option->value;
1564
1565 ConfigOptionBool* allow_newer_file_option = m_config.option<ConfigOptionBool>("allow_newer_file");
1566 if (allow_newer_file_option)
1567 allow_newer_file = allow_newer_file_option->value;
1568
1569 ConfigOptionBool* allow_mix_temp_option = m_config.option<ConfigOptionBool>("allow_mix_temp");
1570 if (allow_mix_temp_option)
1571 allow_mix_temp = allow_mix_temp_option->value;
1572
1573 const ConfigOptionBool* export_statistics_option = m_config.option<ConfigOptionBool>("export_statistics");
1574 const bool export_statistics = export_statistics_option != nullptr && export_statistics_option->value;
1575
1576 ConfigOptionBool* avoid_extrusion_cali_region_option = m_config.option<ConfigOptionBool>("avoid_extrusion_cali_region");
1577 if (avoid_extrusion_cali_region_option)
1578 avoid_extrusion_cali_region = avoid_extrusion_cali_region_option->value;
1579
1580 ConfigOptionString* pipe_option = m_config.option<ConfigOptionString>("pipe");
1581 if (pipe_option) {
1582 pipe_name = pipe_option->value;
1583 if (!pipe_name.empty()) {
1584 BOOST_LOG_TRIVIAL(info) << boost::format("Will use pipe %1%")%pipe_name;
1585#if defined(__linux__) || defined(__LINUX__)
1586 g_cli_callback_mgr.start(pipe_name);
1587 PrintBase::SlicingStatus slicing_status{1, "Start to load files"};
1588 cli_status_callback(slicing_status);
1589#endif
1590 }
1591 }
1592
1593 ConfigOptionString* makerlab_name_option = m_config.option<ConfigOptionString>("makerlab_name");
1594 if (makerlab_name_option)
1595 makerlab_name = makerlab_name_option->value;
1596
1597 ConfigOptionString* makerlab_version_option = m_config.option<ConfigOptionString>("makerlab_version");
1598 if (makerlab_version_option)
1599 makerlab_version = makerlab_version_option->value;
1600
1601 //skip model object map construct
1602 if (need_skip) {
1603 BOOST_LOG_TRIVIAL(info) << boost::format("need to skip objects, size %1%:")%skip_objects.size();
1604 for (int index = 0; index < skip_objects.size(); index++)
1605 {
1606 skip_maps[skip_objects[index]] = false;
1607 BOOST_LOG_TRIVIAL(info) << boost::format("object %1%, id %2%")%index %skip_objects[index];
1608 }
1609 }
1610
1611 std::string custom_gcode_file;
1612 ConfigOptionString* custom_gcode_option = m_config.option<ConfigOptionString>("load_custom_gcodes");
1613 if (custom_gcode_option)
1614 custom_gcode_file = custom_gcode_option->value;
1615
1616 std::string load_assemble_list;
1617 std::vector<assemble_plate_info_t> assemble_plate_info_list;
1618 ConfigOptionString* load_assemble_list_option = m_config.option<ConfigOptionString>("load_assemble_list");
1619 if (load_assemble_list_option)
1620 load_assemble_list = load_assemble_list_option->value;
1621
1622 bool allow_multicolor_oneplate = m_config.option<ConfigOptionBool>("allow_multicolor_oneplate", true)->value;
1623 const std::vector<int> loaded_filament_ids = m_config.option<ConfigOptionInts>("load_filament_ids", true)->values;
1624 const std::vector<int> clone_objects = m_config.option<ConfigOptionInts>("clone_objects", true)->values;
1625 //when load objects from stl/obj, the total used filaments set
1626 std::set<int> used_filament_set;
1627 BOOST_LOG_TRIVIAL(info) << boost::format("allow_multicolor_oneplate %1%, allow_rotations %2% skip_modified_gcodes %3% avoid_extrusion_cali_region %4% loaded_filament_ids size %5%, clone_objects size %6%, skip_useless_pick %7%, allow_newer_file %8%, allow_mix_temp %9%")
1628 %allow_multicolor_oneplate %allow_rotations %skip_modified_gcodes %avoid_extrusion_cali_region %loaded_filament_ids.size() %clone_objects.size() %skip_useless_pick %allow_newer_file %allow_mix_temp;
1629 if (clone_objects.size() > 0)
1630 {
1631 if (clone_objects.size() != m_input_files.size())
1632 {
1633 BOOST_LOG_TRIVIAL(error) << boost::format("clone_objects size %1% should be the same with input files size %2%")%clone_objects.size() %m_input_files.size();
1634 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1636 }
1637 else if (load_filaments.size() == 0)
1638 {
1639 BOOST_LOG_TRIVIAL(error) << boost::format("clone_objects should be used with load_filaments together");
1640 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1642 }
1643 }
1644 if (loaded_filament_ids.size() > 0)
1645 {
1646 if (loaded_filament_ids.size() != m_input_files.size())
1647 {
1648 BOOST_LOG_TRIVIAL(error) << boost::format("loaded_filament_ids size %1% should be the same with input files size %2%")%loaded_filament_ids.size() %m_input_files.size();
1649 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1651 }
1652 else if (load_filaments.size() == 0)
1653 {
1654 BOOST_LOG_TRIVIAL(error) << boost::format("loaded_filament_ids should be used with load_filaments together");
1655 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1657 }
1658 }
1659
1660 /*for (const std::string& file : m_input_files)
1661 if (is_gcode_file(file) && boost::filesystem::exists(file)) {
1662 start_as_gcodeviewer = true;
1663 BOOST_LOG_TRIVIAL(info) << "found a gcode file:" << file << ", will start as gcode viewer\n";
1664 break;
1665 }*/
1666 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;
1667 unsigned int input_index = 0;
1668 std::vector<RGBA> input_obj_colours;
1669 if (!load_assemble_list.empty() && ((m_input_files.size() > 0) || (m_transforms.size() > 0)))
1670 {
1671 BOOST_LOG_TRIVIAL(error) << boost::format("load_assemble_list should not be used with input model files to load and should not be sued with transforms");
1672 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1674 }
1675 if (load_assemble_list.empty()) {
1676 for (const std::string& file : m_input_files) {
1677 if (!boost::filesystem::exists(file)) {
1678 boost::nowide::cerr << "No such file: " << file << std::endl;
1679 record_exit_reson(outfile_dir, CLI_FILE_NOTFOUND, 0, cli_errors[CLI_FILE_NOTFOUND], sliced_info);
1681 }
1682 Model model;
1683 //BBS: add plate related logic
1684 //bool load_aux = false;
1685 BOOST_LOG_TRIVIAL(info) << "read model file:" << file << "\n";
1686 try {
1687 // When loading an AMF or 3MF, config is imported as well, including the printer technology.
1688 DynamicPrintConfig config;
1689 ConfigSubstitutionContext config_substitutions(config_substitution_rule);
1690
1691 //FIXME should we check the version here? // | LoadStrategy::CheckVersion ?
1692 is_bbl_3mf = false;
1693 LoadStrategy strategy;
1694 if (boost::algorithm::iends_with(file, ".3mf") && first_file) {
1695 if ((clone_objects.size() > 0) || (loaded_filament_ids.size() > 0))
1696 {
1697 BOOST_LOG_TRIVIAL(error) << boost::format("can not load 3mf when set loaded_filament_ids or clone_objects");
1698 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1700 }
1702 //load_aux = true;
1703 }
1704 else {
1706 }
1707 // BBS: adjust whebackup
1708 //LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances;
1709 //if (load_aux) strategy = strategy | LoadStrategy::LoadAuxiliary;
1710 model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, plate_to_slice);
1711 if (is_bbl_3mf)
1712 {
1713 if (!first_file)
1714 {
1715 BOOST_LOG_TRIVIAL(info) << "The BBL 3mf file should be placed at the first position, filename=" << file << "\n";
1718 }
1719 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();
1720 need_arrange = false;
1721 /*for (ModelObject* o : model.objects)
1722 {
1723 orients_requirement.insert(std::pair<size_t, bool>(o->id().id, false));
1724 BOOST_LOG_TRIVIAL(info) << "object "<<o->name <<", id :" << o->id().id << ", from bbl 3mf\n";
1725 }*/
1726
1727 Semver cli_ver = *Semver::parse(SoftFever_VERSION);
1728 if (!allow_newer_file && ((cli_ver.maj() < file_version.maj()) || ((cli_ver.maj() == file_version.maj()) && (cli_ver.min() < file_version.min())))){
1729 BOOST_LOG_TRIVIAL(error) << boost::format("Version Check: File Version %1% not supported by current cli version %2%")%file_version.to_string() %SoftFever_VERSION;
1732 }
1733 Semver old_version(1, 5, 9), old_version2(1, 5, 9), old_version3(2, 0, 0), old_version4(2, 2, 0);
1734 if ((file_version < old_version) && !config.empty()) {
1735 translate_old = true;
1736 BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to translate")%file_version.to_string();
1737 }
1738 if ((file_version < old_version2) && !config.empty()) {
1739 regenerate_thumbnails = true;
1740 BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to regenerate_thumbnails for all")%file_version.to_string();
1741 }
1742
1743 if (file_version < old_version4) {
1744 remove_wrapping_detect = true;
1745 BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to set enable_wrapping_detection to false")%file_version.to_string();
1746 }
1747
1748 // ORCA: legacy feature-filament default migration (1 -> 0) is now handled
1749 // uniformly in PrintConfigDef::handle_legacy() via the old->new key rename
1750 // (wall_filament -> wall_filament_id, etc.), which covers presets too.
1751
1752 if (normative_check) {
1753 ConfigOptionStrings* postprocess_scripts = config.option<ConfigOptionStrings>("post_process");
1754 if (postprocess_scripts) {
1755 std::vector<std::string> postprocess_values = postprocess_scripts->values;
1756 if (postprocess_values.size() > 0) {
1757 BOOST_LOG_TRIVIAL(error) << boost::format("normative_check: postprocess not supported, array size %1%")%postprocess_values.size();
1760 }
1761 }
1762 }
1763
1764 /*for (ModelObject *model_object : model.objects)
1765 for (ModelInstance *model_instance : model_object->instances)
1766 {
1767 const Vec3d &instance_offset = model_instance->get_offset();
1768 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;
1769 }*/
1770 // Read defensively — a 3mf missing preset ids (e.g. one produced
1771 // by a non-GUI writer) would otherwise crash here on the deref.
1772 if (const auto *o = config.option<ConfigOptionString>("printer_settings_id")) current_printer_name = o->value;
1773 if (const auto *o = config.option<ConfigOptionString>("print_settings_id")) current_process_name = o->value;
1774 current_printer_model = config.option<ConfigOptionString>("printer_model", true)->value;
1775 if (const auto *o = config.option<ConfigOptionStrings>("filament_settings_id")) current_filaments_name = o->values;
1776 if (const auto *o = config.option<ConfigOptionFloats>("nozzle_diameter")) current_extruder_count = o->values.size();
1777 current_printer_variant_count = config.option<ConfigOptionStrings>("printer_extruder_variant", true)->values.size();
1778 current_print_variant_count = config.option<ConfigOptionStrings>("print_extruder_variant", true)->values.size();
1779 current_is_multi_extruder = current_extruder_count > 1;
1780 //if (current_is_multi_extruder || (current_printer_variant_count > 1)) {
1781 auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(config.option("extruder_type"));
1782 auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(config.option("nozzle_volume_type"));
1783 if (opt_nozzle_volume_type) {
1784 int nozzle_volume_type_size = opt_nozzle_volume_type->values.size();
1785 current_nozzle_volume_type.resize(nozzle_volume_type_size, nvtStandard);
1786 for (int i = 0; i < nozzle_volume_type_size; i++)
1787 {
1788 current_nozzle_volume_type[i] = (NozzleVolumeType)(opt_nozzle_volume_type->values[i]);
1789 }
1790 }
1791
1792 current_extruder_variants.resize(current_extruder_count, "");
1793 for (int e_index = 0; e_index < current_extruder_count; e_index++)
1794 {
1795 ExtruderType extruder_type = opt_extruder_type ? (ExtruderType)(opt_extruder_type->get_at(e_index)):etDirectDrive;
1796 NozzleVolumeType nozzle_volume_type = opt_nozzle_volume_type?(NozzleVolumeType)(opt_nozzle_volume_type->get_at(e_index)) : nvtStandard;
1797 current_extruder_variants[e_index] = get_extruder_variant_string(extruder_type, nozzle_volume_type);
1798 }
1799 //}
1800
1801 BOOST_LOG_TRIVIAL(info) << boost::format("current_printer_name %1%, current_process_name %2%")%current_printer_name %current_process_name;
1802 ConfigOptionStrings* option_strings = config.option<ConfigOptionStrings>("inherits_group");
1803 // One entry for the process, one per filament and one for the printer. A group of another
1804 // length still has the process first and the printer last; one too short for that is ignored.
1805 if (option_strings && option_strings->values.size() != current_filaments_name.size() + 2) {
1806 boost::nowide::cerr << "Warning: inherits_group has " << option_strings->values.size() << " entries, expected "
1807 << current_filaments_name.size() + 2 << " for " << current_filaments_name.size() << " filaments" << std::endl;
1808 if (option_strings->values.size() < 2)
1809 option_strings = nullptr;
1810 }
1811 if (option_strings) {
1812 current_inherits_group = option_strings->values;
1813 size_t size = current_inherits_group.size();
1814 if (current_inherits_group[size-1].empty()) {
1815 current_printer_system_name = current_printer_name;
1816 BOOST_LOG_TRIVIAL(info) << boost::format("inherits of printer is null, should be system preset");
1817 }
1818 else {
1819 current_printer_system_name = current_inherits_group[size-1];
1820 BOOST_LOG_TRIVIAL(info) << boost::format("inherits of printer valid, current_printer_system_name is %1%") %current_printer_system_name;
1821 }
1822
1823 if (current_inherits_group[0].empty()) {
1824 current_process_system_name = current_process_name;
1825 BOOST_LOG_TRIVIAL(info) << boost::format("inherits of process is null, should be system preset");
1826 }
1827 else {
1828 current_process_system_name = current_inherits_group[0];
1829 BOOST_LOG_TRIVIAL(info) << boost::format("inherits of process valid, current_process_system_name is %1%") %current_process_system_name;
1830 }
1831
1832 // A filament without an entry of its own counts as a system preset.
1833 current_filaments_system_name = current_filaments_name;
1834 for (size_t index = 1; index < size - 1 && index <= current_filaments_name.size(); index++) {
1835 if (!current_inherits_group[index].empty())
1836 current_filaments_system_name[index-1] = current_inherits_group[index];
1837 }
1838 }
1839 else {
1840 current_printer_system_name = current_printer_name;
1841 current_process_system_name = current_process_name;
1842 current_filaments_system_name = current_filaments_name;
1843 BOOST_LOG_TRIVIAL(info) << boost::format("no inherits_group: use system name the same as current name");
1844 }
1845 filament_count = current_filaments_name.size();
1846 upward_compatible_printers = config.option<ConfigOptionStrings>("upward_compatible_machine", true)->values;
1847 current_print_compatible_printers = config.option<ConfigOptionStrings>("print_compatible_printers", true)->values;
1848 current_different_settings = config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
1849
1850 //use Pointfs insteadof Points
1851 old_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values;
1852 old_exclude_area = legacy_bed_exclude_area_points(config);
1853 if (old_printable_area.size() >= 4) {
1854 BoundingBoxf old_printable_bbox(old_printable_area);
1855 old_printable_width = static_cast<int>(old_printable_bbox.size().x());
1856 old_printable_depth = static_cast<int>(old_printable_bbox.size().y());
1857 }
1858 // A 3mf can carry an empty project_settings.config - the models in
1859 // resources/handy_models do - and opt_float() dereferences without checking.
1860 if (config.option<ConfigOptionFloat>("printable_height"))
1861 old_printable_height = (int)(config.opt_float("printable_height"));
1862
1863 if (config.option<ConfigOptionFloat>("extruder_clearance_height_to_rod"))
1864 old_height_to_rod = config.opt_float("extruder_clearance_height_to_rod");
1865 if (config.option<ConfigOptionFloat>("extruder_clearance_height_to_lid"))
1866 old_height_to_lid = config.opt_float("extruder_clearance_height_to_lid");
1867 if (config.option<ConfigOptionFloat>("extruder_clearance_radius"))
1868 old_max_radius = config.opt_float("extruder_clearance_radius");
1869 if (config.option<ConfigOptionFloats>("max_layer_height"))
1870 old_max_layer_height = config.option<ConfigOptionFloats>("max_layer_height")->values;
1871 if (config.option<ConfigOptionFloats>("min_layer_height"))
1872 old_min_layer_height = config.option<ConfigOptionFloats>("min_layer_height")->values;
1873 BOOST_LOG_TRIVIAL(info) << boost::format("old printable size from 3mf: {%1%, %2%, %3%}")%old_printable_width %old_printable_depth %old_printable_height;
1874 BOOST_LOG_TRIVIAL(info) << boost::format("old extruder_clearance_height_to_rod %1%, extruder_clearance_height_to_lid %2%, extruder_clearance_radius %3%}")%old_height_to_rod %old_height_to_lid %old_max_radius;
1875 }
1876 else
1877 {
1878 need_arrange = true;
1879 int object_extruder_id = 0, clone_count = 1;
1880 if (loaded_filament_ids.size() > input_index) {
1881 if (loaded_filament_ids[input_index] > 0) {
1882 if (loaded_filament_ids[input_index] > load_filaments.size()) {
1883 BOOST_LOG_TRIVIAL(error) << boost::format("invalid filament_id %1% at index %2%, max %3%")%loaded_filament_ids[input_index] % (input_index + 1) %load_filaments.size();
1884 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1886 }
1887 object_extruder_id = loaded_filament_ids[input_index];
1888 used_filament_set.emplace(object_extruder_id);
1889 }
1890 }
1891
1892 if (clone_objects.size() > input_index) {
1893 if (clone_objects[input_index] > 0) {
1894 if (clone_objects[input_index] > MAX_CLONEABLE_SIZE) {
1895 BOOST_LOG_TRIVIAL(error) << boost::format("invalid clone count %1% at index %2%, max %3%")%clone_objects[input_index] % (input_index + 1) %MAX_CLONEABLE_SIZE;
1896 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
1898 }
1899 clone_count = clone_objects[input_index];
1900 }
1901 }
1902
1903 //clone objects process
1904 if (clone_count > 1)
1905 {
1906 unsigned int object_count = model.objects.size();
1907
1908 for (unsigned int obj_index = 0; obj_index < object_count; obj_index++)
1909 {
1910 ModelObject* object = model.objects[obj_index];
1911
1912 for (unsigned int clone_index = 1; clone_index < clone_count; clone_index++)
1913 {
1914 ModelObject* newObj = model.add_object(*object);
1915 newObj->name = object->name +"_"+ std::to_string(clone_index+1);
1916 }
1917 object->name = object->name +"_"+ std::to_string(1);
1918 }
1919 }
1920
1921 for (ModelObject* o : model.objects)
1922 {
1923 if (object_extruder_id != 0) {
1924 o->config.set_key_value("extruder", new ConfigOptionInt(object_extruder_id));
1925 }
1926
1927 //default not orient for all, if need to orient use the action
1928 //orients_requirement.insert(std::pair<size_t, bool>(o->id().id, false));
1929 BOOST_LOG_TRIVIAL(info) << "object "<<o->name <<", id :" << o->id().id << ", from stl or other 3mf\n";
1930 o->ensure_on_bed();
1931 }
1932 }
1933 first_file = false;
1934
1935 PrinterTechnology other_printer_technology = get_printer_technology(config);
1937 printer_technology = other_printer_technology;
1938 }
1939 if ((printer_technology != other_printer_technology) && (other_printer_technology != ptUnknown)) {
1940 boost::nowide::cerr << "invalid printer_technology " <<printer_technology<<", from source file "<< file <<std::endl;
1943 }
1944 if (!config_substitutions.substitutions.empty()) {
1945 BOOST_LOG_TRIVIAL(info) << "Found legacy configuration values, substituted when loading " << file << ":\n";
1946 for (const ConfigSubstitution &subst : config_substitutions.substitutions)
1947 BOOST_LOG_TRIVIAL(info) << "\tkey = \"" << subst.opt_def->opt_key << "\"\t old_value = \"" << subst.old_value << "\tnew_value = \"" << subst.new_value->serialize() << "\"\n";
1948 }
1949
1950 // config is applied to m_print_config before the current m_config values.
1951 config += std::move(m_print_config);
1952 m_print_config = std::move(config);
1953 input_index++;
1954 }
1955 catch (std::exception& e) {
1956 boost::nowide::cerr << file << ": " << e.what() << std::endl;
1959 }
1960 if (model.objects.empty()) {
1961 boost::nowide::cerr << "Error: file is empty: " << file << std::endl;
1962 continue;
1963 }
1964 m_models.push_back(std::move(model));
1965 }
1966 }
1967 else {
1968 //parse the json and assemble object here
1969 Model model;
1970
1971 AssembleListResult list_result = load_assemble_plate_list(load_assemble_list, assemble_plate_info_list, MAX_PLATE_COUNT);
1972 int ret = CLI_SUCCESS;
1973 if (list_result == AssembleListResult::FileNotFound)
1974 ret = CLI_FILE_NOTFOUND;
1975 else if (list_result == AssembleListResult::ConfigError)
1977 if (ret) {
1978 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
1979 flush_and_exit(ret);
1980 }
1981
1982 try {
1983 ret = construct_assemble_list(assemble_plate_info_list, model, plate_data_src, input_obj_colours);
1984 if (ret) {
1985 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
1986 flush_and_exit(ret);
1987 }
1988 }
1989 catch (std::exception& e) {
1990 boost::nowide::cerr << "construct_assemble_list: " << e.what() << std::endl;
1993 }
1994 model.add_default_instances();
1995 m_models.push_back(std::move(model));
1996 }
1997
1998 if (!is_bbl_3mf && plate_to_slice > 0)
1999 {
2000 BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: not support to slice plate %2%, reset to 0")%__LINE__ %plate_to_slice;
2001 plate_to_slice = 0;
2002 }
2003
2004 //load custom gcode file
2005 std::map<int, CustomGCode::Info> custom_gcodes_map;
2006 if (!custom_gcode_file.empty()) {
2007 // parse the custom gcode json file
2008 std::string file = custom_gcode_file;
2009 if(!boost::filesystem::exists(file)) {
2010 boost::nowide::cerr << __FUNCTION__ << ": can not find custom_gcode file: " << file << std::endl;
2011 record_exit_reson(outfile_dir, CLI_FILE_NOTFOUND, 0, cli_errors[CLI_FILE_NOTFOUND], sliced_info);
2013 }
2014 try {
2015 nlohmann::json jj;
2016 boost::nowide::ifstream ifs(file);
2017 ifs >> jj;
2018 ifs.close();
2019
2020 int plate_id = 0;
2021 if (plate_to_slice == 0)
2022 plate_id = 0;
2023 else
2024 plate_id = plate_to_slice-1;
2025
2026 CustomGCode::Info info;
2027 info.from_json(jj);
2028
2029 custom_gcodes_map.emplace(plate_id, info);
2030 BOOST_LOG_TRIVIAL(info) << boost::format("load custom_gcode from file %1% success, store custom gcodes to plate %2%")%file %(plate_id+1);
2031 }
2032 catch (std::exception &ex) {
2033 boost::nowide::cerr << __FUNCTION__<< ":Loading custom-gcode file \"" << file << "\" failed: " << ex.what() << std::endl;
2036 }
2037 }
2038
2039 std::unique_ptr<PresetBundle> cli_preset_bundle;
2040 auto ensure_cli_preset_bundle = [&cli_preset_bundle](std::string &error) -> PresetBundle * {
2041 if (cli_preset_bundle)
2042 return cli_preset_bundle.get();
2043 try {
2044 AppConfig app_config;
2045 const std::string app_config_error = app_config.load_if_exists();
2046 if (!app_config_error.empty()) {
2047 BOOST_LOG_TRIVIAL(warning) << "Ignoring invalid app config during CLI preset resolution: " << app_config_error;
2048 app_config.reset();
2049 }
2050
2051 auto bundle = std::make_unique<PresetBundle>();
2052 std::string load_error;
2053 bundle->load_presets(app_config, config_substitution_rule,
2054 PresetBundle::PresetPreferences(), &load_error, true);
2055 if (!load_error.empty()) {
2056 error = "Failed to load presets for inheritance resolution: " + load_error;
2057 return nullptr;
2058 }
2059 cli_preset_bundle = std::move(bundle);
2060 return cli_preset_bundle.get();
2061 } catch (const std::exception &ex) {
2062 error = ex.what();
2063 return nullptr;
2064 }
2065 };
2066
2067 // One resolver for the whole run, so presets from the same vendor tree share its load.
2068 std::unique_ptr<PresetBundle> system_preset_resolver;
2069 auto ensure_system_preset_resolver = [&system_preset_resolver]() -> PresetBundle & {
2070 if (!system_preset_resolver)
2071 system_preset_resolver = std::make_unique<PresetBundle>();
2072 return *system_preset_resolver;
2073 };
2074 auto resolve_preset = [&ensure_cli_preset_bundle, &ensure_system_preset_resolver](const std::string &file, DynamicPrintConfig &config,
2075 std::string &config_type, const std::string &config_from,
2076 bool probe_type, std::string &error) {
2077 const auto *inherits = config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS);
2078 if (!probe_type && (inherits == nullptr || inherits->value.empty()))
2079 return true;
2080
2081 PresetBundle *bundle = nullptr;
2082 bool allow_source_manifest = false;
2083 if (config_from == "system") {
2084 bundle = &ensure_system_preset_resolver();
2085 allow_source_manifest = true;
2086 } else {
2087 bundle = ensure_cli_preset_bundle(error);
2088 if (bundle == nullptr)
2089 return false;
2090 }
2091
2092 if (probe_type) {
2093 Preset::Type preset_type;
2094 if (!bundle->resolve_preset_config_type(config, preset_type, file, config_substitution_rule,
2095 error, allow_source_manifest))
2096 return false;
2097 config_type = Preset::get_type_string(preset_type);
2098 return true;
2099 }
2100
2101 Preset::Type preset_type;
2102 if (config_type == "process")
2103 preset_type = Preset::TYPE_PRINT;
2104 else if (config_type == "filament")
2105 preset_type = Preset::TYPE_FILAMENT;
2106 else if (config_type == "machine")
2107 preset_type = Preset::TYPE_PRINTER;
2108 else {
2109 error = "Unsupported preset type: " + config_type;
2110 return false;
2111 }
2112 return bundle->resolve_preset_config(config, preset_type, file, config_substitution_rule,
2113 error, allow_source_manifest);
2114 };
2115
2116 //ORCA: list the keys a user preset overrides relative to its system parent, for the
2117 // `different_settings_to_system` column of an exported 3MF. Without it the CLI
2118 // writes an empty column, so re-opening a CLI-exported project in the GUI shows
2119 // spurious "unsaved changes" and can revert inherited process/filament/machine
2120 // values to system defaults.
2121 //
2122 // The parent comes from the preset bundle that inherits resolution already builds,
2123 // so this adds no extra loading. Returns "" whenever the parent cannot be resolved,
2124 // which is exactly the previous behaviour.
2125 auto cli_different_settings = [&ensure_cli_preset_bundle](const DynamicPrintConfig &resolved,
2126 const std::string &parent_name,
2127 Preset::Type type) -> std::string {
2128 if (parent_name.empty())
2129 return std::string();
2130 std::string error;
2131 PresetBundle *bundle = ensure_cli_preset_bundle(error);
2132 if (bundle == nullptr) {
2133 BOOST_LOG_TRIVIAL(warning) << "CLI: no preset bundle for different_settings_to_system: " << error;
2134 return std::string();
2135 }
2136 const PresetCollection *collection = nullptr;
2137 switch (type) {
2138 case Preset::TYPE_PRINT: collection = &bundle->prints; break;
2139 case Preset::TYPE_FILAMENT: collection = &bundle->filaments; break;
2140 case Preset::TYPE_PRINTER: collection = &bundle->printers; break;
2141 default: return std::string();
2142 }
2143 const Preset *parent = collection->find_preset2(parent_name, true);
2144 if (parent == nullptr) {
2145 BOOST_LOG_TRIVIAL(warning) << boost::format("CLI: parent preset '%1%' not found; leaving different_settings_to_system empty")%parent_name;
2146 return std::string();
2147 }
2148 std::vector<std::string> keys = resolved.diff(parent->config);
2149 //ORCA: preset metadata, not user-tunable settings. compatible_printers /
2150 // compatible_prints have their own tracking columns and would double-count.
2151 keys.erase(std::remove_if(keys.begin(), keys.end(), [](const std::string &k) {
2152 return k == "inherits" || k == "compatible_printers" || k == "compatible_prints"
2153 || k == "compatible_printers_condition" || k == "compatible_prints_condition"
2154 || k == "print_settings_id" || k == "filament_settings_id" || k == "printer_settings_id";
2155 }),
2156 keys.end());
2157 BOOST_LOG_TRIVIAL(info) << boost::format("CLI: %1% overrides vs parent '%2%'")%keys.size()%parent_name;
2158 return Slic3r::escape_strings_cstyle(keys);
2159 };
2160
2161 auto load_config_file = [&resolve_preset](const std::string& file, DynamicPrintConfig& config, std::string& config_type,
2162 std::string& config_name, std::string& filament_id, std::string& config_from) {
2163 if (! boost::filesystem::exists(file)) {
2164 boost::nowide::cerr << __FUNCTION__<< ": can not find setting file: " << file << std::endl;
2165 return CLI_FILE_NOTFOUND;
2166 }
2167 ConfigSubstitutions config_substitutions;
2168 try {
2169 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ":load setting file "<< file << ", with rule "<< config_substitution_rule << std::endl;
2170 std::map<std::string, std::string> key_values;
2171 std::string reason;
2172
2173 config_substitutions = config.load_from_json(file, config_substitution_rule, key_values, reason);
2174 if (!reason.empty()) {
2175 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< ":Can not load config from file "<<file<<"\n";
2176 return CLI_CONFIG_FILE_ERROR;
2177 }
2178
2179 config_name = key_values[BBL_JSON_KEY_NAME];
2180 auto from_iter = key_values.find(BBL_JSON_KEY_FROM);
2181 if (from_iter != key_values.end()) {
2182 config_from = from_iter->second;
2183 }
2184 if ((config_from != "system")&&(config_from != "User")&&(config_from != "user")) {
2185 boost::nowide::cerr <<__FUNCTION__ << boost::format(":file %1%'s from %2% unsupported") % file % config_from;
2186 return CLI_CONFIG_FILE_ERROR;
2187 }
2188
2189 auto type_iter = key_values.find(BBL_JSON_KEY_TYPE);
2190 const bool probe_type = type_iter == key_values.end();
2191 if (!probe_type)
2192 config_type = type_iter->second;
2193
2194 if (!resolve_preset(file, config, config_type, config_from, probe_type, reason)) {
2195 boost::nowide::cerr << __FUNCTION__ << boost::format(": can not resolve preset %1%: %2%") % file % reason << std::endl;
2196 return CLI_CONFIG_FILE_ERROR;
2197 }
2198
2199 if (config_type == "machine") {
2200 //config.set("printer_settings_id", config_name, true);
2201 //printer_inherits = config.option<ConfigOptionString>("inherits", true)->value;
2202 }
2203 else if (config_type == "process") {
2204 //config.set("print_settings_id", config_name, true);
2205 //print_inherits = config.option<ConfigOptionString>("inherits", true)->value;
2206 }
2207 else if (config_type == "filament") {
2208 auto filament_id_iter = key_values.find(BBL_JSON_KEY_FILAMENT_ID);
2209 if (filament_id_iter != key_values.end())
2210 filament_id = filament_id_iter->second;
2211 }
2212 else {
2213 boost::nowide::cerr <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-settings") % config_type % file;
2214 return CLI_CONFIG_FILE_ERROR;
2215 }
2216 config.normalize_fdm();
2217
2218 if (! config_substitutions.empty()) {
2219 BOOST_LOG_TRIVIAL(info) << "Found legacy configuration values, substituted when loading " << file << ":\n";
2220 for (const ConfigSubstitution &subst : config_substitutions)
2221 BOOST_LOG_TRIVIAL(info) << "\tkey = \"" << subst.opt_def->opt_key << "\"\t old_value = \"" << subst.old_value << "\tnew_value = \"" << subst.new_value->serialize() << "\"\n";
2222 }
2223 else {
2224 BOOST_LOG_TRIVIAL(info) << "no substitutions performed from file " << file << "\n";
2225 }
2226 //config.erase("inherits");
2227 //config.erase("compatible_printers");
2228 //BOOST_LOG_TRIVIAL(info) << "got printable_area "<< config.option("printable_area")->serialize() << std::endl;
2229 } catch (std::exception &ex) {
2230 boost::nowide::cerr << __FUNCTION__<< ":Loading setting file \"" << file << "\" failed: " << ex.what() << std::endl;
2231 return CLI_CONFIG_FILE_ERROR;
2232 }
2233 return 0;
2234 };
2235 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< ":before load settings, file count="<< load_configs.size() << std::endl;
2236 //std::vector<std::string> filament_compatible_printers;
2237 // load config files supplied via --load
2238 for (auto const &file : load_configs) {
2239 DynamicPrintConfig config;
2240 std::string config_type, config_name, filament_id, config_from;
2241 int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from);
2242 if (ret) {
2243 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2244 flush_and_exit(ret);
2245 }
2246
2247 if (config_type == "machine") {
2248 if (!new_printer_name.empty()) {
2249 boost::nowide::cerr << "duplicate machine config file: " << file << std::endl;
2252 }
2253 new_printer_name = config_name;
2254 if (config_from == "system") {
2255 new_printer_system_name = new_printer_name;
2256 new_printer_config_is_system = true;
2257 }
2258 else {
2259 new_printer_system_name = config.option<ConfigOptionString>("inherits", true)->value;
2260 new_printer_config_is_system = false;
2261 }
2262 config.set("printer_settings_id", new_printer_name, true);
2263
2264 //get printer_model_id
2265 printer_model = config.option<ConfigOptionString>("printer_model", true)->value;
2266 if (!printer_model.empty()) {
2267 std::string printer_model_path = resources_dir() + "/profiles/BBL/machine_full/"+printer_model+".json";
2268 if (boost::filesystem::exists(printer_model_path))
2269 {
2270 std::map<std::string, std::string> key_values;
2271
2272 load_key_values_from_json(printer_model_path, key_values);
2273 if (key_values.find("model_id") != key_values.end()) {
2274 printer_model_id = key_values["model_id"];
2275 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, load printer_model_id %2% from current printer model %3%")%__LINE__ %printer_model_id %printer_model;
2276 }
2277 }
2278 }
2279 //new_default_process_name
2280 if (config.option<ConfigOptionString>("default_print_profile"))
2281 new_default_process_name = config.option<ConfigOptionString>("default_print_profile")->value;
2282
2283 //printer_inherits = config.option<ConfigOptionString>("inherits", true)->value;
2284 load_machine_config = std::move(config);
2285 BOOST_LOG_TRIVIAL(info) << boost::format("loaded machine config %1%, type %2%, name %3%, inherits %4%, printer_model_id %5%")%file %config_name %config_from % new_printer_system_name %printer_model_id;
2286 }
2287 else if (config_type == "process") {
2288 if (!new_process_name.empty()) {
2289 boost::nowide::cerr << "duplicate process config file: " << file << std::endl;
2292 }
2293 new_process_name = config_name;
2294 if (config_from == "system") {
2295 new_process_system_name = new_process_name;
2296 new_process_config_is_system = true;
2297 }
2298 else {
2299 new_process_system_name = config.option<ConfigOptionString>("inherits", true)->value;
2300 new_process_config_is_system = false;
2301 }
2302 config.set("print_settings_id", new_process_name, true);
2303 //print_inherits = config.option<ConfigOptionString>("inherits", true)->value;
2304 new_print_compatible_printers = config.option<ConfigOptionStrings>("compatible_printers", true)->values;
2305
2306 if (!is_bbl_3mf && config.option<ConfigOptionStrings>("different_settings_to_system")) {
2307 std::vector<std::string> diff_settings = config.option<ConfigOptionStrings>("different_settings_to_system")->values;
2308 different_process_setting = diff_settings[0];
2309 }
2310 load_process_config = std::move(config);
2311 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;
2312 }
2313
2314 PrinterTechnology other_printer_technology = get_printer_technology(config);
2316 printer_technology = other_printer_technology;
2317 }
2318
2319 if ((printer_technology != other_printer_technology)&&(other_printer_technology != ptUnknown)){
2320 boost::nowide::cerr << "invalid printer_technology " <<printer_technology<<", from config "<< file <<std::endl;
2323 }
2324 }
2325
2326 //load filaments files
2327 int load_filament_count = load_filaments.size();
2328 std::vector<int> load_filaments_index;
2329 std::set<std::string> load_filaments_set;
2330 bool disable_wipe_tower_after_mapping = false;
2331 std::vector<DynamicPrintConfig> load_filaments_config;
2332 std::vector<std::string> load_filaments_id;
2333 std::vector<std::string> load_filaments_name, load_filaments_inherit;
2334 int current_index = 0;
2335 std::string default_load_fila_name, default_load_fila_id, default_filament_file, default_filament_inherit;
2336 DynamicPrintConfig default_load_fila_config;
2337 if (use_first_fila_as_default) {
2338 //construct default filament
2339 for (int index = 0; index < load_filament_count; index++) {
2340 const std::string& file = load_filaments[index];
2341 if (default_filament_file.empty() && !file.empty()) {
2342 DynamicPrintConfig config;
2343 std::string config_type, config_name, filament_id, config_from;
2344 int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from);
2345 if (ret) {
2346 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2347 flush_and_exit(ret);
2348 }
2349
2350 if (config_type != "filament") {
2351 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-filaments") % config_type % file;
2354 }
2355
2356 if ((config_from == "User")||(config_from == "user")) {
2357 default_filament_inherit = config.option<ConfigOptionString>("inherits", true)->value;
2358 }
2359
2360 default_filament_file = file;
2361 default_load_fila_name = config_name;
2362 default_load_fila_id = filament_id;
2363 default_load_fila_config = std::move(config);
2364
2365 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;
2366 break;
2367 }
2368 }
2369 if ((load_filament_count > 0) && default_filament_file.empty())
2370 {
2371 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": load_filament_count is %1%, but can not load a default filament") % load_filament_count;
2374 }
2375 }
2376 for (int index = 0; index < load_filament_count; index++) {
2377 const std::string& file = load_filaments[index];
2378 current_index++;
2379 if (!file.empty()) {
2380 DynamicPrintConfig config;
2381 std::string config_type, config_name, filament_id, config_from;
2382 int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from);
2383 if (ret) {
2384 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2385 flush_and_exit(ret);
2386 }
2387
2388 if (config_type != "filament") {
2389 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-filaments") % config_type % file;
2392 }
2393
2394 PrinterTechnology other_printer_technology = get_printer_technology(config);
2396 printer_technology = other_printer_technology;
2397 }
2398 if ((printer_technology != other_printer_technology) && (other_printer_technology != ptUnknown)) {
2399 BOOST_LOG_TRIVIAL(error) << "invalid printer_technology " <<printer_technology<<", from filament file "<< file;
2402 }
2403 std::string inherits;
2404 if ((config_from == "User")||(config_from == "user")) {
2405 inherits = config.option<ConfigOptionString>("inherits", true)->value;
2406 }
2407 load_filaments_inherit.push_back(inherits);
2408 load_filaments_id.push_back(filament_id);
2409 load_filaments_name.push_back(config_name);
2410 load_filaments_config.push_back(std::move(config));
2411 load_filaments_index.push_back(current_index);
2412
2413 load_filaments_set.emplace(config_name);
2414
2415 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;
2416 }
2417 else {
2418 if (use_first_fila_as_default) {
2419 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;
2420 load_filaments_id.push_back(default_load_fila_id);
2421 load_filaments_name.push_back(default_load_fila_name);
2422 load_filaments_config.push_back(default_load_fila_config);
2423 load_filaments_index.push_back(current_index);
2424 load_filaments_inherit.push_back(default_filament_inherit);
2425
2426 load_filaments_set.emplace(default_load_fila_name);
2427 }
2428 continue;
2429 }
2430 }
2431
2432 //add logic for obj auto colors
2433 if (input_obj_colours.size() > 0) {
2434 BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, got input obj colors %3%")%__FUNCTION__ %__LINE__ %input_obj_colours.size();
2435 int input_color_count = input_obj_colours.size();
2436 if (load_filament_count == 0) {
2437 BOOST_LOG_TRIVIAL(error) << boost::format("filament config not loaded when loading colored obj");
2438 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
2440 }
2441
2442 //check filament_color from extra config
2443 ConfigOptionStrings *selected_filament_colors_option = m_extra_config.option<ConfigOptionStrings>("filament_colour");
2444 if (selected_filament_colors_option) {
2445 BOOST_LOG_TRIVIAL(error) << boost::format("filament_colour should not be set when loading colored obj");
2446 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
2448 }
2449 if (load_filament_count < input_color_count) {
2450 int delta = input_color_count - load_filament_count;
2451 for (int index = 0; index < delta; index++) {
2452 load_filaments_id.push_back(load_filaments_id[0]);
2453 load_filaments_name.push_back(load_filaments_name[0]);
2454 load_filaments_config.push_back(load_filaments_config[0]);
2455 load_filaments_index.push_back(index+1+load_filament_count);
2456 load_filaments_inherit.push_back(load_filaments_inherit[0]);
2457 }
2458 load_filament_count = input_color_count;
2459 }
2460
2461 selected_filament_colors_option = m_extra_config.option<ConfigOptionStrings>("filament_colour", true);
2462 std::vector<std::string>& filament_colors = selected_filament_colors_option->values;
2463 filament_colors.resize(input_color_count);
2464 for (int index = 0; index < input_color_count; index++)
2465 {
2466 std::string color_string;
2467 int color[4];
2468 color[0] = std::clamp((int) (input_obj_colours[index][0] * 255.f), 0, 255);
2469 color[1] = std::clamp((int) (input_obj_colours[index][1] * 255.f), 0, 255);
2470 color[2] = std::clamp((int) (input_obj_colours[index][2] * 255.f), 0, 255);
2471 color[3] = std::clamp((int) (input_obj_colours[index][3] * 255.f), 0, 255);
2472
2473 std::stringstream stream;
2474 stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[0];
2475 stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[1];
2476 stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[2];
2477 stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[3];
2478 std::string result(stream.str());
2479
2480 filament_colors[index] = "#" + result;
2481
2482 BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, index %3%, argb {%4%,%5%,%6%,%7%} to string %8%")%__FUNCTION__ %__LINE__ %(index+1) %color[0] %color[1] %color[2] %color[3] %filament_colors[index];
2483 }
2484 }
2485
2486 if (filament_count == 0)
2487 filament_count = load_filament_count;
2488
2489 if (is_bbl_3mf && (load_filament_count > 0) && (load_filaments_set.size() == 1))
2490 {
2491 disable_wipe_tower_after_mapping = true;
2492 BOOST_LOG_TRIVIAL(info) << boost::format("map all the filaments to the same one, load_filament_count %1%")%load_filament_count;
2493 }
2494
2495 //load system config if needed
2496 bool fetch_compatible_values = false, fetch_upward_values = false;
2497 if (is_bbl_3mf && up_config_to_date) {
2498 if (uptodate_configs.size() > 0)
2499 {
2500 for (auto const &file : uptodate_configs) {
2501 DynamicPrintConfig config;
2502 std::string config_type, config_name, filament_id, config_from;
2503 int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from);
2504 if (ret) {
2505 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2506 flush_and_exit(ret);
2507 }
2508
2509 if (config_type == "machine") {
2510 if ( config_name != current_printer_system_name ) {
2511 BOOST_LOG_TRIVIAL(error) << boost::format("wrong machine config file %1% loaded, current machine config name %2% ")%config_name %current_printer_system_name;
2514 }
2515 upward_compatible_printers = config.option<ConfigOptionStrings>("upward_compatible_machine", true)->values;
2516 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();
2517 if (new_printer_name.empty() && !current_printer_system_name.empty())
2518 {
2519 config.set("printer_settings_id", config_name, true);
2520
2521 //get printer_model_id
2522 printer_model = config.option<ConfigOptionString>("printer_model", true)->value;
2523 if (!printer_model.empty()) {
2524 std::string printer_model_path = resources_dir() + "/profiles/BBL/machine_full/"+printer_model+".json";
2525 if (boost::filesystem::exists(printer_model_path))
2526 {
2527 std::map<std::string, std::string> key_values;
2528
2529 load_key_values_from_json(printer_model_path, key_values);
2530 if (key_values.find("model_id") != key_values.end()) {
2531 printer_model_id = key_values["model_id"];
2532 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, load printer_model_id %2% from current printer model %3%")%__LINE__ %printer_model_id %printer_model;
2533 }
2534 }
2535 }
2536
2537 int orig_printable_width = 0, orig_printable_depth = 0, orig_printable_height = 0;
2538 Pointfs orig_printable_area;
2539 orig_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values;
2540 if (orig_printable_area.size() >= 4) {
2541 BoundingBoxf orig_printable_bbox(orig_printable_area);
2542 orig_printable_width = static_cast<int>(orig_printable_bbox.size().x());
2543 orig_printable_depth = static_cast<int>(orig_printable_bbox.size().y());
2544 }
2545 if (config.option<ConfigOptionFloat>("printable_height"))
2546 orig_printable_height = (int)(config.opt_float("printable_height"));
2547 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, check printable size: old_printable_width=%2%, orig_printable_width=%3%, old_printable_depth=%4%, orig_printable_depth=%5%, old_printable_height=%6%, orig_printable_height=%7%")
2548 %__LINE__ %old_printable_width %orig_printable_width %old_printable_depth %orig_printable_depth %old_printable_height %orig_printable_height;
2549 if ((orig_printable_width > 0) && (orig_printable_depth > 0) && (orig_printable_height > 0))
2550 {
2551 if ((old_printable_width > orig_printable_width) || (old_printable_depth > orig_printable_depth) || (old_printable_height > orig_printable_height))
2552 {
2553 std::string error_str = (boost::format("Printer Settings: the printable size {%1%, %2%, %3%} exceeds the default size.")%old_printable_width %old_printable_depth %old_printable_height).str();
2554 BOOST_LOG_TRIVIAL(error) << error_str;
2555 record_exit_reson(outfile_dir, CLI_MODIFIED_PARAMS_TO_PRINTER, 0, error_str, sliced_info);
2557 }
2558 }
2559
2560 load_machine_config = std::move(config);
2561 }
2562 }
2563 else if (config_type == "process") {
2564 if ( config_name != current_process_system_name ) {
2565 BOOST_LOG_TRIVIAL(error) << boost::format("wrong process config file %1% loaded, current process config name %2% ")%config_name %current_process_system_name;
2568 }
2569 current_print_compatible_printers = config.option<ConfigOptionStrings>("compatible_printers", true)->values;
2570 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();
2571 if (new_process_name.empty() && !current_process_system_name.empty())
2572 {
2573 config.set("print_settings_id", config_name, true);
2574 load_process_config = std::move(config);
2575 }
2576 }
2577 else {
2578 BOOST_LOG_TRIVIAL(error) << boost::format("found invalid config type %1% from config %2% ")%config_type %file;
2581 }
2582 }
2583 }
2584 else {
2585 if (new_printer_name.empty() && !current_printer_system_name.empty()) {
2586 //use the original printer name in 3mf
2587 std::string system_printer_path = resources_dir() + "/profiles/BBL/machine_full/"+current_printer_system_name+".json";
2588 if (! boost::filesystem::exists(system_printer_path)) {
2589 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_printer_path;
2590 //use original one
2591 }
2592 else {
2593 DynamicPrintConfig config;
2594 std::string config_type, config_name, filament_id, config_from;
2595 int ret = load_config_file(system_printer_path, config, config_type, config_name, filament_id, config_from);
2596 if (ret) {
2597 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2598 flush_and_exit(ret);
2599 }
2600
2601 upward_compatible_printers = config.option<ConfigOptionStrings>("upward_compatible_machine", true)->values;
2602 config.set("printer_settings_id", config_name, true);
2603
2604 //get printer_model_id
2605 printer_model = config.option<ConfigOptionString>("printer_model", true)->value;
2606 if (!printer_model.empty()) {
2607 std::string printer_model_path = resources_dir() + "/profiles/BBL/machine_full/"+printer_model+".json";
2608 if (boost::filesystem::exists(printer_model_path))
2609 {
2610 std::map<std::string, std::string> key_values;
2611
2612 load_key_values_from_json(printer_model_path, key_values);
2613 if (key_values.find("model_id") != key_values.end()) {
2614 printer_model_id = key_values["model_id"];
2615 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, load printer_model_id %2% from current printer model %3%")%__LINE__ %printer_model_id %printer_model;
2616 }
2617 }
2618 }
2619
2620 load_machine_config = std::move(config);
2621 }
2622 }
2623 else
2624 fetch_upward_values = true;
2625
2626 if (new_process_name.empty() && !current_process_system_name.empty()) {
2627 //use the original printer name in 3mf
2628 std::string system_process_path = resources_dir() + "/profiles/BBL/process_full/"+current_process_system_name+".json";
2629 if (! boost::filesystem::exists(system_process_path)) {
2630 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_process_path;
2631 //use original one
2632 }
2633 else {
2634 DynamicPrintConfig config;
2635 std::string config_type, config_name, filament_id, config_from;
2636 int ret = load_config_file(system_process_path, config, config_type, config_name, filament_id, config_from);
2637 if (ret) {
2638 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2639 flush_and_exit(ret);
2640 }
2641 current_print_compatible_printers = config.option<ConfigOptionStrings>("compatible_printers", true)->values;
2642 config.set("print_settings_id", config_name, true);
2643 load_process_config = std::move(config);
2644 }
2645 }
2646 else
2647 fetch_compatible_values = true;
2648 }
2649
2650 //filament processes
2651 if (load_filament_count == 0)
2652 {
2653 if (uptodate_filaments.size() > 0)
2654 {
2655 if (uptodate_filaments.size() != (size_t)filament_count)
2656 {
2657 BOOST_LOG_TRIVIAL(error) << boost::format("uptodate_filaments size %1% not equal to filament_count %2% ")%uptodate_filaments.size() %filament_count;
2658 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
2660 }
2661 for (int index = 0; index < filament_count; index ++)
2662 {
2663 std::string file = uptodate_filaments[index];
2664 DynamicPrintConfig config;
2665 std::string config_type, config_name, filament_id, config_from;
2666 int ret = load_config_file(file, config, config_type, config_name, filament_id, config_from);
2667 if (ret) {
2668 BOOST_LOG_TRIVIAL(error) << boost::format("load uptodate_filaments %1% fail, index %2%!")%file %index;
2669 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2670 flush_and_exit(ret);
2671 }
2672
2673 if (config_type != "filament") {
2674 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-filaments") % config_type % file;
2677 }
2678
2679 if (config_name != current_filaments_system_name[index]) {
2680 BOOST_LOG_TRIVIAL(error) << boost::format("wrong filament config file %1% loaded, current filament config name %2%, index %3%")%config_name %current_filaments_system_name[index] %index;
2683 }
2684
2685 load_filaments_id.push_back(filament_id);
2686 load_filaments_name.push_back(config_name);
2687 load_filaments_config.push_back(std::move(config));
2688 load_filaments_index.push_back(index+1);
2689 load_filaments_inherit.push_back(config_name);
2690 BOOST_LOG_TRIVIAL(info) << boost::format("load a filament config %1% from file %2%, using uptodate_filaments index %3%")%config_name %file %index;
2691 }
2692 }
2693 else
2694 {
2695 current_index = 0;
2696 for (int index = 0; index < current_filaments_system_name.size(); index++)
2697 {
2698 std::string system_filament_path = resources_dir() + "/profiles/BBL/filament_full/"+current_filaments_system_name[index]+".json";
2699 current_index++;
2700 if (! boost::filesystem::exists(system_filament_path)) {
2701 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_filament_path;
2702 continue;
2703 }
2704 DynamicPrintConfig config;
2705 std::string config_type, config_name, filament_id, config_from;
2706 int ret = load_config_file(system_filament_path, config, config_type, config_name, filament_id, config_from);
2707 if (ret) {
2708 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2709 flush_and_exit(ret);
2710 }
2711
2712 if (config_type != "filament") {
2713 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(": unknown config type %1% of file %2% in load-filaments") % config_type % system_filament_path;
2716 }
2717
2718 load_filaments_id.push_back(filament_id);
2719 load_filaments_name.push_back(config_name);
2720 load_filaments_config.push_back(std::move(config));
2721 load_filaments_index.push_back(current_index);
2722 load_filaments_inherit.push_back(config_name);
2723 BOOST_LOG_TRIVIAL(info) << boost::format("LINE %4%: load a filament config %1% from file %2%, index %3%")%config_name %system_filament_path %index %__LINE__;
2724 }
2725 }
2726 }
2727 }
2728 else if (is_bbl_3mf){
2729 fetch_upward_values = true;
2730 fetch_compatible_values = true;
2731 }
2732
2733 // A project whose different_settings_to_system is missing or empty in every entry does not say which of its
2734 // values were changed: the GUI saves it that way when its presets match their parents, but also when it cannot
2735 // find those parents. Only --uptodate then replaces the stored values with system ones.
2736 const bool refresh_unlisted = up_config_to_date || std::any_of(current_different_settings.begin(), current_different_settings.end(),
2737 [](const std::string &keys) { return !keys.empty(); });
2738
2739 // Refresh every project filament no loaded filament replaces from its current system preset, as the GUI
2740 // does when it loads the project; the filament merge below keeps the keys the project lists as changed.
2741 // Without refresh_unlisted it keeps every key the project has except filament_extruder_variant, whose
2742 // system layout the kept values are mapped onto.
2743 // Entries stay in slot order, which the merge's variant bookkeeping relies on.
2744 std::vector<bool> load_filaments_refresh(load_filaments_config.size(), false);
2745 std::set<std::string> project_keys;
2746 if (!refresh_unlisted) {
2747 for (const std::string &key : m_print_config.keys())
2748 project_keys.insert(key);
2749 project_keys.erase("filament_extruder_variant");
2750 }
2751 if (is_bbl_3mf && new_printer_name.empty()) {
2752 const ConfigOptionStrings *project_filament_ids = m_print_config.option<ConfigOptionStrings>("filament_ids");
2753 for (size_t index = 0; index < current_filaments_system_name.size(); index++) {
2754 const int slot = static_cast<int>(index) + 1;
2755 if (std::find(load_filaments_index.begin(), load_filaments_index.end(), slot) != load_filaments_index.end())
2756 continue;
2757 std::string system_name = current_filaments_system_name[index];
2758 if (system_name.empty())
2759 continue;
2760 PresetBundle::convert_filament_preset_name(current_printer_name, system_name);
2761 DynamicPrintConfig config;
2762 std::string error;
2763 if (!ensure_system_preset_resolver().resolve_system_preset(config, Preset::TYPE_FILAMENT, system_name, config_substitution_rule, error)) {
2764 BOOST_LOG_TRIVIAL(warning) << boost::format("CLI: system filament preset '%1%' not resolved (%2%); filament %3% keeps its values") % system_name % error % slot;
2765 continue;
2766 }
2767 const size_t at = std::upper_bound(load_filaments_index.begin(), load_filaments_index.end(), slot) - load_filaments_index.begin();
2768 load_filaments_id.insert(load_filaments_id.begin() + at,
2769 project_filament_ids != nullptr && index < project_filament_ids->size() ? project_filament_ids->values[index] : std::string());
2770 load_filaments_name.insert(load_filaments_name.begin() + at, system_name);
2771 load_filaments_config.insert(load_filaments_config.begin() + at, std::move(config));
2772 load_filaments_index.insert(load_filaments_index.begin() + at, slot);
2773 load_filaments_inherit.insert(load_filaments_inherit.begin() + at, system_name);
2774 load_filaments_refresh.insert(load_filaments_refresh.begin() + at, true);
2775 }
2776 }
2777
2778 //fetch upward_compatible_machine
2779 if (fetch_upward_values) {
2780 if (!current_printer_system_name.empty()) {
2781 //use the original printer name in 3mf
2782 std::string system_printer_path = resources_dir() + "/profiles/BBL/machine_full/"+current_printer_system_name+".json";
2783 if (! boost::filesystem::exists(system_printer_path)) {
2784 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_printer_path;
2785 //skip
2786 }
2787 else {
2788 DynamicPrintConfig config;
2789 std::string config_type, config_name, filament_id, config_from;
2790 int ret = load_config_file(system_printer_path, config, config_type, config_name, filament_id, config_from);
2791 if (ret) {
2792 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2793 flush_and_exit(ret);
2794 }
2795 upward_compatible_printers = config.option<ConfigOptionStrings>("upward_compatible_machine", true)->values;
2796 //ORCA: keep the full config so compatible_printers_condition can be evaluated against it below
2797 current_printer_full_config = std::move(config);
2798 }
2799 }
2800 }
2801
2802 //fetch print_compatible_printers
2803 if (fetch_compatible_values) {
2804 if (!current_process_system_name.empty()) {
2805 //use the original printer name in 3mf
2806 std::string system_process_path = resources_dir() + "/profiles/BBL/process_full/"+current_process_system_name+".json";
2807 if (! boost::filesystem::exists(system_process_path)) {
2808 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__<< boost::format(":%1%, can not find system preset file: %2% ")%__LINE__ %system_process_path;
2809 //use original one
2810 }
2811 else {
2812 DynamicPrintConfig config;
2813 std::string config_type, config_name, filament_id, config_from;
2814 int ret = load_config_file(system_process_path, config, config_type, config_name, filament_id, config_from);
2815 if (ret) {
2816 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
2817 flush_and_exit(ret);
2818 }
2819 current_print_compatible_printers = config.option<ConfigOptionStrings>("compatible_printers", true)->values;
2820 //ORCA: keep the full config so compatible_printers_condition can be evaluated against it below
2821 current_process_full_config = std::move(config);
2822 }
2823 }
2824 }
2825
2826 //upwards check
2827 bool process_compatible = false, machine_upwards = false, machine_switch = false;
2828 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;
2829 BOOST_LOG_TRIVIAL(info) << boost::format("current printer inherits %1%, new printer inherits %2%, current process inherits %3%, new process inherits %4%")
2830 %current_printer_system_name %new_printer_system_name %current_process_system_name %new_process_system_name;
2831 for (int index = 0; index < current_print_compatible_printers.size(); index++) {
2832 BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, current print compatible printer %2%")%index %current_print_compatible_printers[index];
2833 }
2834 for (int index = 0; index < new_print_compatible_printers.size(); index++) {
2835 BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, new print compatible printer %2%")%index %new_print_compatible_printers[index];
2836 }
2837 for (int index = 0; index < upward_compatible_printers.size(); index++) {
2838 BOOST_LOG_TRIVIAL(info) << boost::format("index %1%, upward_compatible_printers %2%")%index %upward_compatible_printers[index];
2839 }
2840 //ORCA: Replace the four manual equality-loop checks below with is_compatible_with_printer(), the
2841 // same helper the GUI uses, which also evaluates compatible_printers_condition. Process
2842 // profiles that declare compatibility via condition only -- leaving compatible_printers
2843 // empty -- were always reported incompatible by the literal-name match, so a CLI slice with
2844 // such a preset exited with CLI_PROCESS_NOT_COMPATIBLE (-17) even though the GUI accepts the
2845 // same pair. Behaviour is unchanged where an explicit list exists: is_compatible_with_printer
2846 // does the same name match, and returns true when both list and condition are empty (which
2847 // matches the "old 3mf, no compatible printers" path below).
2848 auto check_compat = [](const DynamicPrintConfig &process_cfg,
2849 const DynamicPrintConfig &printer_cfg,
2850 const std::string &printer_name) -> bool {
2851 return is_compatible_with_printer(process_cfg, Preset::TYPE_PRINT, printer_cfg, printer_name);
2852 };
2853
2854 //ORCA: a 3MF's project config does not carry compatible_printers / compatible_printers_condition.
2855 // PresetBundle::construct_full_config() erases both and re-emits them as
2856 // print_compatible_printers and compatible_machine_expression_group; they are renamed back
2857 // only on the PresetBundle load path, which the CLI does not take. Feeding the project config
2858 // to the check as-is therefore presents no list and no condition, and
2859 // is_compatible_with_printer() reads that as "no constraint" and accepts every printer.
2860 // Translate the two keys back. Index 0 of the expression group is the print preset -- the
2861 // group is filled print, filaments, printer (PresetBundle.cpp).
2862 // The raw keys win whenever they carry something. A project the CLI exported itself has the
2863 // real compatible_printers_condition AND an all-empty compatible_machine_expression_group,
2864 // so copying the group's first entry unconditionally would overwrite a valid condition with
2865 // "" and accept every printer. The renamed keys are only a fallback, and an empty value is
2866 // never written over a real one.
2867 auto cli_process_compat_config = [](const DynamicPrintConfig &project_cfg) -> DynamicPrintConfig {
2868 DynamicPrintConfig cfg = project_cfg;
2869 const auto *raw_list = project_cfg.option<ConfigOptionStrings>("compatible_printers");
2870 const auto *list = project_cfg.option<ConfigOptionStrings>("print_compatible_printers");
2871 if ((raw_list == nullptr || raw_list->values.empty()) && list != nullptr && !list->values.empty())
2872 cfg.set_key_value("compatible_printers", new ConfigOptionStrings(list->values));
2873 const auto *raw_cond = project_cfg.option<ConfigOptionString>("compatible_printers_condition");
2874 const auto *group = project_cfg.option<ConfigOptionStrings>("compatible_machine_expression_group");
2875 if ((raw_cond == nullptr || raw_cond->value.empty()) && group != nullptr && !group->values.empty() &&
2876 !group->values.front().empty())
2877 cfg.set_key_value("compatible_printers_condition", new ConfigOptionString(group->values.front()));
2878 return cfg;
2879 };
2880 if (!new_printer_name.empty()) {
2881 if (!new_process_name.empty()) {
2882 //new process + new printer: both configs came from --load-settings
2883 process_compatible = check_compat(load_process_config, load_machine_config, new_printer_system_name);
2884 BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, inherited from %2%, new process %3%, inherited from %4% ,compatible %5%")
2885 %new_printer_name %new_printer_system_name %new_process_name %new_process_system_name %process_compatible;
2886 }
2887 else {
2888 //3MF-embedded process vs new printer. current_process_full_config is only populated from
2889 //profiles/BBL/process_full/, so for every other vendor fall back to the 3MF's own project
2890 //config in m_print_config, with its renamed compatibility keys translated back (see
2891 //cli_process_compat_config above). Without this a 3MF built from a condition-only process
2892 //is rejected when re-sliced with the very printer it was made for.
2893 {
2894 //ORCA: profiles/BBL/{process,machine}_full/ are gitignored and not generated in-tree,
2895 // so current_*_full_config is always empty and this fallback is the only live path.
2896 const DynamicPrintConfig process_cfg = current_process_full_config.empty()
2897 ? cli_process_compat_config(m_print_config)
2898 : current_process_full_config;
2899 process_compatible = check_compat(process_cfg, load_machine_config, new_printer_system_name);
2900 }
2901 BOOST_LOG_TRIVIAL(info) << boost::format("new printer %1%, inherited from %2%, old process %3%, inherited from %4% ,compatible %5%")
2902 %new_printer_name %new_printer_system_name %current_process_name %current_process_system_name %process_compatible;
2903 }
2904 }
2905 else if (!new_process_name.empty()) {
2906 //new process vs 3MF-embedded printer. As above, current_printer_full_config only resolves for
2907 //BBL profiles; otherwise evaluate against the 3MF's own project config in m_print_config, which
2908 //holds the embedded printer's printer_notes / nozzle_diameter.
2909 {
2910 const DynamicPrintConfig &printer_cfg = current_printer_full_config.empty() ? m_print_config : current_printer_full_config;
2911 process_compatible = check_compat(load_process_config, printer_cfg, current_printer_system_name);
2912 }
2913 BOOST_LOG_TRIVIAL(info) << boost::format("old printer %1%, inherited from %2%, new process %3%, inherited from %4% ,compatible %5%")
2914 %current_printer_name %current_printer_system_name %new_process_name %new_process_system_name %process_compatible;
2915 }
2916 else {
2917 //both sides 3MF-embedded (pure reprocess)
2918 if (!current_process_full_config.empty() && !current_printer_full_config.empty())
2919 process_compatible = check_compat(current_process_full_config, current_printer_full_config, current_printer_system_name);
2920 else
2921 process_compatible = std::find(current_print_compatible_printers.begin(), current_print_compatible_printers.end(), current_printer_system_name) != current_print_compatible_printers.end();
2922 if (!process_compatible && current_print_compatible_printers.empty())
2923 {
2924 BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf, no compatible printers, set to compatible");
2925 process_compatible = true;
2926 }
2927 BOOST_LOG_TRIVIAL(info) << boost::format("old printer %1%, inherited from %2%, old process %3%, inherited from %4% ,compatible %5%")
2928 %current_printer_name %current_printer_system_name %current_process_name %current_process_system_name %process_compatible;
2929 }
2930 if (!process_compatible && !new_printer_name.empty() && !current_printer_name.empty() && (new_printer_name != current_printer_name)) {
2931 //set all printer to compatible
2932 if (new_process_name.empty())
2933 process_compatible = true;
2934 machine_switch = true;
2935 BOOST_LOG_TRIVIAL(info) << boost::format("switch to new printers, set to compatible");
2936 if (upward_compatible_printers.size() > 0) {
2937 for (int index = 0; index < upward_compatible_printers.size(); index++) {
2938 if (upward_compatible_printers[index] == new_printer_system_name) {
2939 process_compatible = true;
2940 machine_upwards = true;
2941 BOOST_LOG_TRIVIAL(info) << boost::format("new printer is upward_compatible");
2942 break;
2943 }
2944 }
2945 if (!process_compatible) {
2946 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;
2949 }
2950 }
2951 /*else {
2952 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(" %1%: current 3mf file not support upward_compatible_printers, can not change machine preset.")%__LINE__;
2953 record_exit_reson(outfile_dir, CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE, 0, cli_errors[CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE], sliced_info);
2954 flush_and_exit(CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE);
2955 }*/
2956 }
2957
2958 if (!process_compatible) {
2959 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__ << boost::format(" %1%: process not compatible with printer.")%__LINE__;
2962 }
2963 sliced_info.upward_machines = upward_compatible_printers;
2964
2965 //create project embedded preset if needed
2966 Preset *new_preset = NULL;
2967 if (is_bbl_3mf && machine_switch) {
2968 //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
2969 Preset *current_preset = NULL;
2970 size_t project_presets_count = project_presets.size();
2971 for (int index = 0; index < project_presets_count; index++)
2972 {
2973 if (project_presets[index]->name == current_process_name) {
2974 current_preset = project_presets[index];
2975 break;
2976 }
2977 }
2978 new_preset = new Preset(Preset::TYPE_PRINT, current_process_name);
2979 if (current_preset) {
2980 *new_preset = *current_preset;
2981 std::vector<std::string>& compatible_printers = new_preset->config.option<ConfigOptionStrings>("compatible_printers", true)->values;
2982 bool need_insert = true;
2983 for (int index = 0; index < compatible_printers.size(); index++) {
2984 if (compatible_printers[index] == new_printer_system_name) {
2985 need_insert = false;
2986 break;
2987 }
2988 }
2989 if (need_insert)
2990 compatible_printers.push_back(new_printer_system_name);
2991 }
2992 else {
2993 //store a project-embedded preset
2994 const std::vector<std::string>& process_keys = Preset::print_options();
2995 new_preset->config.apply_only(m_print_config, process_keys);
2996 std::vector<std::string>& compatible_printers = new_preset->config.option<ConfigOptionStrings>("compatible_printers", true)->values;
2997 compatible_printers = current_print_compatible_printers;
2998 compatible_printers.push_back(new_printer_system_name);
2999 if (current_process_system_name != current_process_name) {
3000 std::string& inherits = new_preset->config.option<ConfigOptionString>("inherits", true)->value;
3001 inherits = current_process_system_name;
3002 }
3003 new_preset->is_project_embedded = true;
3004 new_preset->type = Preset::TYPE_PRINT;
3005 //new_preset->name = current_process_name;
3006 }
3007 new_preset->name += "(auto)";
3008 new_preset->config.set("print_settings_id", new_preset->name, true);
3009 m_print_config.set("print_settings_id", new_preset->name, true);
3010 project_presets.push_back(new_preset);
3011 }
3012
3013 //compute extruder variant index
3014 auto compute_variant_index = [](DynamicPrintConfig& full_config, const DynamicPrintConfig& new_config, std::string id_name, std::string variant_name, std::vector<int>& new_index, bool& count_changed) {
3015 auto curr_variant_opt = dynamic_cast<const ConfigOptionStrings*>(full_config.option(variant_name));
3016 auto new_variant_opt = dynamic_cast<const ConfigOptionStrings*>(new_config.option(variant_name));
3017 auto curr_id_opt = dynamic_cast<const ConfigOptionInts*>(full_config.option(id_name));
3018 auto new_id_opt = dynamic_cast<const ConfigOptionInts*>(new_config.option(id_name));
3019 if (!new_variant_opt || !new_id_opt) {
3020 BOOST_LOG_TRIVIAL(error) << boost::format("%1%:%2%, can not get %3% or %4% from new config")%__FUNCTION__ %__LINE__ % variant_name %id_name;
3021 count_changed = false;
3022 return;
3023 }
3024 int new_variant_count = new_variant_opt->size(), curr_variant_count = 0;
3025 new_index.clear();
3026 new_index.resize(new_variant_count, -1);
3027 if (curr_variant_opt && curr_id_opt) {
3028 if (curr_variant_opt->size() != curr_id_opt->size()) {
3029 BOOST_LOG_TRIVIAL(error) << boost::format("%1%:%2%, %3%'s size %4% not equal to %5%'s size %6%")%__FUNCTION__ %__LINE__ % variant_name %curr_variant_opt->size() %id_name %curr_id_opt->size();
3030 count_changed = false;
3031 return;
3032 }
3033 curr_variant_count = curr_variant_opt->size();
3034 count_changed = (curr_variant_count != new_variant_count);
3035 }
3036 else
3037 count_changed = (new_variant_count != 1);
3038
3039
3040 for (int i = 0; i < new_variant_count; i++)
3041 {
3042 if (curr_variant_count > 0) {
3043 for (int j = 0; j < curr_variant_count; j++)
3044 {
3045 if ((curr_variant_opt->values[j] == new_variant_opt->values[i]) && (curr_id_opt->values[j] == new_id_opt->values[i])) {
3046 new_index[i] = j;
3047 break;
3048 }
3049 }
3050 }
3051 else if ((new_variant_opt->values[i] == "Direct Drive Standard") && (new_id_opt->values[i] == 1))
3052 new_index[i] = 0;
3053 }
3054 };
3055
3056 //update separate configs into full config
3057 auto update_full_config = [](DynamicPrintConfig& full_config, const DynamicPrintConfig& config, std::set<std::string>& diff_key_sets, bool variant_count_changed, std::set<std::string>& key_set_1, std::set<std::string>& key_set_2, std::vector<int> variant_index, bool update_all = false, bool skip_gcodes = false) {
3058 const t_config_option_keys& config_keys = config.keys();
3059 BOOST_LOG_TRIVIAL(info) << boost::format("update_full_config: config keys count %1%")%config_keys.size();
3060 for (const t_config_option_key &opt_key : config_keys) {
3061 if (!update_all && !diff_key_sets.empty()) {
3062 std::set<std::string>::iterator iter = diff_key_sets.find(opt_key);
3063 if ( iter != diff_key_sets.end()) {
3064 if (skip_gcodes && (gcodes_key_set.find(opt_key) != gcodes_key_set.end()))
3065 {
3066 diff_key_sets.erase(iter);
3067 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, gcodes %2% modified, reset to default.")%__LINE__ %opt_key;
3068 }
3069 else {
3070 //uptodate, diff keys, continue
3071 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, keep key %2%")%__LINE__ %opt_key;
3072 if (variant_count_changed) {
3073 //
3074 int stride = 0;
3075 if (key_set_1.count(opt_key) > 0)
3076 {
3077 stride = 1;
3078 }
3079 else if (key_set_2.count(opt_key) > 0)
3080 {
3081 stride = 2;
3082 }
3083
3084 if (stride > 0) {
3085 const ConfigOption *source_opt = config.option(opt_key);
3086 ConfigOption *dest_opt = full_config.option(opt_key, true);
3087 const ConfigOptionVectorBase* opt_vec_src = static_cast<const ConfigOptionVectorBase*>(source_opt);
3088 ConfigOptionVectorBase* opt_vec_dest = static_cast<ConfigOptionVectorBase*>(dest_opt);
3089
3090 opt_vec_dest->set_with_restore(opt_vec_src, variant_index, stride);
3091 }
3092 else
3093 continue;
3094 }
3095 else
3096 continue;
3097 }
3098 }
3099 }
3100 const ConfigOption *source_opt = config.option(opt_key);
3101 if (source_opt == nullptr) {
3102 // The key was not found in the source config, therefore it will not be initialized!
3103 boost::nowide::cerr << __FUNCTION__<<": can not found option " <<opt_key<<"from config." <<std::endl;
3104 return CLI_CONFIG_FILE_ERROR;
3105 }
3106 if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "model_id" || opt_key == "inherits" ||opt_key == "dev_model_name"
3107 || opt_key == "name" || opt_key == "from" || opt_key == "type" || opt_key == "version" || opt_key == "setting_id" || opt_key == "instantiation" )
3108 continue;
3109 else {
3110 ConfigOption *dest_opt = full_config.option(opt_key, true);
3111 if (dest_opt == nullptr) {
3112 boost::nowide::cerr << __FUNCTION__<<":can not create option " <<opt_key<<" to full_config "<<std::endl;
3113 return CLI_CONFIG_FILE_ERROR;
3114 }
3115 dest_opt->set(source_opt);
3116 //*dest_opt = *source_opt;
3117 }
3118 }
3119 return 0;
3120 };
3121
3122 // Load the project's printer and process settings as the GUI loads its presets: over the default preset,
3123 // with every key the project does not list as changed, including keys saved before an option existed,
3124 // taken from its current system preset. Without refresh_unlisted the project only gets the keys it lacks.
3125 auto load_project_preset = [this, &ensure_system_preset_resolver, &current_different_settings, refresh_unlisted, filament_count](const std::string &system_name, Preset::Type type) {
3126 if (system_name.empty())
3127 return;
3128 // Preset bookkeeping the CLI keeps in its own groups, e.g. inherits_group and print_compatible_printers.
3129 static const std::set<std::string> bookkeeping_keys = {"inherits", "compatible_printers", "compatible_prints", "compatible_printers_condition",
3130 "compatible_prints_condition", "print_settings_id", "printer_settings_id"};
3131 const size_t index = type == Preset::TYPE_PRINTER ? filament_count + 1 : 0;
3132 PresetBundle &resolver = ensure_system_preset_resolver();
3133 DynamicPrintConfig system_config;
3135 const DynamicPrintConfig config = Preset::load_external_config(type,
3137 m_print_config, PresetBundle::project_different_keys(index < current_different_settings.size() ? current_different_settings[index] : std::string()),
3138 [&](const std::string &) -> DynamicPrintConfig * {
3139 std::string error;
3140 if (resolver.resolve_system_preset(system_config, type, system_name, config_substitution_rule, error))
3141 return &system_config;
3142 BOOST_LOG_TRIVIAL(warning) << boost::format("CLI: system preset '%1%' not resolved (%2%); the project keeps its values") % system_name % error;
3143 return nullptr;
3144 }, &keys);
3145 for (const std::string &key : keys) {
3146 const ConfigOption *opt = config.option(key);
3147 const ConfigOption *old = m_print_config.option(key);
3148 if (bookkeeping_keys.count(key) != 0 || opt == nullptr || (old != nullptr && (!refresh_unlisted || *old == *opt)))
3149 continue;
3150 BOOST_LOG_TRIVIAL(info) << boost::format("CLI: %1% from '%2%': %3% -> %4%") % key % system_name % (old ? old->serialize() : std::string("(missing)")) % opt->serialize();
3151 m_print_config.set_key_value(key, opt->clone());
3152 }
3153 };
3154 // The --uptodate path refreshes the project from its own system configs.
3155 if (new_printer_name.empty() && load_machine_config.empty())
3156 load_project_preset(current_printer_system_name, Preset::TYPE_PRINTER);
3157 if (new_process_name.empty() && load_process_config.empty())
3158 load_project_preset(current_process_system_name, Preset::TYPE_PRINT);
3159
3160 std::vector<std::string>& different_settings = m_print_config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
3161 std::vector<std::string>& inherits_group = m_print_config.option<ConfigOptionStrings>("inherits_group", true)->values;
3162 inherits_group.resize(filament_count + 2, std::string());
3163 different_settings.resize(filament_count + 2, std::string());
3164 if (!is_bbl_3mf && !different_process_setting.empty()) {
3165 different_settings[0] = different_process_setting;
3166 }
3167
3168 std::vector<int> new_variant_index;
3169 bool variant_count_changed = false;
3170 //set the machine settings into print config
3171 if (!new_printer_name.empty() || up_config_to_date) {
3172 std::vector<std::string> different_keys;
3173
3174 if (new_printer_name.empty()) {
3175 std::string diff_settings;
3176 if (!different_settings.empty()) {
3177 diff_settings = different_settings[filament_count+1];
3178 Slic3r::unescape_strings_cstyle(diff_settings, different_keys);
3179 }
3180 }
3181 else {
3182 //ORCA: was a //todo — compute the user's overrides instead of writing an empty column.
3183 different_settings[filament_count+1] = new_printer_config_is_system
3184 ? std::string()
3185 : cli_different_settings(load_machine_config, new_printer_system_name, Preset::TYPE_PRINTER);
3186 if (new_printer_config_is_system)
3187 inherits_group[filament_count+1] = "";
3188 else
3189 inherits_group[filament_count+1] = new_printer_system_name;
3190 }
3191
3192 std::set<std::string> different_keys_set(different_keys.begin(), different_keys.end());
3193 BOOST_LOG_TRIVIAL(info) << boost::format("update printer config to newest, different size %1%, different_settings: %2%")%different_keys_set.size() %different_settings[filament_count+1];
3194
3195 int ret;
3196
3198 if (new_printer_name.empty()) {
3199 int diff_keys_size = different_keys_set.size();
3200 compute_variant_index(m_print_config, load_machine_config, "printer_extruder_id", "printer_extruder_variant", new_variant_index, variant_count_changed);
3201 ret = update_full_config(m_print_config, load_machine_config, different_keys_set, variant_count_changed, printer_options_with_variant_1, printer_options_with_variant_2, new_variant_index, false, skip_modified_gcodes);
3202 if (diff_keys_size != different_keys_set.size()) {
3203 //changed
3204 BOOST_LOG_TRIVIAL(info) << boost::format("new different key size %1%")%different_keys_set.size();
3205 different_keys.clear();
3206
3207 for (std::set<std::string>::iterator iter=different_keys_set.begin(); iter !=different_keys_set.end(); ++iter)
3208 different_keys.emplace_back(*iter);
3209 different_settings[filament_count+1] = Slic3r::escape_strings_cstyle(different_keys);
3210 }
3211 BOOST_LOG_TRIVIAL(info) << boost::format("no new printer, only update the different key, new different_settings: %1%")%different_settings[filament_count+1];
3212 }
3213 else {
3214 ret = update_full_config(m_print_config, load_machine_config, different_keys_set, variant_count_changed, printer_options_with_variant_1, printer_options_with_variant_2, new_variant_index, true);
3215 BOOST_LOG_TRIVIAL(info) << boost::format("load a new printer, update all the keys, different_settings: %1%")%different_settings[filament_count+1];
3216 if (new_printer_name != current_printer_name)
3217 {
3218 //printer safe check
3219 BOOST_LOG_TRIVIAL(info) << boost::format("check printer cli safe params, current_printer_name %1%, new_printer_name %2%, printer_model %3%")%current_printer_name %new_printer_name %printer_model;
3220 std::map<std::string, std::string> printer_params;
3221 std::string cli_config_file = resources_dir() + "/profiles/BBL/cli_config.json";
3222 boost::filesystem::path directory_path(cli_config_file);
3223
3224 BOOST_LOG_TRIVIAL(info) << boost::format("line %1% , will parse file %2%")%__LINE__ % cli_config_file;
3225 if (!fs::exists(directory_path)) {
3226 BOOST_LOG_TRIVIAL(warning) << boost::format("file %1% not exist.")%cli_config_file;
3227 }
3228 else {
3229 try {
3230 json root_json;
3231 boost::nowide::ifstream ifs(cli_config_file);
3232 ifs >> root_json;
3233 ifs.close();
3234
3235 if (root_json.contains("printer")) {
3236 json printer_json = root_json["printer"];
3237 if (!printer_model.empty() && printer_json.contains(printer_model)) {
3238 json printer_model_json = printer_json[printer_model];
3239
3240 if (printer_model_json.contains("machine_limits")) {
3241 json machine_limits_json = printer_model_json["machine_limits"];
3242 printer_params = machine_limits_json.get<std::map<std::string, std::string>>();
3243
3244 for (auto param_iter = printer_params.begin(); param_iter != printer_params.end(); param_iter++)
3245 {
3246 std::string key = param_iter->first;
3247 //replace "cli_safe" with "machine_max"
3248 key.replace(0, 8, "machine_max");
3249
3251 if (option) {
3252 //de-serialize the values from param_iter->second, and do the compare here
3253 unsigned int array_count = option->size();
3254 ConfigOptionFloats new_option;
3255 new_option.deserialize(param_iter->second);
3256 unsigned int new_array_count = new_option.size();
3257 for (unsigned int index = 0; index < array_count; index++)
3258 {
3259 if ((index < new_array_count) && new_option.values[index] != 0.f && (new_option.values[index] < option->values[index]))
3260 {
3261 BOOST_LOG_TRIVIAL(info) << boost::format("set key %1% index %2%, from %3% to %4%") % key %index %option->values[index] % new_option.values[index];
3262 option->values[index] = new_option.values[index];
3263 }
3264 }
3265 }
3266 else
3267 BOOST_LOG_TRIVIAL(warning) << boost::format("can not find key %1% in config") %key;
3268 }
3269 }
3270 else {
3271 BOOST_LOG_TRIVIAL(info) << boost::format("can not find machine_limits for printer %1% in cli_config.json")%printer_model;
3272 }
3273 }
3274 else {
3275 BOOST_LOG_TRIVIAL(info) << boost::format("can not find key %1% in the file")%printer_model;
3276 }
3277 }
3278 else {
3279 BOOST_LOG_TRIVIAL(warning) << boost::format("can not find key printer in the file");
3280 }
3281 }
3282 catch (std::exception &err) {
3283 BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse file "<<cli_config_file<<" got a generic exception, reason = " << err.what();
3284 }
3285 }
3286 }
3287 }
3288
3289 if (ret) {
3290 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
3291 flush_and_exit(ret);
3292 }
3293 }
3294 if (m_print_config.option<ConfigOptionFloats>("nozzle_diameter")) {
3295 new_extruder_count = m_print_config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
3296 new_is_multi_extruder = new_extruder_count > 1;
3297 new_printer_extruder_variants = m_print_config.option<ConfigOptionStrings>("printer_extruder_variant", true)->values;
3298 new_printer_variant_count = new_printer_extruder_variants.size();
3299 }
3300
3301 //set the process settings into print config
3302 std::vector<std::string>& print_compatible_printers = m_print_config.option<ConfigOptionStrings>("print_compatible_printers", true)->values;
3303 if (!new_process_name.empty() || up_config_to_date) {
3304 std::vector<std::string> different_keys;
3305
3306 if (new_process_name.empty()) {
3307 std::string diff_settings;
3308 if (!different_settings.empty()) {
3309 diff_settings = different_settings[0];
3310 Slic3r::unescape_strings_cstyle(diff_settings, different_keys);
3311 int remove_index = -1;
3312 for (int index = 0; index < different_keys.size(); index++) {
3313 if (different_keys[index] == "compatible_printers") {
3314 remove_index = index;
3315 break;
3316 }
3317 }
3318 if (remove_index != -1) {
3319 different_keys[remove_index] = different_keys[different_keys.size() - 1];
3320 different_keys.erase(different_keys.begin() + different_keys.size() - 1);
3321 different_settings[0] = Slic3r::escape_strings_cstyle(different_keys);
3322 }
3323 }
3324 print_compatible_printers = std::move(current_print_compatible_printers);
3325 }
3326 else {
3327 //ORCA: was a //todo. Prefer a value the loaded JSON already carried, otherwise
3328 // compute the overrides against the system parent.
3329 if (!different_process_setting.empty())
3330 different_settings[0] = different_process_setting;
3331 else
3332 different_settings[0] = new_process_config_is_system
3333 ? std::string()
3334 : cli_different_settings(load_process_config, new_process_system_name, Preset::TYPE_PRINT);
3335 if (new_process_config_is_system)
3336 inherits_group[0] = "";
3337 else
3338 inherits_group[0] = new_process_system_name;
3339 print_compatible_printers = std::move(new_print_compatible_printers);
3340 }
3341
3342 std::set<std::string> different_keys_set(different_keys.begin(), different_keys.end());
3343 BOOST_LOG_TRIVIAL(info) << boost::format("update process config to newest, different size %1%, different_settings: %2%")%different_keys_set.size() %different_settings[0];
3344
3345 int ret;
3346
3348 if (new_process_name.empty()) {
3349 int diff_keys_size = different_keys_set.size();
3350 compute_variant_index(m_print_config, load_process_config, "print_extruder_id", "print_extruder_variant", new_variant_index, variant_count_changed);
3351 ret = update_full_config(m_print_config, load_process_config, different_keys_set, variant_count_changed, print_options_with_variant, empty_options, new_variant_index, false, skip_modified_gcodes);
3352 if (diff_keys_size != different_keys_set.size()) {
3353 //changed
3354 BOOST_LOG_TRIVIAL(info) << boost::format("new different key size %1%")%different_keys_set.size();
3355 different_keys.clear();
3356
3357 for (std::set<std::string>::iterator iter=different_keys_set.begin(); iter !=different_keys_set.end(); ++iter)
3358 different_keys.emplace_back(*iter);
3359 different_settings[0] = Slic3r::escape_strings_cstyle(different_keys);
3360 }
3361 BOOST_LOG_TRIVIAL(info) << boost::format("no new process, only update the different key, new different_settings: %1%")%different_settings[0];
3362 }
3363 else {
3364 ret = update_full_config(m_print_config, load_process_config, different_keys_set, variant_count_changed, print_options_with_variant, empty_options, new_variant_index, true);
3365 BOOST_LOG_TRIVIAL(info) << boost::format("load a new process, update all the keys, different_settings: %1%")%different_settings[0];
3366 }
3367 current_print_variant_count = m_print_config.option<ConfigOptionStrings>("print_extruder_variant", true)->values.size();
3368
3369 if (ret) {
3370 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
3371 flush_and_exit(ret);
3372 }
3373 }
3374
3375 //get nozzle_volume_type
3376 if(m_extra_config.has("nozzle_volume_type")) {
3377 auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(m_extra_config.option("nozzle_volume_type"));
3378 if (opt_nozzle_volume_type) {
3379 int nozzle_volume_type_size = opt_nozzle_volume_type->values.size();
3380 new_nozzle_volume_type.resize(nozzle_volume_type_size, nvtStandard);
3381 for (int i = 0; i < nozzle_volume_type_size; i++)
3382 {
3383 new_nozzle_volume_type[i] = (NozzleVolumeType) (opt_nozzle_volume_type->values[i]);
3384 }
3385 }
3386 }
3387 else {
3388 new_nozzle_volume_type.resize(new_extruder_count, nvtStandard);
3389 }
3390 new_extruder_variants.resize(new_extruder_count, "");
3391 const ConfigOptionEnumsGeneric *opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric *>(m_print_config.option("extruder_type"));
3392 for (int e_index = 0; e_index < new_extruder_count; e_index++) {
3393 ExtruderType extruder_type = opt_extruder_type ? (ExtruderType) (opt_extruder_type->get_at(e_index)) : etDirectDrive;
3394 new_extruder_variants[e_index] = get_extruder_variant_string(extruder_type, new_nozzle_volume_type[e_index]);
3395 }
3396
3397 if (current_nozzle_volume_type.empty())
3398 {
3399 current_nozzle_volume_type.resize(current_extruder_count, nvtStandard);
3400 }
3401 current_print_extruder_variants = m_print_config.option<ConfigOptionStrings>("print_extruder_variant", true)->values;
3402
3403 if (machine_switch) {
3404 print_compatible_printers.push_back(new_printer_system_name);
3405
3406 std::string old_setting = different_settings[0];
3407 if (old_setting.empty())
3408 different_settings[0] = "compatible_printers";
3409 else {
3410 std::vector<std::string> different_keys;
3411 Slic3r::unescape_strings_cstyle(old_setting, different_keys);
3412 bool need_insert = true;
3413 for (int index = 0; index < different_keys.size(); index++) {
3414 if (different_keys[index] == "compatible_printers") {
3415 need_insert = false;
3416 break;
3417 }
3418 }
3419 if (need_insert)
3420 different_settings[0] = old_setting + ";compatible_printers";
3421 }
3422
3423 //add multi-extruder related logic
3424 if (new_process_name.empty() && ((current_extruder_count != new_extruder_count) || (current_print_variant_count != new_printer_variant_count))) {
3425 if (new_default_process_name.empty()) {
3426 BOOST_LOG_TRIVIAL(error) << boost::format("machine_switch: default process configis null, should not happen, new_printer_name %1%")%new_printer_name;
3429 }
3430 else {
3431 std::string default_path = resources_dir() + "/profiles/BBL/process_full/";
3432 std::string file_path = default_path + new_default_process_name + ".json";
3433 DynamicPrintConfig config;
3434 std::string config_type, config_name, filament_id, config_from, downward_printer;
3435 int ret = load_config_file(file_path, config, config_type, config_name, filament_id, config_from);
3436 if (ret) {
3437 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
3438 flush_and_exit(ret);
3439 }
3440 if ((config_type != "process") || (config_from != "system")) {
3441 BOOST_LOG_TRIVIAL(error) << boost::format("found invalid config type %1% or from %2% in file %3% when downward_check")%config_type %config_from %file_path;
3444 }
3445 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, machine_switch: loaded default process config %2%, from %3%")%__LINE__ %config_name %file_path ;
3446 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, current_is_multi_extruder: %2%, new_is_multi_extruder: %3%, current_print_variant_count: %4%, new_printer_variant_count: %5%")
3447 %__LINE__ %current_is_multi_extruder %new_is_multi_extruder %current_print_variant_count %new_printer_variant_count;
3448
3449 if (!current_is_multi_extruder && new_is_multi_extruder && (current_print_variant_count == 1)) {
3450 //single -> multiple
3451 ret = m_print_config.update_values_from_single_to_multi(config, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
3452 }
3453 else {
3454 //multiple -> single
3455 ret = m_print_config.update_values_from_multi_to_multi(config, print_options_with_variant, "print_extruder_id", "print_extruder_variant", new_extruder_variants);
3456 }
3457
3458 if (ret) {
3459 BOOST_LOG_TRIVIAL(error) << boost::format("current_is_multi_extruder %1% new_is_multi_extruder %2%, update_values failed") % current_is_multi_extruder % new_is_multi_extruder;
3462 }
3463 }
3464 }
3465 }
3466
3467 //set the filament settings into print config
3468 if ((load_filament_count > 0) || (up_config_to_date) || !load_filaments_config.empty())
3469 {
3470 //std::vector<int> filament_variant_count(filament_count, 1);
3471 std::vector<int> old_start_indice(filament_count, 0);
3472 std::vector<int> old_variant_counts(filament_count, 1), new_variant_counts;
3473
3474 ConfigOptionInts* filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index");
3475 bool need_regenerate_self_index = !filament_self_index_opt;
3476 if (filament_self_index_opt) {
3477 // a filament_self_index carried over from a stale project can disagree with the
3478 // current filament_count. old_start_indice/old_variant_counts below are sized to
3479 // filament_count and walked with 1-based group indices, so an index above
3480 // filament_count overruns old_start_indice[++k], and a non-positive first index
3481 // writes old_variant_counts[-1] - both heap corruption.
3482 int max_self_index = 0, min_self_index = 1;
3483 for (int v : filament_self_index_opt->values) {
3484 max_self_index = std::max(max_self_index, v);
3485 min_self_index = std::min(min_self_index, v);
3486 }
3487 // And a project saved with FEWER filaments than are now loaded (a
3488 // one-filament project sliced with two --load-filaments) leaves the tables half filled:
3489 // the variant matching below then reads past filament_extruder_variant and
3490 // set_with_restore_2 throws an uncaught size error. Regenerate in that case too.
3491 if (max_self_index > filament_count || min_self_index < 1 || max_self_index < filament_count
3492 || (int) filament_self_index_opt->values.size() < filament_count) {
3493 BOOST_LOG_TRIVIAL(warning) << boost::format("filament_self_index range [%1%, %2%] (size %4%) is invalid for filament_count %3%, regenerating")
3494 % min_self_index % max_self_index % filament_count % filament_self_index_opt->values.size();
3495 need_regenerate_self_index = true;
3496 }
3497 }
3498 if (need_regenerate_self_index) {
3499 filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index", true);
3500 std::vector<int>& filament_self_indice = filament_self_index_opt->values;
3501 filament_self_indice.resize(filament_count);
3502 for (int index = 0; index < filament_count; index++)
3503 filament_self_indice[index] = index + 1;
3504 }
3505
3506 std::vector<int> old_self_indice= filament_self_index_opt->values;
3507 int old_self_indice_size = old_self_indice.size();
3508 int k = -1, current_filament = 0;
3509 for (size_t i = 0; i < old_self_indice_size; i++) {
3510 if (old_self_indice[i] > current_filament) {
3511 current_filament = old_self_indice[i];
3512 old_start_indice[++k] = i;
3513 old_variant_counts[k] = 1;
3514 }
3515 else {
3516 old_variant_counts[k] = old_variant_counts[k] + 1;
3517 }
3518 }
3519 new_variant_counts = old_variant_counts;
3520 //filament_variant_count = old_variant_counts;
3521 //ORCA: lay the per-variant options out one value per variant of the current filaments before each
3522 // loaded filament replaces its own variants, including an option only a loaded filament
3523 // defines, which otherwise starts as a single default value and never reaches the others.
3524 for (const DynamicPrintConfig &config : load_filaments_config)
3525 for (const std::string &opt_key : filament_options_with_variant)
3526 if (opt_key != "filament_extruder_variant" && config.has(opt_key))
3527 m_print_config.option(opt_key, true);
3529 for (int index = 0; index < load_filaments_config.size(); index++) {
3530 DynamicPrintConfig& config = load_filaments_config[index];
3531 int filament_index = load_filaments_index[index];
3532 // A filament given with --load-filaments replaces the slot; a refreshed one keeps the project's changed keys.
3533 const bool loaded = load_filament_count > 0 && !load_filaments_refresh[index];
3534 // A refreshed filament of a project that lists no changed settings keeps every key it has.
3535 const bool keep_project = load_filaments_refresh[index] && !refresh_unlisted;
3536 std::vector<std::string> different_keys;
3537
3538 //ORCA: diff before load_default_gcodes_to_config, the way the process and machine
3539 // slots above already do. That call materialises absent gcode keys via
3540 // option(..., true), and DynamicConfig::diff only compares keys present in
3541 // both configs -- so a gcode key the leaf did not carry would go from "not
3542 // compared" to "compared as empty against the parent" and land in the column
3543 // as an override the user never made.
3544 std::string filament_different_settings;
3545 if (loaded)
3546 filament_different_settings = cli_different_settings(config, load_filaments_inherit[index], Preset::TYPE_FILAMENT);
3547
3549
3550 if (loaded) {
3551 ConfigOptionStrings *opt_filament_settings = static_cast<ConfigOptionStrings *> (m_print_config.option("filament_settings_id", true));
3552 std::string& filament_name = load_filaments_name[index];
3553 ConfigOptionString* filament_name_setting = new ConfigOptionString(filament_name);
3554 if (opt_filament_settings->size() < filament_count)
3555 opt_filament_settings->resize(filament_count, filament_name_setting);
3556 opt_filament_settings->set_at(filament_name_setting, filament_index-1, 0);
3557 config.erase("filament_settings_id");
3558
3559 //ORCA: was a //todo — same treatment as process/machine above.
3560 different_settings[filament_index] = filament_different_settings;
3561 inherits_group[filament_index] = load_filaments_inherit[index];
3562 }
3563 else if (keep_project)
3564 different_keys.assign(project_keys.begin(), project_keys.end());
3565 else {
3566 std::string diff_settings;
3567
3568 if (!different_settings.empty()) {
3569 diff_settings = different_settings[filament_index];
3570 Slic3r::unescape_strings_cstyle(diff_settings, different_keys);
3571 }
3572 }
3573
3574 //add filament_id
3575 std::string& filament_id = load_filaments_id[index];
3576 ConfigOptionStrings *opt_filament_ids = static_cast<ConfigOptionStrings *> (m_print_config.option("filament_ids", true));
3577 ConfigOptionString* filament_id_setting = new ConfigOptionString(filament_id);
3578 if (opt_filament_ids->size() < filament_count)
3579 opt_filament_ids->resize(filament_count, filament_id_setting);
3580 opt_filament_ids->set_at(filament_id_setting, filament_index-1, 0);
3581
3582 //compute the variant index logic
3583 ConfigOptionStrings *curr_variant_opt = m_print_config.option<ConfigOptionStrings>("filament_extruder_variant");
3584 if (!curr_variant_opt) {
3585 curr_variant_opt = m_print_config.option<ConfigOptionStrings>("filament_extruder_variant", true);
3586 std::vector<std::string>& filament_variants = curr_variant_opt->values;
3587 filament_variants.resize(filament_count, get_extruder_variant_string(etDirectDrive, nvtStandard));
3588 }
3589 // See the filament_self_index note above: one variant per filament for
3590 // the filaments the project did not know about.
3591 if ((int) curr_variant_opt->values.size() < filament_count)
3592 curr_variant_opt->values.resize(filament_count, get_extruder_variant_string(etDirectDrive, nvtStandard));
3593 const ConfigOptionStrings *new_variant_opt = dynamic_cast<const ConfigOptionStrings*>(config.option("filament_extruder_variant", true));
3594
3595 std::vector<int> new_variant_indice;
3596 int new_variant_count = new_variant_opt->size(), old_variant_count = old_variant_counts[filament_index - 1];
3597 new_variant_indice.resize(new_variant_count, -1);
3598
3599 for (int i = 0; i < new_variant_count; i++)
3600 {
3601 for (int j = old_start_indice[filament_index - 1]; j < old_start_indice[filament_index - 1] + old_variant_count && j < (int) curr_variant_opt->values.size(); j++)
3602 {
3603 if (curr_variant_opt->values[j] == new_variant_opt->values[i]) {
3604 new_variant_indice[i] = j;
3605 break;
3606 }
3607 }
3608 }
3609
3610 //parse the filament value to index th
3611 //loop through options and apply them
3612 std::set<std::string> different_keys_set(different_keys.begin(), different_keys.end());
3613 int diff_keys_size = different_keys_set.size();
3614 BOOST_LOG_TRIVIAL(info) << boost::format("update filament %1%'s config to newest, different size %2%, name %3%, different_settings %4%")
3615 %filament_index%different_keys_set.size()%load_filaments_name[index] % different_settings[filament_index];
3616 for (const t_config_option_key &opt_key : config.keys()) {
3617 // Create a new option with default value for the key.
3618 // If the key is not in the parameter definition, or this ConfigBase is a static type and it does not support the parameter,
3619 // an exception is thrown if not ignore_nonexistent.
3620 const ConfigOption *source_opt = config.option(opt_key);
3621 if (source_opt == nullptr) {
3622 // The key was not found in the source config, therefore it will not be initialized!
3623 BOOST_LOG_TRIVIAL(error) << boost::format("can not find %1% from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index];
3626 }
3627
3628 if (!loaded && !different_keys_set.empty())
3629 {
3630 std::set<std::string>::iterator iter = different_keys_set.find(opt_key);
3631 if ( iter != different_keys_set.end()) {
3632 if (skip_modified_gcodes && (gcodes_key_set.find(opt_key) != gcodes_key_set.end()))
3633 {
3634 different_keys_set.erase(iter);
3635 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, filament %2%'s gcodes %3% modified, reset to default.")%__LINE__ %filament_index %opt_key;
3636 }
3637 else {
3638 //uptodate, diff keys, continue
3639 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, keep key %2%")%__LINE__ %opt_key;
3641 {
3642 ConfigOption *opt = m_print_config.option(opt_key);
3643 if (opt == nullptr) {
3644 // opt_key does not exist in this ConfigBase and it cannot be created, because it is not defined by this->def().
3645 // This is only possible if other is of DynamicConfig type.
3646 BOOST_LOG_TRIVIAL(error) << boost::format("can not find option %1% from config, but can find it in different settings")%opt_key;
3649 }
3650 ConfigOptionVectorBase* opt_vec_dst = static_cast<ConfigOptionVectorBase*>(opt);
3651 const ConfigOptionVectorBase* opt_vec_src = static_cast<const ConfigOptionVectorBase*>(source_opt);
3652 //set with index
3653 try {
3654 // A project with fewer filaments than are loaded: grow the
3655 // destination to the filament's slot first (set_with_restore_2 only restores).
3656 if (opt_vec_src->size() > 0 && opt_vec_dst->size() < size_t(old_start_indice[filament_index - 1] + old_variant_count))
3657 opt_vec_dst->resize(size_t(old_start_indice[filament_index - 1] + old_variant_count), opt_vec_src);
3658 // A kept key is padded like a refreshed one below rather than failing the run.
3659 opt_vec_dst->set_with_restore_2(opt_vec_src, new_variant_indice, old_start_indice[filament_index - 1], old_variant_count, keep_project);
3660 } catch (const std::exception &ex) { // Was an uncaught abort
3661 BOOST_LOG_TRIVIAL(error) << boost::format("filament %1%: option %2% could not be applied: %3%") % filament_index % opt_key % ex.what();
3662 boost::nowide::cerr << "filament " << filament_index << ": option " << opt_key << " could not be applied: " << ex.what() << std::endl;
3665 }
3666 }
3667
3668 continue;
3669 }
3670 }
3671 }
3672
3673 if (source_opt->is_scalar()) {
3674 if (opt_key == "compatible_printers_condition") {
3675 ConfigOption *opt = m_print_config.option("compatible_machine_expression_group", true);
3676 ConfigOptionStrings* opt_vec_dst = static_cast<ConfigOptionStrings*>(opt);
3677 if (opt_vec_dst->size() == 0)
3678 opt_vec_dst->resize(filament_count+2, new ConfigOptionString());
3679 opt_vec_dst->set_at(source_opt, filament_index, 0);
3680 }
3681 else if (opt_key == "compatible_prints_condition") {
3682 ConfigOption *opt = m_print_config.option("compatible_process_expression_group", true);
3683 ConfigOptionStrings* opt_vec_dst = static_cast<ConfigOptionStrings*>(opt);
3684 if (opt_vec_dst->size() == 0)
3685 opt_vec_dst->resize(filament_count, new ConfigOptionString());
3686 opt_vec_dst->set_at(source_opt, filament_index-1, 0);
3687 }
3688 else {
3689 //skip the scalar values
3690 BOOST_LOG_TRIVIAL(info) << boost::format("skip scalar option %1% from filament %2%: %3%")%opt_key%filament_index%load_filaments_name[index];;
3691 continue;
3692 }
3693 }
3694 else
3695 {
3696 if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "model_id" || opt_key == "dev_model_name" || opt_key == "filament_settings_id")
3697 continue;
3698 // rebuilt from every filament after this loop
3699 if (filament_dev_options.find(opt_key) != filament_dev_options.end())
3700 continue;
3701 ConfigOption *opt = m_print_config.option(opt_key, true);
3702 if (opt == nullptr) {
3703 // opt_key does not exist in this ConfigBase and it cannot be created, because it is not defined by this->def().
3704 // This is only possible if other is of DynamicConfig type.
3705 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];
3708 }
3709 ConfigOptionVectorBase* opt_vec_dst = static_cast<ConfigOptionVectorBase*>(opt);
3710 const ConfigOptionVectorBase* opt_vec_src = static_cast<const ConfigOptionVectorBase*>(source_opt);
3712 std::vector<int> temp_variant_indice;
3713 temp_variant_indice.resize(new_variant_count, -1);
3714 try {
3715 if (opt_vec_src->size() > 0 && opt_vec_dst->size() < size_t(old_start_indice[filament_index - 1] + old_variant_count)) // See above
3716 opt_vec_dst->resize(size_t(old_start_indice[filament_index - 1] + old_variant_count), opt_vec_src);
3717 opt_vec_dst->set_with_restore_2(opt_vec_src, temp_variant_indice, old_start_indice[filament_index - 1], old_variant_count, true);
3718 } catch (const std::exception &ex) { // Was an uncaught abort
3719 BOOST_LOG_TRIVIAL(error) << boost::format("filament %1%: option %2% could not be applied: %3%") % filament_index % opt_key % ex.what();
3720 boost::nowide::cerr << "filament " << filament_index << ": option " << opt_key << " could not be applied: " << ex.what() << std::endl;
3723 }
3724
3725 if (opt_key == "filament_extruder_variant")
3726 new_variant_counts[filament_index - 1] = opt_vec_src->size();
3727 }
3728 else {
3729 opt_vec_dst->set_at(opt_vec_src, filament_index - 1, 0);
3730 }
3731 }
3732 }
3733
3734 //ORCA: a per-variant option the loaded filament does not define keeps the values of the
3735 // variants the filament already had, and a variant new to it takes its first one's.
3736 const int old_start = old_start_indice[filament_index - 1];
3737 std::vector<int> kept_variant_indice = new_variant_indice;
3738 for (int &i : kept_variant_indice)
3739 if (i < 0)
3740 i = old_start;
3741 for (const std::string &opt_key : filament_options_with_variant) {
3742 if (config.has(opt_key))
3743 continue;
3744 auto *opt_vec_dst = dynamic_cast<ConfigOptionVectorBase *>(m_print_config.option(opt_key));
3745 if (opt_vec_dst == nullptr || opt_vec_dst->size() < size_t(old_start + old_variant_count))
3746 continue;
3747 // set_with_restore_2() pads its source in place
3748 std::unique_ptr<ConfigOption> old_values(opt_vec_dst->clone());
3749 opt_vec_dst->set_with_restore_2(static_cast<ConfigOptionVectorBase *>(old_values.get()), kept_variant_indice, old_start,
3750 old_variant_count, true);
3751 }
3752
3753 //update the old index
3754 if (old_variant_count != new_variant_count)
3755 {
3756 for (int i = index + 1; i < filament_count; i++ )
3757 {
3758 old_start_indice[i] += new_variant_count - old_variant_count;
3759 }
3760 }
3761
3762 if (!keep_project && diff_keys_size != different_keys_set.size()) {
3763 //changed
3764 different_keys.clear();
3765
3766 for (std::set<std::string>::iterator iter=different_keys_set.begin(); iter !=different_keys_set.end(); ++iter)
3767 different_keys.emplace_back(*iter);
3768 different_settings[filament_index] = Slic3r::escape_strings_cstyle(different_keys);
3769 BOOST_LOG_TRIVIAL(info) << boost::format("filament %1% new different key size %2%, different_settings %3%")%filament_index %different_keys_set.size() %different_settings[filament_index];
3770 }
3771 }
3772
3773 // The stored values cannot be told apart per filament, so they are kept as they are unless every slot has a config.
3774 std::vector<const DynamicPrintConfig *> filament_configs(filament_count, nullptr);
3775 for (size_t index = 0; index < load_filaments_config.size(); index++)
3776 if (load_filaments_index[index] >= 1 && load_filaments_index[index] <= filament_count)
3777 filament_configs[load_filaments_index[index] - 1] = &load_filaments_config[index];
3778 // Without refresh_unlisted and with no filament loaded from a file, only the keys the project lacks are
3779 // filled, with the option defaults for a filament that has no system preset. A loaded filament still
3780 // replaces them all.
3781 if (refresh_unlisted || std::find(load_filaments_refresh.begin(), load_filaments_refresh.end(), false) != load_filaments_refresh.end()) {
3782 if (std::find(filament_configs.begin(), filament_configs.end(), nullptr) == filament_configs.end())
3783 set_filament_dev_options(m_print_config, filament_configs);
3784 } else {
3785 const DynamicPrintConfig no_preset;
3786 std::replace(filament_configs.begin(), filament_configs.end(), static_cast<const DynamicPrintConfig *>(nullptr), &no_preset);
3787 DynamicPrintConfig dev_config;
3788 set_filament_dev_options(dev_config, filament_configs);
3789 for (const std::string &key : dev_config.keys())
3790 if (project_keys.count(key) == 0)
3791 m_print_config.set_key_value(key, dev_config.option(key)->clone());
3792 }
3793
3794 if (m_print_config.option<ConfigOptionStrings>("filament_extruder_variant")) {
3795 std::vector<int>& filament_self_indice = m_print_config.option<ConfigOptionInts>("filament_self_index", true)->values;
3796 int index_size = m_print_config.option<ConfigOptionStrings>("filament_extruder_variant")->size();
3797 filament_self_indice.resize(index_size, 1);
3798 int k = 0;
3799 for (size_t i = 0; i < filament_count; i++) {
3800 for (size_t j = 0; j < new_variant_counts[i]; j++) {
3801 filament_self_indice[k++] = i + 1;
3802 }
3803 }
3804 }
3805 }
3806
3807 //compute the flush volume
3808 ConfigOptionStrings *selected_filament_colors_option = m_extra_config.option<ConfigOptionStrings>("filament_colour");
3809 ConfigOptionStrings *project_filament_colors_option = m_print_config.option<ConfigOptionStrings>("filament_colour");
3810 if ((!project_filament_colors_option || (project_filament_colors_option->values.size() == 0)) && selected_filament_colors_option)
3811 {
3812 BOOST_LOG_TRIVIAL(info) << boost::format("initial project_filament_colors is null, create it due to filament_colour set in cli");
3813 project_filament_colors_option = m_print_config.option<ConfigOptionStrings>("filament_colour", true);
3814 std::vector<std::string>& project_filament_colors = project_filament_colors_option->values;
3815 project_filament_colors.resize(filament_count, "#FFFFFF");
3816 }
3817
3818 if (project_filament_colors_option &&
3819 (selected_filament_colors_option || !m_print_config.option<ConfigOptionFloats>("flush_volumes_matrix") || (current_extruder_count != new_extruder_count) || (new_nozzle_volume_type != current_nozzle_volume_type)))
3820 {
3821 std::vector<std::string> selected_filament_colors;
3822 if (selected_filament_colors_option) {
3823 selected_filament_colors = selected_filament_colors_option->values;
3824 //erase here
3825 m_extra_config.erase("filament_colour");
3826
3827 if (disable_wipe_tower_after_mapping) {
3828 std::set<std::string> filament_color_set;
3829 for (unsigned int color_index = 0; color_index <selected_filament_colors.size(); color_index++)
3830 {
3831 if (!selected_filament_colors[color_index].empty()) {
3832 filament_color_set.emplace(selected_filament_colors[color_index]);
3833 BOOST_LOG_TRIVIAL(info) << boost::format("add color, index %1%, value %2% to set")%color_index %selected_filament_colors[color_index];
3834 }
3835 else
3836 BOOST_LOG_TRIVIAL(info) << boost::format("skip empty color %1%")%color_index;
3837 }
3838
3839 if (filament_color_set.size() > 1) {
3840 disable_wipe_tower_after_mapping = false;
3841 BOOST_LOG_TRIVIAL(info) << boost::format("different filament colours, switch disable_wipe_tower_after_mapping back to false");
3842 }
3843 else {
3844 BOOST_LOG_TRIVIAL(warning) << boost::format("only %1% filament colour, finally set disable_wipe_tower_after_mapping to true")%filament_color_set.size();
3845 }
3846
3847 }
3848 }
3849
3850 std::vector<std::string> &project_filament_colors = project_filament_colors_option->values;
3851 size_t project_filament_count = project_filament_colors.size();
3852 BOOST_LOG_TRIVIAL(info) << boost::format("select filament color from cli, size %1%")%selected_filament_colors.size();
3853 BOOST_LOG_TRIVIAL(info) << boost::format("project filament colors size %1%")%project_filament_colors.size();
3854 if (project_filament_count > 0)
3855 {
3856 for ( size_t index = 0; index < project_filament_count; index++ )
3857 {
3858 BOOST_LOG_TRIVIAL(info) << boost::format("project filament %1% original color %2%")%index %project_filament_colors[index];
3859 if (selected_filament_colors.size() > index)
3860 {
3861 if (!selected_filament_colors[index].empty())
3862 {
3863 BOOST_LOG_TRIVIAL(info) << boost::format("changed to new color %1%")%selected_filament_colors[index];
3864 unsigned char ori_rgb_color[4] = {}, new_rgb_color[4] = {};
3865 Slic3r::GUI::BitmapCache::parse_color4(project_filament_colors[index], ori_rgb_color);
3866 Slic3r::GUI::BitmapCache::parse_color4(selected_filament_colors[index], new_rgb_color);
3867 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]))
3868 {
3869 BOOST_LOG_TRIVIAL(info) << boost::format("found color changes, need to regenerate thumbnail");
3870 filament_color_changed = true;
3871 }
3872 project_filament_colors[index] = selected_filament_colors[index];
3873 }
3874 }
3875 }
3876
3877 //computing
3878 ConfigOptionBools* filament_is_support = m_print_config.option<ConfigOptionBools>("filament_is_support", true);
3879
3880 const std::vector<int> &min_flush_volumes = Slic3r::GUI::get_min_flush_volumes(m_print_config, 0);
3881
3882 if (filament_is_support->size() != project_filament_count)
3883 {
3884 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;
3887 }
3888
3889 {
3890 std::ostringstream volumes_str;
3891 std::copy(min_flush_volumes.begin(), min_flush_volumes.end(), std::ostream_iterator<int>(volumes_str, ","));
3892 BOOST_LOG_TRIVIAL(info) << boost::format("extra_flush_volume: %1%") % volumes_str.str();
3893 BOOST_LOG_TRIVIAL(info) << boost::format("filament_is_support: %1%") % filament_is_support->serialize();
3894 BOOST_LOG_TRIVIAL(info) << boost::format("flush_volumes_matrix before computing: %1%") % m_print_config.option<ConfigOptionFloats>("flush_volumes_matrix", true)->serialize();
3895 }
3896
3897 std::vector<double> &flush_vol_matrix = m_print_config.option<ConfigOptionFloats>("flush_volumes_matrix", true)->values;
3898 flush_vol_matrix.resize(project_filament_count * project_filament_count * new_extruder_count, 0.f);
3899
3900 // set multiplier to 1?
3901 std::vector<double>& flush_multipliers = m_print_config.option<ConfigOptionFloats>("flush_multiplier", true)->values;
3902 flush_multipliers.resize(new_extruder_count, 1.f);
3903
3904 std::vector<int> nozzle_flush_dataset(new_extruder_count, 0);
3905 {
3906 std::vector<int> nozzle_flush_dataset_full = m_print_config.option<ConfigOptionIntsNullable>("nozzle_flush_dataset",true)->values;
3907 if (m_print_config.has("printer_extruder_variant"))
3908 nozzle_flush_dataset_full.resize(new_printer_variant_count, 0);
3909 else
3910 nozzle_flush_dataset_full.resize(1, 0);
3911
3912 std::vector<int> extruders;
3913 if (m_print_config.has("extruder_type"))
3914 extruders = m_print_config.option<ConfigOptionEnumsGeneric>("extruder_type")->values;
3915 else
3916 extruders.resize(1,int(ExtruderType::etDirectDrive));
3917
3918 std::vector<int> volume_types;
3919 if (m_print_config.has("nozzle_volume_type"))
3920 volume_types = m_print_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values; // get volume type from 3mf
3921 else
3922 volume_types.resize(1, int(NozzleVolumeType::nvtStandard));
3923
3924 if(m_extra_config.has("nozzle_volume_type")) // get volume type from input
3925 volume_types = m_extra_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values;
3926
3927 for (int eidx = 0; eidx < new_extruder_count; ++eidx) {
3928 int index = 0;
3929 if (m_print_config.has("printer_extruder_id") && m_print_config.has("printer_extruder_variant"))
3930 index = m_print_config.get_index_for_extruder(eidx + 1, "printer_extruder_id", ExtruderType(extruders[eidx]), NozzleVolumeType(volume_types[eidx]), "printer_extruder_variant");
3931 nozzle_flush_dataset[eidx] = nozzle_flush_dataset_full[index];
3932 }
3933 }
3934
3935 // A mixed slot never reaches a nozzle, so its row and column stay empty, as in the GUI.
3936 // Command line options are not merged into m_print_config yet, so they win here.
3937 const ConfigOptionBools *is_mixed_opt = m_extra_config.option<ConfigOptionBools>("filament_is_mixed");
3938 if (!is_mixed_opt)
3939 is_mixed_opt = m_print_config.option<ConfigOptionBools>("filament_is_mixed");
3940 auto is_mixed_slot = [is_mixed_opt](int idx) {
3941 return is_mixed_opt && idx < static_cast<int>(is_mixed_opt->values.size()) && is_mixed_opt->values[idx];
3942 };
3943
3944 for (size_t nozzle_id = 0; nozzle_id < new_extruder_count; ++nozzle_id) {
3945 std::vector<double> flush_vol_mtx = get_flush_volumes_matrix(flush_vol_matrix, nozzle_id, new_extruder_count);
3946 for (int from_idx = 0; from_idx < project_filament_count; from_idx++) {
3947 const std::string &from_color = project_filament_colors[from_idx];
3948 unsigned char from_rgb[4] = {};
3949 Slic3r::GUI::BitmapCache::parse_color4(from_color, from_rgb);
3950 bool is_from_support = filament_is_support->get_at(from_idx);
3951 for (int to_idx = 0; to_idx < project_filament_count; to_idx++) {
3952 bool is_to_support = filament_is_support->get_at(to_idx);
3953 if (from_idx == to_idx || is_mixed_slot(from_idx) || is_mixed_slot(to_idx)) {
3954 flush_vol_mtx[project_filament_count * from_idx + to_idx] = 0.f;
3955 } else {
3956 int flushing_volume = 0;
3957 if (is_to_support) {
3958 flushing_volume = Slic3r::g_flush_volume_to_support;
3959 } else {
3960 const std::string &to_color = project_filament_colors[to_idx];
3961 unsigned char to_rgb[4] = {};
3963
3964 Slic3r::FlushVolCalculator calculator(min_flush_volumes[from_idx], Slic3r::g_max_flush_volume,nozzle_flush_dataset[nozzle_id]);
3965 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]);
3966 if (is_from_support) { flushing_volume = std::max(Slic3r::g_min_flush_volume_from_support, flushing_volume); }
3967 }
3968
3969 flush_vol_mtx[project_filament_count * from_idx + to_idx] = flushing_volume;
3970 }
3971 }
3972 set_flush_volumes_matrix(flush_vol_matrix, flush_vol_mtx, nozzle_id, new_extruder_count);
3973 }
3974 }
3975 BOOST_LOG_TRIVIAL(info) << boost::format("flush_volumes_matrix after computed: %1%")%m_print_config.option<ConfigOptionFloats>("flush_volumes_matrix")->serialize();
3976 }
3977 else
3978 {
3979 BOOST_LOG_TRIVIAL(warning) << boost::format("filament colors count is 0 in projects");
3980 }
3981 }
3982 else
3983 {
3984 BOOST_LOG_TRIVIAL(warning) << boost::format("no filament colors found in projects");
3985 }
3986
3987 //BBS: set default to ptFFF
3990
3991 //BBS: merge these models into one
3992 BOOST_LOG_TRIVIAL(info) << "total " << m_models.size() << " models, "<<orients_requirement.size()<<" objects"<<std::endl;
3993 if (m_models.size() > 1)
3994 {
3995 BOOST_LOG_TRIVIAL(info) << "merge all the models into one\n";
3996 Model m;
3997 m.set_backup_path(m_models[0].get_backup_path());
3998 for (auto& model : m_models)
3999 for (ModelObject* o : model.objects)
4000 {
4001 ModelObject* new_object = m.add_object(*o);
4002 //BOOST_LOG_TRIVIAL(info) << "object "<<o->name <<", id :" << o->id().id << "\n";
4003 //orients_requirement.emplace(new_object->id().id, orients_requirement[o->id().id]);
4004 //orients_requirement.erase(o->id().id);
4005 }
4007 m_models.clear();
4008 m_models.emplace_back(std::move(m));
4009 }
4010
4011
4012 //update the object config due to extruder count change
4013 if ((machine_switch) && ((current_extruder_count != new_extruder_count) || (current_print_variant_count != new_printer_variant_count)))
4014 {
4015 //process the object params here
4016 size_t num_objects = m_models[0].objects.size();
4017 for (int i = 0; i < num_objects; ++i) {
4018 ModelObject* object = m_models[0].objects[i];
4019 DynamicPrintConfig object_config = object->config.get();
4020 if (!object_config.empty()) {
4021 /* if (current_extruder_count < new_extruder_count)
4022 object_config.update_values_from_single_to_multi_2(m_print_config, print_options_with_variant);
4023 else
4024 object_config.update_values_from_multi_to_single_2(print_options_with_variant);*/
4025 object_config.update_values_from_multi_to_multi_2(current_print_extruder_variants, new_printer_extruder_variants, m_print_config, print_options_with_variant);
4026 object->config.assign_config(std::move(object_config));
4027 }
4028 for (ModelVolume* v : object->volumes) {
4029 if (v->is_model_part() || v->is_modifier()) {
4030 DynamicPrintConfig volume_config = v->config.get();
4031 if (!volume_config.empty()) {
4032 /* if (current_extruder_count < new_extruder_count)
4033 volume_config.update_values_from_single_to_multi_2(m_print_config, print_options_with_variant);
4034 else
4035 volume_config.update_values_from_multi_to_single_2(print_options_with_variant);*/
4036 volume_config.update_values_from_multi_to_multi_2(current_print_extruder_variants, new_printer_extruder_variants, m_print_config, print_options_with_variant);
4037 v->config.assign_config(std::move(volume_config));
4038 }
4039 }
4040 }
4041
4042 for (auto &layer_config_it : object->layer_config_ranges) {
4043 ModelConfig& layer_model_config = layer_config_it.second;
4044 DynamicPrintConfig layer_config = layer_model_config.get();
4045 if (!layer_config.empty()) {
4046 /*if (current_extruder_count < new_extruder_count)
4047 layer_config.update_values_from_single_to_multi_2(m_print_config, print_options_with_variant);
4048 else
4049 layer_config.update_values_from_multi_to_single_2(print_options_with_variant);*/
4050 layer_config.update_values_from_multi_to_multi_2(current_print_extruder_variants, new_printer_extruder_variants, m_print_config, print_options_with_variant);
4051 layer_model_config.assign_config(std::move(layer_config));
4052 }
4053 }
4054 }
4055 }
4056
4057 //load custom gcodes into model if needed
4058 if ((custom_gcodes_map.size() > 0)&&(m_models.size() > 0))
4059 {
4060 m_models[0].plates_custom_gcodes = custom_gcodes_map;
4061 /*m_models[0].plates_custom_gcodes.clear();
4062
4063 for (auto& custom_gcode: custom_gcodes_map)
4064 {
4065 BOOST_LOG_TRIVIAL(info) << boost::format("insert custom_gocde %1% into plate %2%")%plate_id;
4066 m_models[0].plates_custom_gcodes.emplace(custom_gcode.first, custom_gcode.second);
4067 }*/
4068 }
4069
4070 if (skip_modified_gcodes)
4071 {
4072 std::map<int, CustomGCode::Info>::iterator gcodes_iter;
4073 for (gcodes_iter = m_models[0].plates_custom_gcodes.begin(); gcodes_iter != m_models[0].plates_custom_gcodes.end(); gcodes_iter++)
4074 {
4075 CustomGCode::Info &gcode_info = gcodes_iter->second;
4076 std::vector<CustomGCode::Item>::iterator item_iter = gcode_info.gcodes.begin();
4077
4078 while ( item_iter != gcode_info.gcodes.end() )
4079 {
4080 if (item_iter->type == CustomGCode::Custom) {
4081 BOOST_LOG_TRIVIAL(warning) << boost::format("skip_modified_gcodes: remove custom gcodes %1% in plate %2%") %item_iter->extra %gcodes_iter->first;
4082 item_iter = gcode_info.gcodes.erase(item_iter);
4083 }
4084 else
4085 item_iter++;
4086 }
4087 }
4088 }
4089
4090 //ORCA: settings passed on the command line (--sparse-infill-density 25% ...) override the loaded
4091 // presets right here, so they belong in different_settings_to_system just as a preset
4092 // override does. Without them re-opening the exported project in the GUI shows nothing
4093 // modified and reverts those values to the system presets'.
4094 //
4095 // The keys come from m_config, not m_extra_config: read_cli() puts only what the user typed
4096 // into m_config (setup() adds nothing but CLI-own defaults), whereas the CLI writes its own
4097 // values into m_extra_config. Only keys whose value the override actually changed are
4098 // recorded -- a typed value equal to the loaded one modifies nothing -- and each lands in
4099 // the column(s) whose preset type owns it: [0] process, [1..n-2] filaments, [n-1] printer.
4100 //
4101 // "Changed" is judged the way the value is read: a list is compared entry by entry with a
4102 // missing entry read as the first, as get_at() does -- so --nozzle-temperature 245 against
4103 // 245,245,245 is no change, although the two serialize differently.
4104 //
4105 // A key the loaded config does not carry at all is always recorded, even if the typed value
4106 // equals the built-in default. On reopen the GUI restores an unlisted key from the SYSTEM
4107 // preset, which need not match that default: a 3MF written before an option existed leaves
4108 // it absent here, and --sparse-infill-density 20% (the default) against a Prusa system 15%
4109 // would otherwise go unrecorded and be reverted. Over-recording is cosmetic; under-recording
4110 // loses the value.
4111 std::map<std::string, std::unique_ptr<ConfigOption>> cli_override_before;
4112 for (const std::string &key : m_config.keys()) {
4113 if (!m_extra_config.has(key))
4114 continue;
4115 const ConfigOption *loaded = m_print_config.option(key);
4116 cli_override_before[key].reset(loaded != nullptr ? loaded->clone() : nullptr); // null: always recorded
4117 }
4118
4119 // Apply command line options to a more specific DynamicPrintConfig which provides normalize()
4120 // (command line options override --load files)
4121 m_print_config.apply(m_extra_config, true);
4122
4123 if (!cli_override_before.empty()) {
4124 std::vector<std::string> &columns = m_print_config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
4125 auto owned_by = [](const std::vector<std::string> &options, const std::string &key) {
4126 return std::find(options.begin(), options.end(), key) != options.end();
4127 };
4128 auto add_to_column = [&columns](size_t index, const std::string &key) {
4129 std::vector<std::string> keys;
4130 Slic3r::unescape_strings_cstyle(columns[index], keys);
4131 if (std::find(keys.begin(), keys.end(), key) == keys.end()) {
4132 keys.push_back(key);
4133 columns[index] = Slic3r::escape_strings_cstyle(keys);
4134 }
4135 };
4136 auto same_value = [](const ConfigOption *a, const ConfigOption *b) {
4137 if (a == nullptr || b == nullptr)
4138 return false;
4139 const auto *va = dynamic_cast<const ConfigOptionVectorBase *>(a);
4140 const auto *vb = dynamic_cast<const ConfigOptionVectorBase *>(b);
4141 if (va == nullptr || vb == nullptr)
4142 return va == vb && a->serialize() == b->serialize();
4143 const std::vector<std::string> ea = va->vserialize(), eb = vb->vserialize();
4144 if (ea.empty() || eb.empty())
4145 return ea.empty() && eb.empty();
4146 for (size_t i = 0; i < std::max(ea.size(), eb.size()); ++i)
4147 if (ea[i < ea.size() ? i : 0] != eb[i < eb.size() ? i : 0])
4148 return false;
4149 return true;
4150 };
4151 //ORCA: always true after the resize to filament_count + 2 above, and nothing in between can
4152 // shrink the column vector -- different_settings_to_system is not a CLI option. Kept as
4153 // a check rather than an assert: release builds compile asserts out, so an assert would
4154 // protect nothing, while a build with _GLIBCXX_ASSERTIONS would abort on columns[0].
4155 if (columns.size() >= 2) {
4156 for (const auto &[key, before] : cli_override_before) {
4157 if (same_value(before.get(), m_print_config.option(key)))
4158 continue;
4159 bool recorded = false;
4160 if (owned_by(Preset::print_options(), key)) {
4161 add_to_column(0, key);
4162 recorded = true;
4163 }
4164 if (owned_by(Preset::filament_options(), key)) {
4165 for (size_t i = 1; i + 1 < columns.size(); ++i)
4166 add_to_column(i, key);
4167 recorded = true;
4168 }
4169 if (owned_by(Preset::printer_options(), key)) {
4170 add_to_column(columns.size() - 1, key);
4171 recorded = true;
4172 }
4173 if (recorded)
4174 BOOST_LOG_TRIVIAL(info) << boost::format("CLI: override %1% recorded in different_settings_to_system") % key;
4175 }
4176 }
4177 }
4178 // Normalizing after importing the 3MFs / AMFs
4179 m_print_config.normalize_fdm();
4180
4181 // A mixed slot is virtual but still needs a filament entry of its own. Without one, feature
4182 // filament ids aimed at it fall outside the filament count, are reset to the first filament
4183 // and the model silently prints in a single colour.
4184 if (const auto *is_mixed_opt = m_print_config.option<ConfigOptionBools>("filament_is_mixed")) {
4185 const auto &is_mixed = is_mixed_opt->values;
4186 for (size_t slot = static_cast<size_t>(std::max(filament_count, 0)); slot < is_mixed.size(); ++slot) {
4187 if (!is_mixed[slot])
4188 continue;
4189 BOOST_LOG_TRIVIAL(error) << boost::format("mixed filament slot %1% has no filament of its own, only %2% filaments are loaded; "
4190 "load one filament per slot, including each mixed one")
4191 % (slot + 1) % filament_count;
4194 }
4195 }
4196 if (filament_count > 0)
4197 resize_mixed_filament_metadata(m_print_config, size_t(filament_count), size_t(filament_count));
4198
4199 m_print_config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value = printer_technology;
4200
4201 bool has_wipe_tower_position = m_print_config.option<ConfigOptionFloats>("wipe_tower_x") && m_print_config.option<ConfigOptionFloats>("wipe_tower_y");
4202 // Initialize full print configs for both the FFF and SLA technologies.
4203 FullPrintConfig fff_print_config;
4204 //SLAFullPrintConfig sla_print_config;
4205
4206 // Synchronize the default parameters and the ones received on the command line.
4207 if (printer_technology == ptFFF) {
4208 fff_print_config.apply(m_print_config, true);
4209 m_print_config.apply(fff_print_config, true);
4210 //ORCA: an option no preset or project defines has just come in as its single default value, and a
4211 // command line override may hold one value per filament.
4213 } else {
4214 boost::nowide::cerr << "invalid printer_technology " << std::endl;
4217 /*assert(printer_technology == ptSLA);
4218 sla_print_config.filename_format.value = "[input_filename_base].sl1";
4219
4220 // The default bed shape should reflect the default display parameters
4221 // and not the fff defaults.
4222 double w = sla_print_config.display_width.getFloat();
4223 double h = sla_print_config.display_height.getFloat();
4224 sla_print_config.printable_area.values = { Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) };
4225
4226 sla_print_config.apply(m_print_config, true);
4227 m_print_config.apply(sla_print_config, true);*/
4228 }
4229
4230 std::map<std::string, std::string> validity = m_print_config.validate(true);
4231 if (!validity.empty()) {
4232 boost::nowide::cerr << "Param values in 3mf/config error: "<< std::endl;
4233 for (std::map<std::string, std::string>::iterator it=validity.begin(); it!=validity.end(); ++it)
4234 boost::nowide::cerr << it->first <<": "<< it->second << std::endl;
4237 }
4238
4239 ConfigOptionBool* enable_wrapping_detection_option = m_print_config.option<ConfigOptionBool>("enable_wrapping_detection", true);
4240 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, remove_wrapping_detect %2%, old value %3%")%__LINE__ %remove_wrapping_detect %enable_wrapping_detection_option->value;
4241 if (is_bbl_3mf && remove_wrapping_detect) {
4242 enable_wrapping_detection_option->value = false;
4243 }
4244 enable_wrapping_detect = enable_wrapping_detection_option->value;
4245 Pointfs current_wrapping_exclude_area = m_print_config.opt<ConfigOptionPoints>("wrapping_exclude_area", true)->values;
4246 if (disable_wipe_tower_after_mapping && enable_wrapping_detect && !current_wrapping_exclude_area.empty())
4247 {
4248 disable_wipe_tower_after_mapping = false;
4249 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, set disable_wipe_tower_after_mapping back to false due to wrapping detect")%__LINE__;
4250 }
4251
4252 // Belt printers never get the classic wipe tower (see Print::has_wipe_tower()), so reserve no space for it.
4253 const ConfigOptionBool* belt_printer_opt = m_print_config.option<ConfigOptionBool>("belt_printer");
4254 const bool is_belt_printer = belt_printer_opt && belt_printer_opt->value;
4255
4256 auto timelapse_type_opt = m_print_config.option("timelapse_type");
4257 bool is_smooth_timelapse = false;
4258 if (enable_timelapse && timelapse_type_opt && (timelapse_type_opt->getInt() == TimelapseType::tlSmooth))
4259 is_smooth_timelapse = true;
4260 // A mixed filament swaps between its components every layer, so it needs the tower even when
4261 // every loaded preset is the same.
4262 if (disable_wipe_tower_after_mapping) {
4263 if (const auto *is_mixed_opt = m_print_config.option<ConfigOptionBools>("filament_is_mixed");
4264 is_mixed_opt && has_any_mixed_filament(is_mixed_opt->values)) {
4265 disable_wipe_tower_after_mapping = false;
4266 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, set disable_wipe_tower_after_mapping back to false due to a mixed filament")%__LINE__;
4267 }
4268 }
4269 if (disable_wipe_tower_after_mapping) {
4270 if (is_smooth_timelapse)
4271 {
4272 disable_wipe_tower_after_mapping = false;
4273 BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: disable_wipe_tower_after_mapping: set back to false due to smooth timelapse!")%__LINE__;
4274 }
4275 else {
4276 ConfigOptionBool* enable_wipe_op = m_print_config.option<ConfigOptionBool>("enable_prime_tower", true);
4277 enable_wipe_op->value = false;
4278 BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: disable_wipe_tower_after_mapping: disable prime tower for only one filament!")%__LINE__;
4279
4280 std::string diff_settings = different_settings[0];
4281
4282 if (diff_settings.empty())
4283 different_settings[0] = "enable_prime_tower";
4284 else {
4285 std::vector<std::string> different_keys;
4286 Slic3r::unescape_strings_cstyle(diff_settings, different_keys);
4287 bool need_insert = true;
4288 for (int index = 0; index < different_keys.size(); index++) {
4289 if (different_keys[index] == "enable_prime_tower") {
4290 need_insert = false;
4291 break;
4292 }
4293 }
4294 if (need_insert)
4295 different_settings[0] = diff_settings + ";enable_prime_tower";
4296 }
4297 }
4298 }
4299
4300 //BBS: partplate list
4301 Slic3r::GUI::PartPlateList partplate_list(NULL, m_models.data(), printer_technology);
4302 //use Pointfs insteadof Points
4303 Pointfs current_printable_area = m_print_config.opt<ConfigOptionPoints>("printable_area")->values;
4305 std::vector<Pointfs> current_extruder_areas;
4306 //update part plate's size
4307 double print_height = m_print_config.opt_float("printable_height");
4308 std::vector<double> current_extruder_print_heights;
4309 double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid");
4310 double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod");
4311 double clearance_radius = m_print_config.opt_float("extruder_clearance_radius");
4312 double nozzle_height = m_print_config.opt_float("nozzle_height");
4313 Vec2d align_center = m_print_config.option<ConfigOptionPoint>("best_object_pos")->value;
4314 int shared_printable_width = 0, shared_printable_depth = 0, shared_printable_height = 0, shared_center_x = 0, shared_center_y = 0;
4315 //double plate_stride;
4316 std::string bed_texture;
4317
4318 if (m_print_config.opt<ConfigOptionPointsGroups>("extruder_printable_area")) {
4319 current_extruder_areas = m_print_config.opt<ConfigOptionPointsGroups>("extruder_printable_area")->values;
4320 }
4321 if (m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")) {
4322 current_extruder_print_heights = m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")->values;
4323 }
4324 {
4325 BoundingBoxf current_printable_bbox(current_printable_area);
4326 current_printable_width = static_cast<int>(current_printable_bbox.size().x());
4327 current_printable_depth = static_cast<int>(current_printable_bbox.size().y());
4328 }
4329 current_printable_height = print_height;
4330 if (old_printable_width == 0)
4331 old_printable_width = current_printable_width;
4332 if (old_printable_depth == 0)
4333 old_printable_depth = current_printable_depth;
4334 if (old_printable_height == 0)
4335 old_printable_height = current_printable_height;
4336 if ((current_extruder_areas.size() > 0) && (current_extruder_areas.size() == current_extruder_print_heights.size())) {
4337 BoundingBox current_bbox({0, 0}, {current_printable_width, current_printable_depth});
4338 int temp_height = current_printable_height;
4339
4340 for (unsigned int temp_index = 0; temp_index < current_extruder_areas.size(); temp_index++)
4341 {
4342 BoundingBox temp_bbox;
4343 Pointfs& temp_extruder_shape = current_extruder_areas[temp_index];
4344 for (unsigned int temp_index2 = 0; temp_index2 < temp_extruder_shape.size(); temp_index2++)
4345 temp_bbox.merge({temp_extruder_shape[temp_index2].x(), temp_extruder_shape[temp_index2].y()});
4346
4347 if (current_bbox.min.x() < temp_bbox.min.x())
4348 current_bbox.min.x() = temp_bbox.min.x();
4349 if (current_bbox.min.y() < temp_bbox.min.y())
4350 current_bbox.min.y() = temp_bbox.min.y();
4351 if (current_bbox.max.x() > temp_bbox.max.x())
4352 current_bbox.max.x() = temp_bbox.max.x();
4353 if (current_bbox.max.y() > temp_bbox.max.y())
4354 current_bbox.max.y() = temp_bbox.max.y();
4355 if ((int)(current_extruder_print_heights[temp_index]) < temp_height)
4356 temp_height = current_extruder_print_heights[temp_index];
4357 }
4358 shared_printable_width = current_bbox.size().x();
4359 shared_printable_depth = current_bbox.size().y();
4360 shared_printable_height = temp_height;
4361 shared_center_x = current_bbox.center().x();
4362 shared_center_y = current_bbox.center().y();
4363
4364 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: shared_printable_size {%2%, %3%, %4%}, shared_center {%5%, %6%}")
4365 %__LINE__ %shared_printable_width %shared_printable_depth %shared_printable_height %shared_center_x %shared_center_y;
4366 }
4367 if (is_bbl_3mf && (old_printable_width > 0) && (old_printable_depth > 0) && (old_printable_height > 0))
4368 {
4369 //check the printable size logic
4370 //if ((old_printable_width > current_printable_width) || (old_printable_depth > current_printable_depth) || (old_printable_height > current_printable_height))
4371 if ((old_printable_width > current_printable_width) || (old_printable_depth > current_printable_depth) || (old_printable_height > current_printable_height))
4372 {
4373 BOOST_LOG_TRIVIAL(error) << boost::format("old printable size {%1%, %2%, %3%} is larger than new printable size {%4%, %5%, %6%}, the object size should be limited")
4374 %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height;
4375 /*record_exit_reson(outfile_dir, CLI_PRINTABLE_SIZE_REDUCED, 0, cli_errors[CLI_PRINTABLE_SIZE_REDUCED], sliced_info);
4376 flush_and_exit(CLI_PRINTABLE_SIZE_REDUCED);*/
4377 shrink_to_new_bed = 2;
4378 }
4379 else if ((old_printable_width < current_printable_width) || (old_printable_depth < current_printable_depth))
4380 {
4381 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")
4382 %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height;
4383 shrink_to_new_bed = 1;
4384 }
4385 else {
4386 if ((current_exclude_area.size() > 0)&&(current_exclude_area != old_exclude_area)) {
4387 BOOST_LOG_TRIVIAL(info) << boost::format("old printable size {%1%, %2%, %3%}, new printable size {%4%, %5%, %6%}, exclude_area different, need to shrink")
4388 %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height;
4389 shrink_to_new_bed = 2;
4390 }
4391 else {
4392 BOOST_LOG_TRIVIAL(info) << boost::format("old printable size {%1%, %2%, %3%}, new printable size {%4%, %5%, %6%}, extract the same, no need shrink")
4393 %old_printable_width %old_printable_depth %old_printable_height %current_printable_width %current_printable_depth %current_printable_height;
4394 }
4395 }
4396 }
4397
4398 if (m_models.size() > 0)
4399 {
4400 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;
4401 if (translate_old) {
4402 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;
4403 partplate_list.reset_size(old_printable_width + bed3d_ax3s_default_tip_radius, old_printable_depth + bed3d_ax3s_default_tip_radius, old_printable_height, false);
4404 }
4405 else {
4406 partplate_list.reset_size(old_printable_width, old_printable_depth, old_printable_height, false);
4407 }
4408 partplate_list.set_shapes(make_counter_clockwise(current_printable_area), current_exclude_area, current_wrapping_exclude_area, current_extruder_areas, current_extruder_print_heights, bed_texture,
4409 height_to_lid, height_to_rod);
4410 //plate_stride = partplate_list.plate_stride_x();
4411 }
4412
4413 //process some old params
4414 if (is_bbl_3mf && keep_old_params) {
4415 std::vector<std::string> different_keys;
4416 Slic3r::unescape_strings_cstyle(different_settings[0], different_keys);
4417 std::set<std::string> different_key_set(different_keys.begin(), different_keys.end());
4418 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, process old params for support and wipe tower")%__LINE__;
4419
4420 //wipe tower params process
4421 ConfigOptionEnum<WipeTowerWallType> *prime_tower_rib_wall_option = m_print_config.option<ConfigOptionEnum<WipeTowerWallType>>("wipe_tower_wall_type", true);
4422 prime_tower_rib_wall_option->value = WipeTowerWallType::wtwRectangle;
4423
4424 ConfigOptionPercent *prime_tower_infill_gap_option = m_print_config.option<ConfigOptionPercent>("prime_tower_infill_gap", true);
4425 prime_tower_infill_gap_option->value = 100;
4426
4427 ConfigOptionInts *filament_adhesiveness_category_option = m_print_config.option<ConfigOptionInts>("filament_adhesiveness_category", true);
4428 std::vector<int>& filament_adhesiveness_category_values = filament_adhesiveness_category_option->values;
4429 filament_adhesiveness_category_values.resize(filament_count);
4430 for (int index = 0; index < filament_count; index++)
4431 filament_adhesiveness_category_values[index] = 100;
4432
4433 //support params process
4434 ConfigOptionBool *enable_support_option = m_print_config.option<ConfigOptionBool>("enable_support", true);
4435 ConfigOptionEnum<SupportType>* support_type_option = m_print_config.option<ConfigOptionEnum<SupportType>>("support_type", true);
4436 ConfigOptionEnum<SupportMaterialStyle>* support_style_option = m_print_config.option<ConfigOptionEnum<SupportMaterialStyle>>("support_style", true);
4437 ConfigOptionFloat *support_top_z_distance_option = m_print_config.option<ConfigOptionFloat>("support_top_z_distance", true);
4438 if (support_type_option->value == stTreeAuto)
4439 {
4440 if (different_key_set.find("support_type") == different_key_set.end())
4441 support_type_option->value = stNormalAuto;
4442 }
4443
4444 //traverse each object one by one
4445 size_t num_objects = m_models[0].objects.size();
4446 for (int i = 0; i < num_objects; ++i) {
4447 ModelObject* object = m_models[0].objects[i];
4448 DynamicPrintConfig object_config = object->config.get();
4449 ConfigOptionBool *obj_enable_support_option = object_config.option<ConfigOptionBool>("enable_support");
4450 if (enable_support_option->value || (obj_enable_support_option && obj_enable_support_option->value)) {
4451 ConfigOptionEnum<SupportType>* obj_support_type_option = object_config.option<ConfigOptionEnum<SupportType>>("support_type");
4452 ConfigOptionEnum<SupportMaterialStyle>* obj_support_style_option = object_config.option<ConfigOptionEnum<SupportMaterialStyle>>("support_style");
4453 ConfigOptionFloat *obj_support_top_z_distance_option = object_config.option<ConfigOptionFloat>("support_top_z_distance");
4454
4455 SupportType obj_support_type = obj_support_type_option? obj_support_type_option->value: support_type_option->value;
4456 SupportMaterialStyle obj_support_style = obj_support_style_option? obj_support_style_option->value: support_style_option->value;
4457 if ((obj_support_type == stTreeAuto) && (obj_support_style == smsDefault ))
4458 {
4459 float support_top_z_distance = obj_support_top_z_distance_option? obj_support_top_z_distance_option->value: support_top_z_distance_option->value;
4460 if (!object->has_custom_layering() && (support_top_z_distance == 0)) {
4461 obj_support_style_option = object_config.option<ConfigOptionEnum<SupportMaterialStyle>>("support_style", true);
4462 obj_support_style_option->value = smsTreeOrganic;
4463 }
4464 }
4465 }
4466 }
4467
4468 //travel_acceleration
4469 ConfigOptionFloatsNullable *travel_acceleration_option = m_print_config.option<ConfigOptionFloatsNullable>("travel_acceleration", true);
4470 ConfigOptionFloatsNullable *default_acceleration_option = m_print_config.option<ConfigOptionFloatsNullable>("default_acceleration");
4471 travel_acceleration_option->values = default_acceleration_option->values;
4472
4473 ConfigOptionFloatsNullable *initial_layer_travel_acceleration_option = m_print_config.option<ConfigOptionFloatsNullable>("initial_layer_travel_acceleration", true);
4474 ConfigOptionFloatsNullable *initial_layer_acceleration_option = m_print_config.option<ConfigOptionFloatsNullable>("initial_layer_acceleration");
4475 initial_layer_travel_acceleration_option->values = initial_layer_acceleration_option->values;
4476 }
4477
4478 auto get_print_sequence = [](Slic3r::GUI::PartPlate* plate, DynamicPrintConfig& print_config, bool &is_seq_print) {
4479 PrintSequence curr_plate_seq = plate->get_print_seq();
4480 if (curr_plate_seq == PrintSequence::ByDefault) {
4481 auto seq_print = print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence");
4482 if (seq_print && (seq_print->value == PrintSequence::ByObject)) {
4483 BOOST_LOG_TRIVIAL(info) << boost::format("plate print by object, set from global");
4484 is_seq_print = true;
4485 }
4486 }
4487 else if (curr_plate_seq == PrintSequence::ByObject) {
4488 BOOST_LOG_TRIVIAL(info) << boost::format("plate print by object, set from plate self");
4489 is_seq_print = true;
4490 }
4491 };
4492
4493 auto check_plate_wipe_tower = [get_print_sequence, is_smooth_timelapse, is_belt_printer](Slic3r::GUI::PartPlate* plate, int plate_index, DynamicPrintConfig& print_config, plate_obj_size_info_t &plate_obj_size_info) {
4494 plate_obj_size_info.obj_bbox= plate->get_objects_bounding_box();
4495 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, object bbox: min {%2%, %3%, %4%} - max {%5%, %6%, %7%}")
4496 %(plate_index+1) %plate_obj_size_info.obj_bbox.min.x() % plate_obj_size_info.obj_bbox.min.y() % plate_obj_size_info.obj_bbox.min.z() %plate_obj_size_info.obj_bbox.max.x() % plate_obj_size_info.obj_bbox.max.y() % plate_obj_size_info.obj_bbox.max.z();
4497 if (is_belt_printer || !print_config.has("wipe_tower_x")) {
4498 plate_obj_size_info.has_wipe_tower = false;
4499 BOOST_LOG_TRIVIAL(info) << boost::format("can not found wipe_tower_x in config, set to no wipe tower");
4500 return;
4501 }
4502
4503 int valid_count = plate->printable_instance_size();
4504 if (valid_count <= 0){
4505 plate_obj_size_info.has_wipe_tower = false;
4506 BOOST_LOG_TRIVIAL(info) << boost::format("no printable object found, set to no wipe tower");
4507 return;
4508 }
4509
4510 bool is_sequence = false;
4511 get_print_sequence(plate, print_config, is_sequence);
4512 if (is_sequence && valid_count > 1) {
4513 plate_obj_size_info.has_wipe_tower = false;
4514 BOOST_LOG_TRIVIAL(info) << boost::format("sequence print, valid_count=%1%, set to no wipe tower")%valid_count;
4515 return;
4516 }
4517
4518 std::vector<int> extruders = plate->get_extruders_under_cli(true, print_config);
4519 unsigned int filaments_cnt = extruders.size();
4520 if ((filaments_cnt <= 1) && !is_smooth_timelapse){
4521 plate_obj_size_info.has_wipe_tower = false;
4522 BOOST_LOG_TRIVIAL(info) << boost::format("filaments_cnt=%1%, set to no wipe tower")%filaments_cnt;
4523 return;
4524 }
4525
4526 ConfigOptionFloats *wipe_x_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_x"));
4527 ConfigOptionFloats *wipe_y_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_y"));
4528
4529 // get_at() silently clamps an out-of-range index to entry 0 - make the reuse visible
4530 if (static_cast<size_t>(plate_index) >= wipe_x_option->values.size() || static_cast<size_t>(plate_index) >= wipe_y_option->values.size()) {
4531 BOOST_LOG_TRIVIAL(warning) << boost::format("plate %1%: wipe_tower_x/y only has %2%/%3% entries, reusing entry 0's position")
4532 %(plate_index+1) %wipe_x_option->values.size() %wipe_y_option->values.size();
4533 }
4534 plate_obj_size_info.wipe_x = wipe_x_option->get_at(plate_index);
4535 plate_obj_size_info.wipe_y = wipe_y_option->get_at(plate_index);
4536
4537 // Body and brim from one estimate: resolving an auto (-1) brim against a different
4538 // height would size the two halves of the same tower from two different objects.
4539 const WipeTowerFootprint footprint = plate->estimate_wipe_tower_footprint(print_config, filaments_cnt);
4540 float brim_width = float(footprint.brim_width);
4541
4542 plate_obj_size_info.wipe_width = footprint.width;
4543 plate_obj_size_info.wipe_depth = footprint.depth;
4544
4545 Vec3d origin = plate->get_origin();
4546 Vec3d start(origin(0) + plate_obj_size_info.wipe_x - brim_width, origin(1) + plate_obj_size_info.wipe_y, 0.f);
4547 plate_obj_size_info.obj_bbox.merge(start);
4548 Vec3d end(origin(0) + plate_obj_size_info.wipe_x + plate_obj_size_info.wipe_width + brim_width, origin(1) + plate_obj_size_info.wipe_y + plate_obj_size_info.wipe_depth, 0.f);
4549 plate_obj_size_info.obj_bbox.merge(end);
4550 plate_obj_size_info.has_wipe_tower = true;
4551 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, has wipe tower, wipe bbox: min {%2%, %3%, %4%} - max {%5%, %6%, %7%}")
4552 %(plate_index+1) %start.x() % start.y() % start.z() %end.x() % end.y() % end.z();
4553 };
4554
4555 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, shared_center_x, shared_center_y, current_exclude_area, &plate_obj_size_infos] (Slic3r::GUI::PartPlateList& plate_list, DynamicPrintConfig& print_config) {
4556 //BBS: translate old 3mf to correct positions
4557 if (translate_old) {
4558 //translate the objects
4559 int plate_count = plate_list.get_plate_count();
4560 for (int index = 1; index < plate_count; index ++) {
4561 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)plate_list.get_plate(index);
4562
4563 Vec3d cur_origin = cur_plate->get_origin();
4564 Vec3d new_origin = plate_list.compute_origin_using_new_size(index, old_printable_width, old_printable_depth);
4565
4566 cur_plate->translate_all_instance(new_origin - cur_origin);
4567 }
4568 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;
4569 plate_list.reset_size(old_printable_width, old_printable_depth, old_printable_height, true, true);
4570 }
4571
4572 if (shrink_to_new_bed > 0)
4573 {
4574 int plate_count = plate_list.get_plate_count();
4575 ConfigOptionFloats *wipe_x_option = nullptr, *wipe_y_option = nullptr;
4576 Vec3d wipe_offset;
4577 if (print_config.has("wipe_tower_x")) {
4578 wipe_x_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_x"));
4579 wipe_y_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_y"));
4580 }
4581 double exclude_width = 0.f, exclude_depth = 0.f;
4582
4583 if (current_exclude_area.size() >= 4) {
4584 exclude_width = current_exclude_area[2].x() - current_exclude_area[0].x();
4585 exclude_depth = current_exclude_area[2].y() - current_exclude_area[0].y();
4586 }
4587 for (int index = 0; index < plate_count; index ++) {
4588 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)plate_list.get_plate(index);
4589 Vec3d cur_origin = cur_plate->get_origin();
4590 Vec3d new_origin = plate_list.compute_origin_using_new_size(index, current_printable_width, current_printable_depth);
4591 Vec3d offset;
4592
4593
4594 if (shrink_to_new_bed == 1) {
4595 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};
4596 Vec3d cur_center = cur_origin + cur_center_offset;
4597 Vec3d new_center = new_origin + new_center_offset;
4598
4599 offset = new_center - cur_center;
4600
4601 if (index == 0)
4602 wipe_offset = offset;
4603
4604 BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed 1, plate %1%, cur_origin: {%2%, %3%}, new_origin: {%4%, %5%}, cur_center {%6%, %7%} new_center {%8%, %9%}")
4605 %(index+1) %cur_origin(0) %cur_origin(1) %new_origin(0) %new_origin(1) %cur_center(0) %cur_center(1) %new_center(0) %new_center(1);
4606 }
4607 else {
4608 //center the object
4609 Vec3d new_center_offset { ((double)current_printable_width + exclude_width)/2, ((double)current_printable_depth + exclude_depth)/2, 0};
4610 BoundingBoxf3& bbox = plate_obj_size_infos[index].obj_bbox;
4611 Vec3d size = bbox.size();
4612 if (shared_center_x != 0)
4613 new_center_offset(0) = (double)shared_center_x;
4614 else if (exclude_width > 0) {
4615 if (size.x() > (current_printable_width - exclude_width))
4616 new_center_offset(0) = ((double)current_printable_width)/2;
4617 }
4618 if (shared_center_y != 0)
4619 new_center_offset(1) = (double)shared_center_y;
4620 else if (exclude_depth > 0) {
4621 if (size.y() > (current_printable_depth - exclude_depth))
4622 new_center_offset(1) = ((double)current_printable_depth)/2;
4623 }
4624 Vec3d new_center = new_origin + new_center_offset;
4625
4626 offset = new_center - bbox.center();
4627
4628 wipe_offset = offset + cur_origin - new_origin;
4629 BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed 2, plate %1%, new_origin: {%2%, %3%}, new_center: {%4%, %5%}, obj bbox(including wipe tower) min {%6%, %7%} max {%8%, %9%}")
4630 %(index+1) %new_origin(0) %new_origin(1) %new_center(0) %new_center(1) %bbox.min(0) %bbox.min(1) %bbox.max(0) %bbox.max(1);
4631 }
4632 offset(2) = 0.f;
4633 BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed %1%, plate %2% translate offset: {%3%, %4%} wipe_offset {%5%, %6%}") %shrink_to_new_bed %(index+1) %offset[0] %offset[1] %wipe_offset[0] %wipe_offset[1];
4634 cur_plate->translate_all_instance(offset);
4635
4636 if (wipe_x_option) {
4637 BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed %4%, plate %1%: wipe tower src: {%2%, %3%}")%(index+1) %wipe_x_option->get_at(index) %wipe_y_option->get_at(index)%shrink_to_new_bed;
4638 ConfigOptionFloat wipe_tower_x(wipe_x_option->get_at(index) + wipe_offset(0));
4639 ConfigOptionFloat wipe_tower_y(wipe_y_option->get_at(index) + wipe_offset(1));
4640
4641 wipe_x_option->set_at(&wipe_tower_x, index, 0);
4642 wipe_y_option->set_at(&wipe_tower_y, index, 0);
4643 BOOST_LOG_TRIVIAL(info) << boost::format("shrink_to_new_bed %4%, plate %1% wipe tower changes to: {%2%, %3%}")%(index+1) %wipe_x_option->get_at(index) %wipe_y_option->get_at(index) %shrink_to_new_bed;
4644 }
4645
4646
4647 }
4648 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;
4649 plate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true);
4650 }
4651 };
4652 if (plate_data_src.size() > 0)
4653 {
4654 partplate_list.load_from_3mf_structure(plate_data_src);
4655
4656 int plate_count = partplate_list.get_plate_count();
4657 plate_obj_size_infos.resize(plate_count, plate_obj_size_info_t());
4658 for (int index = 0; index < plate_count; index ++) {
4659 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(index);
4660
4661 check_plate_wipe_tower(cur_plate, index, m_print_config, plate_obj_size_infos[index]);
4662 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, has_wipe_tower %2%, wipe_x %3%, wipe_y %4%, width %5%, depth %6%")
4663 %(index+1) %plate_obj_size_infos[index].has_wipe_tower %plate_obj_size_infos[index].wipe_x %plate_obj_size_infos[index].wipe_y %plate_obj_size_infos[index].wipe_width %plate_obj_size_infos[index].wipe_depth;
4664 }
4665
4666 translate_models(partplate_list, m_print_config);
4667 }
4668
4669 /*for (ModelObject *model_object : m_models[0].objects)
4670 for (ModelInstance *model_instance : model_object->instances)
4671 {
4672 const Vec3d &instance_offset = model_instance->get_offset();
4673 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;
4674 }*/
4675
4676 //doing downward_check
4677 std::vector<printer_plate_info_t> downward_check_printers;
4678 std::vector<bool> downward_check_status;
4679 if (downward_check) {
4680 bool use_default = false;
4681 std::string default_path;
4682 if (downward_settings.size() == 0) {
4683 //parse from internal
4684 std::string cli_config_file = resources_dir() + "/profiles/BBL/cli_config.json";
4685 load_downward_settings_list_from_config(cli_config_file, current_printer_system_name, current_printer_model, downward_settings);
4686 use_default = true;
4687 default_path = resources_dir() + "/profiles/BBL/machine_full/";
4688 }
4689 for (auto const &file : downward_settings) {
4690 DynamicPrintConfig config;
4691 std::string config_type, config_name, filament_id, config_from, downward_printer;
4692 std::string file_path = use_default?(default_path+file+".json"):file;
4693 int ret = load_config_file(file_path, config, config_type, config_name, filament_id, config_from);
4694 if (ret) {
4695 record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
4696 flush_and_exit(ret);
4697 }
4698 if ((config_type != "machine") || (config_from != "system")) {
4699 BOOST_LOG_TRIVIAL(info) << boost::format("found invalid config type %1% or from %2% in file %3% when downward_check")%config_type %config_from %file_path;
4700 record_exit_reson(outfile_dir, ret, 0, cli_errors[CLI_CONFIG_FILE_ERROR], sliced_info);
4701 flush_and_exit(ret);
4702
4703 }
4704 BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: loaded machine config %1%, from %2%")%config_name %file_path ;
4705
4706 printer_plate_info_t printer_plate;
4707 Pointfs temp_printable_area, temp_exclude_area, temp_wrapping_area;
4708 std::vector<Pointfs> temp_extruder_areas;
4709 std::vector<double> temp_extruder_print_heights;
4710
4711 printer_plate.printer_name = config_name;
4712
4713 temp_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values;
4714 temp_exclude_area = legacy_bed_exclude_area_points(config);
4715 temp_wrapping_area = config.option<ConfigOptionPoints>("wrapping_exclude_area", true)->values;
4716 temp_extruder_areas = config.option<ConfigOptionPointsGroups>("extruder_printable_area", true)->values;
4717 temp_extruder_print_heights = config.option<ConfigOptionFloatsNullable>("extruder_printable_height", true)->values;
4718
4719 if (temp_printable_area.size() >= 4) {
4720 BoundingBoxf temp_printable_bbox(temp_printable_area);
4721 printer_plate.printable_width = static_cast<int>(temp_printable_bbox.size().x());
4722 printer_plate.printable_depth = static_cast<int>(temp_printable_bbox.size().y());
4723 if (config.option<ConfigOptionFloat>("printable_height"))
4724 printer_plate.printable_height = (int)(config.opt_float("printable_height"));
4725 }
4726 if (temp_exclude_area.size() >= 4) {
4727 printer_plate.exclude_width = (int)(temp_exclude_area[2].x() - temp_exclude_area[0].x());
4728 printer_plate.exclude_depth = (int)(temp_exclude_area[2].y() - temp_exclude_area[0].y());
4729 printer_plate.exclude_x = (int)temp_exclude_area[0].x();
4730 printer_plate.exclude_y = (int)temp_exclude_area[0].y();
4731 }
4732 if (temp_wrapping_area.size() >= 4) {
4733 printer_plate.wrapping_width = (int)(temp_wrapping_area[2].x() - temp_wrapping_area[0].x());
4734 printer_plate.wrapping_depth = (int)(temp_wrapping_area[2].y() - temp_wrapping_area[0].y());
4735 printer_plate.wrapping_x = (int)temp_wrapping_area[0].x();
4736 printer_plate.wrapping_y = (int)temp_wrapping_area[0].y();
4737 }
4738 if ((temp_extruder_areas.size() > 0) && (temp_extruder_print_heights.size() == temp_extruder_areas.size())) {
4739 double temp_extruder_height = printer_plate.printable_height;
4740 BoundingBox current_bbox({0, 0}, {printer_plate.printable_width, printer_plate.printable_depth});
4741
4742 for (unsigned int temp_index = 0; temp_index < temp_extruder_areas.size(); temp_index++)
4743 {
4744 BoundingBox temp_bbox;
4745 Pointfs& temp_extruder_shape = temp_extruder_areas[temp_index];
4746 for (unsigned int temp_index2 = 0; temp_index2 < temp_extruder_shape.size(); temp_index2++)
4747 temp_bbox.merge({temp_extruder_shape[temp_index2].x(), temp_extruder_shape[temp_index2].y()});
4748
4749 if (current_bbox.min.x() < temp_bbox.min.x())
4750 current_bbox.min.x() = temp_bbox.min.x();
4751 if (current_bbox.min.y() < temp_bbox.min.y())
4752 current_bbox.min.y() = temp_bbox.min.y();
4753 if (current_bbox.max.x() > temp_bbox.max.x())
4754 current_bbox.max.x() = temp_bbox.max.x();
4755 if (current_bbox.max.y() > temp_bbox.max.y())
4756 current_bbox.max.y() = temp_bbox.max.y();
4757 if (temp_extruder_height > temp_extruder_print_heights[temp_index])
4758 temp_extruder_height = temp_extruder_print_heights[temp_index];
4759 }
4760 printer_plate.printable_width = current_bbox.size().x();
4761 printer_plate.printable_depth = current_bbox.size().y();
4762 printer_plate.printable_height = temp_extruder_height;
4763
4764 BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: for multi-extruder printer, change printable size to bbox {%1%, %2%, 0} - {%3%, %4%, %5%}")
4765 %current_bbox.min.x() %current_bbox.min.y() %current_bbox.max.x() %current_bbox.max.y() %temp_extruder_height;
4766 }
4767 BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: printable size{%1%,%2%, %3%}, exclude area{%4%, %5%: %6% x %7%}, wrapping area{%8%, %9%: %10% x %11%}, enable_wrapping_detect %12%")
4768 %printer_plate.printable_width %printer_plate.printable_depth %printer_plate.printable_height
4769 %printer_plate.exclude_x %printer_plate.exclude_y %printer_plate.exclude_width %printer_plate.exclude_depth
4770 %printer_plate.wrapping_x %printer_plate.wrapping_y %printer_plate.wrapping_width %printer_plate.wrapping_depth %enable_wrapping_detect;
4771
4772 downward_check_printers.push_back(std::move(printer_plate));
4773 }
4774 }
4775
4776 int downward_check_size = downward_check_printers.size();
4777 if (downward_check_size > 0)
4778 {
4779 downward_check_status.resize(downward_check_size, false);
4780 int plate_count = partplate_list.get_plate_count();
4781 int failed_count = 0;
4782 for (int index = 0; index < plate_count; index ++)
4783 {
4784 if (failed_count == downward_check_size) {
4785 BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: all failed, size %1%")%downward_check_size;
4786 break;
4787 }
4788 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(index);
4789 Vec3d size = plate_obj_size_infos[index].obj_bbox.size();
4790
4791 for (int index2 = 0; index2 < downward_check_size; index2 ++)
4792 {
4793 if (failed_count == downward_check_size) {
4794 break;
4795 }
4796 if (downward_check_status[index2])
4797 continue;
4798 printer_plate_info_t& plate_info = downward_check_printers[index2];
4799 if ((size.z() > plate_info.printable_height) || (size.y() > plate_info.printable_depth) || (size.x() > plate_info.printable_width)) {
4800 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, downward_check index %2%, name %3%, bbox {%4%, %5%, %6%} exceeds printer size {%7%, %8%, %9%}")
4801 %(index+1) %(index2+1) %plate_info.printer_name
4802 %size.x() % size.y() % size.z() %plate_info.printable_width %plate_info.printable_depth %plate_info.printable_height;
4803 downward_check_status[index2] = true;
4804 failed_count ++;
4805 continue;
4806 }
4807 if (plate_info.exclude_width > 0) {
4808 int real_width = plate_info.printable_width - plate_info.exclude_width;
4809 int real_depth = plate_info.printable_depth - plate_info.exclude_depth;
4810 if ((size.x() > real_width) && (size.y() > real_depth)) {
4811 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, downward_check index %2%, name %3%, bbox {%4%, %5%} exceeds real size without exclude_area {%6%, %7%}")
4812 %(index+1) %(index2+1) %plate_info.printer_name
4813 %size.x() % size.y() %real_width %real_depth;
4814 downward_check_status[index2] = true;
4815 failed_count ++;
4816 continue;
4817 }
4818 }
4819 if (enable_wrapping_detect && plate_info.wrapping_width > 0) {
4820 //int real_width = plate_info.printable_width - plate_info.wrapping_width;
4821 int real_depth = plate_info.printable_depth - plate_info.wrapping_depth;
4822 if ( size.y() > real_depth) {
4823 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, downward_check index %2%, name %3%, bbox {%4%, %5%} exceeds real size without wrapping_area {%6%, %7%}")
4824 %(index+1) %(index2+1) %plate_info.printer_name
4825 %size.x() % size.y() %plate_info.printable_width %real_depth;
4826 downward_check_status[index2] = true;
4827 failed_count ++;
4828 continue;
4829 }
4830 }
4831 }
4832 }
4833 if (failed_count < downward_check_size)
4834 {
4835 //has success ones
4836 BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: downward_check_size %1%, failed_count %2%")%downward_check_size %failed_count;
4837 for (int index2 = 0; index2 < downward_check_size; index2 ++)
4838 {
4839 if (downward_check_status[index2])
4840 continue;
4841 printer_plate_info_t& plate_info = downward_check_printers[index2];
4842 BOOST_LOG_TRIVIAL(info) << boost::format("downward_check: found compatible printer %1%")%plate_info.printer_name;
4843 downward_compatible_machines.push_back(plate_info.printer_name);
4844 }
4845 sliced_info.downward_machines = downward_compatible_machines;
4846 }
4847 }
4848
4849 // Loop through transform options.
4850 bool user_center_specified = false;
4852 ArrangeParams arrange_cfg;
4853
4854 BOOST_LOG_TRIVIAL(info) << "will start transforms, commands count " << m_transforms.size() << "\n";
4855#if defined(__linux__) || defined(__LINUX__)
4856 if (g_cli_callback_mgr.is_started()) {
4857 PrintBase::SlicingStatus slicing_status{2, "Loading files finished"};
4858 cli_status_callback(slicing_status);
4859 }
4860#endif
4861
4862 for (auto const &opt_key : m_transforms) {
4863 BOOST_LOG_TRIVIAL(info) << "process transform " << opt_key << "\n";
4864 if (opt_key == "assemble") {
4865 if (clone_objects.size() > 0) {
4866 BOOST_LOG_TRIVIAL(error) << "Invalid params: can not set assemble and clone_objects together." << std::endl;
4867 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
4869 }
4870 if (m_models.empty()) {
4871 boost::nowide::cerr << "Invalid params: --assemble needs at least one input model." << std::endl;
4872 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
4874 }
4875 Model m;
4876 ModelObject* new_object = m.add_object();
4877 new_object->name = _u8L("Assembly");
4878 new_object->add_instance();
4879 int idx = 0;
4880 for (auto& model : m_models)
4881 for (ModelObject* o : model.objects) {
4882 for (auto volume : o->volumes) {
4883 ModelVolume* new_volume = new_object->add_volume(*volume);
4884 // set extruder id
4885 new_volume->config.set_key_value("extruder", new ConfigOptionInt(o->config.extruder()));
4886 }
4887 }
4888 m_models.clear();
4889 m_models.emplace_back(std::move(m));
4890 }
4891 else if (opt_key == "repetitions") {
4892 int repetitions_count = m_config.option<ConfigOptionInt>("repetitions")->value;
4893 if (repetitions_count <= 1)
4894 {
4895 BOOST_LOG_TRIVIAL(info) << "invalid repetitions value " << repetitions_count << ", just skip\n";
4896 }
4897 else {
4898 if (plate_to_slice == 0) {
4899 BOOST_LOG_TRIVIAL(error) << "Invalid params: can not set repetitions when slice all." << std::endl;
4900 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
4902 }
4903 else if (plate_to_slice > partplate_list.get_plate_count()) {
4904 BOOST_LOG_TRIVIAL(error) << boost::format("Invalid params:invalid plate %1% to slice, total %2%")%plate_to_slice %partplate_list.get_plate_count();
4905 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
4907 }
4908
4909 BOOST_LOG_TRIVIAL(info) << "repetitions value " << repetitions_count << std::endl;
4910
4911 duplicate_count = repetitions_count - 1;
4912 }
4913 }
4914 else if (opt_key == "convert_unit") {
4915 for (auto& model : m_models) {
4916 if (model.looks_like_saved_in_meters()) {
4917 BOOST_LOG_TRIVIAL(info) << "convert from meter to millimeter\n";
4918 model.convert_from_meters(true);
4919 }
4920 else if (model.looks_like_imperial_units()) {
4921 BOOST_LOG_TRIVIAL(info) << "convert from inch to millimeter\n";
4922 model.convert_from_imperial_units(true);
4923 }
4924 }
4925 }
4926 else if (opt_key == "orient") {
4927 //BBS: orient 0 means disable, 1 means force orient, others means auto
4928 int orient_option = m_config.option<ConfigOptionInt>("orient")->value;
4929
4930 if (orient_option == 0)
4931 {
4932 //orients_requirement.clear();
4933 for (auto& model : m_models)
4934 for (ModelObject* o : model.objects)
4935 {
4936 orients_requirement.insert(std::pair<size_t, bool>(o->id().id, false));
4937 BOOST_LOG_TRIVIAL(info) << "object "<<o->name <<", id :" << o->id().id << ", no need to orient when setting orient to 0\n";
4938 }
4939 }
4940 else if (orient_option == 1)
4941 {
4942 //force orient
4943 //orients_requirement.clear();
4944 for (auto& model : m_models)
4945 for (ModelObject* o : model.objects)
4946 {
4947 orients_requirement.insert(std::pair<size_t, bool>(o->id().id, true));
4948 BOOST_LOG_TRIVIAL(info) << "object "<<o->name <<", id :" << o->id().id << ", need to orient when setting orient to 1\n";
4949 }
4950 }
4951 else
4952 {
4953 //auto arrange, keep the original logic
4954 }
4955 BOOST_LOG_TRIVIAL(info) << boost::format("orient_option %1%")%orient_option;
4956 }
4957 else if (opt_key == "copy") {
4958 for (auto &model : m_models) {
4959 const bool all_objects_have_instances = std::none_of(
4960 model.objects.begin(), model.objects.end(),
4961 [](ModelObject* o){ return o->instances.empty(); }
4962 );
4963
4964 int dups = m_config.opt_int("copy");
4965 if (!all_objects_have_instances) model.add_default_instances();
4966
4967 try {
4968 if (dups > 1) {
4969 // if all input objects have defined position(s) apply duplication to the whole model
4970 duplicate(model, size_t(dups), beds, arrange_cfg);
4971 } else {
4972 arrange_objects(model, beds, arrange_cfg);
4973 }
4974 } catch (std::exception &ex) {
4975 boost::nowide::cerr << "error: " << ex.what() << std::endl;
4978 }
4979 }
4980 } else if (opt_key == "center") {
4981 user_center_specified = true;
4982 for (auto &model : m_models) {
4983 model.add_default_instances();
4984 // this affects instances:
4985 model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
4986 // this affects volumes:
4987 //FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body?
4988 //model.align_to_ground();
4989 BoundingBoxf3 bbox;
4990 for (ModelObject *model_object : model.objects)
4991 // We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only.
4992 bbox.merge(model_object->instance_bounding_box(0, false));
4993 for (ModelObject *model_object : model.objects)
4994 for (ModelInstance *model_instance : model_object->instances)
4995 model_instance->set_offset(Z, model_instance->get_offset(Z) - bbox.min.z());
4996 }
4997 } else if (opt_key == "align_xy") {
4998 const Vec2d &p = m_config.option<ConfigOptionPoint>("align_xy")->value;
4999 for (auto &model : m_models) {
5000 BoundingBoxf3 bb = model.bounding_box_exact();
5001 // this affects volumes:
5002 model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
5003 }
5004 } else if (opt_key == "arrange") {
5005 //BBS: arrange 0 means disable, 1 means force arrange, others means auto
5006 int arrange_option = m_config.option<ConfigOptionInt>("arrange")->value;
5007
5008 if (arrange_option == 0)
5009 {
5010 need_arrange = false;
5011 }
5012 else if (arrange_option == 1)
5013 {
5014 need_arrange = true;
5015 }
5016 else
5017 {
5018 //auto arrange, keep the original logic
5019 }
5020 } else if (opt_key == "ensure_on_bed") {
5021 // do nothing, the value is used later
5022 } else if (opt_key == "rotate") {
5023 for (auto &model : m_models)
5024 for (auto &o : model.objects)
5025 // this affects volumes:
5026 o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Z);
5027 } else if (opt_key == "rotate_x") {
5028 for (auto &model : m_models)
5029 for (auto &o : model.objects)
5030 // this affects volumes:
5031 o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), X);
5032 } else if (opt_key == "rotate_y") {
5033 for (auto &model : m_models)
5034 for (auto &o : model.objects)
5035 // this affects volumes:
5036 o->rotate(Geometry::deg2rad(m_config.opt_float(opt_key)), Y);
5037 } else if (opt_key == "ground_largest_face" || opt_key == "ground_face_normal" || opt_key == "ground_face_point") {
5038 // Each instance is laid on one of its lay-on-face planes, which are computed from the current part
5039 // transformations, so the rotations given before this option are respected. A direction or point is in
5040 // object coordinates, so it names the same face for every instance of an object.
5041 std::function<int(const std::vector<LayOnFacePlane>&, const Transform3d&)> pick;
5042 if (opt_key == "ground_largest_face") {
5043 if (m_config.opt_bool(opt_key))
5044 pick = [](const std::vector<LayOnFacePlane>& planes, const Transform3d&) { return find_largest_plane(planes); };
5045 } else {
5046 // Only options given on the command line reach this loop, so an empty value is malformed input too.
5047 const std::string& value = m_config.opt_string(opt_key);
5048 Vec3d v;
5049 int consumed = 0;
5050 if (sscanf(value.c_str(), "%lf,%lf,%lf%n", &v.x(), &v.y(), &v.z(), &consumed) != 3 || consumed != int(value.size()) ||
5051 !v.allFinite() || (opt_key == "ground_face_normal" && v.norm() < EPSILON)) {
5052 BOOST_LOG_TRIVIAL(error) << boost::format("Invalid params: %1% expects three comma-separated numbers, got \"%2%\"") % opt_key % value;
5053 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
5055 }
5056 if (opt_key == "ground_face_normal")
5057 pick = [v](const std::vector<LayOnFacePlane>& planes, const Transform3d&) { return find_plane_by_normal(planes, v); };
5058 else
5059 pick = [v](const std::vector<LayOnFacePlane>& planes, const Transform3d& inst_matrix) {
5060 return find_plane_at_point(planes, inst_matrix, v, 0.01);
5061 };
5062 }
5063 if (pick) {
5064 size_t laid = 0, missed = 0;
5065 for (auto& model : m_models) {
5066 model.add_default_instances();
5067 for (ModelObject* o : model.objects)
5068 for (size_t i = 0; i < o->instances.size(); ++i) {
5069 const Transform3d inst_matrix = o->instances[i]->get_matrix_no_offset();
5070 const std::vector<LayOnFacePlane> planes = lay_on_face_planes(*o, inst_matrix);
5071 if (planes.empty()) {
5072 // Small or smooth parts (e.g. a sphere) have no face to rest on; the gizmo offers none either.
5073 BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: object %2% has no face large enough to lay on, left as it is") % opt_key % o->name;
5074 continue;
5075 }
5076 const int idx = pick(planes, inst_matrix);
5077 if (idx < 0) {
5078 // Only a point can miss: with several objects it usually belongs to one of them.
5079 BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: no face of object %2% contains the point, left as it is") % opt_key % o->name;
5080 ++missed;
5081 continue;
5082 }
5083 BOOST_LOG_TRIVIAL(info) << boost::format("%1%: object %2% instance %3% laid on the %4% mm2 face with normal %5%")
5084 % opt_key % o->name % i % planes[idx].area % planes[idx].normal.transpose();
5085 lay_on_face(*o, i, planes[idx].normal);
5086 ++laid;
5087 }
5088 }
5089 if (laid == 0 && missed > 0) {
5090 BOOST_LOG_TRIVIAL(error) << boost::format("Invalid params: %1%: no face of any object contains the point") % opt_key;
5091 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
5093 }
5094 }
5095 } else if (opt_key == "scale") {
5096 float ratio = m_config.opt_float(opt_key);
5097 if (ratio <= 0.f) {
5098 BOOST_LOG_TRIVIAL(error) << boost::format("Invalid params:invalid scale ratio %1%")%ratio;
5099 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
5101 }
5102 for (auto &model : m_models)
5103 for (auto &o : model.objects)
5104 // this affects volumes:
5105 o->scale(ratio);
5106 } else if (opt_key == "scale_to_fit") {
5107 const Vec3d &opt = m_config.opt<ConfigOptionPoint3>(opt_key)->value;
5108 if (opt.x() <= 0 || opt.y() <= 0 || opt.z() <= 0) {
5109 boost::nowide::cerr << "--scale-to-fit requires a positive volume" << std::endl;
5112 }
5113 for (auto &model : m_models)
5114 for (auto &o : model.objects)
5115 // this affects volumes:
5116 o->scale_to_fit(opt);
5117 } else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
5118 boost::nowide::cerr << "Cut operation is not supported yet" << std::endl;
5119 return CLI_INVALID_PARAMS;
5120
5121 // std::vector<Model> new_models;
5122 // for (auto &model : m_models) {
5123 // model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
5124 // size_t num_objects = model.objects.size();
5125 // for (size_t i = 0; i < num_objects; ++ i) {
5126
5127 // #if 0
5128 // if (opt_key == "cut_x") {
5129 // o->cut(X, m_config.opt_float("cut_x"), &out);
5130 // } else if (opt_key == "cut_y") {
5131 // o->cut(Y, m_config.opt_float("cut_y"), &out);
5132 // } else if (opt_key == "cut") {
5133 // o->cut(Z, m_config.opt_float("cut"), &out);
5134 // }
5135 // #else
5136 // ModelObject* object = model.objects.front();
5137 // const BoundingBoxf3& box = object->bounding_box();
5138 // const float Margin = 20.0;
5139 // const float max_x = box.size()(0) / 2.0 + Margin;
5140 // const float min_x = -max_x;
5141 // const float max_y = box.size()(1) / 2.0 + Margin;
5142 // const float min_y = -max_y;
5143
5144 // std::array<Vec3d, 4> plane_points;
5145 // plane_points[0] = { min_x, min_y, 0 };
5146 // plane_points[1] = { max_x, min_y, 0 };
5147 // plane_points[2] = { max_x, max_y, 0 };
5148 // plane_points[3] = { min_x, max_y, 0 };
5149 // for (Vec3d& point : plane_points) {
5150 // point += box.center();
5151 // }
5152 // model.objects.front()->cut(0, plane_points, ModelObjectCutAttribute::KeepUpper |
5153 // ModelObjectCutAttribute::KeepLower);
5154 // #endif
5155 // model.delete_object(size_t(0));
5156 // }
5157 // }
5158
5159 // // TODO: copy less stuff around using pointers
5160 // m_models = new_models;
5161
5162 // if (m_actions.empty())
5163 // m_actions.push_back("export_stl");
5164 }
5165#if 0
5166 else if (opt_key == "cut_grid") {
5167 std::vector<Model> new_models;
5168 for (auto &model : m_models) {
5169 TriangleMesh mesh = model.mesh();
5170 mesh.repair();
5171
5172 std::vector<TriangleMesh> meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
5173 size_t i = 0;
5174 for (TriangleMesh* m : meshes) {
5175 Model out;
5176 auto o = out.add_object();
5177 o->add_volume(*m);
5178 o->input_file += "_" + std::to_string(i++);
5179 delete m;
5180 }
5181 }
5182
5183 // TODO: copy less stuff around using pointers
5184 m_models = new_models;
5185
5186 if (m_actions.empty())
5187 m_actions.push_back("export_stl");
5188 }
5189#endif
5190 else if (opt_key == "split") {
5191 for (Model &model : m_models) {
5192 size_t num_objects = model.objects.size();
5193 for (size_t i = 0; i < num_objects; ++ i) {
5194 ModelObjectPtrs new_objects;
5195 model.objects.front()->split(&new_objects, false); // TODO: add cli option to enable this?
5196 model.delete_object(size_t(0));
5197 }
5198 }
5199 } else if (opt_key == "repair") {
5200 if (m_config.opt_bool(opt_key))
5201 for (Model &model : m_models)
5202 for (ModelObject *object : model.objects) {
5203 for (ModelVolume *volume : object->volumes) {
5204 MeshRepairStats stats;
5205 std::string error;
5206 if (!volume->repair(true, &stats, &error))
5207 BOOST_LOG_TRIVIAL(warning) << "repair of " << object->name << " / " << volume->name << " failed: " << error;
5208 else if (stats.modified)
5209 BOOST_LOG_TRIVIAL(info) << "repaired " << object->name << " / " << volume->name;
5210 }
5211 object->invalidate_bounding_box();
5212 }
5213 } else {
5214 boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
5217 }
5218 }
5219
5220 BOOST_LOG_TRIVIAL(info) << "finished model pre-process commands\n";
5221 bool oriented_or_arranged = false;
5222 bool arranged_without_rotations = false;
5223 //BBS: add orient and arrange logic here
5224 for (auto& model : m_models)
5225 {
5226 for (ModelObject* o : model.objects)
5227 {
5228 if (orients_requirement[o->id().id])
5229 {
5230 BOOST_LOG_TRIVIAL(info) << "Before process command, Orient object, name=" << o->name <<",id="<<o->id().id<<std::endl;
5232 oriented_or_arranged = true;
5233 }
5234 else
5235 {
5236 BOOST_LOG_TRIVIAL(debug) << "Before process command, no need to orient, object id :" << o->id().id<<std::endl;
5237 }
5238 }
5239 }
5240 //BBS: clear the orient objects lists
5241 orients_requirement.clear();
5242
5243 bool is_seq_print_for_curr_plate = false;
5244 if ((plate_to_slice < 0) || (plate_to_slice > partplate_list.get_plate_count()))
5245 {
5246 BOOST_LOG_TRIVIAL(error) << boost::format("invalid plate id %1%, total %2%")%plate_to_slice %partplate_list.get_plate_count();
5247 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
5249 }
5250 else if (plate_to_slice > 0){
5251 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1);
5252 PrintSequence curr_plate_seq = cur_plate->get_print_seq();
5253
5254 if (curr_plate_seq == PrintSequence::ByDefault) {
5255 auto seq_print = m_print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence");
5256 if (seq_print && (seq_print->value == PrintSequence::ByObject)) {
5257 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;
5258 is_seq_print_for_curr_plate = true;
5259 }
5260 }
5261 else if (curr_plate_seq == PrintSequence::ByObject) {
5262 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;
5263 is_seq_print_for_curr_plate = true;
5264 }
5265
5266 if (duplicate_count > 0) {
5267 const ConfigOptionBool* spiral_vase = m_print_config.option<ConfigOptionBool>("spiral_mode");
5268 if ((spiral_vase != nullptr) && spiral_vase->value)
5269 {
5270 //spiral mode can only be duplicated with by-object
5271 if (!is_seq_print_for_curr_plate) {
5272 BOOST_LOG_TRIVIAL(warning) << boost::format("Spiral mode can not be duplicated under by-object print, skip duplicate");
5273 duplicate_count = 0;
5274 }
5275 }
5276 }
5277
5278 if (duplicate_count > 0)
5279 need_arrange = true;
5280 }
5281
5282 if ((!need_arrange) && is_bbl_3mf && !shrink_to_new_bed && (plate_to_slice > 0))
5283 {
5284 if (((old_height_to_rod != 0.f) && (old_height_to_rod != height_to_rod))
5285 || ((old_height_to_lid != 0.f) && (old_height_to_lid != height_to_lid))
5286 || ((old_max_radius != 0.f) && (old_max_radius != clearance_radius)))
5287 {
5288 if (is_seq_print_for_curr_plate) {
5289 need_arrange = true;
5290 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%, clearance_radius %6%, need arrange!")
5291 %old_height_to_rod %old_height_to_lid %old_max_radius %height_to_rod %height_to_lid %clearance_radius;
5292 }
5293 }
5294 }
5295
5296 oriented_or_arranged |= need_arrange;
5297
5298 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;
5299 if (need_arrange || filament_color_changed)
5300 {
5301 for (int index = 0; index < partplate_list.get_plate_count(); index ++)
5302 {
5303 if ((plate_to_slice != 0) && (plate_to_slice != (index + 1))) {
5304 continue;
5305 }
5306
5307 if (plate_data_src.size() > index) {
5308 if (!plate_data_src[index]->thumbnail_file.empty()) {
5309 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded thumbnail %2%.")%(index+1)%plate_data_src[index]->thumbnail_file;
5310 plate_data_src[index]->thumbnail_file.clear();
5311 }
5312 if (!plate_data_src[index]->no_light_thumbnail_file.empty()) {
5313 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded no_light_thumbnail %2%.")%(index+1)%plate_data_src[index]->no_light_thumbnail_file;
5314 plate_data_src[index]->no_light_thumbnail_file.clear();
5315 }
5316 if (!plate_data_src[index]->top_file.empty()) {
5317 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded top_thumbnail %2%.")%(index+1)%plate_data_src[index]->top_file;
5318 plate_data_src[index]->top_file.clear();
5319 }
5320 if (!plate_data_src[index]->pick_file.empty()) {
5321 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded pick_thumbnail %2%.")%(index+1)%plate_data_src[index]->pick_file;
5322 plate_data_src[index]->pick_file.clear();
5323 }
5324 }
5325 }
5326 }
5327
5328 if (!assemble_plate_info_list.empty())
5329 {
5330 //need to arrange for assemble cases
5331 int plate_count = assemble_plate_info_list.size();
5332 if (plate_count != partplate_list.get_plate_count())
5333 {
5334 BOOST_LOG_TRIVIAL(error) << boost::format("mismatch plate count, to_assemble %1%, generated %2%") % plate_count % partplate_list.get_plate_count();
5335 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
5337 }
5338
5339 for (size_t i = 0; i < plate_count; i++)
5340 {
5341 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i);
5342 //lock those plates no need to arrange
5343 if (!assemble_plate_info_list[i].need_arrange)
5344 cur_plate->lock(true);
5345 }
5346
5347 for (size_t i = 0; i < plate_count; i++)
5348 {
5349 assemble_plate_info_t& assemble_plate = assemble_plate_info_list[i];
5350 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i);
5351 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, need arrange %2%, filaments_count %3%") % (i+1) % assemble_plate.need_arrange % assemble_plate.filaments_count;
5352 if (assemble_plate.need_arrange)
5353 {
5354 //do arrange for plate
5355 ArrangePolygons selected, unselected;
5356 Model& model = m_models[0];
5357 arrange_cfg = ArrangeParams(); // reset all params
5358 get_print_sequence(cur_plate, m_print_config, arrange_cfg.is_seq_print);
5359
5360 //Step-1: prepare the arranged data
5361 partplate_list.lock_plate(i, false);
5362 partplate_list.select_plate(i);
5363 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% set to selected") % i;
5364 size_t plate_obj_count = assemble_plate.loaded_obj_list.size();
5365 for (size_t oidx = 0; oidx < plate_obj_count; ++oidx)
5366 {
5367 ModelObject* mo = assemble_plate.loaded_obj_list[oidx];
5368
5369 for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx)
5370 {
5371 ModelInstance* minst = mo->instances[inst_idx];
5373 ap.itemid = selected.size();
5374 selected.emplace_back(std::move(ap));
5375 BOOST_LOG_TRIVIAL(debug) << boost::format("plate %1%: add object %2% object index %3%, into selected") % (i+1) % ap.name %(oidx+1);
5376 }
5377 }
5378
5379 if (!is_belt_printer && ((!arrange_cfg.is_seq_print && (assemble_plate.filaments_count > 1)) || (enable_wrapping_detect && !current_wrapping_exclude_area.empty())))
5380 {
5381 //prepare the wipe tower
5382 int plate_count = partplate_list.get_plate_count();
5383
5384 auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure");
5385 // This margin only pre-adjusts the default away from the near edges;
5386 // estimate_wipe_tower_polygon below computes the real clamped position.
5387 float tower_brim_width = m_print_config.option<ConfigOptionFloat>("prime_tower_brim_width", true)->value;
5388 if (tower_brim_width < 0.f) tower_brim_width = 8.f; // auto: object heights unknown here, 8 mm is the auto cap
5389 const float tower_margin = WIPE_TOWER_MARGIN + tower_brim_width;
5390
5391 // set the default position, the same with print config(left top)
5392 float x = WIPE_TOWER_DEFAULT_X_POS;
5393 float y = WIPE_TOWER_DEFAULT_Y_POS;
5394 if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) {
5397 }
5398 if (x < tower_margin) {
5399 x = tower_margin;
5400 }
5401 if (y < tower_margin) {
5402 y = tower_margin;
5403 }
5404
5405 //create the options using default if necessary
5406 ConfigOptionFloats* wipe_x_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true);
5407 ConfigOptionFloats* wipe_y_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true);
5408 ConfigOptionFloat* width_option = m_print_config.option<ConfigOptionFloat>("prime_tower_width", true);
5409 ConfigOptionFloat* rotation_angle_option = m_print_config.option<ConfigOptionFloat>("wipe_tower_rotation_angle", true);
5410 ConfigOptionFloat* volume_option = m_print_config.option<ConfigOptionFloat>("prime_volume", true);
5411 ConfigOptionEnum<WipeTowerWallType> *prime_tower_rib_wall_option = m_print_config.option<ConfigOptionEnum<WipeTowerWallType>>("wipe_tower_wall_type", true);
5412
5413 BOOST_LOG_TRIVIAL(info) << boost::format("prime_tower_width %1% wipe_tower_rotation_angle %2% prime_volume %3%, rib_wall %4%") % width_option->value % rotation_angle_option->value % volume_option->value %prime_tower_rib_wall_option->value;
5414
5415 ConfigOptionFloat wt_x_opt(x);
5416 ConfigOptionFloat wt_y_opt(y);
5417
5418 wipe_x_option->set_at(&wt_x_opt, i, 0);
5419 wipe_y_option->set_at(&wt_y_opt, i, 0);
5420
5421 Vec3d wipe_tower_size, wipe_tower_pos;
5422 ArrangePolygon wipe_tower_ap = cur_plate->estimate_wipe_tower_polygon(m_print_config, i, wipe_tower_pos, wipe_tower_size, assemble_plate.filaments_count, true);
5423
5424 //update the new wp position
5425 wt_x_opt.value = wipe_tower_pos(0);
5426 wt_y_opt.value = wipe_tower_pos(1);
5427 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, after estimate_wipe_tower_polygon pos {%2%, %3%}, size {%4%, %5%}")%__LINE__ % wipe_tower_pos(0) % wipe_tower_pos(1) % wipe_tower_size(0) %wipe_tower_size(1);
5428
5429 wipe_x_option->set_at(&wt_x_opt, i, 0);
5430 wipe_y_option->set_at(&wt_y_opt, i, 0);
5431
5432 wipe_tower_ap.bed_idx = i;
5433 unselected.emplace_back(wipe_tower_ap);
5434 }
5435
5436 // add the virtual object into unselect list if has
5437 partplate_list.preprocess_exclude_areas(unselected, m_print_config, enable_wrapping_detect, i + 1);
5438 if (avoid_extrusion_cali_region)
5439 partplate_list.preprocess_nonprefered_areas(unselected, i + 1);
5440
5441 //Step-2:prepare the arrange params
5442 arrange_cfg.allow_rotations = allow_rotations;
5443 arrange_cfg.allow_multi_materials_on_same_plate = allow_multicolor_oneplate;
5444 arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region;
5445 arrange_cfg.clearance_height_to_rod = height_to_rod;
5446 arrange_cfg.clearance_height_to_lid = height_to_lid;
5447 arrange_cfg.clearance_radius = clearance_radius;
5448 arrange_cfg.nozzle_height = nozzle_height;
5449 arrange_cfg.align_center = align_center;
5450 arrange_cfg.printable_height = print_height;
5451 arrange_cfg.min_obj_distance = 0;
5452 if (arrange_cfg.is_seq_print) {
5453 arrange_cfg.bed_shrink_x = BED_SHRINK_SEQ_PRINT;
5454 arrange_cfg.bed_shrink_y = BED_SHRINK_SEQ_PRINT;
5455 }
5456 if (align_to_y_axis.has_value()) {
5457 arrange_cfg.align_to_y_axis = *align_to_y_axis;
5458 } else if (auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure")) {
5459 arrange_cfg.align_to_y_axis = (printer_structure_opt->value == PrinterStructure::psI3);
5460 }
5461
5462 arrangement::update_arrange_params(arrange_cfg, &m_print_config, selected);
5466
5467 beds = get_shrink_bedpts(&m_print_config, arrange_cfg);
5468
5469 partplate_list.preprocess_exclude_areas(
5470 arrange_cfg.excluded_regions, m_print_config, enable_wrapping_detect, 1, scale_(1));
5471
5472 {
5473 BOOST_LOG_TRIVIAL(debug) << "arrange bedpts:" << beds[0].transpose() << ", " << beds[1].transpose() << ", " << beds[2].transpose() << ", " << beds[3].transpose();
5474 BOOST_LOG_TRIVIAL(info) << "Arrange full params: " << arrange_cfg.to_json();
5475 BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items selected before arranging: %1%") % selected.size();
5476 for (auto item : selected)
5477 BOOST_LOG_TRIVIAL(trace) << item.name << ", extruder: " << item.extrude_ids.back() << ", bed: " << item.bed_idx
5478 << ", trans: " << item.translation.transpose();
5479 BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items unselected before arranging: %1%") % unselected.size();
5480 for (auto item : unselected)
5481 BOOST_LOG_TRIVIAL(trace) << item.name << ", bed: " << item.bed_idx << ", trans: " << item.translation.transpose();
5482 }
5483 arrange_cfg.progressind = [](unsigned st, std::string str = "") {
5484 //boost::nowide::cout << "st=" << st << ", " << str << std::endl;
5485 };
5486
5487 //Step-3:do the arrange
5488 BOOST_LOG_TRIVIAL(info) << boost::format("start plate %1%'s arranging...") % (i + 1);
5489 arrangement::arrange(selected, unselected, beds, arrange_cfg);
5490 //arrangement::arrange(unprintable, {}, beds, arrange_cfg);
5491 arranged_without_rotations |= !arrange_cfg.allow_rotations;
5492 BOOST_LOG_TRIVIAL(info) << boost::format("finished plate %1%'s arranging") % (i + 1);
5493
5494 //step-4: postprocess the bed index and result
5495 partplate_list.clear(false, false, true, i);
5496
5497 for (ArrangePolygon& ap : selected) {
5498 partplate_list.postprocess_bed_index_for_current_plate(ap);
5499 if (ap.bed_idx != i)
5500 {
5501 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":arrange failed: ap.name %1% ap.bed_idx %2%, plate index %3%") % ap.name % ap.bed_idx % i;
5504 }
5505 }
5506
5507 // Apply the arrange result to all selected objects
5508 for (ArrangePolygon& ap : selected) {
5509 //BBS: partplate postprocess
5510 partplate_list.postprocess_arrange_polygon(ap, true);
5511
5512 ap.apply();
5513 }
5514 //lock here
5515 cur_plate->lock(true);
5516 }
5517 else {
5518 size_t plate_obj_count = assemble_plate.loaded_obj_list.size();
5519 Vec3d plate_origin = cur_plate->get_origin();
5520
5521 for (size_t oidx = 0; oidx < plate_obj_count; ++oidx)
5522 {
5523 ModelObject* mo = assemble_plate.loaded_obj_list[oidx];
5524
5525 mo->translate_instances(plate_origin);
5526
5527 BOOST_LOG_TRIVIAL(debug) << boost::format("plate %1%: no arrange, directly translate object %2% by {%3%, %4%}") % (i+1) % mo->name %plate_origin(0) %plate_origin(1);
5528 }
5529
5530 bool is_seq_print = false;
5531 get_print_sequence(cur_plate, m_print_config, is_seq_print);
5532
5533 if (!is_belt_printer && !is_seq_print && (assemble_plate.filaments_count > 1) && !has_wipe_tower_position)
5534 {
5535 //prepare the wipe tower
5536 auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure");
5537 // set the default position, the same with print config(left top)
5538 float x = WIPE_TOWER_DEFAULT_X_POS;
5539 float y = WIPE_TOWER_DEFAULT_Y_POS;
5540 if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) {
5543 }
5544 if (x < WIPE_TOWER_MARGIN) {
5546 }
5547 if (y < WIPE_TOWER_MARGIN) {
5549 }
5550
5551 //create the options using default if necessary
5552 ConfigOptionFloats* wipe_x_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true);
5553 ConfigOptionFloats* wipe_y_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true);
5554 ConfigOptionFloat wt_x_opt(x);
5555 ConfigOptionFloat wt_y_opt(y);
5556
5557 wipe_x_option->set_at(&wt_x_opt, i, 0);
5558 wipe_y_option->set_at(&wt_y_opt, i, 0);
5559 }
5560 }
5561 }
5562
5563 for (size_t i = 0; i < plate_count; i++)
5564 {
5565 //unlock all the plates
5566 Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i);
5567 cur_plate->lock(false);
5568 }
5569
5570 partplate_list.reload_all_objects(false, -1);
5571 }
5572 else if (need_arrange)
5573 {
5574 ArrangePolygons selected, unselected, unprintable, locked_aps;
5575
5576 //for (Model &model : m_models)
5577 if (m_models.size() > 0)
5578 {
5579 Model &model = m_models[0];
5580 Model original_model;
5581 std::set<std::pair<int, int>> backup_set;
5582 bool finished_arrange = false, first_run = true;
5583 Slic3r::GUI::PartPlate* cur_plate = nullptr;
5584 int low_duplicate_count = 0, up_duplicate_count = duplicate_count, arrange_count = 0;
5585 float orig_wipe_x = 0.f, orig_wipe_y = 0.f;
5586
5587 if (duplicate_count > 0) {
5588 original_model = model;
5589 }
5590
5591 while(!finished_arrange)
5592 {
5593 arrange_cfg = ArrangeParams(); // reset all params
5594 arrange_count++;
5595 //step-0: duplicate model
5596 if (duplicate_count > 0)
5597 {
5598 //copy model objects and instances on plate
5599 if (!first_run) {
5600 BOOST_LOG_TRIVIAL(info) << boost::format("restore model object and plate, new duplicate_count %1%, arrange_count=%2%")%duplicate_count%arrange_count;
5602 model.clear_objects();
5603 model.clear_materials();
5604 model = original_model;
5605 partplate_list.load_from_3mf_structure(plate_data_src);
5606
5607 selected.clear();
5608 unselected.clear();
5609 unprintable.clear();
5610 locked_aps.clear();
5611 }
5612 else {
5613 first_run = false;
5614 if (plate_to_slice > 0) {
5615 ConfigOptionFloats* wipe_x_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_x");
5616 ConfigOptionFloats* wipe_y_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_y");
5617
5618 if (wipe_x_option && (wipe_x_option->size() > (plate_to_slice-1))) {
5619 orig_wipe_x = wipe_x_option->get_at(plate_to_slice-1);
5620 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, plate_to_slice %2%, orig_wipe_x=%3%")%__LINE__%plate_to_slice%orig_wipe_x;
5621 }
5622 if (wipe_y_option && (wipe_y_option->size() > (plate_to_slice-1))) {
5623 orig_wipe_y = wipe_y_option->get_at(plate_to_slice-1);
5624 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, plate_to_slice %2%, orig_wipe_y=%3%")%__LINE__%plate_to_slice%orig_wipe_y;
5625 }
5626 }
5627 }
5628
5629 cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1);
5630 cur_plate->duplicate_all_instance(duplicate_count, need_skip, skip_maps);
5631 }
5632 else if (plate_to_slice > 0)
5633 {
5634 cur_plate = (Slic3r::GUI::PartPlate *)partplate_list.get_plate(plate_to_slice-1);
5635 }
5636
5637 if (cur_plate) {
5638 get_print_sequence(cur_plate, m_print_config, arrange_cfg.is_seq_print);
5639 }
5640
5641 //Step-1: prepare arrange polygons
5642 if ((duplicate_count == 0) && (plate_to_slice == 0))
5643 {
5644 //global arrange
5645 for (size_t oidx = 0; oidx < model.objects.size(); ++oidx)
5646 {
5647 ModelObject* mo = model.objects[oidx];
5648 for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx)
5649 {
5650 ModelInstance* minst = mo->instances[inst_idx];
5652
5653 //preprocess by partplate list
5654 //remove the locked plate's instances, neither in selected, nor in un-selected
5655 bool locked = partplate_list.preprocess_arrange_polygon(oidx, inst_idx, ap, true);
5656 if (!locked)
5657 {
5658 ap.itemid = selected.size();
5659 if (minst->printable)
5660 selected.emplace_back(ap);
5661 else
5662 unprintable.emplace_back(ap);
5663 }
5664 else
5665 {
5666 //skip this object due to be locked in plate
5667 ap.itemid = locked_aps.size();
5668 locked_aps.emplace_back(ap);
5669 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": skip locked instance, obj_id %1%, instance_id %2%") % oidx % inst_idx;
5670 }
5671 }
5672 }
5673
5674 if (m_print_config.has("print_sequence")) {
5675 PrintSequence seq = m_print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence")->value;
5676 arrange_cfg.is_seq_print = (seq == PrintSequence::ByObject);
5677 }
5678
5679 //add the virtual object into unselect list if has
5680 partplate_list.preprocess_exclude_areas(unselected, m_print_config, enable_wrapping_detect);
5681
5682 // Filament ids given on the command line size the tower for STL input. A project
5683 // records its filament use per plate, so count there and keep its tower positions.
5684 const int plate_count = partplate_list.get_plate_count();
5685 const bool from_project = used_filament_set.empty();
5686 std::vector<int> plate_filament_counts(plate_count, static_cast<int>(used_filament_set.size()));
5687 if (from_project)
5688 for (int plate_index = 0; plate_index < plate_count; ++plate_index)
5689 plate_filament_counts[plate_index] = static_cast<int>(partplate_list.get_plate(plate_index)->get_extruders_under_cli(true, m_print_config).size());
5690 // A project only gets a tower the slicer will print: the prime tower enabled, and not
5691 // a by-object print unless a smooth timelapse needs it, as the per-plate arrange decides.
5692 const bool project_tower_allowed = m_print_config.option<ConfigOptionBool>("enable_prime_tower", true)->value &&
5693 (is_smooth_timelapse || !arrange_cfg.is_seq_print);
5694 const auto plate_needs_wipe_tower = [from_project, project_tower_allowed, is_smooth_timelapse](int filament_count) {
5695 if (!from_project)
5696 return filament_count > 0;
5697 return project_tower_allowed && (filament_count > 1 || (filament_count > 0 && is_smooth_timelapse));
5698 };
5699 const int max_filament_count = plate_count > 0 ? *std::max_element(plate_filament_counts.begin(), plate_filament_counts.end()) : 0;
5700
5701 if (!is_belt_printer && plate_needs_wipe_tower(max_filament_count))
5702 {
5703 //prepare the wipe tower
5704 auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure");
5705 // This margin only pre-adjusts the default away from the near edges;
5706 // estimate_wipe_tower_polygon below computes the real clamped position.
5707 float tower_brim_width = m_print_config.option<ConfigOptionFloat>("prime_tower_brim_width", true)->value;
5708 if (tower_brim_width < 0.f) tower_brim_width = 8.f; // auto: object heights unknown here, 8 mm is the auto cap
5709 const float tower_margin = WIPE_TOWER_MARGIN + tower_brim_width;
5710 // set the default position, the same with print config(left top)
5711 float x = WIPE_TOWER_DEFAULT_X_POS;
5712 float y = WIPE_TOWER_DEFAULT_Y_POS;
5713 if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) {
5716 }
5717
5718 if (x < tower_margin) {
5719 x = tower_margin;
5720 }
5721 if (y < tower_margin) {
5722 y = tower_margin;
5723 }
5724 ConfigOptionFloat wt_x_opt(x);
5725 ConfigOptionFloat wt_y_opt(y);
5726
5727 //create the options using default if necessary
5728 ConfigOptionFloats* wipe_x_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true);
5729 ConfigOptionFloats* wipe_y_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true);
5730 ConfigOptionFloat* width_option = m_print_config.option<ConfigOptionFloat>("prime_tower_width", true);
5731 ConfigOptionFloat* rotation_angle_option = m_print_config.option<ConfigOptionFloat>("wipe_tower_rotation_angle", true);
5732 ConfigOptionFloat* volume_option = m_print_config.option<ConfigOptionFloat>("prime_volume", true);
5733
5734 BOOST_LOG_TRIVIAL(info) << boost::format("prime_tower_width %1% wipe_tower_rotation_angle %2% prime_volume %3%")%width_option->value %rotation_angle_option->value %volume_option->value ;
5735
5736
5737 for (int bedid = 0; bedid < MAX_PLATE_COUNT; bedid++) {
5738 int plate_index_valid = std::min(bedid, plate_count - 1);
5739 // Overflow beds may receive objects from any plate, so size them for the busiest one.
5740 const int extruder_size = bedid < plate_count ? plate_filament_counts[bedid] : max_filament_count;
5741 if (!plate_needs_wipe_tower(extruder_size))
5742 continue;
5743 if (bedid < plate_count && !from_project) {
5744 wipe_x_option->set_at(&wt_x_opt, plate_index_valid, 0);
5745 wipe_y_option->set_at(&wt_y_opt, plate_index_valid, 0);
5746 }
5747
5748 Vec3d wipe_tower_size, wipe_tower_pos;
5749 ArrangePolygon wipe_tower_ap = partplate_list.get_plate(plate_index_valid)->estimate_wipe_tower_polygon(m_print_config, plate_index_valid, wipe_tower_pos, wipe_tower_size, extruder_size, true);
5750
5751 //update the new wp position
5752 if (bedid < plate_count) {
5753 wt_x_opt.value = wipe_tower_pos(0);
5754 wt_y_opt.value = wipe_tower_pos(1);
5755
5756 wipe_x_option->set_at(&wt_x_opt, plate_index_valid, 0);
5757 wipe_y_option->set_at(&wt_y_opt, plate_index_valid, 0);
5758
5759 BOOST_LOG_TRIVIAL(info) << boost::format("%1%, after estimate_wipe_tower_polygon, pos {%2%, %3%}, size {%4%, %5%}, plate %6%")%__LINE__ % wipe_tower_pos(0) % wipe_tower_pos(1) % wipe_tower_size(0) %wipe_tower_size(1) %bedid;
5760 }
5761
5762 wipe_tower_ap.bed_idx = bedid;
5763 unselected.emplace_back(wipe_tower_ap);
5764 }
5765 }
5766 }
5767 else {
5768 //only arrange current plate
5769 partplate_list.lock_plate(plate_to_slice - 1, false);
5770 partplate_list.select_plate(plate_to_slice-1);
5771 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% set to selected")%plate_to_slice;
5772
5773 for (size_t oidx = 0; oidx < model.objects.size(); ++oidx)
5774 {
5775 ModelObject* mo = model.objects[oidx];
5776
5777 for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx)
5778 {
5779 ModelInstance* minst = mo->instances[inst_idx];
5780 bool in_plate = cur_plate->contain_instance(oidx, inst_idx) || cur_plate->intersect_instance(oidx, inst_idx);
5782
5783 ArrangePolygons& cont = mo->instances[inst_idx]->printable ?
5784 (in_plate ? selected : unselected) :
5785 unprintable;
5786 bool locked = partplate_list.preprocess_arrange_polygon_other_locked(oidx, inst_idx, ap, in_plate);
5787 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 ;
5788 if (!locked)
5789 {
5790 ap.itemid = cont.size();
5791 cont.emplace_back(std::move(ap));
5792 }
5793 else
5794 {
5795 //skip this object due to be not in current plate, treated as locked
5796 ap.itemid = locked_aps.size();
5797 locked_aps.emplace_back(std::move(ap));
5798 BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format("arrange: skip locked instance, obj_id %1%, name %2%") % oidx % mo->name;
5799 }
5800 }
5801 }
5802 if ((duplicate_count > 0)&&(selected.size() == (duplicate_count + 1)))
5803 {
5804 duplicate_single_object = true;
5805 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found single object mode");
5806 }
5807
5808 if (!is_belt_printer && m_print_config.has("wipe_tower_x") && (is_smooth_timelapse || !arrange_cfg.is_seq_print || (selected.size() <= 1))) {
5809 float x;
5810 float y;
5811 if (duplicate_count > 0) {
5812 auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure");
5815 if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) {
5818 }
5819 }
5820 else {
5821 //keep the original
5822 x = dynamic_cast<const ConfigOptionFloats *>(m_print_config.option("wipe_tower_x"))->get_at(plate_to_slice-1);
5823 y = dynamic_cast<const ConfigOptionFloats *>(m_print_config.option("wipe_tower_y"))->get_at(plate_to_slice-1);
5824 }
5825 float w = dynamic_cast<const ConfigOptionFloat *>(m_print_config.option("prime_tower_width"))->value;
5826 float a = dynamic_cast<const ConfigOptionFloat *>(m_print_config.option("wipe_tower_rotation_angle"))->value;
5827 float v = dynamic_cast<const ConfigOptionFloat *>(m_print_config.option("prime_volume"))->value;
5828 unsigned int filaments_cnt = (plate_data_src.size() >= static_cast<size_t>(plate_to_slice)) ? plate_data_src[plate_to_slice-1]->slice_filaments_info.size() : 0;
5829 if ((filaments_cnt == 0) || need_skip)
5830 {
5831 // slice filaments info invalid
5832 std::vector<int> extruders = cur_plate->get_extruders_under_cli(true, m_print_config);
5833 filaments_cnt = extruders.size();
5834 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange: slice filaments info invalid or need_skip, get from partplate: filament_count %1%")%filaments_cnt;
5835 }
5836
5837 if ((filaments_cnt <= 1) && !is_smooth_timelapse && (!enable_wrapping_detect || current_wrapping_exclude_area.empty()))
5838 {
5839 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("arrange: not a multi-color object anymore, drop the wipe tower before arrange.");
5840 }
5841 else
5842 {
5843 float layer_height = 0.2;
5844 ConfigOption* layer_height_opt = m_print_config.option("layer_height");
5845 if (layer_height_opt)
5846 layer_height = layer_height_opt->getFloat();
5847
5848 //float depth = v * (filaments_cnt - 1) / (layer_height * w);
5849
5850 const WipeTowerFootprint footprint = cur_plate->estimate_wipe_tower_footprint(m_print_config, filaments_cnt);
5851 Vec3d wipe_tower_size(footprint.width, footprint.depth, footprint.height);
5852 Vec3d plate_origin = cur_plate->get_origin();
5853 int plate_width, plate_depth;
5854 double plate_height;
5855 partplate_list.get_plate_size(plate_width, plate_depth, plate_height);
5856 float depth = wipe_tower_size(1);
5857 // Brim already resolved against the height the body was sized from.
5858 float margin = 15.f, wp_brim_width = float(footprint.brim_width);
5859 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: wp_brim_width %1%")%wp_brim_width;
5860 w = wipe_tower_size(0);
5861
5862 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: x=%1%, y=%2%, width=%3%, depth=%4%, angle=%5%, prime_volume=%6%, filaments_cnt=%7%, layer_height=%8%, plate_width=%9%, plate_depth=%10%")
5863 %x %y %w %depth %a %v %filaments_cnt %layer_height %plate_width %plate_depth;
5864 if ((y + depth + margin + wp_brim_width) > (float)plate_depth) {
5865 y = (float)plate_depth - depth - margin - wp_brim_width;
5866 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: exceeds the border, change y to %1%, plate_depth=%2%")%y %plate_depth;
5867 }
5868
5869 if ((x + w + margin + wp_brim_width) > (float)plate_width) {
5870 x = (float)plate_width - w - margin - wp_brim_width;
5871 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("arrange wipe_tower: exceeds the border, change x to %1%, plate_width=%2%")%y %plate_width;
5872 }
5873 if (x < margin) {
5874 x = margin;
5875 }
5876 if (y < margin) {
5877 y = margin;
5878 }
5879 //update wipe_tower_x and wipe_tower_y
5880 ConfigOptionFloat wt_x_opt(x);
5881 ConfigOptionFloat wt_y_opt(y);
5882 ConfigOptionFloats* wipe_x_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true);
5883 ConfigOptionFloats* wipe_y_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true);
5884
5885 wipe_x_option->set_at(&wt_x_opt, plate_to_slice-1, 0);
5886 wipe_y_option->set_at(&wt_y_opt, plate_to_slice-1, 0);
5887
5888 ArrangePolygon wipe_tower_ap;
5889
5890 Polygon ap({
5891 {scaled(x - wp_brim_width), scaled(y - wp_brim_width)},
5892 {scaled(x + w + wp_brim_width), scaled(y - wp_brim_width)},
5893 {scaled(x + w + wp_brim_width), scaled(y + depth + wp_brim_width)},
5894 {scaled(x - wp_brim_width), scaled(y + depth + wp_brim_width)}
5895 });
5896 wipe_tower_ap.bed_idx = 0;
5897 wipe_tower_ap.setter = NULL; // do not move wipe tower
5898
5899 wipe_tower_ap.poly.contour = std::move(ap);
5900 wipe_tower_ap.translation = {scaled(0.f), scaled(0.f)};
5901 wipe_tower_ap.rotation = a;
5902 wipe_tower_ap.name = "WipeTower";
5903 wipe_tower_ap.is_virt_object = true;
5904 wipe_tower_ap.is_wipe_tower = true;
5905 ++wipe_tower_ap.priority;
5906 unselected.emplace_back(std::move(wipe_tower_ap));
5907 }
5908 }
5909
5910 // add the virtual object into unselect list if has
5911 partplate_list.preprocess_exclude_areas(
5912 unselected, m_print_config, enable_wrapping_detect, plate_to_slice);
5913 }
5914
5915
5916 //Step-2:prepare the arrange params
5917 arrange_cfg.allow_rotations = allow_rotations;
5918 arrange_cfg.allow_multi_materials_on_same_plate = allow_multicolor_oneplate;
5919 arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region;
5920 arrange_cfg.clearance_height_to_rod = height_to_rod;
5921 arrange_cfg.clearance_height_to_lid = height_to_lid;
5922 arrange_cfg.clearance_radius = clearance_radius;
5923 arrange_cfg.nozzle_height = nozzle_height;
5924 arrange_cfg.align_center = align_center;
5925 arrange_cfg.printable_height = print_height;
5926 arrange_cfg.min_obj_distance = 0;
5927 if (arrange_cfg.is_seq_print) {
5928 arrange_cfg.bed_shrink_x = BED_SHRINK_SEQ_PRINT;
5929 arrange_cfg.bed_shrink_y = BED_SHRINK_SEQ_PRINT;
5930 }
5931 if (align_to_y_axis.has_value()) {
5932 arrange_cfg.align_to_y_axis = *align_to_y_axis;
5933 } else if (auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure")) {
5934 arrange_cfg.align_to_y_axis = (printer_structure_opt->value == PrinterStructure::psI3);
5935 }
5936
5937 arrangement::update_arrange_params(arrange_cfg, &m_print_config, selected);
5941
5942 beds=get_shrink_bedpts(&m_print_config, arrange_cfg);
5943
5944 partplate_list.preprocess_exclude_areas(
5945 arrange_cfg.excluded_regions, m_print_config, enable_wrapping_detect, 1, scale_(1));
5946
5947 {
5948 BOOST_LOG_TRIVIAL(debug) << "arrange bedpts:" << beds[0].transpose() << ", " << beds[1].transpose() << ", " << beds[2].transpose() << ", " << beds[3].transpose();
5949 BOOST_LOG_TRIVIAL(info)<< "Arrange full params: "<< arrange_cfg.to_json();
5950 BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items selected before arranging: %1%")%selected.size();
5951 for (auto item : selected)
5952 BOOST_LOG_TRIVIAL(trace) << item.name << ", extruder: " << item.extrude_ids.back() << ", bed: " << item.bed_idx
5953 << ", trans: " << item.translation.transpose();
5954 BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items unselected before arranging: %1%") % unselected.size();
5955 for (auto item : unselected)
5956 BOOST_LOG_TRIVIAL(trace) << item.name << ", bed: " << item.bed_idx << ", trans: " << item.translation.transpose();
5957 }
5958 arrange_cfg.progressind= [](unsigned st, std::string str = "") {
5959 //boost::nowide::cout << "st=" << st << ", " << str << std::endl;
5960 };
5961
5962 //Step-3:do the arrange
5963 BOOST_LOG_TRIVIAL(info) << boost::format("start %1% th arranging...")%arrange_count;
5964 arrangement::arrange(selected, unselected, beds, arrange_cfg);
5965 arrangement::arrange(unprintable, {}, beds, arrange_cfg);
5966 arranged_without_rotations |= !arrange_cfg.allow_rotations;
5967 BOOST_LOG_TRIVIAL(info) << boost::format("finished %1% th arranging...")%arrange_count;
5968
5969 //Step-4:postprocess by partplate list&&apply the result
5970 int bed_idx_max = 0;
5971 if (duplicate_count == 0)
5972 {
5973 if (plate_to_slice > 0)
5974 {
5975 //only for partplate case
5976 partplate_list.clear(false, false, true, plate_to_slice-1);
5977
5978 for (ArrangePolygon& ap : selected) {
5979 partplate_list.postprocess_bed_index_for_current_plate(ap);
5980 if (ap.bed_idx != (plate_to_slice-1))
5981 {
5982 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);
5985 }
5986 bed_idx_max = std::max(ap.bed_idx, bed_idx_max);
5987 }
5988 }
5989 else
5990 {
5991 //clear all the relations before apply the arrangement results
5992 partplate_list.clear();
5993
5994 // Apply the arrange result to all selected objects
5995 for (ArrangePolygon &ap : selected) {
5996 //BBS: partplate postprocess
5997 partplate_list.postprocess_bed_index_for_selected(ap);
5998
5999 bed_idx_max = std::max(ap.bed_idx, bed_idx_max);
6000 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";
6001 }
6002 }
6003 for (ArrangePolygon &ap : locked_aps) {
6004 bed_idx_max = std::max(ap.bed_idx, bed_idx_max);
6005
6006 partplate_list.postprocess_arrange_polygon(ap, false);
6007
6008 ap.apply();
6009 }
6010
6011 // Apply the arrange result to all selected objects
6012 for (ArrangePolygon &ap : selected) {
6013 //BBS: partplate postprocess
6014 partplate_list.postprocess_arrange_polygon(ap, true);
6015
6016 ap.apply();
6017 }
6018
6019 // Apply the arrange result to unselected objects(due to the sukodu-style column changes, the position of unselected may also be modified)
6020 for (ArrangePolygon& ap : unselected)
6021 {
6022 if (ap.is_virt_object)
6023 continue;
6024
6025 //BBS: partplate postprocess
6026 partplate_list.postprocess_arrange_polygon(ap, false);
6027
6028 ap.apply();
6029 }
6030
6031 // Move the unprintable items to the last virtual bed.
6032 // Note ap.apply() moves relatively according to bed_idx, so we need to subtract the original bed_idx
6033 for (ArrangePolygon& ap : unprintable)
6034 {
6035 ap.bed_idx = bed_idx_max + 1;
6036 partplate_list.postprocess_arrange_polygon(ap, true);
6037
6038 ap.apply();
6039 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;
6040 }
6041
6042 //BBS: reload all objects due to arrange
6043 if (plate_to_slice > 0)
6044 partplate_list.rebuild_plates_after_arrangement(false, true, plate_to_slice-1);
6045 else
6046 partplate_list.rebuild_plates_after_arrangement();
6047 }
6048 else {
6049 //only for partplate case
6050 partplate_list.clear(false, false, true, plate_to_slice-1);
6051
6052 //BBS: adjust the bed_index, create new plates, get the max bed_index
6053 bool failed_this_time = false;
6054 for (ArrangePolygon& ap : selected) {
6055 partplate_list.postprocess_bed_index_for_current_plate(ap);
6056 if (ap.bed_idx != (plate_to_slice-1))
6057 {
6058 //
6059 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);
6060 if (!duplicate_single_object)
6061 {
6062 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;
6063
6064 if (duplicate_count == 1)
6065 {
6066 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": failed even on duplicate 1 copy, just print one original model");
6067 duplicate_count = 0;
6068 }
6069 else
6070 {
6071 if (duplicate_count == low_duplicate_count)
6072 {
6073 BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": previous success, but currently failed count %1%!!!")%duplicate_count;
6074 up_duplicate_count = duplicate_count;
6075 low_duplicate_count --;
6076 duplicate_count --;
6077 }
6078 else {
6079 up_duplicate_count = duplicate_count;
6080 duplicate_count = (up_duplicate_count + low_duplicate_count)/2;
6081 }
6082 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;
6083 }
6084 //record_exit_reson(outfile_dir, CLI_OBJECT_ARRANGE_FAILED, 0, cli_errors[CLI_OBJECT_ARRANGE_FAILED], sliced_info);
6085 //flush_and_exit(CLI_OBJECT_ARRANGE_FAILED);
6086 failed_this_time = true;
6087 break;
6088 }
6089 }
6090 else {
6091 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);
6092 real_duplicate_count ++;
6093 }
6094
6095 bed_idx_max = std::max(ap.bed_idx, bed_idx_max);
6096 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;
6097 }
6098
6099 if (failed_this_time) {
6100 if (duplicate_count == 0)
6101 {
6102 //restore to the original
6103 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": restore to the original model and plates, orig_wipe_x %1%, orig_wipe_y %2%")%orig_wipe_x %orig_wipe_y;
6104 finished_arrange = true;
6105 model = original_model;
6106 partplate_list.load_from_3mf_structure(plate_data_src);
6107 partplate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true);
6108 if ((orig_wipe_x > 0.f) && (orig_wipe_y > 0.f))
6109 {
6110 ConfigOptionFloat wt_x_opt(orig_wipe_x);
6111 ConfigOptionFloat wt_y_opt(orig_wipe_y);
6112 ConfigOptionFloats* wipe_x_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true);
6113 ConfigOptionFloats* wipe_y_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true);
6114
6115 wipe_x_option->set_at(&wt_x_opt, plate_to_slice-1, 0);
6116 wipe_y_option->set_at(&wt_y_opt, plate_to_slice-1, 0);
6117 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": restore wipe_tower position to {%1%, %2%}")%orig_wipe_x %orig_wipe_y;
6118 }
6119 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": exit arrange process");
6120 }
6121 continue;
6122 }
6123
6124 if (duplicate_single_object)
6125 {
6126 if (real_duplicate_count <= 1) {
6127 BOOST_LOG_TRIVIAL(warning) << boost::format("no object can be placed under single object mode, restore to the original model and plates also, orig_wipe_x %1%, orig_wipe_y %2%")%orig_wipe_x %orig_wipe_y;
6128 //record_exit_reson(outfile_dir, CLI_OBJECT_ARRANGE_FAILED, 0, cli_errors[CLI_OBJECT_ARRANGE_FAILED], sliced_info);
6129 //flush_and_exit(CLI_OBJECT_ARRANGE_FAILED);
6130 finished_arrange = true;
6131 model = original_model;
6132 partplate_list.load_from_3mf_structure(plate_data_src);
6133 partplate_list.reset_size(current_printable_width, current_printable_depth, current_printable_height, true, true);
6134 duplicate_count = 0;
6135 if ((orig_wipe_x > 0.f) && (orig_wipe_y > 0.f))
6136 {
6137 ConfigOptionFloat wt_x_opt(orig_wipe_x);
6138 ConfigOptionFloat wt_y_opt(orig_wipe_y);
6139 ConfigOptionFloats* wipe_x_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true);
6140 ConfigOptionFloats* wipe_y_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true);
6141
6142 wipe_x_option->set_at(&wt_x_opt, plate_to_slice-1, 0);
6143 wipe_y_option->set_at(&wt_y_opt, plate_to_slice-1, 0);
6144 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": restore wipe_tower position to {%1%, %2%}")%orig_wipe_x %orig_wipe_y;
6145 }
6146 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": exit arrange process");
6147 continue;
6148 }
6149 duplicate_count = real_duplicate_count - 1;
6150 }
6151 else {
6152 //multiple objects case
6153 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;
6154 if ((duplicate_count == up_duplicate_count) || (duplicate_count == (up_duplicate_count - 1)))
6155 {
6156 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found the max arrangeable count %1%")%duplicate_count;
6157 }
6158 else {
6159 low_duplicate_count = duplicate_count;
6160 duplicate_count = (up_duplicate_count + low_duplicate_count)/2;
6161 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;
6162 continue;
6163 }
6164 }
6165
6166 //BBS: adjust the bed_index, create new plates, get the max bed_index
6167 for (ArrangePolygon& ap : unselected)
6168 {
6169 if (ap.is_virt_object)
6170 continue;
6171
6172 bed_idx_max = std::max(ap.bed_idx, bed_idx_max);
6173 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;
6174 }
6175
6176 for (ArrangePolygon& ap : locked_aps)
6177 {
6178 bed_idx_max = std::max(ap.bed_idx, bed_idx_max);
6179
6180 partplate_list.postprocess_arrange_polygon(ap, false);
6181
6182 ap.apply();
6183 }
6184
6185 // Apply the arrange result to all selected objects
6186 for (ArrangePolygon& ap : selected) {
6187 //BBS: partplate postprocess
6188 partplate_list.postprocess_arrange_polygon(ap, true);
6189
6190 ap.apply();
6191 }
6192
6193 // Apply the arrange result to unselected objects(due to the sukodu-style column changes, the position of unselected may also be modified)
6194 for (ArrangePolygon& ap : unselected)
6195 {
6196 if (ap.is_virt_object)
6197 continue;
6198
6199 //BBS: partplate postprocess
6200 partplate_list.postprocess_arrange_polygon(ap, false);
6201
6202 ap.apply();
6203 }
6204
6205 // Move the unprintable items to the last virtual bed.
6206 // Note ap.apply() moves relatively according to bed_idx, so we need to subtract the original bed_idx
6207 for (ArrangePolygon& ap : unprintable)
6208 {
6209 ap.bed_idx = bed_idx_max + 1;
6210 partplate_list.postprocess_arrange_polygon(ap, true);
6211
6212 ap.apply();
6213 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;
6214 }
6215
6216 partplate_list.rebuild_plates_after_arrangement(false, true, plate_to_slice-1);
6217 }
6218 finished_arrange = true;
6219 }
6220 original_model.clear_objects();
6221 original_model.clear_materials();
6222 }
6223 }
6224
6225 // All transforms have been dealt with. Now ensure that the objects are on bed.
6226 // (Unless the user said otherwise.)
6227 //BBS: current only support models on bed, 0407 sinking supported
6228 if (m_config.opt_bool("ensure_on_bed"))
6229 {
6230 BOOST_LOG_TRIVIAL(info) << "ensure_on_bed: need to ensure each object on beds";
6231 for (auto &model : m_models)
6232 for (auto &o : model.objects)
6233 o->ensure_on_bed();
6234 }
6235
6236 // loop through action options
6237 bool export_to_3mf = false, load_slicedata = false, export_slicedata = false, export_slicedata_error = false;
6238 bool no_check = false;
6239 std::string export_3mf_file, load_slice_data_dir, export_slice_data_dir, export_stls_dir;
6240 std::vector<ThumbnailData*> calibration_thumbnails;
6241 std::vector<int> plate_object_count(partplate_list.get_plate_count(), 0);
6242 int max_slicing_time_per_plate = 0, max_triangle_count_per_plate = 0, sliced_plate = -1;
6243 std::vector<bool> plate_has_skips(partplate_list.get_plate_count(), false);
6244 std::vector<std::vector<size_t>> plate_skipped_objects(partplate_list.get_plate_count());
6245
6246 global_current_time = (long long)Slic3r::Utils::get_current_time_utc();
6247 sliced_info.prepare_time = (size_t) (global_current_time - global_begin_time);
6248 global_begin_time = global_current_time;
6249
6250 for (auto const &opt_key : m_actions) {
6251 if (opt_key == "help") {
6252 this->print_help();
6253 } else if (opt_key == "help_fff") {
6254 this->print_help(true, ptFFF);
6255 } else if (opt_key == "help_sla") {
6256 this->print_help(true, ptSLA);
6257 } else if (opt_key == "pipe") {
6258 //already processed before
6259 } else if (opt_key == "load_slicedata") {
6260 load_slicedata = true;
6261 load_slice_data_dir = m_config.opt_string(opt_key);
6262 if (export_slicedata) {
6263 BOOST_LOG_TRIVIAL(error) << "should not set load_slicedata and export_slicedata together." << std::endl;
6264 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
6266 }
6267 else if (duplicate_count > 0)
6268 {
6269 BOOST_LOG_TRIVIAL(error) << "should not set load_slicedata when set repetitions." << std::endl;
6270 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
6272 }
6273 else if (shrink_to_new_bed > 0)
6274 {
6275 BOOST_LOG_TRIVIAL(warning) << "use load_slicedata when shrink_to_new_bed." << std::endl;
6276 //record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
6277 //flush_and_exit(CLI_INVALID_PARAMS);
6278 }
6279 } else if (opt_key == "export_settings") {
6280 //FIXME check for mixing the FFF / SLA parameters.
6281 // or better save fff_print_config vs. sla_print_config
6282 //m_print_config.save(m_config.opt_string("save"));
6283 const std::string &settings_file = m_config.opt_string(opt_key);
6284 if (settings_file == "-")
6285 m_print_config.save_to_json(boost::nowide::cout, "project_settings", "project", SoftFever_VERSION, /*replace_invalid_utf8=*/true);
6286 else
6287 m_print_config.save_to_json(settings_file, std::string("project_settings"), std::string("project"), std::string(SoftFever_VERSION));
6288 } else if (opt_key == "info") {
6289 // --info works on unrepaired model
6290 for (Model &model : m_models) {
6291 model.add_default_instances();
6292 model.print_info();
6293 }
6294 } else if (opt_key == "inspect_mesh") {
6295 // Machine-readable alternative to --info. Registered as an action so it satisfies the
6296 // "needs an action" check and bypasses the GUI fallback, then exits once the JSON is out.
6297 for (Model &model : m_models) {
6298 model.add_default_instances();
6299 Slic3r::MeshInspect::inspect_to_json(model, m_input_files, boost::nowide::cout);
6300 }
6301 boost::nowide::cout.flush();
6302 // Conflicting actions were rejected before loading. Finish like the end of run().
6303 // flush_and_exit() is not usable here: it prints "found error ..." to stdout,
6304 // which would corrupt the JSON.
6305#if defined(__linux__) || defined(__LINUX__)
6306 if (g_cli_callback_mgr.is_started()) {
6307 PrintBase::SlicingStatus slicing_status{100, "All done, Success"};
6308 cli_status_callback(slicing_status);
6309 }
6310 g_cli_callback_mgr.stop();
6311#endif
6312 for (Model &m : m_models)
6314 record_exit_reson(outfile_dir, CLI_SUCCESS, plate_to_slice, cli_errors[CLI_SUCCESS], sliced_info);
6315 boost::nowide::cerr.flush();
6316 return CLI_SUCCESS;
6317 } else if (opt_key == "inspect_paint") {
6318 // --inspect-paint — read the per-facet enforcer/blocker/extruder/
6319 // fuzzy state from the loaded model and emit a JSON summary.
6320 // Machine-readable alternative to opening the paint gizmos.
6321 for (Model &model : m_models) {
6322 model.add_default_instances();
6323 Slic3r::PaintCLI::inspect_to_json(model, m_input_files, boost::nowide::cout);
6324 }
6325 boost::nowide::cout.flush();
6326 // The tooltip promises "then exit"; conflicting actions were rejected before
6327 // loading. Finish like the end of run(). flush_and_exit() is not usable here:
6328 // it prints "found error ..." to stdout, which would corrupt the JSON.
6329#if defined(__linux__) || defined(__LINUX__)
6330 if (g_cli_callback_mgr.is_started()) {
6331 PrintBase::SlicingStatus slicing_status{100, "All done, Success"};
6332 cli_status_callback(slicing_status);
6333 }
6334 g_cli_callback_mgr.stop();
6335#endif
6336 for (Model &m : m_models)
6338 record_exit_reson(outfile_dir, CLI_SUCCESS, plate_to_slice, cli_errors[CLI_SUCCESS], sliced_info);
6339 boost::nowide::cerr.flush();
6340 return CLI_SUCCESS;
6341 } else if (opt_key == "uptodate") {
6342 //already processed before
6343 } else if (opt_key == "min_save") {
6344 //already processed before
6345 } else if (opt_key == "load_defaultfila") {
6346 //already processed before
6347 } else if (opt_key == "mtcpp") {
6348 max_triangle_count_per_plate = m_config.option<ConfigOptionInt>("mtcpp")->value;
6349 } else if (opt_key == "mstpp") {
6350 max_slicing_time_per_plate = m_config.option<ConfigOptionInt>("mstpp")->value;
6351 } else if (opt_key == "export_stl") {
6352 for (auto &model : m_models)
6353 model.add_default_instances();
6354 if (! this->export_models(IO::STL)) {
6357 }
6358 } else if (opt_key == "export_stls") {
6359 export_stls_dir = m_config.opt_string(opt_key);
6360 for (auto &model : m_models)
6361 model.add_default_instances();
6362 if (! this->export_models(IO::STL, export_stls_dir)) {
6365 }
6366 } else if (opt_key == "export_obj") {
6367 for (auto &model : m_models)
6368 model.add_default_instances();
6369 if (! this->export_models(IO::OBJ)) {
6372 }
6373 }/* else if (opt_key == "export_amf") {
6374 if (! this->export_models(IO::AMF))
6375 return 1;
6376 } */else if (opt_key == "export_3mf") {
6377 export_to_3mf = true;
6378 export_3mf_file = m_config.opt_string(opt_key);
6379 }else if(opt_key=="no_check"){
6380 no_check = m_config.opt_bool(opt_key);
6381 }else if(opt_key=="strict"){
6382 //already read into sliced_info at the start of run()
6383 //} else if (opt_key == "export_gcode" || opt_key == "export_sla" || opt_key == "slice") {
6384 } else if (opt_key == "normative_check") {
6385 //already processed before
6386 } else if (opt_key == "export_slicedata") {
6387 export_slicedata = true;
6388 export_slice_data_dir = m_config.opt_string(opt_key);
6389 if (load_slicedata) {
6390 BOOST_LOG_TRIVIAL(error) << "should not set load_slicedata and export_slicedata together." << std::endl;
6391 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
6393 }
6394 } else if (opt_key == "slice") {
6395 //BBS: slice 0 means all plates, i means plate i;
6396 plate_to_slice = m_config.option<ConfigOptionInt>("slice")->value;
6397 sliced_plate = plate_to_slice;
6398 bool pre_check = (plate_to_slice == 0)?true:false;
6399 bool finished = false;
6400
6401 /*if (opt_key == "export_gcode" && printer_technology == ptSLA) {
6402 boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl;
6403 record_exit_reson(outfile_dir, 1, 0, cli_errors[1], sliced_info);
6404 flush_and_exit(1);
6405 } else if (opt_key == "export_sla" && printer_technology == ptFFF) {
6406 boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl;
6407 record_exit_reson(outfile_dir, 1, 0, cli_errors[1], sliced_info);
6408 flush_and_exit(1);
6409 }*/
6410 BOOST_LOG_TRIVIAL(info) << "Need to slice for plate "<<plate_to_slice <<", total plate count "<<partplate_list.get_plate_count()<<" partplates!" << std::endl;
6411#if defined(__linux__) || defined(__LINUX__)
6412 if (g_cli_callback_mgr.is_started()) {
6413 PrintBase::SlicingStatus slicing_status{3, "Prepare slicing"};
6414 cli_status_callback(slicing_status);
6415 }
6416#endif
6417 // Make a copy of the model if the current action is not the last action, as the model may be
6418 // modified by the centering and such.
6419 Model model_copy;
6420 bool make_copy = &opt_key != &m_actions.back();
6421 for (Model &model_in : m_models) {
6422 if (make_copy)
6423 model_copy = model_in;
6424 Model &model = make_copy ? model_copy : model_in;
6425 // If all objects have defined instances, their relative positions will be
6426 // honored when printing (they will be only centered, unless --dont-arrange
6427 // is supplied); if any object has no instances, it will get a default one
6428 // and all instances will be rearranged (unless --dont-arrange is supplied).
6429 std::string outfile;
6430 //Print fff_print;
6431 std::vector<size_t> plate_triangle_counts(partplate_list.get_plate_count(), 0);
6432
6433 // The stored (or default) tower position may not fit the tower these plates
6434 // need, and no CLI placement site runs on a plain slice - mirror the GUI's
6435 // reload clamp and fit every plate's tower into the printable area first.
6436 if (!is_belt_printer && m_print_config.option<ConfigOptionBool>("enable_prime_tower", true)->value) {
6437 for (int index = 0; index < partplate_list.get_plate_count(); index++) {
6438 if ((plate_to_slice != 0) && (plate_to_slice != (index + 1)))
6439 continue;
6440 Slic3r::GUI::PartPlate *plate = partplate_list.get_plate(index);
6441 // Printing by object disables the tower only with more than one instance.
6442 bool is_seq_print = false;
6443 get_print_sequence(plate, m_print_config, is_seq_print);
6444 if (is_seq_print && plate->printable_instance_size() > 1)
6445 continue;
6446 // An empty estimate is a plate that prints no tower (one filament and
6447 // neither smooth timelapse, wrapping detection nor a raft).
6448 Vec3d wt_pos, wt_size;
6449 Slic3r::GUI::WipeTowerRelocation relocation;
6450 plate->estimate_wipe_tower_polygon(m_print_config, index, wt_pos, wt_size, 0, false, &relocation);
6451 if (wt_size(0) < EPSILON || wt_size(1) < EPSILON)
6452 continue;
6453 if (relocation.moved)
6454 BOOST_LOG_TRIVIAL(warning) << boost::format("plate %1%: the wipe tower no longer fits at its saved position {%2%, %3%} and was moved %4%")
6455 % (index + 1) % m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true)->get_at(index)
6456 % m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true)->get_at(index)
6457 % (relocation.overlaps_objects ? "onto an object, as no free space is left" : "to free space");
6458 ConfigOptionFloat wt_x_opt((float) wt_pos(0));
6459 ConfigOptionFloat wt_y_opt((float) wt_pos(1));
6460 m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true)->set_at(&wt_x_opt, index, 0);
6461 m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true)->set_at(&wt_y_opt, index, 0);
6462 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: wipe tower clamped to {%2%, %3%}, size {%4%, %5%}")
6463 % (index + 1) % wt_pos(0) % wt_pos(1) % wt_size(0) % wt_size(1);
6464 }
6465 }
6466
6467 while(!finished)
6468 {
6469 //BBS: slice every partplate one by one
6470 PrintBase *print=NULL;
6471 Print *print_fff = NULL;
6472
6473 Slic3r::GUI::GCodeResult *gcode_result = NULL;
6474 int print_index;
6475 for (int index = 0; index < partplate_list.get_plate_count(); index ++)
6476 {
6477 if ((plate_to_slice != 0) && (plate_to_slice != (index + 1))) {
6478 BOOST_LOG_TRIVIAL(info) << "Skip plate " << index+1 << std::endl;
6479 continue;
6480 }
6481 sliced_plate_info_t sliced_plate_info;
6482 sliced_plate_info.plate_id = index+1;
6483
6484 model.curr_plate_index = index;
6485 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: pre_check %2%, start")%(index+1)%pre_check;
6486 long long start_time = 0, end_time = 0, temp_time = 0, time_using_cache = 0;
6487 start_time = (long long)Slic3r::Utils::get_current_time_utc();
6488 //get the current partplate
6489 Slic3r::GUI::PartPlate* part_plate = partplate_list.get_plate(index);
6490 part_plate->get_print(&print, &gcode_result, &print_index);
6491
6492 print_fff = dynamic_cast<Print *>(print);
6493 /*if (outfile_config.empty())
6494 {
6495 outfile = "plate_" + std::to_string(index + 1) + ".gcode";
6496 }
6497 else
6498 {
6499 outfile = "plate_" + std::to_string(index + 1) + "_" + outfile_config + ".gcode";
6500 }*/
6501
6502 //update plate's bounding box to model
6503#if 0
6504 BoundingBoxf3 print_volume = part_plate->get_bounding_box(false);
6505 print_volume.max(2) = z;
6506 print_volume.min(2) = -1e10;
6507 model.update_print_volume_state(print_volume);
6508 BOOST_LOG_TRIVIAL(info) << boost::format("print_volume {%1%,%2%,%3%}->{%4%, %5%, %6%}") % print_volume.min(0) % print_volume.min(1)
6509 % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) << std::endl;
6510#else
6511 BuildVolume build_volume(part_plate->get_shape(), print_height, part_plate->get_extruder_areas(), current_extruder_print_heights);
6512 //model.update_print_volume_state(build_volume);
6513 unsigned int count = model.update_print_volume_state(build_volume);
6514
6515 if (count == 0) {
6516 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;
6517 std::string no_objects_reason = cli_errors[CLI_NO_SUITABLE_OBJECTS];
6518 if (arranged_without_rotations) {
6519 //ORCA: result.json is only written on Linux, so also print the reason to stderr.
6520 no_objects_reason = "No object is fully inside the print volume. Arrange ran without rotations."
6521 " If an object only fits the bed when rotated, pass --allow-rotations."
6522 " Otherwise the plate is empty or an object is larger than the printable area or height.";
6523 boost::nowide::cerr << "plate " << index+1 << ": " << no_objects_reason << std::endl;
6524 }
6525 record_exit_reson(outfile_dir, CLI_NO_SUITABLE_OBJECTS, index+1, no_objects_reason, sliced_info);
6527 }
6528 else if ((plate_to_slice != 0) || pre_check) {
6529 long long triangle_count = 0;
6530 int printable_instances = 0;
6531 int skipped_count = 0;
6532 std::vector<std::set<int>> unprintable_filament_ids(new_extruder_count, std::set<int>());
6533 for (ModelObject* model_object : model.objects)
6534 for (ModelInstance *i : model_object->instances)
6535 {
6536 i->use_loaded_id_for_label = true;
6537 if (skip_maps.find(i->loaded_id) != skip_maps.end()) {
6538 skip_maps[i->loaded_id] = true;
6539 i->printable = false;
6541 skipped_count++;
6542 plate_has_skips[index] = true;
6543 plate_skipped_objects[index].emplace_back(i->loaded_id);
6544 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: skip object %2%.")%(index+1)%i->loaded_id;
6545 //need to regenerate the thumbnail
6546 if (plate_data_src.size() > index) {
6547 if (!plate_data_src[index]->thumbnail_file.empty()) {
6548 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded thumbnail %2%.")%(index+1)%plate_data_src[index]->thumbnail_file;
6549 plate_data_src[index]->thumbnail_file.clear();
6550 }
6551 if (!plate_data_src[index]->no_light_thumbnail_file.empty()) {
6552 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded no_light_thumbnail %2%.")%(index+1)%plate_data_src[index]->no_light_thumbnail_file;
6553 plate_data_src[index]->no_light_thumbnail_file.clear();
6554 }
6555 if (!plate_data_src[index]->top_file.empty()) {
6556 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded top_thumbnail %2%.")%(index+1)%plate_data_src[index]->top_file;
6557 plate_data_src[index]->top_file.clear();
6558 }
6559 if (!plate_data_src[index]->pick_file.empty()) {
6560 BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded pick_thumbnail %2%.")%(index+1)%plate_data_src[index]->pick_file;
6561 plate_data_src[index]->pick_file.clear();
6562 }
6563 }
6564 }
6565 continue;
6566 }
6567
6569 {
6570 BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Found Object " << model_object->name <<" partly inside, can not be sliced." << std::endl;
6573 }
6575 {
6576 const Transform3d& inst_matrix = i->get_transformation().get_matrix();
6577
6578 //get object filaments
6579 std::set<int> object_filaments;
6580 for (const ModelVolume *vol : model_object->volumes) {
6581 std::vector<int> filaments = vol->get_extruders();
6582 object_filaments.insert(filaments.begin(), filaments.end());
6583 }
6584 for (const ModelVolume* vol : model_object->volumes)
6585 {
6586 if (vol->is_model_part()) {
6587 size_t volume_triangle_count = vol->mesh().facets_count();
6588 triangle_count += volume_triangle_count;
6589 BOOST_LOG_TRIVIAL(debug) << boost::format("volume triangle count %1%, total %2%")%volume_triangle_count %triangle_count;
6590 if ((max_triangle_count_per_plate != 0) && (triangle_count > max_triangle_count_per_plate))
6591 {
6592 BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": triangle count " << triangle_count <<" exceeds the limit:" << max_triangle_count_per_plate;
6595 }
6596
6597 if (new_extruder_count > 1) {
6598 BoundingBoxf3 bbox = vol->get_convex_hull().transformed_bounding_box(inst_matrix * vol->get_matrix());
6599 std::vector<bool> inside_extruders;
6600 BuildVolume::ObjectState state = build_volume.check_volume_bbox_state_with_extruder_areas(bbox, inside_extruders);
6601 if (state == BuildVolume::ObjectState::Limited) {
6602 if (object_filaments.size() == 1) {
6603 // Only check for single-color object
6604 for (size_t j = 0; j < inside_extruders.size(); ++j) {
6605 if (!inside_extruders[j]) {
6606 std::vector<int> filaments = vol->get_extruders();
6607 unprintable_filament_ids[j].insert(filaments.begin(), filaments.end());
6608 }
6609 }
6610 }
6611 }
6612
6613 }
6614 }
6615 }
6616 }
6617
6619 printable_instances++;
6620 }
6621
6622 if (printable_instances == 0) {
6623 BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, after skipping "<<skipped_count<<" objects."<< std::endl;
6626 }
6627
6628 std::vector<int> plate_filaments = part_plate->get_extruders_under_cli(true, m_print_config);
6629 std::vector<int> used_tpu_filaments;
6630 for (int f_index = 0; f_index < plate_filaments.size(); f_index++) {
6631 if (plate_filaments[f_index] <= filament_count) {
6632 std::string filament_type;
6633 m_print_config.get_filament_type(filament_type, plate_filaments[f_index]-1);
6634 if (filament_type == "TPU") {
6635 used_tpu_filaments.push_back(plate_filaments[f_index]);
6636 }
6637 }
6638 }
6639 bool tpu_valid = part_plate->check_tpu_printable_status(m_print_config, used_tpu_filaments);
6640 if (!tpu_valid) {
6641 BOOST_LOG_TRIVIAL(error) << boost::format("plate %1% : Found 2 or more tpu filaments on plate ") % (index + 1);
6644 }
6645
6646 // Same type gate as the GUI's Sidebar::has_broken_mixed_filament: refuse a plate that uses a
6647 // mixed slot whose components are different filament types. Missing or out-of-range
6648 // components never get here, validate() already rejects them for the whole project.
6649 const auto *is_mixed_opt = m_print_config.option<ConfigOptionBools>("filament_is_mixed");
6650 const auto *components_opt = m_print_config.option<ConfigOptionStrings>("filament_mixed_components");
6651 if (is_mixed_opt && components_opt && has_any_mixed_filament(is_mixed_opt->values)) {
6652 const auto &is_mixed = is_mixed_opt->values;
6653 const auto &components = components_opt->values;
6654 const size_t num_physical = static_cast<size_t>(filament_count) - static_cast<size_t>(std::count(is_mixed.begin(), is_mixed.end(), true));
6655 std::vector<std::string> physical_types(num_physical);
6656 for (size_t f_index = 0; f_index < num_physical; ++f_index) {
6657 std::string displayed_type;
6658 physical_types[f_index] = m_print_config.get_filament_type(displayed_type, static_cast<int>(f_index));
6659 if (physical_types[f_index].empty())
6660 physical_types[f_index] = "PLA";
6661 }
6662 const std::vector<size_t> mismatched_slots = check_mixed_filament_type_consistency(is_mixed, components, physical_types);
6663 // plate_filaments has mixed slots expanded to their components; the gate needs the slots.
6664 const std::vector<int> plate_slots = mismatched_slots.empty() ? std::vector<int>() :
6665 part_plate->get_extruders_under_cli(true, m_print_config, false);
6666 for (size_t slot : mismatched_slots) {
6667 if (std::find(plate_slots.begin(), plate_slots.end(), static_cast<int>(slot) + 1) == plate_slots.end())
6668 continue;
6669 BOOST_LOG_TRIVIAL(error) << boost::format("plate %1%: mixed filament %2% mixes components of different filament types")
6670 % (index + 1) % (slot + 1);
6673 }
6674 }
6675
6676 if (new_extruder_count > 1) {
6677 std::vector<std::vector<int>> unprintable_filament_vec;
6678 for (const std::set<int>& filamnt_ids : unprintable_filament_ids) {
6679 unprintable_filament_vec.emplace_back(std::vector<int>(filamnt_ids.begin(), filamnt_ids.end()));
6680 }
6681
6682 FilamentMapMode mode;
6683 if (m_extra_config.option<ConfigOptionEnum<FilamentMapMode>>("filament_map_mode"))
6684 mode = m_extra_config.option<ConfigOptionEnum<FilamentMapMode>>("filament_map_mode")->value;
6685 else
6686 mode = part_plate->get_real_filament_map_mode(m_print_config);
6687 BOOST_LOG_TRIVIAL(info) << boost::format("%1% :filament map mode is %2% ") % __LINE__ %(int)mode;
6688 if (mode < FilamentMapMode::fmmManual) {
6689 std::vector<int> conflict_filament_vector;
6690 for (int index = 0; index < new_extruder_count; index++)
6691 {
6692 if (!unprintable_filament_vec[index].empty())
6693 {
6694 std::sort(unprintable_filament_vec[index].begin(), unprintable_filament_vec[index].end());
6695 if (index == 0)
6696 conflict_filament_vector = unprintable_filament_vec[index];
6697 else
6698 {
6699 std::vector<int> result_filaments;
6700 //result_filaments.reserve(conflict_filaments.size());
6701 std::set_intersection(conflict_filament_vector.begin(), conflict_filament_vector.end(), unprintable_filament_vec[index].begin(),
6702 unprintable_filament_vec[index].end(), std::insert_iterator<std::vector<int>>(result_filaments, result_filaments.begin()));
6703 conflict_filament_vector = result_filaments;
6704 }
6705 }
6706 else
6707 {
6708 conflict_filament_vector.clear();
6709 break;
6710 }
6711 }
6712 //check whether has filament can not map
6713 if (!conflict_filament_vector.empty())
6714 {
6715 BOOST_LOG_TRIVIAL(error) << boost::format("plate %1% : some filaments can not be mapped under auto mode for multi extruder printer ")% (index + 1);
6716 record_exit_reson(outfile_dir, CLI_FILAMENT_CAN_NOT_MAP, index + 1, cli_errors[CLI_FILAMENT_CAN_NOT_MAP], sliced_info);
6718 }
6719 }
6720 else {
6721 std::vector<int> filament_maps;
6722 if (m_extra_config.option<ConfigOptionInts>("filament_map")) {
6723 filament_maps = m_extra_config.option<ConfigOptionInts>("filament_map")->values;
6724 int default_value = -1;
6725 bool has_invalid_value = false;
6726 for (int f_index = 0; f_index < filament_maps.size(); f_index++)
6727 {
6728 if (filament_maps[f_index] != -1)
6729 {
6730 if (default_value == -1)
6731 default_value = filament_maps[f_index];
6732 else
6733 continue;
6734 }
6735 else
6736 has_invalid_value = true;
6737
6738 if (has_invalid_value && (default_value != -1))
6739 break;
6740 }
6741 BOOST_LOG_TRIVIAL(info) << boost::format("%1% :filament map default_value %2%, has_invalid_value %3% ") % __LINE__ %default_value %has_invalid_value;
6742
6743 if (has_invalid_value)
6744 {
6745 for (int f_index = 0; f_index < filament_maps.size(); f_index++)
6746 {
6747 if (filament_maps[f_index] == -1)
6748 {
6749 if (default_value != -1) {
6750 filament_maps[f_index] = default_value;
6751 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% : set filament_map of filament %2% to first value %3%.")% (index + 1) %(f_index+1) %default_value;
6752 }
6753 else {
6754 filament_maps[f_index] = 1;
6755 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% : set filament_map of filament %2% to default value 1.")% (index + 1) %(f_index+1);
6756 }
6757 }
6758 }
6759 m_extra_config.option<ConfigOptionInts>("filament_map")->values = filament_maps;
6760 }
6761 part_plate->set_filament_maps(filament_maps);
6762 }
6763 else
6764 filament_maps = part_plate->get_real_filament_maps(m_print_config);
6765
6766 // Multi-nozzle printers need the per-filament volume assignment as a grouping
6767 // input in the manual modes: synthesize it from the per-extruder flow types when
6768 // the caller did not provide one (an extruder whose nozzle stats span several
6769 // volume types keeps the per-filament choice), and require explicit maps in
6770 // nozzle-manual mode.
6771 auto max_nozzle_counts_opt = m_print_config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
6772 // Skip nil entries: a nullable-int nil is INT_MAX (> 1) and would otherwise falsely pass the gate.
6773 bool support_multi_nozzle =
6774 max_nozzle_counts_opt &&
6775 std::any_of(max_nozzle_counts_opt->values.begin(), max_nozzle_counts_opt->values.end(),
6776 [](int v) { return v > 1 && v != ConfigOptionIntsNullable::nil_value(); });
6777 if (support_multi_nozzle && (mode == fmmManual || mode == fmmNozzleManual) && (plate_to_slice != 0)) {
6778 // Orca: the grouping result is reconstructed purely from the passed maps in
6779 // nozzle-manual mode, so all of them must be present (there are no separate
6780 // per-nozzle CLI parameters to rebuild them from).
6782 (!m_extra_config.has("filament_volume_map") || !m_extra_config.has("filament_nozzle_map") ||
6783 !m_extra_config.has("filament_map"))) {
6784 BOOST_LOG_TRIVIAL(error)
6785 << boost::format("%1%, can not find filament_volume_map/filament_nozzle_map/filament_map under Nozzle Manual mode") % __LINE__;
6786 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, index + 1, cli_errors[CLI_INVALID_PARAMS], sliced_info);
6788 }
6789
6790 if (mode == fmmManual) {
6791 // Build the volume map when absent: filaments on a single-volume extruder
6792 // print with that extruder's volume; a mixed-volume extruder keeps the
6793 // per-filament choice (default Standard).
6794 std::vector<NozzleVolumeType> using_nozzle_volume_type = new_nozzle_volume_type;
6795 using_nozzle_volume_type.resize(new_extruder_count, nvtStandard);
6796 if (auto extruder_nozzle_stats_opt = m_print_config.option<ConfigOptionStrings>("extruder_nozzle_stats")) {
6797 auto nozzle_stats = get_extruder_nozzle_stats(extruder_nozzle_stats_opt->values);
6798 for (int e_index = 0; e_index < new_extruder_count && e_index < (int) nozzle_stats.size(); e_index++) {
6799 if (nozzle_stats[e_index].size() > 1) {
6800 using_nozzle_volume_type[e_index] = nvtHybrid;
6801 BOOST_LOG_TRIVIAL(info) << boost::format("%1% : extruder %2%, set nozzle_volume_type to hybrid ") % __LINE__ % (e_index + 1);
6802 }
6803 }
6804 }
6805 std::vector<int> &manual_volume_maps = m_extra_config.option<ConfigOptionInts>("filament_volume_map", true)->values;
6806 // default to standard flow
6807 manual_volume_maps.resize(filament_count, (int) (nvtStandard));
6808 for (int f_index = 0; f_index < filament_count && f_index < (int) filament_maps.size(); f_index++) {
6809 int f_extruder_index = filament_maps[f_index] - 1;
6810 if (f_extruder_index >= 0 && f_extruder_index < new_extruder_count &&
6811 using_nozzle_volume_type[f_extruder_index] != nvtHybrid) {
6812 manual_volume_maps[f_index] = int(using_nozzle_volume_type[f_extruder_index]);
6813 BOOST_LOG_TRIVIAL(info) << boost::format("%1% : filament %2% extruder %3%, set filament_volume_map to %4% ") % __LINE__ % (f_index + 1) % (f_extruder_index + 1) % manual_volume_maps[f_index];
6814 }
6815 }
6816 }
6817
6818 if (m_extra_config.has("filament_volume_map")) {
6819 part_plate->set_filament_volume_maps(m_extra_config.option<ConfigOptionInts>("filament_volume_map")->values);
6820 }
6821 if (m_extra_config.has("filament_nozzle_map")) {
6822 part_plate->set_filament_nozzle_maps(m_extra_config.option<ConfigOptionInts>("filament_nozzle_map")->values);
6823 }
6824 }
6825 else if (!support_multi_nozzle && (mode == fmmNozzleManual)) {
6826 BOOST_LOG_TRIVIAL(error)
6827 << boost::format("%1%, Nozzle Manual mode not supported for %2%") % __LINE__ % new_printer_name;
6828 record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, index + 1, cli_errors[CLI_INVALID_PARAMS], sliced_info);
6830 }
6831
6832 for (int index = 0; index < filament_maps.size(); index++)
6833 {
6834 int filament_extruder = filament_maps[index];
6835 if (unprintable_filament_ids[filament_extruder - 1].find(index + 1) != unprintable_filament_ids[filament_extruder - 1].end())
6836 {
6837 BOOST_LOG_TRIVIAL(error) << boost::format("plate %1% : some filaments can not be mapped under manual mode for multi extruder printer ") % (index + 1);
6838 record_exit_reson(outfile_dir, CLI_FILAMENT_CAN_NOT_MAP, index + 1, cli_errors[CLI_FILAMENT_CAN_NOT_MAP], sliced_info);
6840 }
6841 }
6842
6843 for (int f_index = 0; f_index < plate_filaments.size(); f_index++) {
6844 for (int f_index = 0; f_index < plate_filaments.size(); f_index++) {
6845 if (plate_filaments[f_index] <= filament_count) {
6846 int filament_extruder = filament_maps[plate_filaments[f_index] - 1];
6847 std::string filament_type;
6848 m_print_config.get_filament_type(filament_type, plate_filaments[f_index] - 1);
6849 auto *filament_printable_status = dynamic_cast<const ConfigOptionInts *>(m_print_config.option("filament_printable"));
6850 if (filament_printable_status && (filament_printable_status->values.size() >= plate_filaments[f_index])) {
6851 int status = filament_printable_status->values.at(plate_filaments[f_index] - 1);
6852 if (!(status >> (filament_extruder - 1) & 1)) {
6853 BOOST_LOG_TRIVIAL(error)
6854 << boost::format(
6855 "plate %1% : filament %2% can not be printed on extruder %3%, under manual mode for multi extruder printer") %
6856 (index + 1) % filament_type % filament_extruder;
6860 }
6861 }
6862 }
6863 }
6864 }
6865 }
6866 }
6867
6868 plate_triangle_counts[index] = triangle_count;
6869 plate_object_count[index] = printable_instances;
6870 BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": load cached data success, go on.";
6871 }
6872 // BBS: TODO
6873 //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)
6874 // % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) % count << std::endl;
6875#endif
6876 DynamicPrintConfig new_print_config = m_print_config;
6877 new_print_config.apply(*part_plate->config());
6878 new_print_config.apply(m_extra_config, true);
6879 if (m_print_config.option<ConfigOptionFloat>("layer_height"))
6880 sliced_info.layer_height = m_print_config.option<ConfigOptionFloat>("layer_height")->value;
6881 if (m_print_config.option<ConfigOptionInt>("wall_loops"))
6882 sliced_info.wall_loops = m_print_config.option<ConfigOptionInt>("wall_loops")->value;
6883 if (m_print_config.option<ConfigOptionPercent>("sparse_infill_density"))
6884 sliced_info.sparse_infill_density = m_print_config.option<ConfigOptionPercent>("sparse_infill_density")->value;
6885 if (new_extruder_count > 1) {
6887 if (new_print_config.option<ConfigOptionEnum<FilamentMapMode>>("filament_map_mode"))
6888 map_mode = new_print_config.option<ConfigOptionEnum<FilamentMapMode>>("filament_map_mode")->value;
6889
6890 if (map_mode < fmmManual) {
6891 //set default params for auto map
6892 std::vector<std::string> extruder_ams_count(new_extruder_count, "");
6893 std::vector<std::vector<DynamicPrintConfig>> extruder_filament_info(new_extruder_count, std::vector<DynamicPrintConfig>());
6894 int color_count = 0;
6895
6896 const ConfigOptionStrings* filament_type = dynamic_cast<const ConfigOptionStrings *>(m_print_config.option("filament_type"));
6897 std::vector<std::string> types = filament_type ? filament_type->vserialize() : std::vector<std::string>{"PLA"};
6898
6899 for (int e_index = 0; e_index < new_extruder_count; e_index++)
6900 {
6901 extruder_ams_count[e_index] = "1#0|4#1";
6902 for (int color_index = 0; color_index < 4; color_index++)
6903 {
6904 DynamicPrintConfig temp_config;
6905 std::vector<std::string> temp_colors(1, "#FFFFFFFF");
6906 std::vector<std::string> temp_types(1, "PLA");
6907 //if (filament_color) {
6908 // temp_colors[0] = colors[color_count % colors.size()];
6909 //}
6910 if (filament_type)
6911 temp_types[0] = types[color_count % types.size()];
6912
6913 temp_config.option<ConfigOptionStrings>("filament_colour", true)->values = temp_colors;
6914 temp_config.option<ConfigOptionStrings>("filament_type", true)->values = temp_types;
6915 temp_config.option<ConfigOptionBools>("filament_is_support",true)->values = { 0 };
6916 extruder_filament_info[e_index].push_back(std::move(temp_config));
6917 color_count++;
6918 }
6919 }
6920 new_print_config.option<ConfigOptionStrings>("extruder_ams_count", true)->values = extruder_ams_count;
6921 print_fff->set_extruder_filament_info(extruder_filament_info);
6922 }
6923 }
6924
6925 //set filament_map
6926 std::vector<int>& final_filament_maps = new_print_config.option<ConfigOptionInts>("filament_map", true)->values;
6927 if (final_filament_maps.size() < filament_count)
6928 final_filament_maps.resize(filament_count, 1);
6929 if (new_extruder_count == 1) {
6930 for (int index = 0; index < filament_count; index++)
6931 final_filament_maps[index] = 1;
6932 }
6933 if(!new_print_config.has("nozzle_volume_type")) {
6934 //set default nozzle_volume_type
6935 ConfigOptionEnumsGeneric* final_nozzle_volume_type_opt = new_print_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true);
6936 final_nozzle_volume_type_opt->values.resize(new_extruder_count, nvtStandard);
6937 }
6938 print->apply(model, new_print_config);
6939 BOOST_LOG_TRIVIAL(info) << boost::format("set no_check to %1%:")%no_check;
6940 print->set_no_check_flag(no_check);//BBS
6941
6942 // Set is_BBL_printer flag before validation as the validation depends on it.
6943 std::string& printer_model_string = new_print_config.opt_string("printer_model", true);
6944 bool is_bbl_vendor_preset = false;
6945
6946 if (!printer_model_string.empty()) {
6947 is_bbl_vendor_preset = (printer_model_string.compare(0, 9, "Bambu Lab") == 0);
6948 BOOST_LOG_TRIVIAL(info) << boost::format("printer_model_string: %1%, is_bbl_vendor_preset %2%")%printer_model_string %is_bbl_vendor_preset;
6949 }
6950 else {
6951 if (!new_printer_name.empty())
6952 is_bbl_vendor_preset = (new_printer_name.compare(0, 9, "Bambu Lab") == 0);
6953 else if (!current_printer_system_name.empty())
6954 is_bbl_vendor_preset = (current_printer_system_name.compare(0, 9, "Bambu Lab") == 0);
6955 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;
6956 }
6957 (dynamic_cast<Print*>(print))->is_BBL_printer() = is_bbl_vendor_preset;
6958
6959 std::vector<StringObjectException> warnings;
6960 print_fff->set_check_multi_filaments_compatibility(!allow_mix_temp);
6961 auto err = print->validate(&warnings);
6962 if (!err.string.empty()) {
6963 if ((STRING_EXCEPT_LAYER_HEIGHT_EXCEEDS_LIMIT == err.type) && no_check) {
6964 BOOST_LOG_TRIVIAL(warning) << "got warnings: "<< err.string << std::endl;
6965 }
6966 else {
6967 BOOST_LOG_TRIVIAL(error) << "got error when validate: "<< err.string << std::endl;
6968 boost::nowide::cerr << err.string << std::endl;
6969 int validate_error;
6970 switch (err.type)
6971 {
6973 validate_error = CLI_FILAMENT_NOT_MATCH_BED_TYPE;
6974 break;
6976 validate_error = CLI_FILAMENTS_DIFFERENT_TEMP;
6977 break;
6979 validate_error = CLI_OBJECT_COLLISION_IN_SEQ_PRINT;
6980 break;
6982 validate_error = CLI_OBJECT_COLLISION_IN_LAYER_PRINT;
6983 break;
6984 default:
6985 validate_error = CLI_VALIDATE_ERROR;
6986 break;
6987 }
6988 if (no_check)
6989 record_exit_reson(outfile_dir, validate_error, index+1, err.string, sliced_info);
6990 else
6991 record_exit_reson(outfile_dir, validate_error, index+1, cli_errors[validate_error], sliced_info);
6992 flush_and_exit(validate_error);
6993 }
6994 }
6995 else if (!warnings.empty()) {
6996 for (const auto& w : warnings)
6997 BOOST_LOG_TRIVIAL(warning) << "got warnings: "<< w.string << std::endl;
6998 }
6999
7000 if (print->empty()) {
7001 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;
7004 }
7005 else {
7006 if (pre_check && (partplate_list.get_plate_count() > 1)) //continue to next plate directly
7007 continue;
7008 try {
7009 std::string outfile_final;
7010 BOOST_LOG_TRIVIAL(info) << "start Print::process for partplate "<<index+1 << std::endl;
7011#if defined(__linux__) || defined(__LINUX__)
7012 BOOST_LOG_TRIVIAL(info) << "cli callback mgr started: "<<g_cli_callback_mgr.m_started << std::endl;
7013 if (g_cli_callback_mgr.is_started()) {
7014 BOOST_LOG_TRIVIAL(info) << "set print's callback to cli_status_callback.";
7015 print->set_status_callback(cli_status_callback);
7016 g_cli_callback_mgr.set_plate_info(index+1, (plate_to_slice== 0)?partplate_list.get_plate_count():1);
7017 if (!warnings.empty()) {
7018 std::string warning_text;
7019 for (const auto& w : warnings) {
7020 if (!warning_text.empty())
7021 warning_text += "\n";
7022 warning_text += w.string;
7023 }
7024 PrintBase::SlicingStatus slicing_status{4, warning_text, 0, 0};
7025 cli_status_callback(slicing_status);
7026 }
7027 else {
7028 PrintBase::SlicingStatus slicing_status{4, "Slicing begins"};
7029 cli_status_callback(slicing_status);
7030 }
7031 }
7032 else {
7033 BOOST_LOG_TRIVIAL(info) << "set print's callback to default_status_callback.";
7035 }
7036#else
7037 BOOST_LOG_TRIVIAL(info) << "set print's callback to default_status_callback.";
7039#endif
7040
7041 //update information for brim
7042 const PrintConfig& print_config = print_fff->config();
7045 if (load_slicedata) {
7046 std::string plate_dir = load_slice_data_dir+"/"+std::to_string(index+1);
7047 int ret = print->load_cached_data(plate_dir);
7048 if (ret) {
7049 BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": load Slicing data error, ret=" << ret;
7050 BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": switch normal slicing";
7051 print->process();
7052 }
7053 else {
7054 BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": load cached data success, go on.";
7055#if defined(__linux__) || defined(__LINUX__)
7056 if (g_cli_callback_mgr.is_started()) {
7057 PrintBase::SlicingStatus slicing_status{69, "Cache data loaded"};
7058 cli_status_callback(slicing_status);
7059 }
7060#endif
7061 print->process(nullptr, true);
7062 BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": finished print::process.";
7063 }
7064 }
7065 else {
7066 print->process(&time_using_cache);
7067 BOOST_LOG_TRIVIAL(info) << "print::process: first time_using_cache is " << time_using_cache << " secs.";
7068 }
7069 if (printer_technology == ptFFF) {
7070 // Read the engine's final grouping back onto the plate so an exported
7071 // project (--export-3mf / gcode.3mf) carries the concrete maps in its
7072 // plate settings, matching what a GUI slice persists.
7073 // Orca: deliberately gated to multi-extruder printers so single-extruder
7074 // exports keep their plate settings unchanged.
7075 if (new_extruder_count > 1) {
7076 FilamentMapMode current_map_mode = print_fff->config().filament_map_mode.value;
7077 if (is_auto_filament_map_mode(current_map_mode)) {
7078 part_plate->set_filament_maps(print_fff->get_filament_maps());
7079 part_plate->set_filament_volume_maps(print_fff->get_filament_volume_maps());
7080 }
7081 if (current_map_mode != FilamentMapMode::fmmNozzleManual) {
7082 part_plate->set_filament_nozzle_maps(print_fff->get_filament_nozzle_maps());
7083 }
7084 }
7085
7086 std::string conflict_result = print_fff->get_conflict_string();
7087 if (!conflict_result.empty()) {
7088 BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": found slicing result conflict!"<< std::endl;
7091 }
7092
7093 //check the warnings
7094 if (!g_slicing_warnings.empty())
7095 {
7096 for (unsigned int i = 0; i < g_slicing_warnings.size(); i++)
7097 {
7098 PrintBase::SlicingStatus& status = g_slicing_warnings[i];
7100 {
7101 sliced_plate_info.warning_message = status.text;
7102
7104 BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": found NON_CRITICAL slicing warnings: "<<status.text <<std::endl;
7105 // Always record for AI/CI consumers; under --strict, elevate to a
7106 // non-zero exit so scripted pipelines don't ship a "warning OK" slice.
7107 cli_record_warning(sliced_info, "slicing_warning_non_critical",
7108 nlohmann::json{{"plate_id", index+1}, {"text", status.text}});
7109 if (sliced_info.strict_mode) {
7110 sliced_info.sliced_plates.push_back(sliced_plate_info);
7111 record_exit_reson(outfile_dir, CLI_SLICING_ERROR, index+1, cli_errors[CLI_SLICING_ERROR], sliced_info);
7113 }
7114 }
7115 else {
7116 BOOST_LOG_TRIVIAL(warning) << boost::format("plate %1%: found slicing warnings: %2%, no_check=%3%")%(index+1) %status.text %no_check;
7117 if (!no_check) {
7118 //only following message will be reported under import mode
7121 {
7122 sliced_info.sliced_plates.push_back(sliced_plate_info);
7123 record_exit_reson(outfile_dir, CLI_SLICING_ERROR, index+1, cli_errors[CLI_SLICING_ERROR], sliced_info);
7125 }
7126 }
7127 }
7128 }
7129 }
7130 g_slicing_warnings.clear();
7131 }
7132 sliced_plate_info.triangle_count = plate_triangle_counts[index];
7133
7134 // The outfile is processed by a PlaceholderParser.
7135 //outfile = part_plate->get_tmp_gcode_path();
7136 if (outfile_dir.empty()) {
7137 outfile = part_plate->get_tmp_gcode_path();
7138 }
7139 else {
7140 outfile = outfile_dir + "/plate_" + std::to_string(index + 1) + ".gcode";
7141 part_plate->set_tmp_gcode_path(outfile);
7142 }
7143 BOOST_LOG_TRIVIAL(info) << "process finished, will export gcode temporarily to " << outfile << std::endl;
7144 temp_time = (long long)Slic3r::Utils::get_current_time_utc();
7145 outfile = print_fff->export_gcode(outfile, gcode_result, nullptr);
7146 time_using_cache = time_using_cache + ((long long)Slic3r::Utils::get_current_time_utc() - temp_time);
7147 BOOST_LOG_TRIVIAL(info) << "export_gcode finished: time_using_cache update to " << time_using_cache << " secs.";
7148 if (gcode_result && gcode_result->gcode_check_result.error_code) {
7149 //found gcode error
7150 if ((gcode_result->gcode_check_result.error_code & 0b11100)>0)
7151 BOOST_LOG_TRIVIAL(error) << "plate " << index + 1 << ": found gcode in unprintable area of the printers! gcode_result->gcode_check_result.error_code = "
7152 << gcode_result->gcode_check_result.error_code << std::endl;
7153 else
7154 BOOST_LOG_TRIVIAL(error) << "plate " << index + 1 << ": found gcode in unprintable area of multi extruder printers! gcode_result->gcode_check_result.error_code = "
7155 << gcode_result->gcode_check_result.error_code << std::endl;
7158 }
7159 // Without an output directory the G-code is a temporary file, kept only in an exported 3MF.
7160 if (export_statistics && !outfile_dir.empty()) {
7161 try {
7162 export_gcode_statistics(*gcode_result, gcode_statistics_path(outfile));
7163 } catch (const std::exception &ex) {
7164 BOOST_LOG_TRIVIAL(error) << "plate " << index + 1 << ": " << ex.what();
7167 }
7168 }
7169
7170
7171 //outfile_final = (dynamic_cast<Print*>(print))->print_statistics().finalize_output_path(outfile);
7172 //m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, [this](const ThumbnailsParams& params) { return this->render_thumbnails(params); });
7173 }/* else {
7174 outfile = sla_print.output_filepath(outfile);
7175 // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
7176 outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
7177 sla_archive.export_print(outfile_final, sla_print);
7178 }*/
7179 /*if (outfile != outfile_final) {
7180 if (Slic3r::rename_file(outfile, outfile_final)) {
7181 boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
7182 record_exit_reson(outfile_dir, 1, index+1, cli_errors[1], sliced_info);
7183 flush_and_exit(1);
7184 }
7185 outfile = outfile_final;
7186 }*/
7187 // Run the post-processing scripts if defined.
7188 //run_post_process_scripts(outfile, print->full_print_config());
7189 BOOST_LOG_TRIVIAL(info) << "Slicing result exported to " << outfile << std::endl;
7190 part_plate->update_slice_result_valid_state(true);
7191#if defined(__linux__) || defined(__LINUX__)
7192 if (g_cli_callback_mgr.is_started()) {
7193 PrintBase::SlicingStatus slicing_status{100, "Slicing finished"};
7194 cli_status_callback(slicing_status);
7195 }
7196#endif
7197 if (export_slicedata) {
7198 BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ":will export Slicing data to " << export_slice_data_dir;
7199 std::string plate_dir = export_slice_data_dir+"/"+std::to_string(index+1);
7200 bool with_space = (get_logging_level() >= 4)?true:false;
7201 int ret = print->export_cached_data(plate_dir, with_space);
7202 if (ret) {
7203 BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": export Slicing data error, ret=" << ret;
7204 export_slicedata_error = true;
7205 if (fs::exists(plate_dir))
7206 fs::remove_all(plate_dir);
7207 record_exit_reson(outfile_dir, ret, index+1, cli_errors[ret], sliced_info);
7208 flush_and_exit(ret);
7209 }
7210 }
7211 end_time = (long long)Slic3r::Utils::get_current_time_utc();
7212 sliced_plate_info.sliced_time = end_time - start_time;
7213 sliced_plate_info.sliced_time_with_cache = time_using_cache;
7214
7215 if (max_slicing_time_per_plate != 0) {
7216 long long time_cost = end_time - start_time;
7217 if (time_cost > max_slicing_time_per_plate) {
7218 sliced_plate_info.warning_message = (boost::format("plate %1%'s slice time %2% exceeds the limit %3%, return error.")%(index+1) %time_cost %max_slicing_time_per_plate).str();
7219 BOOST_LOG_TRIVIAL(error) << sliced_plate_info.warning_message;
7220 sliced_info.sliced_plates.push_back(sliced_plate_info);
7223 }
7224 }
7225 sliced_info.sliced_plates.push_back(sliced_plate_info);
7226 } catch (const Slic3r::SlicingErrors &exs) {
7227 const std::string message = print_fff ? print_fff->slicing_errors_message(exs) : std::string(exs.what());
7228 BOOST_LOG_TRIVIAL(error) << "found slicing or export error for partplate " << index+1 << ": " << message;
7229 boost::nowide::cerr << message << std::endl;
7230 record_exit_reson(outfile_dir, CLI_SLICING_ERROR, index+1, message, sliced_info);
7232 } catch (const std::exception &ex) {
7233 BOOST_LOG_TRIVIAL(error) << "found slicing or export error for partplate "<<index+1 << std::endl;
7234 boost::nowide::cerr << ex.what() << std::endl;
7235 //continue;
7236 record_exit_reson(outfile_dir, CLI_SLICING_ERROR, index+1, cli_errors[CLI_SLICING_ERROR], sliced_info);
7238 }
7239 }
7240 }
7241 if (pre_check&& (partplate_list.get_plate_count() > 1))
7242 pre_check = false;
7243 else
7244 finished = true;
7245 }//end for partplate
7246
7247#if defined(__linux__) || defined(__LINUX__)
7248 if (g_cli_callback_mgr.is_started()) {
7249 int plate_count = (plate_to_slice== 0)?partplate_list.get_plate_count():1;
7250 g_cli_callback_mgr.set_plate_info(0, plate_count);
7251 }
7252#endif
7253/*
7254 print.center = ! m_config.has("center")
7255 && ! m_config.has("align_xy")
7256 && ! m_config.opt_bool("dont_arrange");
7257 print.set_model(model);
7258
7259 // start chronometer
7260 typedef std::chrono::high_resolution_clock clock_;
7261 typedef std::chrono::duration<double, std::ratio<1> > second_;
7262 std::chrono::time_point<clock_> t0{ clock_::now() };
7263
7264 const std::string outfile = this->output_filepath(model, IO::Gcode);
7265 try {
7266 print.export_gcode(outfile);
7267 } catch (std::runtime_error &e) {
7268 boost::nowide::cerr << e.what() << std::endl;
7269 return 1;
7270 }
7271 BOOST_LOG_TRIVIAL(info) << "G-code exported to " << outfile << std::endl;
7272
7273 // output some statistics
7274 double duration { std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
7275 BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0)
7276 << "Done. Process took " << (duration/60) << " minutes and "
7277 << std::setprecision(3)
7278 << std::fmod(duration, 60.0) << " seconds." << std::endl
7279 << std::setprecision(2)
7280 << "Filament required: " << print.total_used_filament() << "mm"
7281 << " (" << print.total_extruded_volume()/1000 << "cm3)" << std::endl;
7282*/
7283 }
7284 } else {
7285 boost::nowide::cerr << "error: option not supported yet: " << opt_key << std::endl;
7288 }
7289 }
7290
7291 global_begin_time = (long long)Slic3r::Utils::get_current_time_utc();
7292 if (export_to_3mf) {
7293 //BBS: export as bbl 3mf
7294 std::vector<ThumbnailData *> thumbnails, no_light_thumbnails, top_thumbnails, pick_thumbnails;
7295 std::vector<PlateBBoxData*> plate_bboxes;
7296 PlateDataPtrs plate_data_list;
7297 partplate_list.store_to_3mf_structure(plate_data_list, true, -1, export_statistics);
7298
7299 if (sliced_plate == -1) {
7300 for (int i = 0; i < plate_data_list.size(); i++) {
7301 Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i);
7302 plate_object_count[i] = part_plate->printable_instance_size();
7303 }
7304 }
7305 else if (sliced_plate == 0){
7306 //slicing all
7307 for (int i = 0; i < plate_data_list.size(); i++) {
7308 if (skip_useless_pick && (plate_object_count[i] == 1)) {
7309 BOOST_LOG_TRIVIAL(info) << boost::format("only has 1 object, set plate %1%'s is_label_object_enabled from %2% to false")%(i+1) % (plate_data_list[i]->is_label_object_enabled);
7310 plate_data_list[i]->is_label_object_enabled = false;
7311 }
7312 }
7313 }
7314 else {
7315 if (skip_useless_pick && (plate_object_count[sliced_plate - 1] == 1)) {
7316 BOOST_LOG_TRIVIAL(info) << boost::format("only has 1 object, set plate %1%'s is_label_object_enabled from %2% to false")%sliced_plate % (plate_data_list[sliced_plate - 1]->is_label_object_enabled);
7317 plate_data_list[sliced_plate - 1]->is_label_object_enabled = false;
7318 }
7319 }
7320
7321 if (!outfile_dir.empty()) {
7322 export_3mf_file = outfile_dir + "/"+export_3mf_file;
7323 }
7324
7325#if defined(__linux__) || defined(__LINUX__)
7326 if (g_cli_callback_mgr.is_started()) {
7327 PrintBase::SlicingStatus slicing_status{94, "Generate thumbnails"};
7328 cli_status_callback(slicing_status);
7329 }
7330#endif
7331
7332 bool need_regenerate_thumbnail = oriented_or_arranged || regenerate_thumbnails;
7333 bool need_regenerate_no_light_thumbnail = oriented_or_arranged || regenerate_thumbnails;
7334 bool need_regenerate_top_thumbnail = oriented_or_arranged || regenerate_thumbnails;
7335 bool need_create_thumbnail_group = false, need_create_no_light_group = false, need_create_top_group = false;
7336
7337 // get type and color for platedata
7338 const ConfigOptionStrings* filament_color = dynamic_cast<const ConfigOptionStrings *>(m_print_config.option("filament_colour"));
7339 auto* filament_id = dynamic_cast<const ConfigOptionStrings*>(m_print_config.option("filament_ids"));
7340 const ConfigOptionFloats* nozzle_diameter_option = dynamic_cast<const ConfigOptionFloats *>(m_print_config.option("nozzle_diameter"));
7341 std::string nozzle_diameter_str;
7342 if (nozzle_diameter_option)
7343 nozzle_diameter_str = nozzle_diameter_option->serialize();
7344#ifdef SLIC3R_GUI
7345 // A Bambu printer reads slice_info.config and knows only its own catalog ids. The GUI
7346 // gates the same translation on PresetBundle::is_bbl_vendor(); the CLI has no
7347 // PresetBundle, so reuse the printer_model prefix that already decides
7348 // Print::is_BBL_printer() for this same run.
7349 auto* printer_model_option = dynamic_cast<const ConfigOptionString*>(m_print_config.option("printer_model"));
7350 const bool is_bbl_printer = printer_model_option && printer_model_option->value.compare(0, 9, "Bambu Lab") == 0;
7351 // No wxApp on the CLI path, so there is no live agent to ask; the translator is stateless
7352 // over a lazily loaded map, so one instance serves every plate and filament below.
7353 // ORCA TODO: this assumes Bambu's is the only agent with a catalog of its own. Once another
7354 // agent carries one, resolve the agent from the selected printer the way
7355 // GUI_App::resolve_printer_agent_id does, rather than hard-coding BBLPrinterAgent here.
7356 const BBLPrinterAgent bbl_agent;
7357#endif /* SLIC3R_GUI */
7358
7359 for (int i = 0; i < plate_data_list.size(); i++) {
7360 PlateData *plate_data = plate_data_list[i];
7361 bool skip_this_plate = ((plate_to_slice != 0) && (plate_to_slice != (i + 1)))?true:false;
7362
7363 plate_data->skipped_objects = plate_skipped_objects[i];
7364 if (!printer_model_id.empty())
7365 plate_data->printer_model_id = printer_model_id;
7366 if (!nozzle_diameter_str.empty())
7367 plate_data->nozzle_diameters = nozzle_diameter_str;
7368
7369 for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) {
7370 // get_at() on an empty vector option is UB - these can be unpopulated on a from-scratch slice
7371 std::string display_filament_type;
7372 it->type = m_print_config.get_filament_type(display_filament_type, it->id);
7373 it->color = (filament_color && !filament_color->values.empty()) ? filament_color->get_at(it->id) : "#FFFFFF";
7374 it->filament_id = (filament_id && !filament_id->values.empty()) ? filament_id->get_at(it->id) : "";
7375#ifdef SLIC3R_GUI
7376 if (is_bbl_printer)
7377 it->filament_id = bbl_agent.from_orca_filament_id(it->filament_id);
7378#endif /* SLIC3R_GUI */
7379 }
7380
7381 if (!plate_data->plate_thumbnail.is_valid()) {
7382 if (!oriented_or_arranged && !regenerate_thumbnails && plate_data_src.size() > i)
7383 plate_data->thumbnail_file = plate_data_src[i]->thumbnail_file;
7384 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;
7385 if (plate_data->thumbnail_file.empty() || (!boost::filesystem::exists(plate_data->thumbnail_file))) {
7386 BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file also not there, need to regenerate")%(i+1);
7387 if (!skip_this_plate) {
7388 need_regenerate_thumbnail = true;
7389 need_create_thumbnail_group = true;
7390 }
7391 }
7392 else {
7393 if (regenerate_thumbnails) {
7394 BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file %2% cleared, need to regenerate")%(i+1) %plate_data->thumbnail_file;
7395 plate_data->thumbnail_file.clear();
7396 }
7397 else
7398 BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file exists, no need to regenerate")%(i+1);
7399 }
7400 }
7401 else {
7402 if (regenerate_thumbnails)
7403 plate_data->plate_thumbnail.reset();
7404
7405 if (!skip_this_plate) {
7406 need_create_thumbnail_group = true;
7407 }
7408 }
7409
7410 if (plate_data->no_light_thumbnail_file.empty()) {
7411 if (!regenerate_thumbnails && (plate_data_src.size() > i)) {
7412 plate_data->no_light_thumbnail_file = plate_data_src[i]->no_light_thumbnail_file;
7413 }
7414 if (plate_data->no_light_thumbnail_file.empty() || (!boost::filesystem::exists(plate_data->no_light_thumbnail_file))) {
7415 BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s no_light_thumbnail_file %2% also not there, need to regenerate")%(i+1)%plate_data->no_light_thumbnail_file;
7416 if (!skip_this_plate) {
7417 need_regenerate_no_light_thumbnail = true;
7418 need_create_no_light_group = true;
7419 }
7420 }
7421 else {
7422 if (regenerate_thumbnails) {
7423 BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s no_light_thumbnail file %2% cleared, need to regenerate")%(i+1) %plate_data->no_light_thumbnail_file;
7424 plate_data->no_light_thumbnail_file.clear();
7425 }
7426 else
7427 BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s no_light_thumbnail file exists, no need to regenerate")%(i+1);
7428 }
7429 }
7430
7431 if (plate_data->top_file.empty() || plate_data->pick_file.empty()) {
7432 if (!regenerate_thumbnails && (plate_data_src.size() > i)) {
7433 plate_data->top_file = plate_data_src[i]->top_file;
7434 plate_data->pick_file = plate_data_src[i]->pick_file;
7435 }
7436 if (plate_data->top_file.empty()|| plate_data->pick_file.empty()
7437 || (!boost::filesystem::exists(plate_data->top_file)) || (!boost::filesystem::exists(plate_data->pick_file))) {
7438 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;
7439 if (!skip_this_plate) {
7440 need_regenerate_top_thumbnail = true;
7441 need_create_top_group = true;
7442 }
7443 }
7444 else {
7445 if (regenerate_thumbnails) {
7446 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;
7447 plate_data->top_file.clear();
7448 plate_data->pick_file.clear();
7449 }
7450 else
7451 BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_thumbnail file exists, no need to regenerate")%(i+1);
7452 }
7453 }
7454 }
7455
7456 if (need_regenerate_thumbnail || need_regenerate_no_light_thumbnail || need_regenerate_top_thumbnail) {
7457 std::vector<std::string> colors;
7458 if (filament_color) {
7459 colors= filament_color->vserialize();
7460 }
7461 else
7462 colors.push_back("#FFFFFFFF");
7463
7464 std::vector<ColorRGBA> colors_out(colors.size());
7465 unsigned char rgb_color[4] = {};
7466 for (const std::string& color : colors) {
7468 size_t color_idx = &color - &colors.front();
7469 colors_out[color_idx] = ColorRGBA(float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, float(rgb_color[3]) / 255.f);
7470 }
7471
7472 int glfw_major, glfw_minor, glfw_revision;
7473 glfwGetVersion(&glfw_major, &glfw_minor, &glfw_revision);
7474 BOOST_LOG_TRIVIAL(info) << boost::format("GLFW version %1%.%2%.%3%") % glfw_major % glfw_minor % glfw_revision;
7475
7476 bool thumbnail_opengl_ready = false;
7477 glfwSetErrorCallback(glfw_callback);
7478 int ret = glfwInit();
7479 if (ret == GLFW_FALSE) {
7480 const char* error_msg;
7481 int code = glfwGetError(&error_msg);
7482 BOOST_LOG_TRIVIAL(error) << "glfwInit return error, Error code: " << code << ", Error: " << error_msg << std::endl;
7483 }
7484 else {
7485 BOOST_LOG_TRIVIAL(info) << "glfwInit Success."<< std::endl;
7486 // Request OrcaSlicer's minimum OpenGL version, independently of the GLFW library version.
7487 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
7488 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
7489 glfwWindowHint(GLFW_RED_BITS, 8);
7490 glfwWindowHint(GLFW_GREEN_BITS, 8);
7491 glfwWindowHint(GLFW_BLUE_BITS, 8);
7492 glfwWindowHint(GLFW_ALPHA_BITS, 8);
7493 glfwWindowHint(GLFW_VISIBLE, false);
7494 //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
7495 //glfwDisable(GLFW_AUTO_POLL_EVENTS);
7496#ifdef __WXMAC__
7497 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
7498 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
7499#else
7500 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
7501#endif
7502
7503 GLFWwindow* window = glfwCreateWindow(640, 480, "base_window", NULL, NULL);
7504#ifndef __WXMAC__
7505 if (window == NULL) {
7506 // Some drivers (e.g. older Mesa) only expose compatibility profile 3.0; take whatever they offer.
7507 BOOST_LOG_TRIVIAL(warning) << "Failed to create OpenGL 3.3 compatibility context, retrying with driver default" << std::endl;
7508 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
7509 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
7510 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
7511 window = glfwCreateWindow(640, 480, "base_window", NULL, NULL);
7512 }
7513#endif
7514 if (window == NULL)
7515 {
7516 BOOST_LOG_TRIVIAL(error) << "Failed to create GLFW window; skipping thumbnail rendering for CLI export" << std::endl;
7517 }
7518 else {
7519 glfwMakeContextCurrent(window);
7520 thumbnail_opengl_ready = true;
7521 }
7522 }
7523
7524 //opengl manager related logic
7525 if (!thumbnail_opengl_ready) {
7526 BOOST_LOG_TRIVIAL(error) << "OpenGL context unavailable; skip thumbnail generating" << std::endl;
7527 need_create_thumbnail_group = false;
7528 need_create_no_light_group = false;
7529 need_create_top_group = false;
7530 }
7531 else
7532 {
7533 GUI::OpenGLManager opengl_mgr;
7534 bool opengl_valid = opengl_mgr.init_gl(false);
7535 if (!opengl_valid) {
7536 BOOST_LOG_TRIVIAL(error) << "init opengl failed! skip thumbnail generating" << std::endl;
7537 }
7538 else {
7539 BOOST_LOG_TRIVIAL(info) << "gladLoadGL Success." << std::endl;
7540 GLVolumeCollection glvolume_collection;
7541 Model &model = m_models[0];
7542 int obj_extruder_id = 1, volume_extruder_id = 1;
7543 for (unsigned int obj_idx = 0; obj_idx < (unsigned int)model.objects.size(); ++ obj_idx) {
7544 const ModelObject &model_object = *model.objects[obj_idx];
7545 const ConfigOption* option = model_object.config.option("extruder");
7546 if (option)
7547 obj_extruder_id = (dynamic_cast<const ConfigOptionInt *>(option))->getInt();
7548 else
7549 obj_extruder_id = 1;
7550 for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) {
7551 const ModelVolume &model_volume = *model_object.volumes[volume_idx];
7552 option = model_volume.config.option("extruder");
7553 if (option)
7554 volume_extruder_id = (dynamic_cast<const ConfigOptionInt *>(option))->getInt();
7555 else
7556 volume_extruder_id = obj_extruder_id;
7557
7558 BOOST_LOG_TRIVIAL(debug) << boost::format("volume %1%'s extruder_id %2%")%volume_idx %volume_extruder_id;
7559 //if (!model_volume.is_model_part())
7560 // continue;
7561 for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) {
7562 const ModelInstance &model_instance = *model_object.instances[instance_idx];
7563 glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, "volume", true, false, true);
7564 //glvolume_collection.volumes.back()->geometry_id = key.geometry_id;
7565 std::string color = filament_color?filament_color->get_at(volume_extruder_id - 1):"#00FF00FF";
7566
7567 BOOST_LOG_TRIVIAL(debug) << boost::format("volume %1%'s color %2%")%volume_idx %color;
7568
7569 unsigned char rgb_color[4] = {};
7571
7572 ColorRGBA new_color;
7573 new_color.r(float(rgb_color[0]) / 255.f);
7574 new_color.g(float(rgb_color[1]) / 255.f);
7575 new_color.b(float(rgb_color[2]) / 255.f);
7576 new_color.a(float(rgb_color[3]) / 255.f);
7577
7578 glvolume_collection.volumes.back()->set_render_color(new_color);
7579 glvolume_collection.volumes.back()->set_color(new_color);
7580 glvolume_collection.volumes.back()->printable = model_instance.printable;
7581 }
7582 }
7583 }
7584
7585 ThumbnailsParams thumbnail_params;
7586 GLShaderProgram* shader = opengl_mgr.get_shader("thumbnail");
7587 if (!shader) {
7588 BOOST_LOG_TRIVIAL(error) << boost::format("can not get shader for rendering thumbnail");
7589 }
7590 else {
7591 for (int i = 0; i < partplate_list.get_plate_count(); i++) {
7592 Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i);
7593 PlateData *plate_data = plate_data_list[i];
7594 if (plate_data->plate_thumbnail.is_valid()) {
7595 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7596 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, reset plate %2%'s thumbnail.")%__LINE__%(i+1);
7597 plate_data->plate_thumbnail.reset();
7598 }
7599 else {
7600 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;
7601 }
7602 }
7603 else if (!plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file)))
7604 {
7605 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7606 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s thumbnail file path to empty.")%__LINE__%(i+1);
7607 plate_data->thumbnail_file.clear();
7608 }
7609 else {
7610 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;
7611 int dec_ret = decode_png_to_thumbnail(plate_data->thumbnail_file, plate_data->plate_thumbnail);
7612 if (!dec_ret)
7613 {
7614 BOOST_LOG_TRIVIAL(info) << boost::format("decode png to mem success.");
7615 }
7616 else {
7617 BOOST_LOG_TRIVIAL(warning) << boost::format("decode png to mem failed.");
7618 }
7619 }
7620 }
7621 else {
7622 ThumbnailData* thumbnail_data = &plate_data->plate_thumbnail;
7623
7624 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7625 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1);
7626 }
7627 else {
7628 unsigned int thumbnail_width = 512, thumbnail_height = 512;
7629 const ThumbnailsParams thumbnail_params = {{}, false, true, true, true, i};
7630
7631 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail, need to regenerate")%(i+1);
7633 {
7635 {
7636 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB");
7638 thumbnail_width, thumbnail_height, thumbnail_params,
7639 partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho);
7640 break;
7641 }
7643 {
7644 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT");
7646 thumbnail_width, thumbnail_height, thumbnail_params,
7647 partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho);
7648 break;
7649 }
7650 default:
7651 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown");
7652 break;
7653 }
7654 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail,finished rendering")%(i+1);
7655 }
7656 }
7657 if (need_create_thumbnail_group) {
7658 thumbnails.push_back(&plate_data->plate_thumbnail);
7659 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1);
7660 }
7661
7662 //no light thumbnail
7663 if (!plate_data->no_light_thumbnail_file.empty() && (boost::filesystem::exists(plate_data->no_light_thumbnail_file)))
7664 {
7665 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7666 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s no_light_thumbnail_file path to empty.")%__LINE__%(i+1);
7667 plate_data->no_light_thumbnail_file.clear();
7668 }
7669 else
7670 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has valid no_light_thumbnail_file extracted from 3mf, directly using it")%(i+1);
7671 }
7672 else{
7673 ThumbnailData *no_light_thumbnail = &part_plate->no_light_thumbnail_data;
7674 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7675 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1);
7676 part_plate->no_light_thumbnail_data.reset();
7677 plate_data->no_light_thumbnail_file.clear();
7678 }
7679 else {
7680 unsigned int thumbnail_width = 512, thumbnail_height = 512;
7681 const ThumbnailsParams thumbnail_params = { {}, false, true, false, true, i };
7682
7683 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s no_light_thumbnail_file missed, need to regenerate")%(i+1);
7685 {
7687 {
7688 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB");
7690 thumbnail_width, thumbnail_height, thumbnail_params,
7691 partplate_list, model.objects, glvolume_collection, colors_out, shader,
7693 false, true);
7694 break;
7695 }
7697 {
7698 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT");
7700 thumbnail_width, thumbnail_height, thumbnail_params,
7701 partplate_list, model.objects, glvolume_collection, colors_out, shader,
7703 false, true);
7704 break;
7705 }
7706 default:
7707 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown");
7708 break;
7709 }
7710 plate_data->no_light_thumbnail_file = "valid_no_light";
7711 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s no_light thumbnail,finished rendering")%(i+1);
7712 }
7713 }
7714
7715 if (need_create_no_light_group) {
7716 no_light_thumbnails.push_back(&part_plate->no_light_thumbnail_data);
7717 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for no_light into group")%(i+1);
7718 }
7719
7720 //top thumbnails
7721 /*if (part_plate->top_thumbnail_data.is_valid() && part_plate->pick_thumbnail_data.is_valid()) {
7722 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7723 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, reset plate %2%'s top/pick thumbnail.")%__LINE__%(i+1);
7724 part_plate->top_thumbnail_data.reset();
7725 part_plate->pick_thumbnail_data.reset();
7726 plate_data->top_file.clear();
7727 plate_data->pick_file.clear();
7728 }
7729 else {
7730 plate_data->top_file = "valid_top";
7731 plate_data->pick_file = "valid_pick";
7732 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid top/pick thumbnail data, directly using it")%(i+1);
7733 }
7734 }
7735 else*/
7736 if ((!plate_data->top_file.empty() && (boost::filesystem::exists(plate_data->top_file)))
7737 &&(!plate_data->pick_file.empty() && (boost::filesystem::exists(plate_data->pick_file))))
7738 {
7739 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7740 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s top/pick thumbnail file path to empty.")%__LINE__%(i+1);
7741 plate_data->top_file.clear();
7742 plate_data->pick_file.clear();
7743 }
7744 else
7745 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has valid top/pick thumbnail extracted from 3mf, directly using it")%(i+1);
7746 }
7747 else{
7748 ThumbnailData* top_thumbnail = &part_plate->top_thumbnail_data;
7749 ThumbnailData* picking_thumbnail = &part_plate->pick_thumbnail_data;
7750 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7751 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1);
7752 part_plate->top_thumbnail_data.reset();
7753 part_plate->pick_thumbnail_data.reset();
7754 plate_data->top_file.clear();
7755 plate_data->pick_file.clear();
7756 }
7757 else {
7758 unsigned int thumbnail_width = 512, thumbnail_height = 512;
7759 const ThumbnailsParams thumbnail_params = { {}, false, true, false, true, i };
7760
7761 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s top/pick thumbnail missed, need to regenerate, objects count %2%, skip_useless_pick %3%")%(i+1) %plate_object_count[i] %skip_useless_pick;
7762 if (skip_useless_pick && ((plate_object_count[i] <= 1) || (plate_object_count[i] > 64)))
7763 {
7764 //don't render pick and top
7765 part_plate->top_thumbnail_data.reset();
7766 part_plate->pick_thumbnail_data.reset();
7767 plate_data->top_file.clear();
7768 plate_data->pick_file.clear();
7769 BOOST_LOG_TRIVIAL(info) << boost::format("skip rendering for top&&pick");
7770 }
7771 else {
7773 {
7775 {
7776 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB");
7778 thumbnail_width, thumbnail_height, thumbnail_params,
7779 partplate_list, model.objects, glvolume_collection, colors_out, shader,
7781 false);
7783 thumbnail_width, thumbnail_height, thumbnail_params,
7784 partplate_list, model.objects, glvolume_collection, colors_out, shader,
7787 break;
7788 }
7790 {
7791 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT");
7793 thumbnail_width, thumbnail_height, thumbnail_params,
7794 partplate_list, model.objects, glvolume_collection, colors_out, shader,
7798 thumbnail_width, thumbnail_height, thumbnail_params, partplate_list, model.objects,
7799 glvolume_collection, colors_out, shader,
7802 true,true);
7803 break;
7804 }
7805 default:
7806 BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown");
7807 break;
7808 }
7809 plate_data->top_file = "valid_top";
7810 plate_data->pick_file = "valid_pick";
7811 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s top_thumbnail,finished rendering")%(i+1);
7812 }
7813 }
7814 }
7815
7816 if (need_create_top_group) {
7817 top_thumbnails.push_back(&part_plate->top_thumbnail_data);
7818 pick_thumbnails.push_back(&part_plate->pick_thumbnail_data);
7819 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for top and pick into group")%(i+1);
7820 }
7821 }
7822 }
7823 }
7824 }
7825 //BBS: release glfw
7826 glfwTerminate();
7827 }
7828 else {
7829 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: use previous thumbnails, no need to regenerate")%__LINE__;
7830 for (int i = 0; i < partplate_list.get_plate_count(); i++) {
7831 PlateData *plate_data = plate_data_list[i];
7832 bool skip_this_plate = ((plate_to_slice != 0) && (plate_to_slice != (i + 1)))?true:false;
7833 Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i);
7834
7835 if (skip_this_plate) {
7836 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s all the thumbnails skipped, reset here")%(i+1);
7837 plate_data->plate_thumbnail.reset();
7838 plate_data->thumbnail_file.clear();
7839 part_plate->no_light_thumbnail_data.reset();
7840 plate_data->no_light_thumbnail_file.clear();
7841
7842 part_plate->top_thumbnail_data.reset();
7843 part_plate->pick_thumbnail_data.reset();
7844 plate_data->top_file.clear();
7845 plate_data->pick_file.clear();
7846 }
7847 else if (!plate_data->plate_thumbnail.is_valid() && !plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file)))
7848 {
7849 BOOST_LOG_TRIVIAL(info) << boost::format("no need to generate: plate %1% has a valid thumbnail %2% extracted from 3mf, convert to data")%(i+1) %plate_data->thumbnail_file;
7850 int dec_ret = decode_png_to_thumbnail(plate_data->thumbnail_file, plate_data->plate_thumbnail);
7851 if (!dec_ret)
7852 {
7853 BOOST_LOG_TRIVIAL(info) << boost::format("decode png to mem success.");
7854 need_create_thumbnail_group = true;
7855 }
7856 else {
7857 BOOST_LOG_TRIVIAL(warning) << boost::format("decode png to mem failed.");
7858 }
7859 }
7860 }
7861
7862 for (int i = 0; i < partplate_list.get_plate_count(); i++) {
7863 PlateData *plate_data = plate_data_list[i];
7864 Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i);
7865
7866 if (need_create_thumbnail_group) {
7867 thumbnails.push_back(&plate_data->plate_thumbnail);
7868 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1);
7869 }
7870
7871 if (need_create_no_light_group) {
7872 no_light_thumbnails.push_back(&part_plate->no_light_thumbnail_data);
7873 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1);
7874 }
7875
7876 if (need_create_top_group) {
7877 top_thumbnails.push_back(&part_plate->top_thumbnail_data);
7878 pick_thumbnails.push_back(&part_plate->pick_thumbnail_data);
7879 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for top and pick into group")%(i+1);
7880 }
7881 }
7882 }
7883
7884 //generate first layer bboxes
7885 for (int i = 0; i < partplate_list.get_plate_count(); i++) {
7886 if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) {
7887 BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: generate bbox, Skip plate %2%.")%__LINE__%(i+1);
7888 plate_bboxes.push_back(new PlateBBoxData());
7889 continue;
7890 }
7891 Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i);
7892 //render calibration thumbnail
7893 if (!part_plate->get_slice_result() || !part_plate->is_slice_result_valid()) {
7894 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% doesn't have a valid sliced result, skip it")%(i+1);
7895 //calibration_thumbnails.push_back(new ThumbnailData());
7896 plate_bboxes.push_back(new PlateBBoxData());
7897 continue;
7898 }
7899 PrintBase *print_base=NULL;
7900 Slic3r::GUI::GCodeResult *gcode_result = NULL;
7901 int print_index;
7902 part_plate->get_print(&print_base, &gcode_result, &print_index);
7903
7904 Print *print = dynamic_cast<Print *>(print_base);
7905
7906 //don't render calibration picture
7907 /*BuildVolume build_volume(part_plate->get_shape(), print_height);
7908 const std::vector<BoundingBoxf3>& exclude_bounding_box = part_plate->get_exclude_areas();
7909 Slic3r::GUI::GCodeViewer gcode_viewer;
7910 gcode_viewer.init(ConfigOptionMode::comAdvanced, nullptr);
7911 gcode_viewer.load(*gcode_result, *print, build_volume, exclude_bounding_box, false, ConfigOptionMode::comAdvanced, false);
7912
7913 std::vector<std::string> colors;
7914 if (filament_color)
7915 colors = filament_color->values;
7916 gcode_viewer.refresh(*gcode_result, colors);
7917
7918 ThumbnailData* calibration_data = new ThumbnailData();
7919 const ThumbnailsParams calibration_params = { {}, false, true, true, true, i };
7920 //BBS fixed size
7921 const int cali_thumbnail_width = 2560;
7922 const int cali_thumbnail_height = 2560;
7923 gcode_viewer.render_calibration_thumbnail(*calibration_data, cali_thumbnail_width, cali_thumbnail_height,
7924 calibration_params, partplate_list, opengl_mgr);
7925 //generate_calibration_thumbnail(*calibration_data, thumbnail_width, thumbnail_height, calibration_params);
7926 // *plate_bboxes[index] = p->generate_first_layer_bbox();
7927 calibration_thumbnails.push_back(calibration_data);*/
7928
7929 PlateBBoxData* plate_bbox = new PlateBBoxData();
7930 std::vector<BBoxData>& id_bboxes = plate_bbox->bbox_objs;
7931 BoundingBoxf bbox_all;
7932 PrintSequence curr_plate_seq = part_plate->get_print_seq();
7933 if (curr_plate_seq == PrintSequence::ByDefault) {
7934 auto seq_print = m_print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence");
7935 if (seq_print && (seq_print->value == PrintSequence::ByObject)) {
7936 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from global")%(i+1);
7937 plate_bbox->is_seq_print = true;
7938 }
7939 }
7940 else if (curr_plate_seq == PrintSequence::ByObject) {
7941 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from plate self")%(i+1);
7942 plate_bbox->is_seq_print = true;
7943 }
7944 plate_bbox->first_extruder = print->get_tool_ordering().first_extruder();
7945 //bed type;
7946 BedType plate_bed_type = part_plate->get_bed_type();
7947 if (plate_bed_type == btDefault) {
7948 auto cur_bed_type = m_print_config.option<ConfigOptionEnum<BedType>>("curr_bed_type");
7949 if (cur_bed_type) {
7950 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% bed type: %2%, set from global")%(i+1) %cur_bed_type->serialize();
7951 plate_bbox->bed_type = bed_type_to_gcode_string(cur_bed_type->value);
7952 }
7953 }
7954 else {
7955 BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% bed type: %2%, set from plate self")%(i+1) %plate_bed_type;
7956 plate_bbox->bed_type = bed_type_to_gcode_string(plate_bed_type);
7957 }
7958 // get nozzle diameter
7959 auto opt_nozzle_diameters = m_print_config.option<ConfigOptionFloats>("nozzle_diameter");
7960 if (opt_nozzle_diameters != nullptr)
7961 plate_bbox->nozzle_diameter = float(opt_nozzle_diameters->get_at(plate_bbox->first_extruder));
7962
7963 auto objects = print->objects();
7964 auto orig = part_plate->get_origin();
7965 Vec2d orig2d = { orig[0], orig[1] };
7966
7967 for (auto obj : objects)
7968 {
7969 BBoxData data;
7970 auto bb_scaled = obj->get_first_layer_bbox(data.area, data.layer_height, data.name);
7971 auto bb = unscaled(bb_scaled);
7972 bbox_all.merge(bb);
7973 data.area *= (SCALING_FACTOR * SCALING_FACTOR); // unscale area
7974 data.id = obj->id().id;
7975 data.bbox = { bb.min.x(),bb.min.y(),bb.max.x(),bb.max.y() };
7976 id_bboxes.emplace_back(std::move(data));
7977 }
7978 // add wipe tower bounding box
7979 if (print->has_wipe_tower()) {
7980 BBoxData data;
7981 auto wt_corners = print->first_layer_wipe_tower_corners();
7982 // when loading gcode.3mf, wipe tower info may not be correct
7983 if (!wt_corners.empty()) {
7984 BoundingBox bb_scaled = {wt_corners[0], wt_corners[2]};
7985 auto bb = unscaled(bb_scaled);
7986 bb.min -= orig2d;
7987 bb.max -= orig2d;
7988 bbox_all.merge(bb);
7989 data.name = "wipe_tower";
7990 data.id = partplate_list.get_curr_plate()->get_index() + 1000;
7991 data.bbox = {bb.min.x(), bb.min.y(), bb.max.x(), bb.max.y()};
7992 id_bboxes.emplace_back(std::move(data));
7993 }
7994 }
7995 plate_bbox->bbox_all = { bbox_all.min.x(),bbox_all.min.y(),bbox_all.max.x(),bbox_all.max.y() };
7996
7997 PlateData *plate_data = plate_data_list[i];
7998 for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) {
7999 plate_bbox->filament_ids.push_back(it->id);
8000 plate_bbox->filament_colors.push_back(it->color);
8001 }
8002 plate_bboxes.push_back(plate_bbox);
8003 }
8004
8005
8006#if defined(__linux__) || defined(__LINUX__)
8007 if (g_cli_callback_mgr.is_started()) {
8008 PrintBase::SlicingStatus slicing_status{97, "Exporting 3mf"};
8009 cli_status_callback(slicing_status);
8010 }
8011#endif
8012
8013 BOOST_LOG_TRIVIAL(info) << "will export 3mf to " << export_3mf_file << std::endl;
8014 if (!makerlab_name.empty()) {
8015 Model &model = m_models[0];
8016
8017 model.mk_name = makerlab_name;
8018 model.mk_version = makerlab_version;
8019 BOOST_LOG_TRIVIAL(info) << boost::format("mk_name %1%, mk_version %2%")%makerlab_name %makerlab_version;
8020 }
8021
8022 if (!metadata_name.empty()) {
8023 Model &model = m_models[0];
8024
8025 model.md_value = metadata_value;
8026 model.md_name = metadata_name;
8027 for (unsigned int i = 0; i < metadata_name.size(); i++)
8028 {
8029 BOOST_LOG_TRIVIAL(info) << boost::format("index %1% metadata_name %2%, metadata_value %3%")%i %metadata_name[i] %metadata_value[i];
8030 }
8031 }
8032
8033 if (!this->export_project(&m_models[0], export_3mf_file, plate_data_list, project_presets, thumbnails, no_light_thumbnails, top_thumbnails, pick_thumbnails,
8034 calibration_thumbnails, plate_bboxes, &m_print_config, minimum_save, plate_to_slice - 1))
8035 {
8036 release_PlateData_list(plate_data_list);
8039 }
8040
8041 for (unsigned int i = 0; i < thumbnails.size(); i++)
8042 thumbnails[i]->reset();
8043 for (unsigned int i = 0; i < no_light_thumbnails.size(); i++)
8044 no_light_thumbnails[i]->reset();
8045 for (unsigned int i = 0; i < top_thumbnails.size(); i++)
8046 top_thumbnails[i]->reset();
8047 for (unsigned int i = 0; i < pick_thumbnails.size(); i++)
8048 pick_thumbnails[i]->reset();
8049
8050 release_PlateData_list(plate_data_list);
8051
8052 for (unsigned int i = 0; i < calibration_thumbnails.size(); i++)
8053 delete calibration_thumbnails[i];
8054
8055 for (int i = 0; i < plate_bboxes.size(); i++)
8056 delete plate_bboxes[i];
8057 }
8058
8059 if (plate_data_src.size() > 0)
8060 {
8061 release_PlateData_list(plate_data_src);
8062 }
8063
8064#if defined(__linux__) || defined(__LINUX__)
8065 if (g_cli_callback_mgr.is_started()) {
8066 PrintBase::SlicingStatus slicing_status{100, "All done, Success"};
8067 cli_status_callback(slicing_status);
8068 }
8069
8070 g_cli_callback_mgr.stop();
8071#endif
8072
8073 for (Model &model : m_models) {
8075 }
8076 //BBS: flush logs
8077 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", Finished" << std::endl;
8078 global_current_time = (long long)Slic3r::Utils::get_current_time_utc();
8079 sliced_info.export_time = (size_t) (global_current_time - global_begin_time);
8080
8081 //record the duplicate here
8082 if (duplicate_count > 0)
8083 {
8084 record_key_values["sliced_count"] = std::to_string(duplicate_count+1);
8085 record_exit_reson(outfile_dir, 0, plate_to_slice, cli_errors[0], sliced_info, record_key_values);
8086 }
8087 else {
8088 record_exit_reson(outfile_dir, 0, plate_to_slice, cli_errors[0], sliced_info);
8089 }
8090
8091 boost::nowide::cout.flush();
8092 boost::nowide::cerr.flush();
8093
8094 return 0;
8095}
#define BED_SHRINK_SEQ_PRINT
Definition Arrange.hpp:17
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
#define EPSILON
Definition ColorUtils.cpp:64
void serialize(Archive &ar, FacenamesSerializer &t, const std::uint32_t version)
Definition GLGizmoEmboss.cpp:3407
void default_status_callback(const PrintBase::SlicingStatus &slicing_status)
Definition OrcaSlicer.cpp:466
static int construct_assemble_list(std::vector< assemble_plate_info_t > &assemble_plate_info_list, Model &model, PlateDataPtrs &plate_list, std::vector< RGBA > &all_colours)
Definition OrcaSlicer.cpp:805
void record_exit_reson(std::string outputdir, int code, int plate_id, std::string error_message, sliced_info_t &sliced_info, std::map< std::string, std::string > key_values=std::map< std::string, std::string >())
Definition OrcaSlicer.cpp:518
static Pointfs legacy_bed_exclude_area_points(const DynamicPrintConfig &config)
Definition OrcaSlicer.cpp:187
int wall_loops
Definition OrcaSlicer.cpp:262
const float bed3d_ax3s_default_tip_radius
Definition OrcaSlicer.cpp:610
static void load_downward_settings_list_from_config(std::string config_file, std::string printer_name, std::string printer_model, std::vector< std::string > &downward_settings)
Definition OrcaSlicer.cpp:1086
float sparse_infill_density
Definition OrcaSlicer.cpp:261
int plate_id
Definition OrcaSlicer.cpp:246
bool strict_mode
Definition OrcaSlicer.cpp:267
static void glfw_callback(int error_code, const char *description)
Definition OrcaSlicer.cpp:603
static void load_default_gcodes_to_config(DynamicPrintConfig &config, Preset::Type type)
Definition OrcaSlicer.cpp:653
size_t triangle_count
Definition OrcaSlicer.cpp:249
std::string warning_message
Definition OrcaSlicer.cpp:250
size_t sliced_time_with_cache
Definition OrcaSlicer.cpp:248
static PrinterTechnology get_printer_technology(const DynamicConfig &config)
Definition OrcaSlicer.cpp:477
struct _sliced_plate_info sliced_plate_info_t
std::vector< std::string > downward_machines
Definition OrcaSlicer.cpp:264
struct _sliced_info sliced_info_t
#define flush_and_exit(ret)
Definition OrcaSlicer.cpp:494
std::vector< PrintBase::SlicingStatus > g_slicing_warnings
Definition OrcaSlicer.cpp:269
size_t sliced_time
Definition OrcaSlicer.cpp:247
std::vector< sliced_plate_info_t > sliced_plates
Definition OrcaSlicer.cpp:257
float layer_height
Definition OrcaSlicer.cpp:260
#define MAX_CLONEABLE_SIZE
Definition OrcaSlicer.cpp:185
size_t export_time
Definition OrcaSlicer.cpp:259
static int load_key_values_from_json(const std::string &file, std::map< std::string, std::string > &key_values)
Definition OrcaSlicer.cpp:613
std::map< int, std::string > cli_errors
Definition OrcaSlicer.cpp:193
static void cli_record_warning(sliced_info_t &sliced_info, const std::string &cls, nlohmann::json details=nlohmann::json::object())
Definition OrcaSlicer.cpp:511
std::vector< std::string > upward_machines
Definition OrcaSlicer.cpp:263
static int decode_png_to_thumbnail(std::string png_file, ThumbnailData &thumbnail_data)
Definition OrcaSlicer.cpp:570
size_t prepare_time
Definition OrcaSlicer.cpp:258
const float WIPE_TOWER_DEFAULT_Y_POS
Definition PartPlate.cpp:143
const float WIPE_TOWER_DEFAULT_X_POS
Definition PartPlate.cpp:142
const float I3_WIPE_TOWER_DEFAULT_X_POS
Definition PartPlate.cpp:147
const float I3_WIPE_TOWER_DEFAULT_Y_POS
Definition PartPlate.cpp:148
#define MAX_PLATE_COUNT
Definition PartPlate.hpp:59
#define BBL_JSON_KEY_NAME
Definition Preset.hpp:58
#define BBL_JSON_KEY_TYPE
Definition Preset.hpp:66
#define BBL_JSON_KEY_FROM
Definition Preset.hpp:67
#define BBL_JSON_KEY_FILAMENT_ID
Definition Preset.hpp:71
#define BBL_JSON_KEY_INHERITS
Definition Preset.hpp:75
void reset()
Definition AppConfig.cpp:122
std::string load_if_exists()
Definition AppConfig.cpp:1940
std::string from_orca_filament_id(const std::string &orca_filament_id) const override
Definition BBLPrinterAgent.cpp:135
PointType center() const
Definition BoundingBox.cpp:237
PointType size() const
Definition BoundingBox.cpp:187
void merge(const PointType &point)
Definition BoundingBox.cpp:139
PointType max
Definition BoundingBox.hpp:21
void merge(const PointType &point)
Definition BoundingBox.cpp:75
PointType min
Definition BoundingBox.hpp:20
ObjectState
Definition BuildVolume.hpp:94
@ Limited
Definition BuildVolume.hpp:105
std::vector< std::string > m_input_files
Definition OrcaSlicer.hpp:68
bool export_models(IO::ExportFormat format, std::string path=std::string())
Exports loaded models to a file of the specified format, according to the options affecting output fi...
Definition OrcaSlicer.cpp:8284
bool export_project(Model *model, std::string &path, PlateDataPtrs &partplate_data, std::vector< Preset * > &project_presets, std::vector< ThumbnailData * > &thumbnails, std::vector< ThumbnailData * > &no_light_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, int plate_to_export=-1)
Definition OrcaSlicer.cpp:8329
DynamicPrintConfig m_extra_config
Definition OrcaSlicer.hpp:67
std::vector< std::string > m_actions
Definition OrcaSlicer.hpp:69
std::set< std::string > m_given_option_keys
Definition OrcaSlicer.hpp:72
bool setup(int argc, char **argv)
Definition OrcaSlicer.cpp:8097
void print_help(bool include_print_options=false, PrinterTechnology printer_technology=ptAny) const
Prints usage of the CLI.
Definition OrcaSlicer.cpp:8249
DynamicPrintConfig m_print_config
Definition OrcaSlicer.hpp:66
std::vector< std::string > m_transforms
Definition OrcaSlicer.hpp:70
float a() const
Definition Color.hpp:108
float b() const
Definition Color.hpp:107
float r() const
Definition Color.hpp:105
float g() const
Definition Color.hpp:106
void apply(const ConfigBase &other, bool ignore_nonexistent=false)
Definition Config.hpp:2802
ConfigOption * option(const t_config_option_key &opt_key, bool create=false)
Definition Config.hpp:2768
void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent=false)
Definition Config.cpp:484
void set(const std::string &opt_key, bool value, bool create=false)
Definition Config.hpp:2822
bool & get_at(size_t i)
Definition Config.hpp:1986
std::string serialize() const override
Definition Config.hpp:1994
t_config_option_key opt_key
Definition Config.hpp:2342
std::string serialize() const override
Definition Config.hpp:909
bool deserialize(const std::string &str, bool append=false) override
Definition Config.hpp:932
bool has(const t_config_option_key &opt_key) const
Definition Config.hpp:2697
T value
Definition Config.hpp:329
bool empty() const
Definition Config.hpp:1170
std::vector< std::string > vserialize() const override
Definition Config.hpp:1212
virtual void resize(size_t n, const ConfigOption *opt_default=nullptr)=0
virtual void set_with_restore_2(const ConfigOptionVectorBase *rhs, std::vector< int > &restore_index, int start, int len, bool skip_error=false)=0
virtual void set_at(const ConfigOption *rhs, size_t i, size_t j)=0
virtual size_t size() const =0
virtual void set_with_restore(const ConfigOptionVectorBase *rhs, std::vector< int > &restore_index, int stride)=0
bool empty() const override
Definition Config.hpp:723
size_t size() const override
Definition Config.hpp:722
std::vector< T > values
Definition Config.hpp:418
const int & get_at(size_t i) const
Definition Config.hpp:676
void resize(size_t n, const ConfigOption *opt_default=nullptr) override
Definition Config.hpp:687
void set_at(const ConfigOption *rhs, size_t i, size_t j) override
Definition Config.hpp:451
virtual ConfigOption * clone() const =0
virtual void set(const ConfigOption *option)=0
virtual double getFloat() const
Definition Config.hpp:295
bool is_scalar() const
Definition Config.hpp:301
virtual std::string serialize() const =0
bool set_key_value(const std::string &opt_key, ConfigOption *opt)
Definition Config.hpp:3022
bool erase(const t_config_option_key &opt_key)
Definition Config.hpp:2992
bool empty() const
Definition Config.hpp:3017
std::string & opt_string(const t_config_option_key &opt_key, bool create=false)
Definition Config.hpp:3042
t_config_option_keys keys() const override
Definition Config.cpp:1919
t_config_option_keys diff(const DynamicConfig &other) const
Definition Config.cpp:2066
double & opt_float(const t_config_option_key &opt_key)
Definition Config.hpp:3047
int update_values_from_multi_to_multi_2(const std::vector< std::string > &src_extruder_variants, const std::vector< std::string > &dst_extruder_variants, const DynamicPrintConfig &dst_config, const std::set< std::string > &key_sets)
Definition PrintConfig.cpp:11275
Polygon contour
Definition ExPolygon.hpp:42
GLVolumePtrs volumes
Definition 3DScene.hpp:468
int load_object_volume(const ModelObject *model_object, int obj_idx, int volume_idx, int instance_idx, const std::string &color_by, bool opengl_initialized, bool in_assemble_view=false, bool use_loaded_id=false, bool need_raycaster=true)
Definition 3DScene.cpp:893
static bool parse_color4(const std::string &scolor, unsigned char *rgba_out)
Definition BitmapCache.cpp:559
static void render_thumbnail_framebuffer_ext(ThumbnailData &thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams &thumbnail_params, PartPlateList &partplate_list, ModelObjectPtrs &model_objects, const GLVolumeCollection &volumes, std::vector< ColorRGBA > &extruder_colors, GLShaderProgram *shader, Camera::EType camera_type, Camera::ViewAngleType camera_view_angle_type=Camera::ViewAngleType::Iso, bool for_picking=false, bool ban_light=false)
Definition GLCanvas3D.cpp:7250
static void render_thumbnail_framebuffer(ThumbnailData &thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams &thumbnail_params, PartPlateList &partplate_list, ModelObjectPtrs &model_objects, const GLVolumeCollection &volumes, std::vector< ColorRGBA > &extruder_colors, GLShaderProgram *shader, Camera::EType camera_type, Camera::ViewAngleType camera_view_angle_type=Camera::ViewAngleType::Iso, bool for_picking=false, bool ban_light=false)
Definition GLCanvas3D.cpp:7137
bool init_gl(bool popup_error=true)
Definition OpenGLManager.cpp:256
GLShaderProgram * get_shader(const std::string &shader_name)
Definition OpenGLManager.hpp:96
static EFramebufferType get_framebuffers_type()
Definition OpenGLManager.hpp:102
@ Arb
Definition OpenGLManager.hpp:23
@ Ext
Definition OpenGLManager.hpp:24
void translate_all_instance(Vec3d position)
Definition PartPlate.cpp:4744
ThumbnailData pick_thumbnail_data
Definition PartPlate.hpp:432
const std::vector< Pointfs > & get_extruder_areas() const
Definition PartPlate.hpp:579
bool intersect_instance(int obj_id, int instance_id, BoundingBoxf3 *bounding_box=nullptr)
Definition PartPlate.cpp:4600
arrangement::ArrangePolygon estimate_wipe_tower_polygon(const DynamicPrintConfig &config, int plate_index, Vec3d &wt_pos, Vec3d &wt_size, int plate_extruder_size=0, bool use_global_objects=false, WipeTowerRelocation *relocation=nullptr) const
Definition PartPlate.cpp:4065
void lock(bool state)
Definition PartPlate.hpp:612
bool contain_instance(int obj_id, int instance_id)
Definition PartPlate.cpp:4432
GCodeProcessorResult * get_slice_result()
Definition PartPlate.hpp:698
int printable_instance_size()
Definition PartPlate.cpp:4921
void set_filament_maps(const std::vector< int > &f_maps)
Definition PartPlate.cpp:5800
Vec3d get_origin()
Definition PartPlate.hpp:479
void set_tmp_gcode_path(std::string new_path)
Definition PartPlate.hpp:703
WipeTowerFootprint estimate_wipe_tower_footprint(const DynamicPrintConfig &config, int plate_extruder_size=0, bool use_global_objects=false) const
Definition PartPlate.cpp:3982
const Pointfs & get_shape() const
Definition PartPlate.hpp:577
std::vector< int > get_extruders_under_cli(bool conside_custom_gcode, DynamicPrintConfig &full_config, bool expand_mixed_slots=true) const
Definition PartPlate.cpp:3252
void set_filament_volume_maps(const std::vector< int > &f_maps)
Definition PartPlate.cpp:5822
void duplicate_all_instance(unsigned int dup_count, bool need_skip, std::map< int, bool > &skip_objects)
Definition PartPlate.cpp:4766
BedType get_bed_type(bool load_from_project=false) const
Definition PartPlate.cpp:266
ThumbnailData no_light_thumbnail_data
Definition PartPlate.hpp:426
bool check_tpu_printable_status(const DynamicPrintConfig &config, const std::vector< int > &tpu_filaments)
Definition PartPlate.cpp:3710
void get_print(PrintBase **print, GCodeResult **result, int *index)
Definition PartPlate.cpp:4361
ThumbnailData top_thumbnail_data
Definition PartPlate.hpp:431
DynamicPrintConfig * config()
Definition PartPlate.hpp:337
void set_filament_nozzle_maps(const std::vector< int > &f_maps)
Definition PartPlate.cpp:5842
FilamentMapMode get_real_filament_map_mode(const DynamicConfig &g_config, bool *use_global_param=nullptr) const
Definition PartPlate.cpp:408
const BoundingBoxf3 & get_bounding_box(bool extended=false)
Definition PartPlate.hpp:595
PrintSequence get_print_seq() const
Definition PartPlate.cpp:358
std::vector< int > get_real_filament_maps(const DynamicConfig &g_config, bool *use_global_param=nullptr) const
Definition PartPlate.cpp:384
bool is_slice_result_valid() const
Definition PartPlate.hpp:660
void update_slice_result_valid_state(bool valid=false)
Definition PartPlate.cpp:5423
std::string get_tmp_gcode_path()
Definition PartPlate.cpp:5465
const Transform3d & get_matrix() const
Definition Geometry.hpp:477
int extruder() const
Definition PrintConfig.hpp:2654
bool set_key_value(const std::string &opt_key, ConfigOption *opt)
Definition PrintConfig.hpp:2635
void assign_config(const ModelConfig &rhs)
Definition PrintConfig.hpp:2612
const DynamicPrintConfig & get() const
Definition PrintConfig.hpp:2645
const ConfigOption * option(const t_config_option_key &opt_key) const
Definition PrintConfig.hpp:2652
bool printable
Definition Model.hpp:1432
size_t loaded_id
Definition Model.hpp:1435
ModelInstanceEPrintVolumeState print_volume_state
Definition Model.hpp:1430
void set_offset(const Vec3d &offset)
Definition Model.hpp:1473
Vec3d get_offset() const
Definition Model.hpp:1470
const Geometry::Transformation & get_transformation() const
Definition Model.hpp:1447
bool use_loaded_id_for_label
Definition Model.hpp:1433
void ensure_on_bed(bool allow_negative_z=false)
Definition Model.cpp:1896
bool has_custom_layering() const
Definition Model.hpp:463
ModelConfigObject config
Definition Model.hpp:395
std::string name
Definition Model.hpp:384
t_layer_config_ranges layer_config_ranges
Definition Model.hpp:397
std::string input_file
Definition Model.hpp:387
void rotate(double angle, Axis axis)
Definition Model.cpp:1958
ModelInstancePtrs instances
Definition Model.hpp:390
ModelVolume * add_volume(const TriangleMesh &mesh, bool modify_to_center_geometry=true)
Definition Model.cpp:1389
void scale(const Vec3d &versor)
Definition Model.cpp:1950
void translate_instances(const Vec3d &vector)
Definition Model.cpp:1923
ModelInstance * add_instance()
Definition Model.cpp:1554
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate=false) const
Definition Model.cpp:1778
ModelVolumePtrs volumes
Definition Model.hpp:393
void scale_to_fit(const Vec3d &size)
Scale the current ModelObject to fit by altering the scaling factor of ModelInstances....
Definition Model.cpp:3059
bool repair(bool keep_painting, MeshRepairStats *stats=nullptr, std::string *error=nullptr, const std::function< void(int)> &progress={})
Definition Model.cpp:3029
const TriangleMesh & mesh() const
Definition Model.hpp:892
bool is_model_part() const
Definition Model.hpp:1025
bool is_modifier() const
Definition Model.hpp:1027
ModelConfigObject config
Definition Model.hpp:904
const TriangleMesh & get_convex_hull() const
Definition Model.cpp:2904
const Transform3d & get_matrix() const
Definition Model.hpp:1139
std::string name
Definition Model.hpp:834
std::vector< int > get_extruders() const
Definition Model.cpp:2713
void convert_from_meters(bool only_small_volumes)
Definition Model.cpp:1030
void delete_object(size_t idx)
Definition Model.cpp:672
void convert_from_imperial_units(bool only_small_volumes)
Definition Model.cpp:1003
void clear_materials()
Definition Model.cpp:776
bool looks_like_saved_in_meters() const
Definition Model.cpp:1018
void print_info() const
Definition Model.hpp:1858
ModelObjectPtrs objects
Definition Model.hpp:1702
void remove_backup_path_if_exist()
Definition Model.cpp:1147
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config=nullptr, ConfigSubstitutionContext *config_substitutions=nullptr, LoadStrategy options=LoadStrategy::AddDefaultInstances, PlateDataPtrs *plate_data=nullptr, std::vector< Preset * > *project_presets=nullptr, bool *is_xxx=nullptr, Semver *file_version=nullptr, Import3mfProgressFn proFn=nullptr, ImportstlProgressFn stlFn=nullptr, BBLProject *project=nullptr, int plate_id=0)
Definition Model.cpp:318
int curr_plate_index
Definition Model.hpp:1742
void translate(coordf_t x, coordf_t y, coordf_t z)
Definition Model.hpp:1841
static void setPrintSpeedTable(const DynamicPrintConfig &config, const PrintConfig &print_config)
Definition Model.cpp:3209
ModelObject * add_object()
Definition Model.cpp:607
void clear_objects()
Definition Model.cpp:717
bool looks_like_imperial_units() const
Definition Model.cpp:978
std::vector< std::string > md_value
Definition Model.hpp:1724
bool add_default_instances()
Definition Model.cpp:805
unsigned int update_print_volume_state(const BuildVolume &build_volume)
Definition Model.cpp:839
std::string mk_version
Definition Model.hpp:1722
std::vector< std::string > md_name
Definition Model.hpp:1723
bool center_instances_around_point(const Vec2d &point)
Definition Model.cpp:850
std::string mk_name
Definition Model.hpp:1721
BoundingBoxf3 bounding_box_exact() const
Definition Model.cpp:823
TriangleMesh mesh() const
Definition Model.cpp:872
void set_backup_path(const std::string &path)
Definition Model.cpp:1174
static void setExtruderParams(const DynamicPrintConfig &config, int extruders_count)
Definition Model.cpp:3267
ObjectID id() const
Definition ObjectID.hpp:69
size_t id
Definition ObjectID.hpp:39
PrinterPresetCollection printers
Definition PresetBundle.hpp:503
bool resolve_preset_config_type(DynamicPrintConfig &config, Preset::Type &type, const std::string &source_file, ForwardCompatibilitySubstitutionRule compatibility_rule, std::string &error, bool allow_source_manifest=true)
Definition PresetBundle.cpp:689
static void convert_filament_preset_name(const std::string &machine_name, std::string &filament_name)
Definition PresetBundle.cpp:6107
PresetCollection prints
Definition PresetBundle.hpp:497
PresetCollection filaments
Definition PresetBundle.hpp:499
bool resolve_preset_config(DynamicPrintConfig &config, Preset::Type type, const std::string &source_file, ForwardCompatibilitySubstitutionRule compatibility_rule, std::string &error, bool allow_source_manifest=true)
Definition PresetBundle.cpp:556
static std::set< std::string > project_different_keys(const std::string &different_settings)
Definition PresetBundle.cpp:5489
Preset * find_preset2(const std::string &name, bool auto_match=true)
Definition Preset.cpp:3790
Preset & default_preset(size_t idx=0)
Definition Preset.hpp:786
std::string name
Definition Preset.hpp:288
bool is_project_embedded
Definition Preset.hpp:281
static std::string get_type_string(Preset::Type type)
Definition Preset.cpp:590
static DynamicPrintConfig load_external_config(Type type, const DynamicPrintConfig &default_config, const DynamicPrintConfig &project_config, const std::set< std::string > &different_settings_list, const std::function< DynamicPrintConfig *(const std::string &inherits)> &find_base, t_config_option_keys *keys=nullptr)
Definition Preset.cpp:1059
DynamicPrintConfig config
Definition Preset.hpp:300
static const std::vector< std::string > & print_options()
Definition Preset.cpp:1705
static const std::vector< std::string > & printer_options()
Definition Preset.cpp:1715
Type type
Definition Preset.hpp:261
static const std::vector< std::string > & filament_options()
Definition Preset.cpp:1706
Type
Definition Preset.hpp:244
@ TYPE_PRINTER
Definition Preset.hpp:250
@ TYPE_PRINT
Definition Preset.hpp:246
@ TYPE_FILAMENT
Definition Preset.hpp:248
virtual bool empty() const =0
virtual ApplyStatus apply(const Model &model, DynamicPrintConfig config, bool extruder_applied=false)=0
virtual void process(long long *time_cost_with_cache=nullptr, bool use_cache=false)=0
void set_no_check_flag(bool no_check)
Definition PrintBase.hpp:529
virtual int load_cached_data(const std::string &directory)
Definition PrintBase.hpp:438
virtual StringObjectException validate(std::vector< StringObjectException > *warnings=nullptr, Polygons *collison_polygons=nullptr, std::vector< std::pair< Polygon, float > > *height_polygons=nullptr) const
Definition PrintBase.hpp:408
void set_status_callback(status_callback_type cb)
Definition PrintBase.hpp:488
virtual int export_cached_data(const std::string &dir_path, bool with_space=false)
Definition PrintBase.hpp:437
@ SlicingGcodeOverlap
Definition PrintBase.hpp:72
@ SlicingEmptyGcodeLayers
Definition PrintBase.hpp:71
@ SlicingDefaultNotification
Definition PrintBase.hpp:68
@ NON_CRITICAL
Definition PrintBase.hpp:62
ConstPrintObjectPtrsAdaptor objects() const
Definition Print.hpp:1134
void set_extruder_filament_info(const std::vector< std::vector< DynamicPrintConfig > > &filament_info)
Definition Print.hpp:1238
std::vector< int > get_filament_maps() const
Definition Print.cpp:4888
std::string get_conflict_string() const
Definition Print.hpp:1321
std::string export_gcode(const std::string &path_template, GCodeProcessorResult *result, ThumbnailsGeneratorCallback thumbnail_cb=nullptr)
Definition Print.cpp:3977
std::vector< int > get_filament_nozzle_maps() const
Definition Print.cpp:4893
const ToolOrdering & get_tool_ordering() const
Definition Print.hpp:1291
std::string slicing_errors_message(const SlicingErrors &errors) const
Definition Print.cpp:1952
std::vector< int > get_filament_volume_maps() const
Definition Print.cpp:4898
void set_check_multi_filaments_compatibility(bool check)
Definition Print.hpp:1356
bool has_wipe_tower() const
Definition Print.cpp:5183
const PrintConfig & config() const
Definition Print.hpp:1131
Points first_layer_wipe_tower_corners(bool check_wipe_tower_existance=true) const
Definition Print.cpp:4462
const Preset & default_preset_for(const DynamicPrintConfig &config) const override
Definition Preset.cpp:4499
int min() const
Definition Semver.hpp:109
static boost::optional< Semver > parse(const std::string &str)
Definition Semver.hpp:63
int maj() const
Definition Semver.hpp:108
std::string to_string() const
Definition Semver.hpp:158
unsigned int first_extruder() const
Definition ToolOrdering.hpp:300
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const
Definition TriangleMesh.cpp:456
size_t facets_count() const
Definition TriangleMesh.hpp:145
static bool SupportsForceSharedMemory()
Definition WebView.cpp:446
static bool NeedsCompositingWorkaround()
Definition WebView.cpp:435
#define GL_TRUE
Definition gl.h:1892
#define CLI_ENVIRONMENT_ERROR
Definition Utils.hpp:43
#define CLI_OBJECT_COLLISION_IN_LAYER_PRINT
Definition Utils.hpp:85
#define CLI_POSTPROCESS_NOT_SUPPORTED
Definition Utils.hpp:62
#define CLI_3MF_NEW_MACHINE_NOT_SUPPORTED
Definition Utils.hpp:59
#define CLI_OBJECT_ARRANGE_FAILED
Definition Utils.hpp:64
#define CLI_EXPORT_OBJ_ERROR
Definition Utils.hpp:55
#define CLI_GCODE_PATH_CONFLICTS
Definition Utils.hpp:93
#define CLI_SLICING_ERROR
Definition Utils.hpp:92
#define CLI_MODIFIED_PARAMS_TO_PRINTER
Definition Utils.hpp:66
#define CLI_FILAMENTS_NOT_SUPPORTED_BY_EXTRUDER
Definition Utils.hpp:89
#define CLI_NO_SUITABLE_OBJECTS
Definition Utils.hpp:71
#define CLI_SUCCESS
Definition Utils.hpp:42
#define CLI_PROCESS_NOT_COMPATIBLE
Definition Utils.hpp:60
#define CLI_UNSUPPORTED_OPERATION
Definition Utils.hpp:50
#define CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT
Definition Utils.hpp:80
#define CLI_MIXED_FILAMENT_INVALID
Definition Utils.hpp:90
#define CLI_CONFIG_FILE_ERROR
Definition Utils.hpp:47
#define CLI_EXPORT_STL_ERROR
Definition Utils.hpp:54
#define CLI_FILAMENTS_DIFFERENT_TEMP
Definition Utils.hpp:83
#define CLI_OBJECT_COLLISION_IN_SEQ_PRINT
Definition Utils.hpp:84
#define CLI_INVALID_VALUES_IN_3MF
Definition Utils.hpp:61
#define CLI_SCALE_TO_FIT_ERROR
Definition Utils.hpp:53
#define CLI_FILAMENT_CAN_NOT_MAP
Definition Utils.hpp:87
#define CLI_GCODE_PATH_IN_UNPRINTABLE_AREA
Definition Utils.hpp:94
#define CLI_FILE_VERSION_NOT_SUPPORTED
Definition Utils.hpp:67
#define CLI_EXPORT_STATISTICS_ERROR
Definition Utils.hpp:68
#define CLI_ONLY_ONE_TPU_SUPPORTED
Definition Utils.hpp:88
#define CLI_EXPORT_3MF_ERROR
Definition Utils.hpp:56
#define CLI_FILE_NOTFOUND
Definition Utils.hpp:45
#define CLI_INVALID_PARAMS
Definition Utils.hpp:44
#define CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP
Definition Utils.hpp:81
#define CLI_COPY_OBJECTS_ERROR
Definition Utils.hpp:52
#define CLI_OBJECTS_PARTLY_INSIDE
Definition Utils.hpp:73
#define CLI_FILAMENT_NOT_MATCH_BED_TYPE
Definition Utils.hpp:82
#define CLI_INVALID_PRINTER_TECH
Definition Utils.hpp:49
#define CLI_DATA_FILE_ERROR
Definition Utils.hpp:48
#define CLI_FILELIST_INVALID_ORDER
Definition Utils.hpp:46
#define CLI_SLICING_TIME_EXCEEDS_LIMIT
Definition Utils.hpp:79
#define CLI_VALIDATE_ERROR
Definition Utils.hpp:72
double SCALING_FACTOR
Definition libslic3r.cpp:3
#define scale_(val)
Definition libslic3r.h:106
#define SLIC3R_APP_FULL_NAME
Definition libslic3r.h:11
static constexpr double WIPE_TOWER_MARGIN
Definition libslic3r.h:100
T * begin(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:838
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
@ Custom
Definition CustomGCode.hpp:23
bool input_gcode
Definition GUI_Init.hpp:37
std::vector< std::string > input_files
Definition GUI_Init.hpp:29
int argc
Definition GUI_Init.hpp:21
arrangement::ArrangePolygon ArrangePolygon
Definition ArrangeJob.cpp:51
bool moved
Definition PartPlate.hpp:103
bool overlaps_objects
Definition PartPlate.hpp:104
DynamicPrintConfig extra_config
Definition GUI_Init.hpp:28
int GUI_Run(GUI_InitParams &params)
Definition GUI_Init.cpp:34
GCodeProcessorResult GCodeResult
Definition PartPlate.hpp:98
char ** argv
Definition GUI_Init.hpp:22
std::vector< std::string > load_configs
Definition GUI_Init.hpp:27
std::vector< int > get_min_flush_volumes(const DynamicPrintConfig &full_config, size_t nozzle_id)
Definition Plater.cpp:1246
constexpr T deg2rad(const T angle)
Definition Geometry.hpp:312
void inspect_to_json(const Model &model, const std::vector< std::string > &source_paths, std::ostream &out, size_t max_planes)
Definition MeshInspect.cpp:30
void inspect_to_json(const Model &model, const std::vector< std::string > &source_paths, std::ostream &out)
Definition PaintCLI.cpp:137
time_t get_current_time_utc()
Definition Time.cpp:183
void update_arrange_params(ArrangeParams &params, const DynamicPrintConfig *print_cfg, const ArrangePolygons &selected)
Definition Arrange.cpp:148
std::vector< ArrangePolygon > ArrangePolygons
Definition Arrange.hpp:126
void update_selected_items_inflation(ArrangePolygons &selected, const DynamicPrintConfig *print_cfg, ArrangeParams &params)
Definition Arrange.cpp:163
void update_selected_items_axis_align(ArrangePolygons &selected, const DynamicPrintConfig *print_cfg, const ArrangeParams &params)
Definition Arrange.cpp:234
Points get_shrink_bedpts(const DynamicPrintConfig *print_cfg, const ArrangeParams &params)
Definition Arrange.cpp:339
void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Points &bed, const ArrangeParams &params)
Definition Arrange.cpp:1280
void update_unselected_items_inflation(ArrangePolygons &unselected, const DynamicPrintConfig *print_cfg, const ArrangeParams &params)
Definition Arrange.cpp:213
void orient(OrientMeshs &arrangables, const OrientMeshs &excludes, const OrientParams &params)
Orients the input polygons.
Definition Orient.cpp:613
int printable_width
Definition OrcaSlicer.hpp:35
zaa_minimize_perimeter_height ConfigOptionFloat
Definition PrintConfig.hpp:1954
void lay_on_face(ModelObject &object, size_t instance_idx, const Vec3d &normal)
Definition LayOnFace.cpp:233
void normalize_filament_values_to_variants(DynamicPrintConfig &config)
Definition PrintConfig.cpp:11431
std::vector< PlateData * > PlateDataPtrs
Definition bbs_3mf.hpp:278
ForwardCompatibilitySubstitutionRule
Definition Config.hpp:241
@ Enable
Definition Config.hpp:245
const int g_max_flush_volume
Definition FlushVolCalc.cpp:15
void release_PlateData_list(PlateDataPtrs &plate_data_list)
Definition bbs_3mf.cpp:9740
int find_plane_at_point(const std::vector< LayOnFacePlane > &planes, const Transform3d &instance_matrix_no_offset, const Vec3d &point, double tolerance)
Definition LayOnFace.cpp:185
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config)
Definition Preset.cpp:909
NozzleVolumeType
Definition PrintConfig.hpp:618
@ nvtHybrid
Definition PrintConfig.hpp:621
@ nvtStandard
Definition PrintConfig.hpp:619
bool has_any_mixed_filament(const std::vector< unsigned char > &is_mixed)
Definition FilamentMixer.cpp:486
bool unescape_strings_cstyle(const std::string &str, std::vector< std::string > &out)
Definition Config.cpp:173
AssembleListResult load_assemble_plate_list(const std::string &config_file, std::vector< assemble_plate_info_t > &assemble_plate_info_list, int max_plate_count)
Definition AssembleList.cpp:44
std::string get_extruder_variant_string(ExtruderType extruder_type, NozzleVolumeType nozzle_volume_type)
Definition PrintConfig.cpp:777
std::vector< ConfigSubstitution > ConfigSubstitutions
Definition Config.hpp:269
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
void set_filament_dev_options(DynamicPrintConfig &config, const std::vector< const DynamicPrintConfig * > &filament_configs)
Definition PrintConfig.cpp:11459
double height
Definition WipeTowerEstimate.hpp:22
zaa_minimize_perimeter_height nozzle_height((ConfigOptionStrings, extruder_colour))((ConfigOptionPoints
std::set< std::string > printer_options_with_variant_1
Definition PrintConfig.cpp:10236
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
@ STRING_EXCEPT_LAYER_HEIGHT_EXCEEDS_LIMIT
Definition PrintBase.hpp:32
@ STRING_EXCEPT_OBJECT_COLLISION_IN_SEQ_PRINT
Definition PrintBase.hpp:30
@ STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT
Definition PrintBase.hpp:31
@ STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP
Definition PrintBase.hpp:29
@ STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE
Definition PrintBase.hpp:28
int exclude_x
Definition OrcaSlicer.hpp:41
int printable_height
Definition OrcaSlicer.hpp:37
std::string printer_name
Definition OrcaSlicer.hpp:34
struct Slic3r::_printer_plate_info printer_plate_info_t
AssembleListResult
Definition AssembleList.hpp:48
@ ConfigError
Definition AssembleList.hpp:52
@ FileNotFound
Definition AssembleList.hpp:50
ArrangePolygon get_instance_arrange_poly(ModelInstance *instance, const Slic3r::DynamicPrintConfig &config)
Definition ModelArrange.cpp:135
printer_technology((ConfigOptionPoints, printable_area))((ConfigOptionFloat
ConfigOptionEnumsGenericTempl< false > ConfigOptionEnumsGeneric
Definition Config.hpp:2311
Eigen::Transform< double, 3, Eigen::Affine, Eigen::DontAlign > Transform3d
Definition Point.hpp:96
double brim_width
Definition WipeTowerEstimate.hpp:23
std::vector< std::map< NozzleVolumeType, int > > get_extruder_nozzle_stats(const std::vector< std::string > &strs)
Definition PrintConfig.cpp:940
layer_height((ConfigOptionInt, faded_layers))((ConfigOptionFloat
bool modified
Definition MeshRepair.hpp:20
BedType
Definition PrintConfig.hpp:507
@ btDefault
Definition PrintConfig.hpp:508
int find_largest_plane(const std::vector< LayOnFacePlane > &planes)
Definition LayOnFace.cpp:160
void duplicate(Model &model, Slic3r::arrangement::ArrangePolygons &copies, VirtualBedFn vfn)
Definition ModelArrange.cpp:77
std::set< std::string > filament_dev_options
Definition PrintConfig.cpp:10291
ConfigOptionIntsTempl< true > ConfigOptionIntsNullable
Definition Config.hpp:1155
PrinterTechnology
Definition Config.hpp:229
@ ptSLA
Definition Config.hpp:233
@ ptFFF
Definition Config.hpp:231
@ ptUnknown
Definition Config.hpp:235
zaa_minimize_perimeter_height filament_type((ConfigOptionBools, filament_soluble))((ConfigOptionStrings
std::vector< Vec2d > Pointfs
Definition Point.hpp:77
int exclude_y
Definition OrcaSlicer.hpp:42
std::vector< size_t > check_mixed_filament_type_consistency(const std::vector< unsigned char > &is_mixed, const std::vector< std::string > &comp_strs, const std::vector< std::string > &filament_types)
Definition FilamentMixer.cpp:564
Pointfs make_counter_clockwise(const Pointfs &pointfs)
Definition ClipperUtils.cpp:1568
int filaments_count
Definition AssembleList.hpp:40
void save_main_thread_id()
Definition Thread.cpp:206
int wrapping_y
Definition OrcaSlicer.hpp:47
bool arrange_objects(Model &model, const TBed &bed, const ArrangeParams &params, VirtualBedFn vfn=throw_if_out_of_bed)
Definition ModelArrange.hpp:39
void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count)
Definition PrintConfig.cpp:11475
bool is_gcode_file(const std::string &path)
Definition utils.cpp:1208
std::string t_config_option_key
Definition Config.hpp:96
static void set_flush_volumes_matrix(std::vector< T > &out_matrix, const std::vector< T > &fv_matrix, size_t extruder_id=-1, size_t nozzle_nums=1)
Definition PrintConfig.hpp:2698
static std::set< std::string > gcodes_key_set
Definition PresetBundle.cpp:3121
nlohmann::json json
Definition AssembleList.cpp:42
ConfigOptionFloatsTempl< true > ConfigOptionFloatsNullable
Definition Config.hpp:1016
const ConfigOptionDef * opt_def
Definition Config.hpp:264
const int g_min_flush_volume_from_support
Definition FlushVolCalc.cpp:12
@ Preset
Definition calib.hpp:39
int wrapping_depth
Definition OrcaSlicer.hpp:45
std::string per_user_temp_id()
Definition utils.cpp:1514
static std::vector< T > get_flush_volumes_matrix(const std::vector< T > &fv_matrix, size_t extruder_id=-1, size_t nozzle_nums=1)
Definition PrintConfig.hpp:2685
@ tlSmooth
Definition PrintConfig.hpp:470
std::vector< std::string > t_config_option_keys
Definition Config.hpp:97
int exclude_width
Definition OrcaSlicer.hpp:39
void set_logging_level(unsigned int level)
Definition utils.cpp:171
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582
bool set_current_thread_name(const char *thread_name)
Definition Thread.cpp:187
std::string old_value
Definition Config.hpp:265
double depth
Definition WipeTowerEstimate.hpp:21
bool need_arrange
Definition AssembleList.hpp:39
PrintSequence
Definition PrintConfig.hpp:310
@ ByObject
Definition PrintConfig.hpp:312
@ ByDefault
Definition PrintConfig.hpp:313
struct Slic3r::_assemble_plate_info assemble_plate_info_t
double width
Definition WipeTowerEstimate.hpp:20
std::string per_user_temp_dir(const std::string &base, const std::string &user_id)
Definition utils.cpp:1523
ConfigOptionIntsTempl< false > ConfigOptionInts
Definition Config.hpp:1154
int wrapping_width
Definition OrcaSlicer.hpp:44
static std::string bed_type_to_gcode_string(const BedType type)
Definition PrintConfig.hpp:701
zaa_minimize_perimeter_height nozzle_volume_type((ConfigOptionStrings, extruder_ams_count))((ConfigOptionStrings
ExtruderType
Definition PrintConfig.hpp:612
@ etDirectDrive
Definition PrintConfig.hpp:613
constexpr Eigen::Matrix< Tout, 2, EigenArgs... > unscaled(const Slic3r::Point &v) noexcept
Definition Arrange.cpp:81
ConfigOptionBoolsTempl< false > ConfigOptionBools
Definition Config.hpp:2066
ConfigOptionEnum< PrinterTechnology >
Definition PrintConfig.hpp:2358
std::vector< LayOnFacePlane > lay_on_face_planes(const ModelObject &object, const Transform3d &inst_matrix)
Definition LayOnFace.cpp:21
const std::string & resources_dir()
Definition utils.cpp:296
std::set< std::string > printer_options_with_variant_2
Definition PrintConfig.cpp:10270
std::set< std::string > empty_options
Definition PrintConfig.cpp:10289
ColorRGB to_rgb(const ColorRGBA &other_rgba)
Definition Color.cpp:406
const int g_flush_volume_to_support
Definition FlushVolCalc.cpp:13
Point normal(Point pt, double scale)
Definition TreeSupport.cpp:112
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:73
SupportType
Definition PrintConfig.hpp:378
@ stTreeAuto
Definition PrintConfig.hpp:379
@ stNormalAuto
Definition PrintConfig.hpp:379
FilamentMapMode
Definition PrintConfig.hpp:632
@ fmmNozzleManual
Definition PrintConfig.hpp:636
@ fmmAutoForFlush
Definition PrintConfig.hpp:633
@ fmmManual
Definition PrintConfig.hpp:635
zaa_minimize_perimeter_height ConfigOptionString
Definition PrintConfig.hpp:1702
void export_gcode_statistics(const GCodeProcessorResult &result, const std::string &path)
Definition GCodeStatistics.cpp:190
@ ModelInstancePVS_Inside
Definition Model.hpp:1411
@ ModelInstancePVS_Partly_Outside
Definition Model.hpp:1413
T unscale(Q v)
Definition libslic3r.h:138
LoadStrategy
Definition bbs_3mf.hpp:218
@ LoadModel
Definition bbs_3mf.hpp:222
@ LoadConfig
Definition bbs_3mf.hpp:223
@ LoadAuxiliary
Definition bbs_3mf.hpp:224
@ AddDefaultInstances
Definition bbs_3mf.hpp:220
@ psI3
Definition PrintConfig.hpp:560
SupportMaterialStyle
Definition PrintConfig.hpp:362
@ smsDefault
Definition PrintConfig.hpp:363
@ smsTreeOrganic
Definition PrintConfig.hpp:363
struct Slic3r::_plate_obj_size_info plate_obj_size_info_t
void set_temporary_dir(const std::string &dir)
Definition utils.cpp:303
int find_plane_by_normal(const std::vector< LayOnFacePlane > &planes, const Vec3d &direction)
Definition LayOnFace.cpp:172
std::vector< ModelObject * > ModelObjectPtrs
Definition CutUtils.hpp:13
unsigned get_logging_level()
Definition utils.cpp:221
void set_logging_file(const std::string &file)
Definition utils.cpp:190
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
@ wtwRectangle
Definition PrintConfig.hpp:606
zaa_minimize_perimeter_height time_cost((ConfigOptionString, layer_change_gcode))((ConfigOptionString
std::set< std::string > filament_options_with_variant
Definition PrintConfig.cpp:10150
@ Y
Definition libslic3r.h:142
@ Z
Definition libslic3r.h:143
@ X
Definition libslic3r.h:141
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273
zaa_minimize_perimeter_height wipe_tower_y((ConfigOptionFloat, prime_tower_width))((ConfigOptionFloat
std::string escape_strings_cstyle(const std::vector< std::string > &strs)
Definition Config.cpp:99
std::set< std::string > print_options_with_variant
Definition PrintConfig.cpp:10102
ConfigOptionFloatsTempl< false > ConfigOptionFloats
Definition Config.hpp:1015
bool is_auto_filament_map_mode(FilamentMapMode mode)
Definition PrintConfig.hpp:649
Points get_bed_shape(const DynamicPrintConfig &config, bool use_share)
Definition PrintConfig.cpp:13837
int exclude_depth
Definition OrcaSlicer.hpp:40
std::string gcode_statistics_path(const std::string &gcode_path)
Definition GCodeStatistics.cpp:185
int printable_depth
Definition OrcaSlicer.hpp:36
int wrapping_x
Definition OrcaSlicer.hpp:46
ConfigOptionUniquePtr new_value
Definition Config.hpp:266
Slic3r::Polygon Polygon
Definition Emboss.cpp:101
RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key)
Definition RaycastManager.cpp:298
#define _u8L(s)
Definition I18N.hpp:11
int id
Definition ThumbnailData.hpp:51
float layer_height
Definition ThumbnailData.hpp:54
std::string name
Definition ThumbnailData.hpp:55
std::vector< coordf_t > bbox
Definition ThumbnailData.hpp:52
float area
Definition ThumbnailData.hpp:53
void from_json(const nlohmann::json &j)
Definition CustomGCode.cpp:33
std::vector< Item > gcodes
Definition CustomGCode.hpp:71
int error_code
Definition GCodeProcessor.hpp:177
GCodeCheckResult gcode_check_result
Definition GCodeProcessor.hpp:211
@ Ortho
Definition Camera.hpp:39
@ Iso
Definition Camera.hpp:44
@ Top_Plate
Definition Camera.hpp:53
std::string bed_type
Definition ThumbnailData.hpp:69
std::vector< BBoxData > bbox_objs
Definition ThumbnailData.hpp:63
std::vector< std::string > filament_colors
Definition ThumbnailData.hpp:65
bool is_seq_print
Definition ThumbnailData.hpp:66
std::vector< coordf_t > bbox_all
Definition ThumbnailData.hpp:62
std::vector< int > filament_ids
Definition ThumbnailData.hpp:64
float nozzle_diameter
Definition ThumbnailData.hpp:68
int first_extruder
Definition ThumbnailData.hpp:67
std::string no_light_thumbnail_file
Definition bbs_3mf.hpp:107
std::string pick_file
Definition bbs_3mf.hpp:110
ThumbnailData plate_thumbnail
Definition bbs_3mf.hpp:108
std::vector< size_t > skipped_objects
Definition bbs_3mf.hpp:121
std::vector< FilamentInfo > slice_filaments_info
Definition bbs_3mf.hpp:118
std::string printer_model_id
Definition bbs_3mf.hpp:99
std::string nozzle_diameters
Definition bbs_3mf.hpp:100
std::string top_file
Definition bbs_3mf.hpp:109
std::string thumbnail_file
Definition bbs_3mf.hpp:106
PrintStateBase::WarningLevel warning_level
Definition PrintBase.hpp:480
std::string text
Definition PrintBase.hpp:460
PrintStateBase::SlicingNotificationType message_type
Definition PrintBase.hpp:479
int warning_step
Definition PrintBase.hpp:477
unsigned int height
Definition ThumbnailData.hpp:19
unsigned int width
Definition ThumbnailData.hpp:18
void reset()
Definition ThumbnailData.cpp:78
bool is_valid() const
Definition ThumbnailData.cpp:85
bool align_to_y_axis
Definition Arrange.hpp:149
float printable_height
Definition Arrange.hpp:158
ArrangePolygons excluded_regions
Definition Arrange.hpp:168
float bed_shrink_y
Definition Arrange.hpp:151
Vec2d align_center
Definition Arrange.hpp:159
coord_t min_obj_distance
The minimum distance which is allowed for any pair of items on the print bed in any direction.
Definition Arrange.hpp:132
float clearance_radius
Definition Arrange.hpp:155
float bed_shrink_x
Definition Arrange.hpp:150
std::function< void(unsigned, std::string)> progressind
Progress indicator callback called when an object gets packed. The unsigned argument is the number of...
Definition Arrange.hpp:173
float clearance_height_to_lid
Definition Arrange.hpp:154
bool avoid_extrusion_cali_region
Definition Arrange.hpp:147
float nozzle_height
Definition Arrange.hpp:157
std::string to_json() const
Definition Arrange.hpp:185
bool is_seq_print
Definition Arrange.hpp:148
bool allow_rotations
Definition Arrange.hpp:141
bool allow_multi_materials_on_same_plate
Definition Arrange.hpp:146
float clearance_height_to_rod
Definition Arrange.hpp:153
bool is_wipe_tower
Definition Arrange.hpp:69
ExPolygon poly
Definition Arrange.hpp:59
void apply()
Helper function to call the setter with the arrange data arguments.
Definition Arrange.hpp:106
int itemid
Definition Arrange.hpp:81
Vec2crd translation
The 2D silhouette to be arranged.
Definition Arrange.hpp:60
std::function< void(const ArrangePolygon &)> setter
Optional setter function which can store arbitrary data in its closure.
Definition Arrange.hpp:103
double rotation
The translation of the poly.
Definition Arrange.hpp:61
int priority
To which logical bed does poly belong...
Definition Arrange.hpp:64
bool is_virt_object
Definition Arrange.hpp:67
std::string name
Definition Arrange.hpp:96
int bed_idx
Arrange with inflated polygon.
Definition Arrange.hpp:63

References _u8L, Slic3r::ColorRGBA::a(), Slic3r::Model::add_default_instances(), Slic3r::ModelObject::add_instance(), Slic3r::Model::add_object(), Slic3r::ModelObject::add_volume(), Slic3r::AddDefaultInstances, Slic3r::arrangement::ArrangeParams::align_center, Slic3r::arrangement::ArrangeParams::align_to_y_axis, Slic3r::arrangement::ArrangeParams::allow_multi_materials_on_same_plate, Slic3r::arrangement::ArrangeParams::allow_rotations, Slic3r::ConfigBase::apply(), Slic3r::PrintBase::apply(), Slic3r::ConfigBase::apply_only(), Slic3r::GUI::OpenGLManager::Arb, Slic3r::BBoxData::area, Slic3r::GUI::GUI_InitParams::argc, Slic3r::GUI::GUI_InitParams::argv, Slic3r::arrangement::arrange(), Slic3r::arrange_objects(), Slic3r::ModelConfig::assign_config(), Slic3r::arrangement::ArrangeParams::avoid_extrusion_cali_region, Slic3r::ColorRGBA::b(), BBL_JSON_KEY_FILAMENT_ID, BBL_JSON_KEY_FROM, BBL_JSON_KEY_INHERITS, BBL_JSON_KEY_NAME, BBL_JSON_KEY_TYPE, Slic3r::BBoxData::bbox, Slic3r::PlateBBoxData::bbox_all, Slic3r::PlateBBoxData::bbox_objs, bed3d_ax3s_default_tip_radius, Slic3r::arrangement::ArrangePolygon::bed_idx, BED_SHRINK_SEQ_PRINT, Slic3r::arrangement::ArrangeParams::bed_shrink_x, Slic3r::arrangement::ArrangeParams::bed_shrink_y, Slic3r::PlateBBoxData::bed_type, Slic3r::bed_type_to_gcode_string(), Slic3r::WipeTowerFootprint::brim_width, Slic3r::btDefault, Slic3r::ByDefault, Slic3r::ByObject, Slic3r::FlushVolCalculator::calc_flush_vol(), Slic3r::BoundingBox3Base< PointType >::center(), Slic3r::check_mixed_filament_type_consistency(), Slic3r::GUI::PartPlate::check_tpu_printable_status(), Slic3r::BuildVolume::check_volume_bbox_state_with_extruder_areas(), Slic3r::GUI::PartPlateList::clear(), Slic3r::Model::clear_materials(), Slic3r::Model::clear_objects(), Slic3r::arrangement::ArrangeParams::clearance_height_to_lid, Slic3r::arrangement::ArrangeParams::clearance_height_to_rod, Slic3r::arrangement::ArrangeParams::clearance_radius, CLI_3MF_NEW_MACHINE_NOT_SUPPORTED, CLI_CONFIG_FILE_ERROR, CLI_COPY_OBJECTS_ERROR, CLI_DATA_FILE_ERROR, CLI_ENVIRONMENT_ERROR, cli_errors, CLI_EXPORT_3MF_ERROR, CLI_EXPORT_OBJ_ERROR, CLI_EXPORT_STATISTICS_ERROR, CLI_EXPORT_STL_ERROR, CLI_FILAMENT_CAN_NOT_MAP, CLI_FILAMENT_NOT_MATCH_BED_TYPE, CLI_FILAMENTS_DIFFERENT_TEMP, CLI_FILAMENTS_NOT_SUPPORTED_BY_EXTRUDER, CLI_FILE_NOTFOUND, CLI_FILE_VERSION_NOT_SUPPORTED, CLI_FILELIST_INVALID_ORDER, CLI_GCODE_PATH_CONFLICTS, CLI_GCODE_PATH_IN_UNPRINTABLE_AREA, CLI_INVALID_PARAMS, CLI_INVALID_PRINTER_TECH, CLI_INVALID_VALUES_IN_3MF, CLI_MIXED_FILAMENT_INVALID, CLI_MODIFIED_PARAMS_TO_PRINTER, CLI_NO_SUITABLE_OBJECTS, CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP, CLI_OBJECT_ARRANGE_FAILED, CLI_OBJECT_COLLISION_IN_LAYER_PRINT, CLI_OBJECT_COLLISION_IN_SEQ_PRINT, CLI_OBJECTS_PARTLY_INSIDE, CLI_ONLY_ONE_TPU_SUPPORTED, CLI_POSTPROCESS_NOT_SUPPORTED, CLI_PROCESS_NOT_COMPATIBLE, cli_record_warning(), CLI_SCALE_TO_FIT_ERROR, CLI_SLICING_ERROR, CLI_SLICING_TIME_EXCEEDS_LIMIT, CLI_SUCCESS, CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT, CLI_UNSUPPORTED_OPERATION, CLI_VALIDATE_ERROR, Slic3r::ConfigOption::clone(), Slic3r::GUI::color_string(), Slic3r::GUI::PartPlate::config(), Slic3r::ModelObject::config, Slic3r::ModelVolume::config, Slic3r::Preset::config, Slic3r::Print::config(), Slic3r::ConfigError, Slic3r::ConfigOptionString, construct_assemble_list(), Slic3r::GUI::PartPlate::contain_instance(), Slic3r::ExPolygon::contour, Slic3r::PresetBundle::convert_filament_preset_name(), count, Slic3r::Model::curr_plate_index, Slic3r::CustomGCode::Custom, decode_png_to_thumbnail(), Slic3r::PresetCollection::default_preset(), Slic3r::PrinterPresetCollection::default_preset_for(), default_status_callback(), Slic3r::Geometry::deg2rad(), Slic3r::WipeTowerFootprint::depth, Slic3r::ConfigOptionFloatsTempl< NULLABLE >::deserialize(), Slic3r::DynamicConfig::diff(), _sliced_info::downward_machines, Slic3r::duplicate(), Slic3r::GUI::PartPlate::duplicate_all_instance(), Slic3r::ConfigOptionString::empty(), Slic3r::DynamicConfig::empty(), Slic3r::empty(), Slic3r::PrintBase::empty(), Slic3r::empty_options, Slic3r::Enable, Slic3r::ModelObject::ensure_on_bed(), EPSILON, Slic3r::DynamicConfig::erase(), Slic3r::GCodeCheckResult::error_code, Slic3r::escape_strings_cstyle(), Slic3r::GUI::PartPlate::estimate_wipe_tower_footprint(), Slic3r::GUI::PartPlate::estimate_wipe_tower_polygon(), Slic3r::etDirectDrive, Slic3r::_printer_plate_info::exclude_depth, Slic3r::_printer_plate_info::exclude_width, Slic3r::_printer_plate_info::exclude_x, Slic3r::_printer_plate_info::exclude_y, Slic3r::arrangement::ArrangeParams::excluded_regions, Slic3r::PrintBase::export_cached_data(), Slic3r::Print::export_gcode(), Slic3r::export_gcode_statistics(), export_models(), export_project(), _sliced_info::export_time, Slic3r::GUI::OpenGLManager::Ext, Slic3r::GUI::GUI_InitParams::extra_config, Slic3r::ModelConfig::extruder(), Slic3r::TriangleMesh::facets_count(), Slic3r::PlateBBoxData::filament_colors, Slic3r::filament_dev_options, Slic3r::PlateBBoxData::filament_ids, Slic3r::Preset::filament_options(), Slic3r::filament_options_with_variant, Slic3r::filament_type(), Slic3r::PresetBundle::filaments, Slic3r::_assemble_plate_info::filaments_count, Slic3r::FileNotFound, Slic3r::find_largest_plane(), Slic3r::find_plane_at_point(), Slic3r::find_plane_by_normal(), Slic3r::PresetCollection::find_preset2(), Slic3r::PlateBBoxData::first_extruder, Slic3r::ToolOrdering::first_extruder(), Slic3r::Print::first_layer_wipe_tower_corners(), flush_and_exit, Slic3r::fmmAutoForFlush, Slic3r::fmmManual, Slic3r::fmmNozzleManual, Slic3r::CustomGCode::Info::from_json(), Slic3r::BBLPrinterAgent::from_orca_filament_id(), Slic3r::ColorRGBA::g(), Slic3r::g_flush_volume_to_support, Slic3r::g_max_flush_volume, Slic3r::g_min_flush_volume_from_support, g_slicing_warnings, Slic3r::GCodeProcessorResult::gcode_check_result, Slic3r::gcode_statistics_path(), Slic3r::CustomGCode::Info::gcodes, Slic3r::gcodes_key_set, Slic3r::ModelConfig::get(), Slic3r::ConfigOptionBoolsTempl< NULLABLE >::get_at(), Slic3r::ConfigOptionVector< T >::get_at(), Slic3r::ConfigOptionVector< double >::get_at(), Slic3r::ConfigOptionVector< int >::get_at(), Slic3r::get_bed_shape(), Slic3r::GUI::PartPlate::get_bed_type(), Slic3r::GUI::PartPlate::get_bounding_box(), Slic3r::Print::get_conflict_string(), Slic3r::ModelVolume::get_convex_hull(), Slic3r::GUI::PartPlateList::get_curr_plate(), Slic3r::Utils::get_current_time_utc(), Slic3r::GUI::PartPlate::get_extruder_areas(), Slic3r::get_extruder_nozzle_stats(), Slic3r::get_extruder_variant_string(), Slic3r::ModelVolume::get_extruders(), Slic3r::GUI::PartPlate::get_extruders_under_cli(), Slic3r::Print::get_filament_maps(), Slic3r::Print::get_filament_nozzle_maps(), Slic3r::Print::get_filament_volume_maps(), Slic3r::get_flush_volumes_matrix(), Slic3r::GUI::OpenGLManager::get_framebuffers_type(), Slic3r::GUI::PartPlate::get_index(), Slic3r::get_instance_arrange_poly(), Slic3r::get_logging_level(), Slic3r::Geometry::Transformation::get_matrix(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::get_min_flush_volumes(), Slic3r::GUI::PartPlate::get_objects_bounding_box(), Slic3r::ModelInstance::get_offset(), Slic3r::GUI::PartPlate::get_origin(), Slic3r::GUI::PartPlateList::get_plate(), Slic3r::GUI::PartPlateList::get_plate_count(), Slic3r::GUI::PartPlateList::get_plate_size(), Slic3r::GUI::PartPlate::get_print(), Slic3r::GUI::PartPlate::get_print_seq(), get_printer_technology(), Slic3r::GUI::PartPlate::get_real_filament_map_mode(), Slic3r::GUI::PartPlate::get_real_filament_maps(), Slic3r::GUI::OpenGLManager::get_shader(), Slic3r::GUI::PartPlate::get_shape(), Slic3r::GUI::PartPlate::get_slice_result(), Slic3r::GUI::PartPlate::get_tmp_gcode_path(), Slic3r::Print::get_tool_ordering(), Slic3r::ModelInstance::get_transformation(), Slic3r::Preset::get_type_string(), Slic3r::ConfigOption::getFloat(), GL_TRUE, glfw_callback(), Slic3r::GUI::GUI_Run(), Slic3r::ConfigOptionResolver::has(), Slic3r::has_any_mixed_filament(), Slic3r::ModelObject::has_custom_layering(), Slic3r::Print::has_wipe_tower(), Slic3r::ThumbnailData::height, Slic3r::WipeTowerFootprint::height, I3_WIPE_TOWER_DEFAULT_X_POS, I3_WIPE_TOWER_DEFAULT_Y_POS, Slic3r::BBoxData::id, Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::GUI::OpenGLManager::init_gl(), Slic3r::GUI::GUI_InitParams::input_files, Slic3r::GUI::GUI_InitParams::input_gcode, Slic3r::MeshInspect::inspect_to_json(), Slic3r::PaintCLI::inspect_to_json(), Slic3r::ModelObject::instance_bounding_box(), Slic3r::ModelObject::instances, Slic3r::GUI::PartPlate::intersect_instance(), Slic3r::is_auto_filament_map_mode(), Slic3r::is_compatible_with_printer(), Slic3r::is_gcode_file(), Slic3r::ModelVolume::is_model_part(), Slic3r::ModelVolume::is_modifier(), Slic3r::Preset::is_project_embedded, Slic3r::ConfigOption::is_scalar(), Slic3r::arrangement::ArrangeParams::is_seq_print, Slic3r::PlateBBoxData::is_seq_print, Slic3r::GUI::PartPlate::is_slice_result_valid(), Slic3r::ThumbnailData::is_valid(), Slic3r::arrangement::ArrangePolygon::is_virt_object, Slic3r::arrangement::ArrangePolygon::is_wipe_tower, Slic3r::GUI::Camera::Iso, Slic3r::arrangement::ArrangePolygon::itemid, Slic3r::DynamicConfig::keys(), Slic3r::lay_on_face(), Slic3r::lay_on_face_planes(), Slic3r::ModelObject::layer_config_ranges, _sliced_info::layer_height, Slic3r::BBoxData::layer_height, Slic3r::layer_height(), legacy_bed_exclude_area_points(), Slic3r::BuildVolume::Limited, Slic3r::load_assemble_plate_list(), Slic3r::PrintBase::load_cached_data(), Slic3r::GUI::GUI_InitParams::load_configs, load_default_gcodes_to_config(), load_downward_settings_list_from_config(), Slic3r::Preset::load_external_config(), Slic3r::GUI::PartPlateList::load_from_3mf_structure(), Slic3r::AppConfig::load_if_exists(), load_key_values_from_json(), Slic3r::GLVolumeCollection::load_object_volume(), Slic3r::LoadAuxiliary, Slic3r::LoadConfig, Slic3r::ModelInstance::loaded_id, Slic3r::_assemble_plate_info::loaded_obj_list, Slic3r::LoadModel, Slic3r::GUI::PartPlate::lock(), Slic3r::GUI::PartPlateList::lock_plate(), m_actions, m_config, m_extra_config, m_given_option_keys, m_input_files, m_models, m_print_config, m_transforms, Slic3r::Semver::maj(), Slic3r::make_counter_clockwise(), Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, std::vector< PointType > >::max, MAX_CLONEABLE_SIZE, MAX_PLATE_COUNT, Slic3r::Model::md_name, Slic3r::Model::md_value, Slic3r::BoundingBox3Base< PointType >::merge(), Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), Slic3r::ModelVolume::mesh(), Slic3r::PrintBase::SlicingStatus::message_type, Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::BoundingBoxBase< PointType, std::vector< PointType > >::min, Slic3r::Semver::min(), Slic3r::arrangement::ArrangeParams::min_obj_distance, Slic3r::Model::mk_name, Slic3r::Model::mk_version, Slic3r::ModelInstancePVS_Inside, Slic3r::ModelInstancePVS_Partly_Outside, Slic3r::MeshRepairStats::modified, Slic3r::GUI::WipeTowerRelocation::moved, Slic3r::arrangement::ArrangePolygon::name, Slic3r::BBoxData::name, Slic3r::ModelObject::name, Slic3r::ModelVolume::name, Slic3r::Preset::name, Slic3r::_assemble_plate_info::need_arrange, WebView::NeedsCompositingWorkaround(), Slic3r::ConfigSubstitution::new_value, Slic3r::GUI::PartPlate::no_light_thumbnail_data, Slic3r::PlateData::no_light_thumbnail_file, Slic3r::PrintStateBase::NON_CRITICAL, Slic3r::normal(), Slic3r::normalize_filament_values_to_variants(), Slic3r::PlateBBoxData::nozzle_diameter, Slic3r::PlateData::nozzle_diameters, Slic3r::arrangement::ArrangeParams::nozzle_height, Slic3r::nozzle_height(), Slic3r::nozzle_volume_type(), Slic3r::nvtHybrid, Slic3r::nvtStandard, Slic3r::IO::OBJ, Slic3r::Model::objects, Slic3r::Print::objects(), Slic3r::offset(), Slic3r::ConfigSubstitution::old_value, Slic3r::ConfigSubstitution::opt_def, Slic3r::DynamicConfig::opt_float(), Slic3r::ConfigOptionDef::opt_key, Slic3r::DynamicConfig::opt_string(), Slic3r::ConfigBase::option(), Slic3r::ModelConfig::option(), Slic3r::orientation::orient(), Slic3r::GUI::Camera::Ortho, Slic3r::GUI::WipeTowerRelocation::overlaps_objects, Slic3r::Semver::parse(), Slic3r::GUI::BitmapCache::parse_color4(), Slic3r::per_user_temp_dir(), Slic3r::per_user_temp_id(), Slic3r::PlateData::pick_file, Slic3r::GUI::PartPlate::pick_thumbnail_data, _sliced_plate_info::plate_id, Slic3r::PlateData::plate_thumbnail, Slic3r::arrangement::ArrangePolygon::poly, Slic3r::GUI::PartPlateList::postprocess_arrange_polygon(), Slic3r::GUI::PartPlateList::postprocess_bed_index_for_current_plate(), Slic3r::GUI::PartPlateList::postprocess_bed_index_for_selected(), _sliced_info::prepare_time, Slic3r::GUI::PartPlateList::preprocess_arrange_polygon(), Slic3r::GUI::PartPlateList::preprocess_arrange_polygon_other_locked(), Slic3r::GUI::PartPlateList::preprocess_exclude_areas(), Slic3r::GUI::PartPlateList::preprocess_nonprefered_areas(), Slic3r::Preset, print_help(), Slic3r::Preset::print_options(), Slic3r::print_options_with_variant, Slic3r::ModelInstance::print_volume_state, Slic3r::ModelInstance::printable, Slic3r::_printer_plate_info::printable_depth, Slic3r::_printer_plate_info::printable_height, Slic3r::arrangement::ArrangeParams::printable_height, Slic3r::GUI::PartPlate::printable_instance_size(), Slic3r::_printer_plate_info::printable_width, Slic3r::PlateData::printer_model_id, Slic3r::_printer_plate_info::printer_name, Slic3r::Preset::printer_options(), Slic3r::printer_options_with_variant_1, Slic3r::printer_options_with_variant_2, Slic3r::printer_technology(), Slic3r::PresetBundle::printers, Slic3r::PresetBundle::prints, Slic3r::arrangement::ArrangePolygon::priority, Slic3r::PrintBase::process(), Slic3r::arrangement::ArrangeParams::progressind, Slic3r::PresetBundle::project_different_keys(), Slic3r::psI3, Slic3r::ptFFF, Slic3r::ptSLA, Slic3r::ptUnknown, Slic3r::ColorRGBA::r(), Slic3r::Model::read_from_file(), Slic3r::GUI::PartPlateList::rebuild_plates_after_arrangement(), record_exit_reson(), Slic3r::release_PlateData_list(), Slic3r::GUI::PartPlateList::reload_all_objects(), Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(), Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(), Slic3r::ModelVolume::repair(), Slic3r::AppConfig::reset(), Slic3r::ThumbnailData::reset(), Slic3r::GUI::PartPlateList::reset_size(), Slic3r::ConfigOptionVector< T >::resize(), Slic3r::ConfigOptionVectorBase::resize(), Slic3r::resize_mixed_filament_metadata(), Slic3r::PresetBundle::resolve_preset_config(), Slic3r::PresetBundle::resolve_preset_config_type(), Slic3r::resources_dir(), Slic3r::arrangement::ArrangePolygon::rotation, Slic3r::save_main_thread_id(), scale_, Slic3r::scaled(), SCALING_FACTOR, Slic3r::GUI::PartPlateList::select_plate(), serialize(), Slic3r::ConfigOption::serialize(), Slic3r::ConfigOptionBoolsTempl< NULLABLE >::serialize(), Slic3r::ConfigOptionFloatsTempl< NULLABLE >::serialize(), Slic3r::ConfigBase::set(), Slic3r::ConfigOption::set(), Slic3r::ConfigOptionVector< T >::set_at(), Slic3r::ConfigOptionVector< double >::set_at(), Slic3r::ConfigOptionVectorBase::set_at(), Slic3r::Model::set_backup_path(), Slic3r::Print::set_check_multi_filaments_compatibility(), Slic3r::set_current_thread_name(), Slic3r::Print::set_extruder_filament_info(), Slic3r::set_filament_dev_options(), Slic3r::GUI::PartPlate::set_filament_maps(), Slic3r::GUI::PartPlate::set_filament_nozzle_maps(), Slic3r::GUI::PartPlate::set_filament_volume_maps(), Slic3r::set_flush_volumes_matrix(), Slic3r::DynamicConfig::set_key_value(), Slic3r::ModelConfig::set_key_value(), Slic3r::set_logging_file(), Slic3r::set_logging_level(), Slic3r::PrintBase::set_no_check_flag(), Slic3r::ModelInstance::set_offset(), Slic3r::GUI::PartPlateList::set_shapes(), Slic3r::PrintBase::set_status_callback(), Slic3r::set_temporary_dir(), Slic3r::GUI::PartPlate::set_tmp_gcode_path(), Slic3r::ConfigOptionVectorBase::set_with_restore(), Slic3r::ConfigOptionVectorBase::set_with_restore_2(), Slic3r::Model::setExtruderParams(), Slic3r::Model::setPrintSpeedTable(), Slic3r::arrangement::ArrangePolygon::setter, setup(), Slic3r::BoundingBox3Base< PointType >::size(), Slic3r::BoundingBoxBase< PointType, APointsType >::size(), Slic3r::ConfigOptionVector< T >::size(), Slic3r::ConfigOptionVector< double >::size(), Slic3r::ConfigOptionVector< unsigned char >::size(), Slic3r::ConfigOptionVectorBase::size(), Slic3r::PlateData::skipped_objects, SLIC3R_APP_FULL_NAME, Slic3r::PlateData::slice_filaments_info, _sliced_info::sliced_plates, _sliced_plate_info::sliced_time, _sliced_plate_info::sliced_time_with_cache, Slic3r::Print::slicing_errors_message(), Slic3r::PrintStateBase::SlicingDefaultNotification, Slic3r::PrintStateBase::SlicingEmptyGcodeLayers, Slic3r::PrintStateBase::SlicingGcodeOverlap, Slic3r::smsDefault, Slic3r::smsTreeOrganic, _sliced_info::sparse_infill_density, Slic3r::IO::STL, Slic3r::stNormalAuto, Slic3r::GUI::PartPlateList::store_to_3mf_structure(), _sliced_info::strict_mode, Slic3r::STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE, Slic3r::STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP, Slic3r::STRING_EXCEPT_LAYER_HEIGHT_EXCEEDS_LIMIT, Slic3r::STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT, Slic3r::STRING_EXCEPT_OBJECT_COLLISION_IN_SEQ_PRINT, Slic3r::stTreeAuto, Slic3r::ConfigSubstitutionContext::substitutions, WebView::SupportsForceSharedMemory(), Slic3r::PrintBase::SlicingStatus::text, Slic3r::PlateData::thumbnail_file, Slic3r::time_cost(), Slic3r::tlSmooth, Slic3r::arrangement::ArrangeParams::to_json(), Slic3r::to_rgb(), Slic3r::Semver::to_string(), Slic3r::PlateData::top_file, Slic3r::GUI::Camera::Top_Plate, Slic3r::GUI::PartPlate::top_thumbnail_data, Slic3r::trace(), Slic3r::TriangleMesh::transformed_bounding_box(), Slic3r::BoundingBox3Base< PointType >::translate(), Slic3r::GUI::PartPlate::translate_all_instance(), Slic3r::ModelObject::translate_instances(), Slic3r::arrangement::ArrangePolygon::translation, _sliced_plate_info::triangle_count, Slic3r::Preset::type, Slic3r::Preset::TYPE_FILAMENT, Slic3r::Preset::TYPE_PRINT, Slic3r::Preset::TYPE_PRINTER, Slic3r::unescape_strings_cstyle(), Slic3r::unscale(), Slic3r::unscaled(), Slic3r::arrangement::update_arrange_params(), Slic3r::Model::update_print_volume_state(), Slic3r::arrangement::update_selected_items_axis_align(), Slic3r::arrangement::update_selected_items_inflation(), Slic3r::GUI::PartPlate::update_slice_result_valid_state(), Slic3r::arrangement::update_unselected_items_inflation(), Slic3r::DynamicPrintConfig::update_values_from_multi_to_multi_2(), _sliced_info::upward_machines, Slic3r::ModelInstance::use_loaded_id_for_label, Slic3r::PrintBase::validate(), Slic3r::ConfigOptionSingle< T >::value, Slic3r::ConfigOptionVector< T >::values, Slic3r::ConfigOptionVector< double >::values, Slic3r::ConfigOptionVector< int >::values, Slic3r::ConfigOptionVector< unsigned char >::values, Slic3r::GLVolumeCollection::volumes, Slic3r::ModelObject::volumes, Slic3r::ConfigOptionStrings::vserialize(), _sliced_info::wall_loops, Slic3r::PrintBase::SlicingStatus::warning_level, _sliced_plate_info::warning_message, Slic3r::PrintBase::SlicingStatus::warning_step, Slic3r::GUI::warning_text, Slic3r::ThumbnailData::width, Slic3r::WipeTowerFootprint::width, WIPE_TOWER_DEFAULT_X_POS, WIPE_TOWER_DEFAULT_Y_POS, WIPE_TOWER_MARGIN, Slic3r::wipe_tower_y(), Slic3r::_printer_plate_info::wrapping_depth, Slic3r::_printer_plate_info::wrapping_width, Slic3r::_printer_plate_info::wrapping_x, Slic3r::_printer_plate_info::wrapping_y, Slic3r::wtwRectangle, Slic3r::X, Slic3r::Y, and Slic3r::Z.

Referenced by main().

Here is the caller graph for this function:

◆ setup()

bool CLI::setup ( int argc,
char ** argv )
private
8098{
8099 // Detect the operating system flavor after SLIC3R_LOGLEVEL is set.
8101
8102#ifdef WIN32
8103 // Notify user that a blacklisted DLL was injected into OrcaSlicer process (for example Nahimic, see GH #5573).
8104 // We hope that if a DLL is being injected into a OrcaSlicer process, it happens at the very start of the application,
8105 // thus we shall detect them now.
8106 if (BlacklistedLibraryCheck::get_instance().perform_check()) {
8107 std::wstring text = L"Following DLLs have been injected into the OrcaSlicer process:\n\n";
8108 text += BlacklistedLibraryCheck::get_instance().get_blacklisted_string();
8109 text += L"\n\n"
8110 L"OrcaSlicer is known to not run correctly with these DLLs injected. "
8111 L"We suggest stopping or uninstalling these services if you experience "
8112 L"crashes or unexpected behaviour while using OrcaSlicer.\n"
8113 L"For example, ASUS Sonic Studio injects a Nahimic driver, which makes OrcaSlicer "
8114 L"to crash on a secondary monitor";
8115 MessageBoxW(NULL, text.c_str(), L"Warning"/*L"Incopatible library found"*/, MB_OK);
8116 }
8117#endif
8118
8119 // See Invoking prusa-slicer from $PATH environment variable crashes #5542
8120 // boost::filesystem::path path_to_binary = boost::filesystem::system_complete(argv[0]);
8121 boost::filesystem::path path_to_binary = boost::dll::program_location();
8122
8123 // Path from the Slic3r binary to its resources.
8124#ifdef __APPLE__
8125 // The application is packed in the .dmg archive as 'Slic3r.app/Contents/MacOS/Slic3r'
8126 // The resources are packed to 'Slic3r.app/Contents/Resources'
8127 boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path().parent_path() / "Resources";
8128#elif defined _WIN32
8129 // The application is packed in the .zip archive in the root,
8130 // The resources are packed to 'resources'
8131 // Path from Slic3r binary to resources:
8132 boost::filesystem::path path_resources = path_to_binary.parent_path() / "resources";
8133#elif defined SLIC3R_FHS
8134 // The application is packaged according to the Linux Filesystem Hierarchy Standard
8135 // Resources are set to the 'Architecture-independent (shared) data', typically /usr/share or /usr/local/share
8136 boost::filesystem::path path_resources = SLIC3R_FHS_RESOURCES;
8137#else
8138 // The application is packed in the .tar.bz archive (or in AppImage) as 'bin/slic3r',
8139 // The resources are packed to 'resources'
8140 // Path from Slic3r binary to resources:
8141 boost::filesystem::path path_resources = boost::filesystem::canonical(path_to_binary).parent_path().parent_path() / "resources";
8142 //Orca: for build systems that support multiple configurations, the binary may be in a subdirectory like "bin/Release" or "bin/Debug".
8143 if( !boost::filesystem::exists(path_resources)) {
8144 // If the resources directory does not exist, try to use the resources directory
8145 path_resources = boost::filesystem::canonical(path_to_binary).parent_path().parent_path().parent_path() / "resources";
8146 }
8147#endif
8148
8149 set_resources_dir(path_resources.string());
8150 set_var_dir((path_resources / "images").string());
8151 set_local_dir((path_resources / "i18n").string());
8152 set_sys_shapes_dir((path_resources / "shapes").string());
8153 set_custom_gcodes_dir((path_resources / "custom_gcodes").string());
8154
8155 // Parse all command line options into a DynamicConfig.
8156 // If any option is unsupported, print usage and abort immediately.
8157 t_config_option_keys opt_order;
8158 if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) {
8159 // Separate error message reported by the CLI parser from the help.
8160 boost::nowide::cerr << std::endl;
8161 this->print_help();
8162 return false;
8163 }
8164
8165 // Orca: resolve here, while the process is still in the directory the user invoked it from.
8166 // GUI_App's constructor moves the working directory to <data_dir>/log, long before the GUI
8167 // opens these files in post_init(), and a relative path would then resolve against that.
8168 for (std::string &input_file : m_input_files)
8169 input_file = resolve_cli_input_path(input_file);
8170
8171 m_given_option_keys.insert(opt_order.begin(), opt_order.end());
8172
8173 // Parse actions and transform options.
8174 for (auto const &opt_key : opt_order) {
8175 if (cli_actions_config_def.has(opt_key))
8176 m_actions.emplace_back(opt_key);
8177 else if (cli_transform_config_def.has(opt_key))
8178 m_transforms.emplace_back(opt_key);
8179 }
8180
8181 //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet.
8182 std::map<std::string, std::string> validity = m_config.validate(true);
8183
8184 // Initialize with defaults.
8186 for (const t_optiondef_map::value_type &optdef : *options)
8187 m_config.option(optdef.first, true);
8188
8189 set_data_dir(m_config.opt_string("datadir"));
8190 if (!data_dir().empty() && !boost::filesystem::exists(data_dir())) {
8191 boost::nowide::cerr << "Could not create data directory: " << data_dir() << std::endl;
8192 return false;
8193 }
8194
8195 //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet.
8196 if (!validity.empty()) {
8197 boost::nowide::cerr << "Params in command line error: "<< std::endl;
8198 for (std::map<std::string, std::string>::iterator it=validity.begin(); it!=validity.end(); ++it)
8199 boost::nowide::cerr << it->first <<": "<< it->second << std::endl;
8200 return false;
8201 }
8202
8203 return true;
8204}
#define L(s)
Definition ExtrusionEntity.cpp:25
bool has(const t_config_option_key &opt_key) const
Definition Config.hpp:2661
t_optiondef_map options
Definition Config.hpp:2658
void set_data_dir(const std::string &dir)
Definition utils.cpp:353
void detect_platform()
Definition Platform.cpp:22
void set_local_dir(const std::string &dir)
Definition utils.cpp:315
void set_custom_gcodes_dir(const std::string &dir)
Definition utils.cpp:339
const std::string & data_dir()
Definition utils.cpp:365
std::map< t_config_option_key, ConfigOptionDef > t_optiondef_map
Definition Config.hpp:2650
void set_resources_dir(const std::string &dir)
Definition utils.cpp:291
std::string resolve_cli_input_path(const std::string &path)
Definition utils.cpp:1532
void set_sys_shapes_dir(const std::string &dir)
Definition utils.cpp:327
void set_var_dir(const std::string &dir)
Definition utils.cpp:268

References Slic3r::cli_actions_config_def, Slic3r::cli_misc_config_def, Slic3r::cli_transform_config_def, Slic3r::data_dir(), Slic3r::detect_platform(), Slic3r::empty(), L, m_actions, m_config, m_given_option_keys, m_input_files, m_transforms, print_help(), Slic3r::resolve_cli_input_path(), Slic3r::set_custom_gcodes_dir(), Slic3r::set_data_dir(), Slic3r::set_local_dir(), Slic3r::set_resources_dir(), Slic3r::set_sys_shapes_dir(), and Slic3r::set_var_dir().

Referenced by run().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_actions

std::vector<std::string> Slic3r::CLI::m_actions
private

Referenced by run(), and setup().

◆ m_config

DynamicPrintAndCLIConfig Slic3r::CLI::m_config
private

◆ m_extra_config

DynamicPrintConfig Slic3r::CLI::m_extra_config
private

Referenced by run().

◆ m_given_option_keys

std::set<std::string> Slic3r::CLI::m_given_option_keys
private

Referenced by run(), and setup().

◆ m_input_files

std::vector<std::string> Slic3r::CLI::m_input_files
private

Referenced by run(), and setup().

◆ m_models

std::vector<Model> Slic3r::CLI::m_models
private

Referenced by export_models(), and run().

◆ m_print_config

DynamicPrintConfig Slic3r::CLI::m_print_config
private

Referenced by run().

◆ m_transforms

std::vector<std::string> Slic3r::CLI::m_transforms
private

Referenced by run(), and setup().


The documentation for this class was generated from the following files: