ENH: supporting encoded filament color

jira: [STUDIO-12346]
Change-Id: I7f5ce7806acb6fdeb3e3d9db52a0b96e5fadd759
(cherry picked from commit a7bdc2707d3825327258965c90c33836a7da628b)
This commit is contained in:
xin.zhang 2025-06-25 16:38:43 +08:00 committed by Noisyfox
parent 98a32f57aa
commit 88eae5d217
8 changed files with 8138 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -109,6 +109,8 @@ set(SLIC3R_GUI_SOURCES
GUI/DragCanvas.hpp
GUI/EditGCodeDialog.cpp
GUI/EditGCodeDialog.hpp
GUI/EncodedFilament.hpp
GUI/EncodedFilament.cpp
GUI/ExtraRenderers.cpp
GUI/ExtraRenderers.hpp
GUI/ExtrusionCalibration.cpp

View file

@ -9,6 +9,8 @@
#include <wx/dcgraph.h>
#include "CalibUtils.hpp"
#include "../Utils/ColorSpaceConvert.hpp"
#include "EncodedFilament.hpp"
namespace Slic3r { namespace GUI {
wxDEFINE_EVENT(EVT_SELECTED_COLOR, wxCommandEvent);
@ -178,6 +180,11 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent)
m_clr_picker->Bind(wxEVT_LEFT_DOWN, &AMSMaterialsSetting::on_clr_picker, this);
m_sizer_colour->Add(m_clr_picker, 0, 0, 0);
m_clr_name = new Label(parent, wxEmptyString);
m_clr_name->SetForegroundColour(*wxBLACK);
m_clr_name->SetBackgroundColour(*wxWHITE);
m_clr_name->SetFont(Label::Body_13);
m_sizer_colour->Add(m_clr_name, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(10));
wxBoxSizer* m_sizer_temperature = new wxBoxSizer(wxHORIZONTAL);
m_title_temperature = new wxStaticText(parent, wxID_ANY, _L("Nozzle\nTemperature"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
@ -724,18 +731,34 @@ void AMSMaterialsSetting::set_color(wxColour color)
//m_clrData->SetColour(color);
m_clr_picker->is_empty(false);
m_clr_picker->set_color(color);
FilamentColor fila_color;
fila_color.m_colors.insert(color);
fila_color.EndSet(m_clr_picker->ctype);
auto clr_query = GUI::wxGetApp().get_filament_color_code_query();
m_clr_name->SetLabelText(clr_query->GetFilaColorName(ams_filament_id, fila_color));
}
void AMSMaterialsSetting::set_empty_color(wxColour color)
{
m_clr_picker->is_empty(true);
m_clr_picker->set_color(color);
m_clr_name->SetLabelText(wxEmptyString);
}
void AMSMaterialsSetting::set_colors(std::vector<wxColour> colors)
{
//m_clrData->SetColour(color);
m_clr_picker->set_colors(colors);
if (!colors.empty())
{
FilamentColor fila_color;
for (const auto& clr : colors) { fila_color.m_colors.insert(clr); }
fila_color.EndSet(m_clr_picker->ctype);
auto clr_query = GUI::wxGetApp().get_filament_color_code_query();
m_clr_name->SetLabelText(clr_query->GetFilaColorName(ams_filament_id, fila_color));
}
}
void AMSMaterialsSetting::set_ctype(int ctype)

View file

@ -130,6 +130,7 @@ public:
std::string m_filament_type;
ColorPickerPopup m_color_picker_popup;
ColorPicker * m_clr_picker;
Label* m_clr_name;
std::vector<PACalibResult> m_pa_profile_items;
protected:

View file

@ -0,0 +1,230 @@
#include "EncodedFilament.hpp"
#include "GUI_App.hpp"
namespace Slic3r
{
static wxString _ColourToString(const wxColour& color)
{
return wxString::Format("#%02X%02X%02X%02X", color.Red(), color.Green(), color.Blue(), color.Alpha());
}
FilamentColorCodeQuery::FilamentColorCodeQuery()
{
m_fila_id2colors_map = new std::unordered_map<wxString, FilamentColorCodes*>;
m_fila_path = data_dir() + "/system/BBL/filament/filaments_color_codes.json";
LoadFromLocal();
}
FilamentColorCodeQuery::~FilamentColorCodeQuery()
{
for (auto& pair : *m_fila_id2colors_map) { delete pair.second; }
delete m_fila_id2colors_map;
m_fila_id2colors_map = nullptr;
}
FilamentColorCodes* FilamentColorCodeQuery::GetFilaInfoMap(const wxString& fila_id) const
{
const auto& iter = m_fila_id2colors_map->find(fila_id);
return (iter != m_fila_id2colors_map->end()) ? iter->second : nullptr;
}
Slic3r::FilamentColorCode* FilamentColorCodeQuery::GetFilaInfo(const wxString& fila_id, const FilamentColor& colors) const
{
FilamentColorCodes* color_info_map = GetFilaInfoMap(fila_id);
#if 0
if (color_info_map && !color_info_map->GetColorCode(colors))
{
wxString clr_strs;
for (const auto& clr : colors.m_colors)
{
clr_strs += " ";
clr_strs += _ColourToString(clr);
}
BOOST_LOG_TRIVIAL(warning) << "FilamentColorCodeQuery::GetFilaInfo: No color code found for " << fila_id << " with color type " << (int)colors.m_color_type << "colors" << clr_strs;
color_info_map->Debug(" ");
}
#endif
return color_info_map ? color_info_map->GetColorCode(colors) : nullptr;
}
wxString FilamentColorCodeQuery::GetFilaColorName(const wxString& fila_id, const FilamentColor& colors) const
{
FilamentColorCode* color_info = GetFilaInfo(fila_id, colors);
return (color_info) ? color_info->GetFilaColorName() : wxString();
}
void FilamentColorCodeQuery::LoadFromLocal()
{
std::ifstream json_file(encode_path(m_fila_path.c_str()));
try
{
if (json_file.is_open())
{
const json& json_content = json::parse(json_file);
if (!json_content.contains("data")) { return; }
const json& json_data = json_content["data"];
for (const auto& json_data_item : json_data)
{
const wxString& fila_id = json_data_item.contains("fila_id") ? json_data_item["fila_id"].get<wxString>() : wxString();
const wxString& fila_type = json_data_item.contains("fila_type") ? json_data_item["fila_type"].get<wxString>() : wxString();
const wxString& fila_color_code = json_data_item.contains("fila_color_code") ? json_data_item["fila_color_code"].get<wxString>() : wxString();
FilamentColor fila_color;
if (json_data_item.contains("fila_color"))
{
const auto& fila_color_strs = json_data_item["fila_color"].get<std::vector<wxString>>();
for (const auto& color_str : fila_color_strs) { fila_color.m_colors.emplace(wxColour(color_str)); }
}
const wxString& fila_color_type = json_data_item.contains("fila_color_type") ? wxString::FromUTF8(json_data_item["fila_color_type"].get<std::string>()) : wxString();
if (fila_color_type == wxString::FromUTF8("单色")) {
fila_color.m_color_type = FilamentColor::ColorType::SINGLE_CLR;
} else if (fila_color_type == wxString::FromUTF8("多拼色")) {
fila_color.m_color_type = FilamentColor::ColorType::MULTI_CLR;
} else if (fila_color_type == wxString::FromUTF8("渐变色"))
{
fila_color.m_color_type = FilamentColor::ColorType::GRADIENT_CLR;
};
std::unordered_map<wxString, wxString> fila_color_names;
if (json_data_item.contains("fila_color_name"))
{
const json& color_names_json = json_data_item["fila_color_name"];
for (const auto& color_name_item : color_names_json.items())
{
const wxString& lang_code = wxString::FromUTF8(color_name_item.key());
const wxString& color_name = wxString::FromUTF8(color_name_item.value().get<std::string>());
fila_color_names[lang_code] = color_name;
}
}
CreateFilaCode(fila_id, fila_type, fila_color_code, std::move(fila_color), std::move(fila_color_names));
}
}
}
catch (...)
{
assert(0 && "FilamentColorCodeQuery::LoadFromLocal failed");
BOOST_LOG_TRIVIAL(error) << "FilamentColorCodeQuery::LoadFromLocal failed";
}
}
void FilamentColorCodeQuery::CreateFilaCode(const wxString& fila_id,
const wxString& fila_type,
const wxString& fila_color_code,
FilamentColor&& fila_color,
std::unordered_map<wxString, wxString>&& fila_color_names)
{
FilamentColorCodes* color_codes = GetFilaInfoMap(fila_id);
if (!color_codes)
{
color_codes = new FilamentColorCodes(fila_id, fila_type);
(*m_fila_id2colors_map)[fila_id] = color_codes;
}
FilamentColorCode* color_code = new FilamentColorCode(fila_color_code, color_codes, std::move(fila_color), std::move(fila_color_names));
color_codes->AddColorCode(color_code);
}
// End of class EncodedFilamentQuery
wxString FilamentColorCode::GetFilaColorName() const
{
const wxString& strLanguage = Slic3r::GUI::wxGetApp().app_config->get("language");
const wxString& lang_code = strLanguage.BeforeFirst('_');
auto it = m_fila_color_names.find(lang_code);
if (it != m_fila_color_names.end())
{
return it->second;
}
it = m_fila_color_names.find("en");// retry with English as fallback
return (it != m_fila_color_names.end()) ? it->second : "Unknown";
}
FilamentColorCode::FilamentColorCode(const wxString& color_code, FilamentColorCodes* owner, FilamentColor&& color, std::unordered_map<wxString, wxString>&& name_map)
: m_fila_color_code(color_code),
m_owner(owner),
m_fila_color(std::move(color)),
m_fila_color_names(std::move(name_map))
{
if (m_owner)
{
m_owner->AddColorCode(this);
}
}
FilamentColorCode::~FilamentColorCode()
{
if (m_owner)
{
m_owner->RemoveColorCode(this);
}
}
void FilamentColorCode::Debug(const char* prefix)
{
BOOST_LOG_TRIVIAL(debug) << prefix << "Fila Color Code: " << m_fila_color_code
<< ", Colors: " << m_fila_color.ColorCount()
<< ", Type: " << static_cast<int>(m_fila_color.m_color_type);
for (const auto& color : m_fila_color.m_colors) { BOOST_LOG_TRIVIAL(debug) << prefix << " Color: " << _ColourToString(color); }
//for (const auto& name_pair : m_fila_color_names) { BOOST_LOG_TRIVIAL(debug) << prefix << " Color Name [" << name_pair.first << "]: " << name_pair.second;}
}
FilamentColorCodes::FilamentColorCodes(const wxString& fila_id, const wxString& fila_type)
: m_fila_id(fila_id), m_fila_type(fila_type)
{
m_fila_colors_map = new FilamentColor2CodeMap;
}
FilamentColorCodes::~FilamentColorCodes()
{
while (!m_fila_colors_map->empty()) // Ensure all color codes are removed before deleting the map
{
delete m_fila_colors_map->begin()->second; // Delete the first color code
}
delete m_fila_colors_map;
}
Slic3r::FilamentColorCode* FilamentColorCodes::GetColorCode(const FilamentColor& colors) const
{
const auto& it = m_fila_colors_map->find(colors);
return (it != m_fila_colors_map->end()) ? it->second : nullptr;
}
void FilamentColorCodes::AddColorCode(FilamentColorCode* code)
{
m_fila_colors_map->emplace(code->GetFilaColor(), code);
}
void FilamentColorCodes::RemoveColorCode(FilamentColorCode* code)
{
m_fila_colors_map->erase(code->GetFilaColor());
}
void FilamentColorCodes::Debug(const char* prefix)
{
BOOST_LOG_TRIVIAL(debug) << prefix << "Fila ID: " << m_fila_id << ", Type: " << m_fila_type;
auto iter = m_fila_colors_map->begin();
while (iter != m_fila_colors_map->end())
{
iter->second->Debug(prefix);
iter++;
}
BOOST_LOG_TRIVIAL(debug) << prefix << "End";
}
} // namespace Slic3r

View file

@ -0,0 +1,243 @@
#ifndef slic3r_ENCODED_FILAMENT_hpp_
#define slic3r_ENCODED_FILAMENT_hpp_
#include <vector>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <wx/colour.h>
#include <wx/string.h>
#include <chrono>
namespace Slic3r
{
//Previous definitions
class FilamentColorCode;
class FilamentColorCodes;
class FilamentColorCodeQuery;
// Hasher
struct FilamentColorHasher
{
std::size_t operator()(const wxColour& c) const noexcept {
return (static_cast<std::size_t>(c.Red()) << 24) ^
(static_cast<std::size_t>(c.Green()) << 16) ^
(static_cast<std::size_t>(c.Blue()) << 8) ^
(static_cast<std::size_t>(c.Alpha()));
}
};
struct FilamentColor
{
enum class ColorType : char
{
SINGLE_CLR = 0, // single color filament
MULTI_CLR, // multi-color filament
GRADIENT_CLR, // gradient filament
};
ColorType m_color_type = ColorType::SINGLE_CLR; // default to single color
std::unordered_set<wxColour, FilamentColorHasher> m_colors;
public:
size_t ColorCount() const noexcept { return m_colors.size(); }
void EndSet(int ctype)
{
if (m_colors.size() < 2)
{
m_color_type = ColorType::SINGLE_CLR;
}
else
{
if (ctype == 0)
{
m_color_type = ColorType::GRADIENT_CLR;
}
else
{
m_color_type = ColorType::MULTI_CLR;
}
}
}
};
// Represents a color in HSV format
struct ColourHSV
{
double h, s, v;
};
inline ColourHSV wxColourToHSV(const wxColour& c)
{
double r = c.Red() / 255.0;
double g = c.Green() / 255.0;
double b = c.Blue() / 255.0;
double maxc = std::max({ r, g, b });
double minc = std::min({ r, g, b });
double delta = maxc - minc;
double h = 0, s = 0, v = maxc;
if (delta > 0.00001) {
if (maxc == r) {
h = 60.0 * (fmod(((g - b) / delta), 6.0));
} else if (maxc == g) {
h = 60.0 * (((b - r) / delta) + 2.0);
} else {
h = 60.0 * (((r - g) / delta) + 4.0);
}
if (h < 0) h += 360.0;
s = delta / maxc;
} else {
h = 0;
s = 0;
}
return { h, s, v };
}
// Compare function for EncodedFilaColor
struct EncodedFilaColorEqual
{
bool operator()(const FilamentColor& lhs, const FilamentColor& rhs) const noexcept
{
if (lhs.ColorCount() != rhs.ColorCount()) { return lhs.ColorCount() < rhs.ColorCount(); };
if (lhs.ColorCount() == 1)
{
ColourHSV ha = wxColourToHSV(*lhs.m_colors.begin());
ColourHSV hb = wxColourToHSV(*rhs.m_colors.begin());
if (ha.h != hb.h) return ha.h < hb.h;
if (ha.s != hb.s) return ha.s < hb.s;
if (ha.v != hb.v) return ha.v < hb.v;
}
if (lhs.m_color_type != rhs.m_color_type)
{
return lhs.m_color_type < rhs.m_color_type;
}
return false;
}
private:
double hue(const wxColour& colour) const
{
double r_norm = colour.Red() / 255.0;
double g_norm = colour.Green() / 255.0;
double b_norm = colour.Blue() / 255.0;
double max_val = std::max({ r_norm, g_norm, b_norm });
double min_val = std::min({ r_norm, g_norm, b_norm });
double delta = max_val - min_val;
if (delta == 0) return 0;
double h;
if (max_val == r_norm) {
h = std::fmod(((g_norm - b_norm) / delta), 6.0);
} else if (max_val == g_norm) {
h = ((b_norm - r_norm) / delta) + 2;
} else {
h = ((r_norm - g_norm) / delta) + 4;
}
h *= 60;
if (h < 0) h += 360;
return h;
}
};
using FilamentColor2CodeMap = std::map<FilamentColor, FilamentColorCode*, EncodedFilaColorEqual>;
// FilamentColorCodeQuery class is used to query filament color codes and their associated information
class FilamentColorCodeQuery
{
public:
FilamentColorCodeQuery();
virtual ~FilamentColorCodeQuery();
public:
FilamentColorCodes* GetFilaInfoMap(const wxString& fila_id) const;
wxString GetFilaColorName(const wxString& fila_id, const FilamentColor& colors) const;
private:
FilamentColorCode* GetFilaInfo(const wxString& fila_id, const FilamentColor& colors) const;
protected:
void LoadFromLocal();
public:
void CreateFilaCode(const wxString& fila_id,
const wxString& fila_type,
const wxString& fila_color_code,
FilamentColor&& fila_color,
std::unordered_map<wxString, wxString>&& fila_color_names);
private:
/* loaded info*/
std::string m_fila_path;
std::unordered_map<wxString, FilamentColorCodes*>* m_fila_id2colors_map; //
};
// EncodedFilaColorsInfo class holds a mapping of filament codes to specific filamet type
class FilamentColorCodes
{
public:
FilamentColorCodes(const wxString& fila_id, const wxString& fila_type);
virtual ~FilamentColorCodes();
public:
wxString GetFilaCode() const { return m_fila_id; }
wxString GetFilaType() const { return m_fila_type; }
FilamentColor2CodeMap* GetFilamentColor2CodeMap() const { return m_fila_colors_map; }
FilamentColorCode* GetColorCode(const FilamentColor& colors) const;
void Debug(const char* prefix);
public:
void AddColorCode(FilamentColorCode* code);
void RemoveColorCode(FilamentColorCode* code);
private:
wxString m_fila_id;//eg. 54600
wxString m_fila_type;//eg. PEBA 90A
FilamentColor2CodeMap* m_fila_colors_map; // key is the color set, value is the info
};
// The EncodedFilaColorInfo class holds information about a specific filament color
class FilamentColorCode
{
public:
FilamentColorCode() = delete;
FilamentColorCode(const wxString& color_code, FilamentColorCodes* owner, FilamentColor&& color, std::unordered_map<wxString, wxString>&& name_map);
~FilamentColorCode();
public:
wxString GetFilaCode() const { return m_owner->GetFilaCode(); }
wxString GetFilaType() const { return m_owner->GetFilaType(); }
wxString GetFilaColorCode() const { return m_fila_color_code; } // eg. Q01B00
FilamentColor GetFilaColor() const { return m_fila_color; }
wxString GetFilaColorName() const;
void Debug(const char* prefix);
private:
FilamentColorCodes* m_owner;
/* color info*/
wxString m_fila_color_code; // eg. Q01B00
FilamentColor m_fila_color;
std::unordered_map<wxString, wxString> m_fila_color_names; // eg. en -> Red
};
}
#endif

View file

@ -70,6 +70,7 @@
#include "MainFrame.hpp"
#include "Plater.hpp"
#include "GLCanvas3D.hpp"
#include "EncodedFilament.hpp"
#include "../Utils/PresetUpdater.hpp"
#include "../Utils/PrintHost.hpp"
@ -6719,6 +6720,17 @@ void GUI_App::check_updates(const bool verbose)
}
}
FilamentColorCodeQuery* GUI_App::get_filament_color_code_query()
{
if (!m_filament_color_code_query)
{
m_filament_color_code_query = new FilamentColorCodeQuery();
}
return m_filament_color_code_query;
}
bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/* = 0*/)
{
return wxLaunchDefaultBrowser(url, flags);

View file

@ -54,6 +54,7 @@ struct wxLanguageInfo;
namespace Slic3r {
class AppConfig;
class FilamentColorCodeQuery;
class PresetBundle;
class PresetUpdater;
class ModelObject;
@ -303,6 +304,7 @@ private:
VersionInfo privacy_version_info;
static std::string version_display;
HMSQuery *hms_query { nullptr };
FilamentColorCodeQuery* m_filament_color_code_query{ nullptr };
boost::thread m_sync_update_thread;
std::shared_ptr<int> m_user_sync_token;
@ -337,6 +339,7 @@ public:
Slic3r::TaskManager* getTaskManager() { return m_task_manager; }
HMSQuery* get_hms_query() { return hms_query; }
NetworkAgent* getAgent() { return m_agent; }
FilamentColorCodeQuery* get_filament_color_code_query();
bool is_editor() const { return m_app_mode == EAppMode::Editor; }
bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; }
bool is_recreating_gui() const { return m_is_recreating_gui; }