diff --git a/src/BambuStudio.cpp b/src/BambuStudio.cpp index dbffa21d7..08ffcefe5 100644 --- a/src/BambuStudio.cpp +++ b/src/BambuStudio.cpp @@ -68,6 +68,7 @@ using namespace nlohmann; #include "libslic3r/Orient.hpp" #include "libslic3r/PNGReadWrite.hpp" +#include "libslic3r/ObjColorUtils.hpp" #include "BambuStudio.hpp" //BBS: add exception handler for win32 @@ -81,6 +82,7 @@ using namespace nlohmann; #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/Camera.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GuiColor.hpp" #include #ifdef __WXGTK__ @@ -777,17 +779,74 @@ void merge_or_add_object(assemble_plate_info_t& assemble_plate_info, Model &mode } } +bool convert_obj_cluster_colors(std::vector& input_colors, std::vector& all_colours, int max_filament_count, std::vector& output_filament_ids) +{ + using namespace Slic3r::GUI; + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, got original input obj colors %3%")%__FUNCTION__ %__LINE__ %input_colors.size(); + if (input_colors.size() > 0) { + std::vector cluster_colors; + std::vector cluster_labels; + char cluster_number = -1; + + obj_color_deal_algo(input_colors, cluster_colors, cluster_labels, cluster_number); + std::vector cluster_color_maps; + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, after obj_color_deal_algo, cluster_colors size %3%, all_colours size %4%, max_filament_count=%5%")%__FUNCTION__ %__LINE__%cluster_colors.size() %all_colours.size() %max_filament_count; + + cluster_color_maps.resize(cluster_colors.size(), 1); + + int init_size = all_colours.size(); + + for (size_t i = 0; i < cluster_colors.size(); i++) { + if ((init_size + i + 1) <= max_filament_count) { + all_colours.push_back(cluster_colors[i]); + cluster_color_maps[i] = all_colours.size(); + BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, cluster color index %3% RGBA {%4%,%5%,%6%,%7%} directly inserted, id %8%") + %__FUNCTION__ %__LINE__%(i+1) %cluster_colors[i][0] %cluster_colors[i][1] %cluster_colors[i][2] %cluster_colors[i][3] %cluster_color_maps[i] ; + } + else { + std::vector color_dists; + color_dists.resize(max_filament_count); + for (size_t j = 0; j < max_filament_count; j++) { + color_dists[j].distance = calc_color_distance(cluster_colors[i], all_colours[j]); + color_dists[j].id = j + 1; + } + std::sort(color_dists.begin(), color_dists.end(), [](ColorDistValue &a, ColorDistValue &b) { return a.distance < b.distance; }); + cluster_color_maps[i] = color_dists[0].id; + BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, cluster color index %3% RGBA {%4%,%5%,%6%,%7%} directly inserted, id %8%") + %__FUNCTION__ %__LINE__%(i+1) %cluster_colors[i][0] %cluster_colors[i][1] %cluster_colors[i][2] %cluster_colors[i][3] %cluster_color_maps[i] ; + } + } + + //3.generate filament_ids + auto input_colors_size = input_colors.size(); + output_filament_ids.resize(input_colors_size); + for (size_t i = 0; i < input_colors_size; i++) { + int label = cluster_labels[i]; + output_filament_ids[i] = cluster_color_maps[label]; + } + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, all_colours size changes to %3%")%__FUNCTION__ %__LINE__%all_colours.size(); + + return true; + } + + return false; +} + #ifdef _WIN32 #define DIR_SEPARATOR '\\' #else #define DIR_SEPARATOR '/' #endif -static int construct_assemble_list(std::vector &assemble_plate_info_list, Model &model, PlateDataPtrs &plate_list) +static int construct_assemble_list(std::vector &assemble_plate_info_list, Model &model, PlateDataPtrs &plate_list, std::vector& all_colours) { int ret = 0; int plate_count = assemble_plate_info_list.size(); ConfigSubstitutionContext config_substitutions(ForwardCompatibilitySubstitutionRule::Enable); Model temp_model; + const int max_filament_count = size_t(EnforcerBlockerType::ExtruderMax); plate_list.resize(plate_count); for (int index = 0; index < plate_count; index++) @@ -821,7 +880,10 @@ static int construct_assemble_list(std::vector &assemble_ { assemble_object_info_t& assemble_object = assemble_plate_info.assemble_obj_list[obj_index]; std::string object_name; + std::string object_1_name; + ModelObject* object = nullptr; TriangleMesh mesh; + bool skip_filament = false; boost::filesystem::path object_path(assemble_object.path); if (!fs::exists(object_path)) { @@ -843,7 +905,13 @@ static int construct_assemble_list(std::vector &assemble_ 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()); + object_name.erase(object_name.end() - 4, object_name.end()); + object_1_name = object_name + "_1"; + object = temp_model.add_object(object_1_name.c_str(), path_str, std::move(mesh)); + if (!object) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": add_object %1% for stl failed, plate index %2%, object index %3%") % object_1_name % (index + 1) % (obj_index + 1); + return CLI_DATA_FILE_ERROR; + } } else if (boost::algorithm::iends_with(assemble_object.path, ".obj")) { @@ -854,20 +922,61 @@ static int construct_assemble_list(std::vector &assemble_ 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()); + object_name.erase(object_name.end() - 4, object_name.end()); + object_1_name = object_name + "_1"; + //process colors + Model obj_temp_model; + ModelObject* temp_object = obj_temp_model.add_object(object_1_name.c_str(), path_str, std::move(mesh)); + if (!temp_object) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": add_object %1% for obj failed, plate index %2%, object index %3%") % object_1_name % (index + 1) % (obj_index + 1); + return CLI_DATA_FILE_ERROR; + } + + std::vector output_filament_ids; + if (obj_info.vertex_colors.size() > 0) { + convert_obj_cluster_colors(obj_info.vertex_colors, all_colours, max_filament_count, output_filament_ids); + if (output_filament_ids.size() > 0) { + unsigned char first_extruder_id = output_filament_ids.front(); + result = Model::obj_import_vertex_color_deal(output_filament_ids, first_extruder_id, & obj_temp_model); + } + skip_filament = true; + } else if (obj_info.face_colors.size() > 0 && obj_info.has_uv_png == false) { // mtl file + convert_obj_cluster_colors(obj_info.face_colors, all_colours, max_filament_count, output_filament_ids); + if (output_filament_ids.size() > 0) { + unsigned char first_extruder_id = output_filament_ids.front(); + result = Model::obj_import_face_color_deal(output_filament_ids, first_extruder_id, & obj_temp_model); + } + skip_filament = true; + } + if (!result) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": failed to convert colors for %1%, plate index %2%, object index %3%, error %4%") % assemble_object.path % (index + 1) % (obj_index + 1) % message; + return CLI_DATA_FILE_ERROR; + } + object = temp_model.add_object(*temp_object); + if (!object) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": add_object %1% for stl failed, plate index %2%, object index %3%") % object_1_name % (index + 1) % (obj_index + 1); + return CLI_DATA_FILE_ERROR; + } + obj_temp_model.clear_objects(); + obj_temp_model.clear_materials(); } else { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": unsupported file %1%, plate index %2%, object index %3%") % assemble_object.path % (index + 1) % (obj_index + 1); return CLI_INVALID_PARAMS; } - 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; + if (!skip_filament) { + object->config.set_key_value("extruder", new ConfigOptionInt(assemble_object.filaments[0])); + used_filaments.emplace(assemble_object.filaments[0]); } - object->config.set_key_value("extruder", new ConfigOptionInt(assemble_object.filaments[0])); + else { + assemble_object.filaments[0] = 0; + for (const ModelVolume* mv : object->volumes) { + std::vector volume_extruders = mv->get_extruders(); + used_filaments.insert(volume_extruders.begin(), volume_extruders.end()); + } + } + if (!assemble_object.print_params.empty()) { for (auto param_iter = assemble_object.print_params.begin(); param_iter != assemble_object.print_params.end(); param_iter++) @@ -903,7 +1012,7 @@ static int construct_assemble_list(std::vector &assemble_ 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]; @@ -922,8 +1031,14 @@ static int construct_assemble_list(std::vector &assemble_ 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 (!skip_filament) { + copy_obj->config.set_key_value("extruder", new ConfigOptionInt(assemble_object.filaments[array_index])); + used_filaments.emplace(assemble_object.filaments[array_index]); + } + else { + assemble_object.filaments[array_index] = 0; + } if (copy_index < assemble_object.assemble_index.size()) array_index = copy_index; @@ -1373,6 +1488,7 @@ 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; unsigned int input_index = 0; + std::vector input_obj_colours; 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"); @@ -1638,7 +1754,7 @@ int CLI::run(int argc, char **argv) } try { - ret = construct_assemble_list(assemble_plate_info_list, model, plate_data_src); + ret = construct_assemble_list(assemble_plate_info_list, model, plate_data_src, input_obj_colours); if (ret) { record_exit_reson(outfile_dir, ret, 0, cli_errors[ret], sliced_info); flush_and_exit(ret); @@ -1956,6 +2072,60 @@ int CLI::run(int argc, char **argv) } } + //add logic for obj auto colors + if (input_obj_colours.size() > 0) { + BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, got input obj colors %3%")%__FUNCTION__ %__LINE__ %input_obj_colours.size(); + int input_color_count = input_obj_colours.size(); + if (load_filament_count == 0) { + BOOST_LOG_TRIVIAL(error) << boost::format("filament config not loaded when loading colored obj"); + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + + //check filament_color from extra config + ConfigOptionStrings *selected_filament_colors_option = m_extra_config.option("filament_colour"); + if (selected_filament_colors_option) { + BOOST_LOG_TRIVIAL(error) << boost::format("filament_colour should not be set when loading colored obj"); + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + if (load_filament_count < input_color_count) { + int delta = input_color_count - load_filament_count; + for (int index = 0; index < delta; index++) { + load_filaments_id.push_back(load_filaments_id[0]); + load_filaments_name.push_back(load_filaments_name[0]); + load_filaments_config.push_back(load_filaments_config[0]); + load_filaments_index.push_back(index+1+load_filament_count); + load_filaments_inherit.push_back(load_filaments_inherit[0]); + } + load_filament_count = input_color_count; + } + + selected_filament_colors_option = m_extra_config.option("filament_colour", true); + std::vector& filament_colors = selected_filament_colors_option->values; + filament_colors.resize(input_color_count); + for (int index = 0; index < input_color_count; index++) + { + std::string color_string; + int color[4]; + color[0] = std::clamp((int) (input_obj_colours[index][0] * 255.f), 0, 255); + color[1] = std::clamp((int) (input_obj_colours[index][1] * 255.f), 0, 255); + color[2] = std::clamp((int) (input_obj_colours[index][2] * 255.f), 0, 255); + color[3] = std::clamp((int) (input_obj_colours[index][3] * 255.f), 0, 255); + + std::stringstream stream; + stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[0]; + stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[1]; + stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[2]; + stream << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << color[3]; + std::string result(stream.str()); + + filament_colors[index] = "#" + result; + + BOOST_LOG_TRIVIAL(info) << boost::format("%1%:%2%, index %3%, argb {%4%,%5%,%6%,%7%} to string %8%")%__FUNCTION__ %__LINE__ %(index+1) %color[0] %color[1] %color[2] %color[3] %filament_colors[index]; + } + } + if (filament_count == 0) filament_count = load_filament_count; @@ -2123,13 +2293,13 @@ int CLI::run(int argc, char **argv) { if (uptodate_filaments.size() > 0) { - if (uptodate_filaments.size() != filament_count) + if (uptodate_filaments.size() != (size_t)filament_count) { BOOST_LOG_TRIVIAL(error) << boost::format("uptodate_filaments size %1% not equal to filament_count %2% ")%uptodate_filaments.size() %filament_count; record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); flush_and_exit(CLI_INVALID_PARAMS); } - for (unsigned int index = 0; index < filament_count; index ++) + for (int index = 0; index < filament_count; index ++) { std::string file = uptodate_filaments[index]; DynamicPrintConfig config; diff --git a/src/slic3r/GUI/GuiColor.cpp b/src/slic3r/GUI/GuiColor.cpp index aeaa8b63f..b5225d41d 100644 --- a/src/slic3r/GUI/GuiColor.cpp +++ b/src/slic3r/GUI/GuiColor.cpp @@ -29,4 +29,14 @@ float calc_color_distance(wxColour c1, wxColour c2) return DeltaE76(lab[0][0], lab[0][1], lab[0][2], lab[1][0], lab[1][1], lab[1][2]); } + +float calc_color_distance(RGBA c1, RGBA c2) +{ + float lab[2][3]; + RGB2Lab(c1[0], c1[1], c1[2], &lab[0][0], &lab[0][1], &lab[0][2]); + RGB2Lab(c2[0], c2[1], c2[2], &lab[1][0], &lab[1][1], &lab[1][2]); + + return DeltaE76(lab[0][0], lab[0][1], lab[0][2], lab[1][0], lab[1][1], lab[1][2]); +} + } } diff --git a/src/slic3r/GUI/GuiColor.hpp b/src/slic3r/GUI/GuiColor.hpp index d83277d93..6eac14f51 100644 --- a/src/slic3r/GUI/GuiColor.hpp +++ b/src/slic3r/GUI/GuiColor.hpp @@ -3,10 +3,18 @@ #include #include "libslic3r/Color.hpp" #include "slic3r/Utils/ColorSpaceConvert.hpp" + +struct ColorDistValue +{ + int id; + float distance; +}; + namespace Slic3r { namespace GUI { wxColour convert_to_wxColour(const RGBA &color); RGBA convert_to_rgba(const wxColour &color); float calc_color_distance(wxColour c1, wxColour c2); +float calc_color_distance(RGBA c1, RGBA c2); } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ObjColorDialog.hpp b/src/slic3r/GUI/ObjColorDialog.hpp index 56808d6dc..7255db970 100644 --- a/src/slic3r/GUI/ObjColorDialog.hpp +++ b/src/slic3r/GUI/ObjColorDialog.hpp @@ -12,11 +12,7 @@ class Button; class Label; class ComboBox; -struct ColorDistValue -{ - int id; - float distance; -}; + class ObjColorPanel : public wxPanel { public: