ENH: CLI: support load_assemble_list

JIRA: STUDIO-4848
Change-Id: Ife11533740988331ea71eac86c370e625970cb8b
This commit is contained in:
lane.wei 2023-11-06 21:57:36 +08:00 committed by Lane.Wei
parent 1d5c7bd442
commit 6412bb9b4d
3 changed files with 606 additions and 28 deletions

View File

@ -577,6 +577,304 @@ static void load_default_gcodes_to_config(DynamicPrintConfig& config, Preset::Ty
} }
} }
static int load_assemble_plate_list(std::string config_file, std::vector<assemble_plate_info_t> &assemble_plate_info_list)
{
int ret = 0;
boost::filesystem::path directory_path(config_file);
BOOST_LOG_TRIVIAL(info) << boost::format("%1% enter, file %2%")%__FUNCTION__ % config_file;
if (!fs::exists(directory_path)) {
BOOST_LOG_TRIVIAL(error) << boost::format("directory %1% not exist.")%config_file;
return CLI_FILE_NOTFOUND;
}
try {
json root_json;
boost::nowide::ifstream ifs(config_file);
ifs >> root_json;
ifs.close();
int plate_count = root_json[JSON_ASSEMPLE_PLATES].size();
if ((plate_count <= 0) || (plate_count > MAX_PLATE_COUNT)) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": invalid plate count %1%")%plate_count;
return CLI_CONFIG_FILE_ERROR;
}
assemble_plate_info_list.resize(plate_count);
for (int plate_index = 0; plate_index < plate_count; plate_index++)
{
assemble_plate_info_t &assemble_plate = assemble_plate_info_list[plate_index];
const json& plate_json = root_json[JSON_ASSEMPLE_PLATES][plate_index];
assemble_plate.plate_name = plate_json[JSON_ASSEMPLE_PLATE_NAME];
assemble_plate.need_arrange = plate_json[JSON_ASSEMPLE_PLATE_NEED_ARRANGE];
if (plate_json.contains(JSON_ASSEMPLE_PLATE_PARAMS)) {
assemble_plate.plate_params = plate_json[JSON_ASSEMPLE_PLATE_PARAMS].get<std::map<std::string, std::string>>();
BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, has %2% plate params") % (plate_index + 1) % assemble_plate.plate_params.size();
}
int object_count = plate_json[JSON_ASSEMPLE_OBJECTS].size();
if (object_count <= 0) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": invalid object count %1% in plate %2%")%object_count %(plate_index+1);
return CLI_CONFIG_FILE_ERROR;
}
assemble_plate.assemble_obj_list.resize(object_count);
for (int object_index = 0; object_index < object_count; object_index++)
{
assemble_object_info_t& assemble_object = assemble_plate.assemble_obj_list[object_index];
const json& object_json = plate_json[JSON_ASSEMPLE_OBJECTS][object_index];
assemble_object.path = object_json[JSON_ASSEMPLE_OBJECT_PATH];
assemble_object.count = object_json[JSON_ASSEMPLE_OBJECT_COUNT];
if (assemble_object.count <= 0) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": invalid object clone count %1% in plate %2% Object %3%") % assemble_object.count % (plate_index + 1) % assemble_object.path;
return CLI_CONFIG_FILE_ERROR;
}
assemble_object.filaments = object_json.at(JSON_ASSEMPLE_OBJECT_FILAMENTS).get<std::vector<int>>();
if ((assemble_object.filaments.size() > 0) && (assemble_object.filaments.size() != assemble_object.count) && (assemble_object.filaments.size() != 1))
{
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s filaments count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.filaments.size() % assemble_object.count;
return CLI_CONFIG_FILE_ERROR;
}
if (object_json.contains(JSON_ASSEMPLE_OBJECT_ASSEMBLE_INDEX)) {
assemble_object.assemble_index = object_json[JSON_ASSEMPLE_OBJECT_ASSEMBLE_INDEX].get<std::vector<int>>();
if ((assemble_object.assemble_index.size() > 0) && (assemble_object.assemble_index.size() != assemble_object.count) && (assemble_object.assemble_index.size() != 1))
{
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s assemble_index count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.assemble_index.size() % assemble_object.count;
return CLI_CONFIG_FILE_ERROR;
}
}
if (object_json.contains(JSON_ASSEMPLE_OBJECT_POS_X)) {
assemble_object.pos_x = object_json[JSON_ASSEMPLE_OBJECT_POS_X].get<std::vector<float>>();
if ((assemble_object.pos_x.size() > 0) && (assemble_object.pos_x.size() != assemble_object.count) && (assemble_object.pos_x.size() != 1))
{
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s pos_x count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.pos_x.size() % assemble_object.count;
return CLI_CONFIG_FILE_ERROR;
}
}
if (object_json.contains(JSON_ASSEMPLE_OBJECT_POS_Y)) {
assemble_object.pos_y = object_json[JSON_ASSEMPLE_OBJECT_POS_Y].get<std::vector<float>>();
if ((assemble_object.pos_y.size() > 0) && (assemble_object.pos_y.size() != assemble_object.count) && (assemble_object.pos_y.size() != 1))
{
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s pos_y count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.pos_y.size() % assemble_object.count;
return CLI_CONFIG_FILE_ERROR;
}
}
if (object_json.contains(JSON_ASSEMPLE_OBJECT_POS_Z)) {
assemble_object.pos_z = object_json[JSON_ASSEMPLE_OBJECT_POS_Z].get<std::vector<float>>();
if ((assemble_object.pos_z.size() > 0) && (assemble_object.pos_z.size() != assemble_object.count) && (assemble_object.pos_z.size() != 1))
{
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": object %1%'s pos_z count %2% not equal to clone count %3%, also not equal to 1") % assemble_object.path % assemble_object.pos_z.size() % assemble_object.count;
return CLI_CONFIG_FILE_ERROR;
}
}
if (object_json.contains(JSON_ASSEMPLE_OBJECT_PRINT_PARAMS)) {
assemble_object.print_params = object_json[JSON_ASSEMPLE_OBJECT_PRINT_PARAMS].get<std::map<std::string, std::string>>();
BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, object %2% has %3% print params") % (plate_index + 1) %assemble_object.path % assemble_object.print_params.size();
}
}
}
}
catch(std::exception &err) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse file "<<config_file<<" got a generic exception, reason = " << err.what();
ret = CLI_CONFIG_FILE_ERROR;
}
return ret;
}
void merge_or_add_object(assemble_plate_info_t& assemble_plate_info, Model &model, int assemble_index, std::map<int, ModelObject*> &merged_objects, ModelObject *ori_object)
{
if (assemble_index > 0) {
auto iter = merged_objects.find(assemble_index);
ModelObject* new_object = nullptr;
if (iter == merged_objects.end()) {
//create the object to merge
new_object = model.add_object();
new_object->name = "assemble_" + std::to_string(assemble_index);
merged_objects[assemble_index] = new_object;
assemble_plate_info.loaded_obj_list.emplace_back(new_object);
}
else
new_object = iter->second;
for (auto volume : ori_object->volumes) {
ModelVolume* new_volume = new_object->add_volume(*volume);
// set extruder id
new_volume->config.set_key_value("extruder", new ConfigOptionInt(ori_object->config.extruder()));
}
BOOST_LOG_TRIVIAL(debug) << boost::format("assemble_index %1%, name %2%, merged to new model %3%") % assemble_index % ori_object->name % new_object->name;
}
else {
ModelObject* new_object = model.add_object(*ori_object);
assemble_plate_info.loaded_obj_list.emplace_back(new_object);
BOOST_LOG_TRIVIAL(debug) << boost::format("assemble_index %1%, name %2%, no need to merge, copy to new model") % assemble_index % ori_object->name;
}
}
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
#else
#define DIR_SEPARATOR '/'
#endif
static int construct_assemble_list(std::vector<assemble_plate_info_t> &assemble_plate_info_list, Model &model, PlateDataPtrs &plate_list)
{
int ret = 0;
int plate_count = assemble_plate_info_list.size();
ConfigSubstitutionContext config_substitutions(ForwardCompatibilitySubstitutionRule::Enable);
Model temp_model;
plate_list.resize(plate_count);
for (int index = 0; index < plate_count; index++)
{
//each plate has its dependent assemble list
std::map<int, ModelObject*> merged_objects;
std::set<int> used_filaments;
//std::map<ModelObject*, int> to_merge_objects;
assemble_plate_info_t& assemble_plate_info = assemble_plate_info_list[index];
int object_count = assemble_plate_info.assemble_obj_list.size();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Plate %1%, name %2%, obj count %3%, plate params count %4%") % (index + 1) %assemble_plate_info.plate_name %object_count %assemble_plate_info.plate_params.size();
PlateData* plate_data = new PlateData();
plate_list[index] = plate_data;
plate_data->plate_name = assemble_plate_info.plate_name;
plate_data->plate_index = index;
if (!assemble_plate_info.plate_params.empty())
{
for (auto plate_iter = assemble_plate_info.plate_params.begin(); plate_iter != assemble_plate_info.plate_params.end(); plate_iter++)
{
plate_data->config.set_deserialize(plate_iter->first, plate_iter->second, config_substitutions);
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": Plate %1%, key %2%, value %3%") % (index + 1) % plate_iter->first % plate_iter->second;
}
}
//construct the object list
for (size_t obj_index = 0; obj_index < object_count; obj_index++)
{
assemble_object_info_t& assemble_object = assemble_plate_info.assemble_obj_list[obj_index];
std::string object_name;
TriangleMesh mesh;
boost::filesystem::path object_path(assemble_object.path);
if (!fs::exists(object_path)) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": directory %1% not exist in plate %2%") % assemble_object.path % (index + 1);
return CLI_FILE_NOTFOUND;
}
const char* path_str = assemble_object.path.c_str();
const char* last_slash = strrchr(path_str, DIR_SEPARATOR);
object_name.assign((last_slash == nullptr) ? path_str : last_slash + 1);
if (boost::algorithm::iends_with(assemble_object.path, ".stl"))
{
if (!mesh.ReadSTLFile(path_str, true, nullptr)) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": failed to read stl file from %1%, plate index %2%, object index %3%") % assemble_object.path % (index+1) % (obj_index+1);
return CLI_DATA_FILE_ERROR;
}
if (mesh.empty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found no mesh data from stl file %1%, plate index %2%, object index %3%") % assemble_object.path % (index + 1) % (obj_index + 1);
return CLI_DATA_FILE_ERROR;
}
object_name.erase(object_name.end() - 3, object_name.end());
}
else if (boost::algorithm::iends_with(assemble_object.path, ".obj"))
{
std::string message;
bool result = load_obj(path_str, &mesh, message);
if (!result) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": failed to read a valid mesh from obj file %1%, plate index %2%, object index %3%, error %4%") % assemble_object.path % (index + 1) % (obj_index + 1) % message;
return CLI_DATA_FILE_ERROR;
}
object_name.erase(object_name.end() - 3, object_name.end());
}
else {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": unsupported file %1%, plate index %2%, object index %3%") % assemble_object.path % (index + 1) % (obj_index + 1);
return CLI_INVALID_PARAMS;
}
std::string object_1_name = object_name + "_1";
ModelObject* object = temp_model.add_object(object_1_name.c_str(), path_str, std::move(mesh));
if (!object) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": add_object %1% failed, plate index %2%, object index %3%") % object_1_name % (index + 1) % (obj_index + 1);
return CLI_DATA_FILE_ERROR;
}
object->config.set_key_value("extruder", new ConfigOptionInt(assemble_object.filaments[0]));
if (!assemble_object.print_params.empty())
{
for (auto param_iter = assemble_object.print_params.begin(); param_iter != assemble_object.print_params.end(); param_iter++)
{
object->config.set_deserialize(param_iter->first, param_iter->second, config_substitutions);
BOOST_LOG_TRIVIAL(debug) << boost::format("Plate %1%, object %2% key %3%, value %4%") % (index + 1) % object_1_name % param_iter->first % param_iter->second;
}
}
if (assemble_object.pos_x.empty())
assemble_object.pos_x.resize(1, 0.f);
if (assemble_object.pos_y.empty())
assemble_object.pos_y.resize(1, 0.f);
if (assemble_object.pos_z.empty())
assemble_object.pos_z.resize(1, 0.f);
if (assemble_object.assemble_index.empty())
assemble_object.assemble_index.resize(1, 0);
object->translate(assemble_object.pos_x[0], assemble_object.pos_y[0], assemble_object.pos_z[0]);
merge_or_add_object(assemble_plate_info, model, assemble_object.assemble_index[0], merged_objects, object);
used_filaments.emplace(assemble_object.filaments[0]);
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": object %1%, name %2%, pos_x %3% pos_y %4%, pos_z %5%, filament %6%, assemble_index %7%")
%obj_index %object->name %assemble_object.pos_x[0] %assemble_object.pos_y[0] %assemble_object.pos_z[0] %assemble_object.filaments[0] %assemble_object.assemble_index[0];
for (size_t copy_index = 1; copy_index < assemble_object.count; copy_index++)
{
int array_index = copy_index;
ModelObject* copy_obj = temp_model.add_object(*object);
copy_obj->name = object_name + "_" + std::to_string(copy_index + 1);
if (copy_index >= assemble_object.pos_x.size())
array_index = 0;
copy_obj->translate(assemble_object.pos_x[array_index], assemble_object.pos_y[array_index], assemble_object.pos_z[array_index]);
if (copy_index < assemble_object.filaments.size())
array_index = copy_index;
else
array_index = 0;
copy_obj->config.set_key_value("extruder", new ConfigOptionInt(assemble_object.filaments[array_index]));
used_filaments.emplace(assemble_object.filaments[array_index]);
if (copy_index < assemble_object.assemble_index.size())
array_index = copy_index;
else
array_index = 0;
merge_or_add_object(assemble_plate_info, model, assemble_object.assemble_index[array_index], merged_objects, copy_obj);
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": cloned object %1%, name %2%, pos_x %3% pos_y %4%, pos_z %5%")
%copy_index %object->name %assemble_object.pos_x[array_index] %assemble_object.pos_y[array_index] %assemble_object.pos_z[array_index];
}
}
assemble_plate_info.filaments_count = used_filaments.size();
assemble_plate_info.assemble_obj_list.clear();
assemble_plate_info.assemble_obj_list.shrink_to_fit();
assemble_plate_info.plate_params.clear();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Plate %1%, used filaments %2%") % (index + 1) % assemble_plate_info.filaments_count;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": has objects need to be merged, total plates %1%, total objects %2%") % plate_count % model.objects.size();
temp_model.clear_objects();
temp_model.clear_materials();
return ret;
}
int CLI::run(int argc, char **argv) int CLI::run(int argc, char **argv)
{ {
// Mark the main thread for the debugger and for runtime checks. // Mark the main thread for the debugger and for runtime checks.
@ -614,18 +912,22 @@ int CLI::run(int argc, char **argv)
/*BOOST_LOG_TRIVIAL(info) << "begin to setup params, argc=" << argc << std::endl; /*BOOST_LOG_TRIVIAL(info) << "begin to setup params, argc=" << argc << std::endl;
for (int index=0; index < argc; index++) for (int index=0; index < argc; index++)
BOOST_LOG_TRIVIAL(info) << "index="<< index <<", arg is "<< argv[index] <<std::endl; BOOST_LOG_TRIVIAL(info) << "index="<< index <<", arg is "<< argv[index] <<std::endl;
int debug_argc = 7; int debug_argc = 14;
char *debug_argv[] = { char *debug_argv[] = {
"F:\work\projects\bambu_debug\bamboo_slicer\build_debug\src\Debug\bambu-studio.exe", "F:\work\projects\bambu_debug\bamboo_slicer\build_debug\src\Debug\bambu-studio.exe",
//"--uptodate", "--debug=2",
//"--load-settings", "--load-settings",
//"machine.json;process.json", "machine.json;process.json",
"--repetitions", "--load-filaments",
"3", "filament.json;filament.json;filament.json;filament.json;filament.json;filament.json",
"--export-3mf=output.3mf", "--export-3mf=output.3mf",
"--slice", "--filament-colour",
"1", "#FFFFFFFF;#0000FFFF;#00FF00FF;#FF0000FF;#00000000;#FFFF00FF",
"test_repetitions.3mf" "--allow-multicolor-oneplate=1",
"--allow-rotations=0",
"--avoid-extrusion-cali-region=1",
"--load-assemble-list",
"assemble_list.json"
}; };
if (! this->setup(debug_argc, debug_argv))*/ if (! this->setup(debug_argc, debug_argv))*/
if (!this->setup(argc, argv)) if (!this->setup(argc, argv))
@ -823,6 +1125,12 @@ int CLI::run(int argc, char **argv)
if (custom_gcode_option) if (custom_gcode_option)
custom_gcode_file = custom_gcode_option->value; custom_gcode_file = custom_gcode_option->value;
std::string load_assemble_list;
std::vector<assemble_plate_info_t> assemble_plate_info_list;
ConfigOptionString* load_assemble_list_option = m_config.option<ConfigOptionString>("load_assemble_list");
if (load_assemble_list_option)
load_assemble_list = load_assemble_list_option->value;
bool allow_multicolor_oneplate = m_config.option<ConfigOptionBool>("allow_multicolor_oneplate", true)->value; bool allow_multicolor_oneplate = m_config.option<ConfigOptionBool>("allow_multicolor_oneplate", true)->value;
const std::vector<int> loaded_filament_ids = m_config.option<ConfigOptionInts>("load_filament_ids", true)->values; const std::vector<int> loaded_filament_ids = m_config.option<ConfigOptionInts>("load_filament_ids", true)->values;
const std::vector<int> clone_objects = m_config.option<ConfigOptionInts>("clone_objects", true)->values; const std::vector<int> clone_objects = m_config.option<ConfigOptionInts>("clone_objects", true)->values;
@ -869,7 +1177,13 @@ int CLI::run(int argc, char **argv)
}*/ }*/
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; BOOST_LOG_TRIVIAL(info) << boost::format("plate_to_slice=%1%, normative_check=%2%, use_first_fila_as_default=%3%")%plate_to_slice %normative_check %use_first_fila_as_default;
unsigned int input_index = 0; unsigned int input_index = 0;
//if (!start_as_gcodeviewer) { if (!load_assemble_list.empty() && ((m_input_files.size() > 0) || (m_transforms.size() > 0)))
{
BOOST_LOG_TRIVIAL(error) << boost::format("load_assemble_list should not be used with input model files to load and should not be sued with transforms");
record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
flush_and_exit(CLI_INVALID_PARAMS);
}
if (load_assemble_list.empty()) {
for (const std::string& file : m_input_files) { for (const std::string& file : m_input_files) {
if (!boost::filesystem::exists(file)) { if (!boost::filesystem::exists(file)) {
boost::nowide::cerr << "No such file: " << file << std::endl; boost::nowide::cerr << "No such file: " << file << std::endl;
@ -1109,7 +1423,32 @@ int CLI::run(int argc, char **argv)
} }
m_models.push_back(std::move(model)); m_models.push_back(std::move(model));
} }
//} }
else {
//parse the json and assemble object here
Model model;
int ret = load_assemble_plate_list(load_assemble_list, assemble_plate_info_list);
if (ret) {
record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
flush_and_exit(ret);
}
try {
ret = construct_assemble_list(assemble_plate_info_list, model, plate_data_src);
if (ret) {
record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info);
flush_and_exit(ret);
}
}
catch (std::exception& e) {
boost::nowide::cerr << construct_assemble_list << ": " << e.what() << std::endl;
record_exit_reson(outfile_dir, CLI_DATA_FILE_ERROR, 0, cli_errors[CLI_DATA_FILE_ERROR], sliced_info);
flush_and_exit(CLI_DATA_FILE_ERROR);
}
model.add_default_instances();
m_models.push_back(std::move(model));
}
//load custom gcode file //load custom gcode file
std::map<int, CustomGCode::Info> custom_gcodes_map; std::map<int, CustomGCode::Info> custom_gcodes_map;
@ -1406,7 +1745,7 @@ int CLI::run(int argc, char **argv)
if (filament_count == 0) if (filament_count == 0)
filament_count = load_filament_count; filament_count = load_filament_count;
if ((load_filament_count > 0) && (load_filaments_set.size() == 1)) if (is_bbl_3mf && (load_filament_count > 0) && (load_filaments_set.size() == 1))
{ {
disable_wipe_tower_after_mapping = true; disable_wipe_tower_after_mapping = true;
BOOST_LOG_TRIVIAL(warning) << boost::format("map all the filaments to the same one, load_filament_count %1%")%load_filament_count; BOOST_LOG_TRIVIAL(warning) << boost::format("map all the filaments to the same one, load_filament_count %1%")%load_filament_count;
@ -2167,9 +2506,16 @@ int CLI::run(int argc, char **argv)
} }
//compute the flush volume //compute the flush volume
ConfigOptionStrings* selected_filament_colors_option = m_extra_config.option<ConfigOptionStrings>("filament_colour"); ConfigOptionStrings *selected_filament_colors_option = m_extra_config.option<ConfigOptionStrings>("filament_colour");
ConfigOptionStrings *project_filament_colors_option = m_print_config.option<ConfigOptionStrings>("filament_colour"); ConfigOptionStrings *project_filament_colors_option = m_print_config.option<ConfigOptionStrings>("filament_colour");
if (project_filament_colors_option) if ((!project_filament_colors_option || (project_filament_colors_option->values.size() == 0)) && selected_filament_colors_option)
{
BOOST_LOG_TRIVIAL(info) << boost::format("initial project_filament_colors is null, create it due to filament_colour set in cli");
project_filament_colors_option = m_print_config.option<ConfigOptionStrings>("filament_colour", true);
std::vector<std::string>& project_filament_colors = project_filament_colors_option->values;
project_filament_colors.resize(filament_count, "#FFFFFF");
}
if (project_filament_colors_option && selected_filament_colors_option)
{ {
std::vector<std::string> selected_filament_colors; std::vector<std::string> selected_filament_colors;
if (selected_filament_colors_option) { if (selected_filament_colors_option) {
@ -2923,7 +3269,204 @@ int CLI::run(int argc, char **argv)
} }
} }
if (need_arrange) auto get_print_sequence = [](Slic3r::GUI::PartPlate* plate, DynamicPrintConfig& print_config, bool &is_seq_print) {
PrintSequence curr_plate_seq = plate->get_print_seq();
if (curr_plate_seq == PrintSequence::ByDefault) {
auto seq_print = print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence");
if (seq_print && (seq_print->value == PrintSequence::ByObject)) {
BOOST_LOG_TRIVIAL(info) << boost::format("plate print by object, set from global");
is_seq_print = true;
}
}
else if (curr_plate_seq == PrintSequence::ByObject) {
BOOST_LOG_TRIVIAL(info) << boost::format("plate print by object, set from plate self");
is_seq_print = true;
}
};
if (!assemble_plate_info_list.empty())
{
//need to arrange for assemble cases
int plate_count = assemble_plate_info_list.size();
if (plate_count != partplate_list.get_plate_count())
{
BOOST_LOG_TRIVIAL(error) << boost::format("mismatch plate count, to_assemble %1%, generated %2%") % plate_count % partplate_list.get_plate_count();
record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info);
flush_and_exit(CLI_INVALID_PARAMS);
}
for (size_t i = 0; i < plate_count; i++)
{
Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i);
//lock those plates no need to arrange
if (!assemble_plate_info_list[i].need_arrange)
cur_plate->lock(true);
}
for (size_t i = 0; i < plate_count; i++)
{
assemble_plate_info_t& assemble_plate = assemble_plate_info_list[i];
Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i);
BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, need arrange %2%, filaments_count %3%") % (i+1) % assemble_plate.need_arrange % assemble_plate.filaments_count;
if (assemble_plate.need_arrange)
{
//do arrange for plate
ArrangePolygons selected, unselected;
Model& model = m_models[0];
arrange_cfg = ArrangeParams(); // reset all params
get_print_sequence(cur_plate, m_print_config, arrange_cfg.is_seq_print);
//Step-1: prepare the arranged data
partplate_list.lock_plate(i, false);
partplate_list.select_plate(i);
BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% set to selected") % i;
size_t plate_obj_count = assemble_plate.loaded_obj_list.size();
for (size_t oidx = 0; oidx < plate_obj_count; ++oidx)
{
ModelObject* mo = assemble_plate.loaded_obj_list[oidx];
for (size_t inst_idx = 0; inst_idx < mo->instances.size(); ++inst_idx)
{
ModelInstance* minst = mo->instances[inst_idx];
ArrangePolygon ap = get_instance_arrange_poly(minst, m_print_config);
ap.itemid = selected.size();
selected.emplace_back(std::move(ap));
BOOST_LOG_TRIVIAL(debug) << boost::format("plate %1%: add object %2% object index %3%, into selected") % (i+1) % ap.name %(oidx+1);
}
}
if (!arrange_cfg.is_seq_print && assemble_plate.filaments_count > 1)
{
//prepare the wipe tower
int plate_count = partplate_list.get_plate_count();
auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure");
// set the default position, the same with print config(left top)
float x = WIPE_TOWER_DEFAULT_X_POS;
float y = WIPE_TOWER_DEFAULT_Y_POS;
if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) {
x = I3_WIPE_TOWER_DEFAULT_X_POS;
y = I3_WIPE_TOWER_DEFAULT_Y_POS;
}
ConfigOptionFloat wt_x_opt(x);
ConfigOptionFloat wt_y_opt(y);
//create the options using default if neccessary
ConfigOptionFloats* wipe_x_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_x", true);
ConfigOptionFloats* wipe_y_option = m_print_config.option<ConfigOptionFloats>("wipe_tower_y", true);
ConfigOptionFloat* width_option = m_print_config.option<ConfigOptionFloat>("prime_tower_width", true);
ConfigOptionFloat* rotation_angle_option = m_print_config.option<ConfigOptionFloat>("wipe_tower_rotation_angle", true);
ConfigOptionFloat* volume_option = m_print_config.option<ConfigOptionFloat>("prime_volume", true);
BOOST_LOG_TRIVIAL(info) << boost::format("prime_tower_width %1% wipe_tower_rotation_angle %2% prime_volume %3%") % width_option->value % rotation_angle_option->value % volume_option->value;
wipe_x_option->set_at(&wt_x_opt, i, 0);
wipe_y_option->set_at(&wt_y_opt, i, 0);
ArrangePolygon wipe_tower_ap = cur_plate->estimate_wipe_tower_polygon(m_print_config, i, assemble_plate.filaments_count, true);
wipe_tower_ap.bed_idx = i;
unselected.emplace_back(wipe_tower_ap);
}
// add the virtual object into unselect list if has
partplate_list.preprocess_exclude_areas(unselected, i + 1);
if (avoid_extrusion_cali_region)
partplate_list.preprocess_nonprefered_areas(unselected, i + 1);
//Step-2:prepare the arrange params
arrange_cfg.allow_rotations = allow_rotations;
arrange_cfg.allow_multi_materials_on_same_plate = allow_multicolor_oneplate;
arrange_cfg.avoid_extrusion_cali_region = avoid_extrusion_cali_region;
arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.cleareance_radius = cleareance_radius;
arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) {
arrange_cfg.bed_shrink_x = BED_SHRINK_SEQ_PRINT;
arrange_cfg.bed_shrink_y = BED_SHRINK_SEQ_PRINT;
}
if (auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure")) {
arrange_cfg.align_to_y_axis = (printer_structure_opt->value == PrinterStructure::psI3);
}
arrangement::update_arrange_params(arrange_cfg, &m_print_config, selected);
arrangement::update_selected_items_inflation(selected, &m_print_config, arrange_cfg);
arrangement::update_unselected_items_inflation(unselected, &m_print_config, arrange_cfg);
arrangement::update_selected_items_axis_align(selected, &m_print_config, arrange_cfg);
beds = get_shrink_bedpts(&m_print_config, arrange_cfg);
partplate_list.preprocess_exclude_areas(arrange_cfg.excluded_regions, 1, scale_(1));
{
BOOST_LOG_TRIVIAL(debug) << "arrange bedpts:" << beds[0].transpose() << ", " << beds[1].transpose() << ", " << beds[2].transpose() << ", " << beds[3].transpose();
BOOST_LOG_TRIVIAL(info) << "Arrange full params: " << arrange_cfg.to_json();
BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items selected before arranging: %1%") % selected.size();
for (auto item : selected)
BOOST_LOG_TRIVIAL(trace) << item.name << ", extruder: " << item.extrude_ids.back() << ", bed: " << item.bed_idx
<< ", trans: " << item.translation.transpose();
BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items unselected before arranging: %1%") % unselected.size();
for (auto item : unselected)
BOOST_LOG_TRIVIAL(trace) << item.name << ", bed: " << item.bed_idx << ", trans: " << item.translation.transpose();
}
arrange_cfg.progressind = [](unsigned st, std::string str = "") {
//boost::nowide::cout << "st=" << st << ", " << str << std::endl;
};
//Step-3:do the arrange
BOOST_LOG_TRIVIAL(info) << boost::format("start plate %1%'s arranging...") % (i + 1);
arrangement::arrange(selected, unselected, beds, arrange_cfg);
//arrangement::arrange(unprintable, {}, beds, arrange_cfg);
BOOST_LOG_TRIVIAL(info) << boost::format("finished plate %1%'s arranging") % (i + 1);
//step-4: postprocess the bed index and result
partplate_list.clear(false, false, true, i);
for (ArrangePolygon& ap : selected) {
partplate_list.postprocess_bed_index_for_current_plate(ap);
if (ap.bed_idx != i)
{
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":arrange failed: ap.name %1% ap.bed_idx %2%, plate index %3%") % ap.name % ap.bed_idx % i;
record_exit_reson(outfile_dir, CLI_OBJECT_ARRANGE_FAILED, 0, cli_errors[CLI_OBJECT_ARRANGE_FAILED], sliced_info);
flush_and_exit(CLI_OBJECT_ARRANGE_FAILED);
}
}
// Apply the arrange result to all selected objects
for (ArrangePolygon& ap : selected) {
//BBS: partplate postprocess
partplate_list.postprocess_arrange_polygon(ap, true);
ap.apply();
}
partplate_list.rebuild_plates_after_arrangement(false, true, i);
}
else {
size_t plate_obj_count = assemble_plate.loaded_obj_list.size();
Vec3d plate_origin = cur_plate->get_origin();
for (size_t oidx = 0; oidx < plate_obj_count; ++oidx)
{
ModelObject* mo = assemble_plate.loaded_obj_list[oidx];
mo->translate_instances(plate_origin);
BOOST_LOG_TRIVIAL(debug) << boost::format("plate %1%: no arrange, directly translate object %2% by {%3%, %4%}") % (i+1) % mo->name %plate_origin(0) %plate_origin(1);
}
}
}
for (size_t i = 0; i < plate_count; i++)
{
//unlock all the plates
Slic3r::GUI::PartPlate* cur_plate = (Slic3r::GUI::PartPlate*)partplate_list.get_plate(i);
cur_plate->lock(false);
}
}
else if (need_arrange)
{ {
ArrangePolygons selected, unselected, unprintable, locked_aps; ArrangePolygons selected, unselected, unprintable, locked_aps;
@ -2974,18 +3517,7 @@ int CLI::run(int argc, char **argv)
} }
if (cur_plate) { if (cur_plate) {
PrintSequence curr_plate_seq = cur_plate->get_print_seq(); get_print_sequence(cur_plate, m_print_config, arrange_cfg.is_seq_print);
if (curr_plate_seq == PrintSequence::ByDefault) {
auto seq_print = m_print_config.option<ConfigOptionEnum<PrintSequence>>("print_sequence");
if (seq_print && (seq_print->value == PrintSequence::ByObject)) {
BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from global")%plate_to_slice;
arrange_cfg.is_seq_print = true;
}
}
else if (curr_plate_seq == PrintSequence::ByObject) {
BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from plate self")%plate_to_slice;
arrange_cfg.is_seq_print = true;
}
} }
//Step-1: prepare arrange polygons //Step-1: prepare arrange polygons
@ -4599,7 +5131,7 @@ int CLI::run(int argc, char **argv)
record_exit_reson(outfile_dir, CLI_EXPORT_3MF_ERROR, 0, cli_errors[CLI_EXPORT_3MF_ERROR], sliced_info); record_exit_reson(outfile_dir, CLI_EXPORT_3MF_ERROR, 0, cli_errors[CLI_EXPORT_3MF_ERROR], sliced_info);
flush_and_exit(CLI_EXPORT_3MF_ERROR); flush_and_exit(CLI_EXPORT_3MF_ERROR);
} }
release_PlateData_list(plate_data_list);
for (unsigned int i = 0; i < thumbnails.size(); i++) for (unsigned int i = 0; i < thumbnails.size(); i++)
thumbnails[i]->reset(); thumbnails[i]->reset();
for (unsigned int i = 0; i < top_thumbnails.size(); i++) for (unsigned int i = 0; i < top_thumbnails.size(); i++)
@ -4607,6 +5139,8 @@ int CLI::run(int argc, char **argv)
for (unsigned int i = 0; i < pick_thumbnails.size(); i++) for (unsigned int i = 0; i < pick_thumbnails.size(); i++)
pick_thumbnails[i]->reset(); pick_thumbnails[i]->reset();
release_PlateData_list(plate_data_list);
for (unsigned int i = 0; i < calibration_thumbnails.size(); i++) for (unsigned int i = 0; i < calibration_thumbnails.size(); i++)
delete calibration_thumbnails[i]; delete calibration_thumbnails[i];

View File

@ -17,6 +17,44 @@ namespace IO {
}; };
} }
#define JSON_ASSEMPLE_PLATES "plates"
#define JSON_ASSEMPLE_PLATE_PARAMS "plate_params"
#define JSON_ASSEMPLE_PLATE_NAME "plate_name"
#define JSON_ASSEMPLE_PLATE_NEED_ARRANGE "need_arrange"
#define JSON_ASSEMPLE_OBJECTS "objects"
#define JSON_ASSEMPLE_OBJECT_PATH "path"
#define JSON_ASSEMPLE_OBJECT_COUNT "count"
#define JSON_ASSEMPLE_OBJECT_FILAMENTS "filaments"
#define JSON_ASSEMPLE_OBJECT_POS_X "pos_x"
#define JSON_ASSEMPLE_OBJECT_POS_Y "pos_y"
#define JSON_ASSEMPLE_OBJECT_POS_Z "pos_z"
#define JSON_ASSEMPLE_OBJECT_ASSEMBLE_INDEX "assemble_index"
#define JSON_ASSEMPLE_OBJECT_PRINT_PARAMS "print_params"
typedef struct _assemble_object_info {
std::string path;
int count;
std::vector<int> filaments;
std::vector<int> assemble_index;
std::vector<float> pos_x;
std::vector<float> pos_y;
std::vector<float> pos_z;
std::map<std::string, std::string> print_params;
}assemble_object_info_t;
typedef struct _assemble_plate_info {
std::string plate_name;
bool need_arrange {false};
int filaments_count {0};
std::map<std::string, std::string> plate_params;
std::vector<assemble_object_info_t> assemble_obj_list;
std::vector<ModelObject *> loaded_obj_list;
}assemble_plate_info_t;
class CLI { class CLI {
public: public:
int run(int argc, char **argv); int run(int argc, char **argv);

View File

@ -5433,6 +5433,12 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->cli_params = "\"filament1.json;filament2.json;...\""; def->cli_params = "\"filament1.json;filament2.json;...\"";
def->set_default_value(new ConfigOptionStrings()); def->set_default_value(new ConfigOptionStrings());
def = this->add("load_assemble_list", coString);
def->label = "Load assemble list";
def->tooltip = "Load assemble object list from config file";
def->cli_params = "assemble_list.json";
def->set_default_value(new ConfigOptionString());
/*def = this->add("output", coString); /*def = this->add("output", coString);
def->label = L("Output File"); def->label = L("Output File");
def->tooltip = L("The file where the output will be written (if not specified, it will be based on the input file)."); def->tooltip = L("The file where the output will be written (if not specified, it will be based on the input file).");