diff --git a/src/BambuStudio.cpp b/src/BambuStudio.cpp index 4c0b653ef8..23e6fb6f60 100644 --- a/src/BambuStudio.cpp +++ b/src/BambuStudio.cpp @@ -97,6 +97,7 @@ using namespace Slic3r; std::string message; }error_message;*/ +#define MAX_CLONEABLE_SIZE 512 std::map cli_errors = { {CLI_SUCCESS, "Success."}, @@ -743,6 +744,43 @@ int CLI::run(int argc, char **argv) if (custom_gcode_option) custom_gcode_file = custom_gcode_option->value; + bool allow_multicolor_oneplate = m_config.option("allow_multicolor_oneplate", true)->value; + const std::vector loaded_filament_ids = m_config.option("load_filament_ids", true)->values; + const std::vector clone_objects = m_config.option("clone_objects", true)->values; + //when load objects from stl/obj, the total used filaments set + std::set used_filament_set; + BOOST_LOG_TRIVIAL(info) << boost::format("allow_multicolor_oneplate %1%, loaded_filament_ids size %2%, clone_objects size %3%")%allow_multicolor_oneplate %loaded_filament_ids.size() %clone_objects.size(); + if (clone_objects.size() > 0) + { + if (clone_objects.size() != m_input_files.size()) + { + BOOST_LOG_TRIVIAL(error) << boost::format("clone_objects size %1% should be the same with input files size %2%")%clone_objects.size() %m_input_files.size(); + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + else if (load_filaments.size() == 0) + { + BOOST_LOG_TRIVIAL(error) << boost::format("clone_objects should be used with load_filaments together"); + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + } + if (loaded_filament_ids.size() > 0) + { + if (loaded_filament_ids.size() != m_input_files.size()) + { + BOOST_LOG_TRIVIAL(error) << boost::format("loaded_filament_ids size %1% should be the same with input files size %2%")%loaded_filament_ids.size() %m_input_files.size(); + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + else if (load_filaments.size() == 0) + { + BOOST_LOG_TRIVIAL(error) << boost::format("loaded_filament_ids should be used with load_filaments together"); + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + } + /*for (const std::string& file : m_input_files) if (is_gcode_file(file) && boost::filesystem::exists(file)) { start_as_gcodeviewer = true; @@ -750,6 +788,7 @@ int CLI::run(int argc, char **argv) break; }*/ BOOST_LOG_TRIVIAL(info) << boost::format("plate_to_slice=%1%, normative_check=%2%, use_first_fila_as_default=%3%")%plate_to_slice %normative_check %use_first_fila_as_default; + unsigned int input_index = 0; //if (!start_as_gcodeviewer) { for (const std::string& file : m_input_files) { if (!boost::filesystem::exists(file)) { @@ -770,11 +809,18 @@ int CLI::run(int argc, char **argv) is_bbl_3mf = false; LoadStrategy strategy; if (boost::algorithm::iends_with(file, ".3mf") && first_file) { + if ((clone_objects.size() > 0) || (loaded_filament_ids.size() > 0)) + { + BOOST_LOG_TRIVIAL(error) << boost::format("can not load 3mf when set loaded_filament_ids or clone_objects"); + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances | LoadStrategy::LoadAuxiliary; //load_aux = true; } - else + else { strategy = LoadStrategy::LoadModel | LoadStrategy::AddDefaultInstances; + } // BBS: adjust whebackup //LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances; //if (load_aux) strategy = strategy | LoadStrategy::LoadAuxiliary; @@ -789,11 +835,11 @@ int CLI::run(int argc, char **argv) } BOOST_LOG_TRIVIAL(info) << boost::format("the first file is a 3mf, version %1%, got plate count %2%") %file_version.to_string() %plate_data_src.size(); need_arrange = false; - for (ModelObject* o : model.objects) + /*for (ModelObject* o : model.objects) { orients_requirement.insert(std::pair(o->id().id, false)); BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << ", from bbl 3mf\n"; - } + }*/ Semver old_version(1, 5, 9), old_version2(1, 5, 9); if ((file_version < old_version) && !config.empty()) { @@ -896,9 +942,56 @@ int CLI::run(int argc, char **argv) else { need_arrange = true; + int object_extruder_id = 0, clone_count = 1; + if (loaded_filament_ids.size() > input_index) { + if (loaded_filament_ids[input_index] > 0) { + if (loaded_filament_ids[input_index] > load_filaments.size()) { + BOOST_LOG_TRIVIAL(error) << boost::format("invalid filament id %1% at index %2%, max %3%")%loaded_filament_ids[input_index] % (input_index + 1) %load_filaments.size(); + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + object_extruder_id = loaded_filament_ids[input_index]; + used_filament_set.emplace(object_extruder_id); + } + } + + if (clone_objects.size() > input_index) { + if (clone_objects[input_index] > 0) { + if (clone_objects[input_index] > MAX_CLONEABLE_SIZE) { + BOOST_LOG_TRIVIAL(error) << boost::format("invalid clone count %1% at index %2%, max %3%")%clone_objects[input_index] % (input_index + 1) %MAX_CLONEABLE_SIZE; + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + clone_count = clone_objects[input_index]; + } + } + + //clone objects process + if (clone_count > 1) + { + unsigned int object_count = model.objects.size(); + + for (unsigned int obj_index = 0; obj_index < object_count; obj_index++) + { + ModelObject* object = model.objects[obj_index]; + + for (unsigned int clone_index = 1; clone_index < clone_count; clone_index++) + { + ModelObject* newObj = model.add_object(*object); + newObj->name = object->name +"_"+ std::to_string(clone_index+1); + } + object->name = object->name +"_"+ std::to_string(1); + } + } + for (ModelObject* o : model.objects) { - orients_requirement.insert(std::pair(o->id().id, true)); + if (object_extruder_id != 0) { + o->config.set_key_value("extruder", new ConfigOptionInt(object_extruder_id)); + } + + //default not orient for all, if need to orient use the action + //orients_requirement.insert(std::pair(o->id().id, false)); BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << ", from stl or other 3mf\n"; o->ensure_on_bed(); } @@ -923,6 +1016,7 @@ int CLI::run(int argc, char **argv) // config is applied to m_print_config before the current m_config values. config += std::move(m_print_config); m_print_config = std::move(config); + input_index++; } catch (std::exception& e) { boost::nowide::cerr << file << ": " << e.what() << std::endl; @@ -1994,8 +2088,8 @@ int CLI::run(int argc, char **argv) { ModelObject* new_object = m.add_object(*o); //BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << "\n"; - orients_requirement.emplace(new_object->id().id, orients_requirement[o->id().id]); - orients_requirement.erase(o->id().id); + //orients_requirement.emplace(new_object->id().id, orients_requirement[o->id().id]); + //orients_requirement.erase(o->id().id); } m.add_default_instances(); m_models.clear(); @@ -2199,22 +2293,27 @@ int CLI::run(int argc, char **argv) for (auto const &opt_key : m_transforms) { BOOST_LOG_TRIVIAL(info) << "process transform " << opt_key << "\n"; - if (opt_key == "merge") { - //BBS: always merge, do nothing here - /*Model m; - for (auto& model : m_models) - for (ModelObject* o : model.objects) - m.add_object(*o); - // Rearrange instances unless --dont-arrange is supplied - if (!m_config.opt_bool("dont_arrange")) { - m.add_default_instances(); - if (this->has_print_action()) - arrange_objects(m, bed, arrange_cfg); - else - arrange_objects(m, InfiniteBed{}, arrange_cfg); + if (opt_key == "assemble") { + if (clone_objects.size() > 0) { + BOOST_LOG_TRIVIAL(error) << "Invalid params: can not set assemble and clone_objects together." << std::endl; + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); } + Model m; + ModelObject* new_object = m.add_object(); + new_object->name = _u8L("Assembly"); + new_object->add_instance(); + int idx = 0; + for (auto& model : m_models) + for (ModelObject* o : model.objects) { + for (auto volume : o->volumes) { + ModelVolume* new_volume = new_object->add_volume(*volume); + // set extruder id + new_volume->config.set_key_value("extruder", new ConfigOptionInt(o->config.extruder())); + } + } m_models.clear(); - m_models.emplace_back(std::move(m));*/ + m_models.emplace_back(std::move(m)); } else if (opt_key == "repetitions") { int repetitions_count = m_config.option("repetitions")->value; @@ -2257,7 +2356,7 @@ int CLI::run(int argc, char **argv) if (orient_option == 0) { - orients_requirement.clear(); + //orients_requirement.clear(); for (auto& model : m_models) for (ModelObject* o : model.objects) { @@ -2268,7 +2367,7 @@ int CLI::run(int argc, char **argv) else if (orient_option == 1) { //force orient - orients_requirement.clear(); + //orients_requirement.clear(); for (auto& model : m_models) for (ModelObject* o : model.objects) { @@ -2675,6 +2774,48 @@ int CLI::run(int argc, char **argv) //add the virtual object into unselect list if has partplate_list.preprocess_exclude_areas(unselected); + + if (used_filament_set.size() > 0) + { + //prepare the wipe tower + int plate_count = partplate_list.get_plate_count(); + int extruder_size = used_filament_set.size(); + + auto printer_structure_opt = m_print_config.option>("printer_structure"); + // set the default position, the same with print config(left top) + float x = WIPE_TOWER_DEFAULT_X_POS; + float y = WIPE_TOWER_DEFAULT_Y_POS; + if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) { + x = I3_WIPE_TOWER_DEFAULT_X_POS; + y = I3_WIPE_TOWER_DEFAULT_Y_POS; + } + ConfigOptionFloat wt_x_opt(x); + ConfigOptionFloat wt_y_opt(y); + + //create the options using default if neccessary + ConfigOptionFloats* wipe_x_option = m_print_config.option("wipe_tower_x", true); + ConfigOptionFloats* wipe_y_option = m_print_config.option("wipe_tower_y", true); + ConfigOptionFloat* width_option = m_print_config.option("prime_tower_width", true); + ConfigOptionFloat* rotation_angle_option = m_print_config.option("wipe_tower_rotation_angle", true); + ConfigOptionFloat* volume_option = m_print_config.option("prime_volume", true); + + BOOST_LOG_TRIVIAL(info) << boost::format("prime_tower_width %1% wipe_tower_rotation_angle %2% prime_volume %3%")%width_option->value %rotation_angle_option->value %volume_option->value ; + + + for (int bedid = 0; bedid < MAX_PLATE_COUNT; bedid++) { + int plate_index_valid = std::min(bedid, plate_count - 1); + if (bedid < plate_count) { + wipe_x_option->set_at(&wt_x_opt, plate_index_valid, 0); + wipe_y_option->set_at(&wt_y_opt, plate_index_valid, 0); + } + + + ArrangePolygon wipe_tower_ap = partplate_list.get_plate(plate_index_valid)->estimate_wipe_tower_polygon(m_print_config, plate_index_valid, extruder_size, true); + + wipe_tower_ap.bed_idx = bedid; + unselected.emplace_back(wipe_tower_ap); + } + } } else { //only arrange current plate @@ -2798,7 +2939,7 @@ int CLI::run(int argc, char **argv) //Step-2:prepare the arrange params arrange_cfg.allow_rotations = true; - arrange_cfg.allow_multi_materials_on_same_plate = true; + arrange_cfg.allow_multi_materials_on_same_plate = allow_multicolor_oneplate; arrange_cfg.avoid_extrusion_cali_region = false; arrange_cfg.clearance_height_to_rod = height_to_rod; arrange_cfg.clearance_height_to_lid = height_to_lid; @@ -2824,7 +2965,7 @@ int CLI::run(int argc, char **argv) { BOOST_LOG_TRIVIAL(debug) << "arrange bedpts:" << beds[0].transpose() << ", " << beds[1].transpose() << ", " << beds[2].transpose() << ", " << beds[3].transpose(); - BOOST_LOG_TRIVIAL(warning)<< "Arrange full params: "<< arrange_cfg.to_json(); + 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 diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3def8df702..84e366b729 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -522,7 +522,7 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionEnum(atKeyPassword)); - + // temporary workaround for compatibility with older Slicer { def = this->add("preset_name", coString); @@ -5208,7 +5208,7 @@ CLIActionsConfigDef::CLIActionsConfigDef() def = this->add("no_check", coBool); def->label = L("No check"); def->tooltip = L("Do not run any validity checks, such as gcode path conflicts check."); - def->set_default_value(new ConfigOptionBool(false)); + def->set_default_value(new ConfigOptionBool(false)); def = this->add("normative_check", coBool); def->label = L("Normative check"); @@ -5308,12 +5308,13 @@ CLITransformConfigDef::CLITransformConfigDef() /*def = this->add("duplicate_grid", coPoint); def->label = L("Duplicate by grid"); - def->tooltip = L("Multiply copies by creating a grid."); + def->tooltip = L("Multiply copies by creating a grid.");*/ def = this->add("assemble", coBool); def->label = L("Assemble"); def->tooltip = L("Arrange the supplied models in a plate and merge them in a single model in order to perform actions once."); - def->cli = "merge|m";*/ + //def->cli = "merge|m"; + def->set_default_value(new ConfigOptionBool(false)); def = this->add("convert_unit", coBool); def->label = L("Convert Unit"); @@ -5406,6 +5407,12 @@ CLIMiscConfigDef::CLIMiscConfigDef() def->cli_params = "\"3,5,10,77\""; def->set_default_value(new ConfigOptionInts()); + def = this->add("clone_objects", coInts); + def->label = L("Clone Objects"); + def->tooltip = L("Clone objects in the load list"); + def->cli_params = "\"1,3,1,10\""; + def->set_default_value(new ConfigOptionInts()); + def = this->add("uptodate_settings", coStrings); def->label = L("load uptodate process/machine settings when using uptodate"); def->tooltip = L("load uptodate process/machine settings from the specified file when using uptodate"); @@ -5454,6 +5461,17 @@ CLIMiscConfigDef::CLIMiscConfigDef() def->tooltip = L("Load custom gcode from json"); def->cli_params = "custom_gcode_toolchange.json"; def->set_default_value(new ConfigOptionString()); + + def = this->add("load_filament_ids", coInts); + def->label = L("Load filament ids"); + def->tooltip = L("Load filament ids for each object"); + def->cli_params = "\"1,2,3,1\""; + def->set_default_value(new ConfigOptionInts()); + + def = this->add("allow_multicolor_oneplate", coBool); + def->label = L("Allow multiple color on one plate"); + def->tooltip = L("If enabled, the arrange will allow multiple color on one plate "); + def->set_default_value(new ConfigOptionBool(true)); } const CLIActionsConfigDef cli_actions_config_def; diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 2e48a7912b..cd5d5be5b8 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -59,11 +59,11 @@ static const int PARTPLATE_TEXT_OFFSET_X2 = 1; static const int PARTPLATE_TEXT_OFFSET_Y = 1; static const int PARTPLATE_PLATENAME_OFFSET_Y = 10; -static const float WIPE_TOWER_DEFAULT_X_POS = 165.; -static const float WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y +const float WIPE_TOWER_DEFAULT_X_POS = 165.; +const float WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y -static const float I3_WIPE_TOWER_DEFAULT_X_POS = 0.; -static const float I3_WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y +const float I3_WIPE_TOWER_DEFAULT_X_POS = 0.; +const float I3_WIPE_TOWER_DEFAULT_Y_POS = 250.; // Max y std::array PlateTextureForeground = {0x0, 0xae, 0x42, 0xff}; @@ -1627,7 +1627,7 @@ std::vector PartPlate::get_used_extruders() return used_extruders; } -Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int plate_extruder_size) const +Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int plate_extruder_size, bool use_global_objects) const { Vec3d wipe_tower_size; @@ -1650,7 +1650,7 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con return wipe_tower_size; for (int obj_idx = 0; obj_idx < m_model->objects.size(); obj_idx++) { - if (!contain_instance_totally(obj_idx, 0)) + if (!use_global_objects && !contain_instance_totally(obj_idx, 0)) continue; BoundingBoxf3 bbox = m_model->objects[obj_idx]->bounding_box(); @@ -1701,14 +1701,14 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con return wipe_tower_size; } -arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const DynamicPrintConfig& config, int plate_index, int plate_extruder_size) const +arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const DynamicPrintConfig& config, int plate_index, int plate_extruder_size, bool use_global_objects) const { float x = dynamic_cast(config.option("wipe_tower_x"))->get_at(plate_index); float y = dynamic_cast(config.option("wipe_tower_y"))->get_at(plate_index); float w = dynamic_cast(config.option("prime_tower_width"))->value; //float a = dynamic_cast(config.option("wipe_tower_rotation_angle"))->value; float v = dynamic_cast(config.option("prime_volume"))->value; - Vec3d wipe_tower_size = estimate_wipe_tower_size(config, w, v, plate_extruder_size); + Vec3d wipe_tower_size = estimate_wipe_tower_size(config, w, v, plate_extruder_size, use_global_objects); int plate_width=m_width, plate_depth=m_depth; float depth = wipe_tower_size(1); float margin = WIPE_TOWER_MARGIN, wp_brim_width = 0.f; diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index c194b017dc..e17644e181 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -47,6 +47,15 @@ inline int compute_colum_count(int count) return cols; } + +extern const float WIPE_TOWER_DEFAULT_X_POS; +extern const float WIPE_TOWER_DEFAULT_Y_POS; // Max y + +extern const float I3_WIPE_TOWER_DEFAULT_X_POS; +extern const float I3_WIPE_TOWER_DEFAULT_Y_POS; // Max y + + + namespace Slic3r { class Model; @@ -293,8 +302,8 @@ public: ModelInstance* get_instance(int obj_id, int instance_id); Vec3d get_origin() { return m_origin; } - Vec3d estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int plate_extruder_size = 0) const; - arrangement::ArrangePolygon estimate_wipe_tower_polygon(const DynamicPrintConfig & config, int plate_index, int plate_extruder_size = 0) const; + Vec3d estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int plate_extruder_size = 0, bool use_global_objects = false) const; + arrangement::ArrangePolygon estimate_wipe_tower_polygon(const DynamicPrintConfig & config, int plate_index, int plate_extruder_size = 0, bool use_global_objects = false) const; std::vector get_extruders(bool conside_custom_gcode = false) const; std::vector get_extruders_under_cli(bool conside_custom_gcode, DynamicPrintConfig& full_config) const; std::vector get_extruders_without_support(bool conside_custom_gcode = false) const;