ENH: CLI: add support for colored obj

jira: no-jira
Change-Id: If9e698d60b71e8835a3ae46c75e9c8c2878556a7
(cherry picked from commit a2b2ec9cb80e3bafabd07a377aff9e11188be346)
This commit is contained in:
lane.wei 2024-07-13 18:39:05 +08:00 committed by Lane.Wei
parent b97d44dae4
commit 201d3b18cc
4 changed files with 204 additions and 20 deletions

View File

@ -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 <GLFW/glfw3.h>
#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<Slic3r::RGBA>& input_colors, std::vector<RGBA>& all_colours, int max_filament_count, std::vector<unsigned char>& 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<Slic3r::RGBA> cluster_colors;
std::vector<int> cluster_labels;
char cluster_number = -1;
obj_color_deal_algo(input_colors, cluster_colors, cluster_labels, cluster_number);
std::vector<int> 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<ColorDistValue> 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_t> &assemble_plate_info_list, Model &model, PlateDataPtrs &plate_list)
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)
{
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_plate_info_t> &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_plate_info_t> &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_plate_info_t> &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<unsigned char> 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<int> 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_plate_info_t> &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_plate_info_t> &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<RGBA> 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<ConfigOptionStrings>("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<ConfigOptionStrings>("filament_colour", true);
std::vector<std::string>& 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;

View File

@ -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]);
}
} }

View File

@ -3,10 +3,18 @@
#include <wx/colour.h>
#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

View File

@ -12,11 +12,7 @@
class Button;
class Label;
class ComboBox;
struct ColorDistValue
{
int id;
float distance;
};
class ObjColorPanel : public wxPanel
{
public: