Compare commits

...
Author SHA1 Message Date
Hanif Koh cca0f61c1b Remove the OBJ Color Dialog That Texture Import Replaced 2026-10-04 20:46:09 +08:00
Kiss Lorand 5efb3bef45 Fix missing slicing progress on the first slice (#16000)
Restore slicing progress after notification reset

Ensure the cleared slicing-progress controller is recreated before its initial state transition, and calculate the Daily Tips size before positioning the popup.
2026-10-04 14:06:23 +03:00
Kris Austin 90ac58d3cd perf: skip the unused curled wall estimate to speed up slicing by up to 9% (#16113)
perf: skip estimating curled walls when nothing reads them

The curled extrusion estimate ran whenever a region had overhang speed on,
which is the default, but only the slowdown for curled perimeters reads the
curled lines it produces, and that slowdown is off by default. The step now
also requires a region with the slowdown on, and clears the curled lines
when it skips the estimate, so none are left from an earlier slice. 
Also fixes stale fan commands due to the stale curled lines on the reused layers.
2026-10-04 09:45:19 +01:00
15 changed files with 97 additions and 1171 deletions
-1
View File
@@ -196,7 +196,6 @@ src/slic3r/GUI/Tab.hpp
src/slic3r/GUI/UnsavedChangesDialog.cpp
src/slic3r/GUI/Auxiliary.cpp
src/slic3r/GUI/UpdateDialogs.cpp
src/slic3r/GUI/ObjColorDialog.cpp
src/slic3r/GUI/SyncAmsInfoDialog.cpp
src/slic3r/GUI/WipeTowerDialog.cpp
src/slic3r/GUI/wxExtensions.cpp
-12
View File
@@ -29,18 +29,6 @@ struct ObjInfo {
std::vector<ObjParser::ObjUseMtl> usemtls; // material spans, for texture import
};
struct ObjDialogInOut
{ // input:colors array
std::vector<RGBA> input_colors;
bool is_single_color{false};
// colors array output:
std::vector<unsigned char> filament_ids;
unsigned char first_extruder_id;
bool deal_vertex_color;
Model * model{nullptr};
std::string lost_material_name{""};
};
typedef std::function<void(ObjDialogInOut &in_out)> ObjImportColorFn;
extern bool load_obj(const char *path, TriangleMesh *mesh, ObjInfo &vertex_colors, std::string &message, ObjParser::MtlData *out_mtl = nullptr);
extern bool load_obj(const char *path, Model *model, ObjInfo &vertex_colors, std::string &message, const char *object_name = nullptr, ObjParser::MtlData *out_mtl = nullptr);
-5
View File
@@ -4414,11 +4414,6 @@ struct PrecomputedLayer
};
} // namespace
template<typename BoolsOption> static bool any_enabled(const BoolsOption &option)
{
return std::any_of(option.values.begin(), option.values.end(), [](unsigned char enabled) { return enabled != 0; });
}
// Whether process_layer() prepares the overhang estimator for `layer`.
template<typename OverhangSpeed>
static bool prepares_overhang_estimator(const Layer &layer, bool overhang_fan, OverhangSpeed overhang_speed)
+1 -2
View File
@@ -321,8 +321,7 @@ Model Model::read_from_file(const std::string&
Import3mfProgressFn proFn,
ImportstlProgressFn stlFn,
BBLProject * project,
int plate_id,
ObjImportColorFn objFn)
int plate_id)
{
Model model;
+1 -2
View File
@@ -1756,8 +1756,7 @@ public:
std::vector<Preset*>* project_presets = nullptr, bool* is_xxx = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr,
ImportstlProgressFn stlFn = nullptr,
BBLProject * project = nullptr,
int plate_id = 0,
ObjImportColorFn objFn = nullptr
int plate_id = 0
);
// BBS
static bool obj_import_vertex_color_deal(const std::vector<unsigned char> &vertex_filament_ids, const unsigned char &first_extruder_id, Model *model);
+6
View File
@@ -789,6 +789,12 @@ class StaticPrintConfig;
// Minimum object distance for arrangement, based on printer technology.
double min_object_distance(const ConfigBase &cfg);
// Whether any value is set, a nil value included.
template<bool NULLABLE> bool any_enabled(const ConfigOptionBoolsTempl<NULLABLE> &option)
{
return std::any_of(option.values.begin(), option.values.end(), [](unsigned char enabled) { return enabled != 0; });
}
// One (extruder type x nozzle volume type) parameter variant a filament prints through, plus a
// representative physical extruder observed using it. Ordering (and set-dedup identity) covers
// the variant pair only, so the same variant reached through two extruders keeps one config slot.
+10 -4
View File
@@ -1025,10 +1025,13 @@ void PrintObject::generate_support_material()
void PrintObject::estimate_curled_extrusions()
{
if (this->set_started(posEstimateCurledExtrusions)) {
if ( std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(), [](const PrintRegion* region) {
const auto& cfg = region->config().enable_overhang_speed.values;
return std::any_of(cfg.begin(), cfg.end(), [](const unsigned char v) { return (bool) v; });
})) {
const auto any_region_enables = [this](ConfigOptionBoolsNullable PrintRegionConfig::*option) {
return std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(),
[option](const PrintRegion* region) { return any_enabled(region->config().*option); });
};
// Only the slowdown for curled perimeters reads the curled lines, and they stay empty unless some region has overhang speed on.
if (any_region_enables(&PrintRegionConfig::enable_overhang_speed) &&
any_region_enables(&PrintRegionConfig::slowdown_for_curled_perimeters)) {
// Estimate curling of support material and add it to the malformaition lines of each layer
float support_flow_width = support_material_flow(this, this->config().layer_height).width();
@@ -1038,6 +1041,9 @@ void PrintObject::estimate_curled_extrusions()
float(this->config().brim_width.getFloat())};
SupportSpotsGenerator::estimate_malformations(this->layers(), params);
m_print->throw_if_canceled();
} else {
for (Layer *layer : m_layers)
layer->curled_lines.clear();
}
//this->set_done(posEstimateCurledExtrusions);
}
-2
View File
@@ -428,8 +428,6 @@ set(SLIC3R_GUI_SOURCES
GUI/NotificationManager.hpp
GUI/OAuthDialog.cpp
GUI/OAuthDialog.hpp
GUI/ObjColorDialog.cpp
GUI/ObjColorDialog.hpp
GUI/ObjectDataViewModel.cpp
GUI/ObjectDataViewModel.hpp
GUI/OG_CustomCtrl.cpp
+4 -3
View File
@@ -276,8 +276,9 @@ void DailyTipsPanel::set_position(const ImVec2& pos)
void DailyTipsPanel::set_size(const ImVec2& size)
{
m_width = size.x;
m_height = size.y;
m_content_height = m_height - m_footer_height;
//Orca: Keep the requested content height so expanding a collapsed panel restores its full size.
m_content_height = size.y - m_footer_height;
m_height = m_can_expand && !m_is_expanded ? m_footer_height : size.y;
}
void DailyTipsPanel::set_can_expand(bool can_expand)
@@ -647,4 +648,4 @@ void DailyTipsWindow::on_change_color_mode(bool is_dark)
m_panel->on_change_color_mode(is_dark);
}
}}
}}
+14 -7
View File
@@ -2880,17 +2880,24 @@ void NotificationManager::update_slicing_notif_dailytips(bool need_change)
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
wxGetApp().plater()->init_notification_manager();
}
// Orca: Ensures the slicing-progress controller exists before applying the first slicing transition.
void NotificationManager::set_slicing_progress_began()
{
for (std::unique_ptr<PopNotification> & notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::SlicingProgress) {
SlicingProgressNotification* spn = dynamic_cast<SlicingProgressNotification*>(notification.get());
spn->set_progress_state(SlicingProgressNotification::SlicingProgressState::SP_BEGAN);
return;
auto find_slicing_progress = [this]() -> SlicingProgressNotification* {
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::SlicingProgress)
return dynamic_cast<SlicingProgressNotification*>(notification.get());
}
return nullptr;
};
SlicingProgressNotification* notification = find_slicing_progress();
if (notification == nullptr) {
wxGetApp().plater()->init_notification_manager();
notification = find_slicing_progress();
}
// Slicing progress notification was not found - init it thru plater so correct cancel callback function is appended
wxGetApp().plater()->init_notification_manager();
if (notification != nullptr)
notification->set_progress_state(SlicingProgressNotification::SlicingProgressState::SP_BEGAN);
}
void NotificationManager::set_slicing_progress_percentage(const std::string& text, float percentage)
{
-971
View File
@@ -1,971 +0,0 @@
#include <algorithm>
#include "libslic3r/TriangleSelector.hpp"
#include "slic3r/GUI/Widgets/HyperLink.hpp"
#include "slic3r/GUI/GUI_Utils.hpp"
#include "libslic3r/Format/OBJ.hpp"
#include <cstddef>
#include "libslic3r/Color.hpp"
#include "slic3r/GUI/GuiColor.hpp"
#include "slic3r/GUI/Widgets/SpinInput.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/wxExtensions.hpp"
#include <cmath>
#include <cstdlib>
#include <boost/log/trivial.hpp>
#include "slic3r/GUI/PartPlate.hpp"
#include "slic3r/GUI/Camera.hpp"
#include <sstream>
//#include "libslic3r/FlushVolCalc.hpp"
#include "ObjColorDialog.hpp"
#include "BitmapCache.hpp"
#include "GUI.hpp"//for ICON_SIZE
#include "I18N.hpp"
#include "GUI_App.hpp"
#include "MsgDialog.hpp"
#include "Widgets/Button.hpp"
#include "MainFrame.hpp"
#include "libslic3r/Config.hpp"
#include "BitmapComboBox.hpp"
#include "Widgets/ComboBox.hpp"
#include "Widgets/DialogButtons.hpp"
#include <wx/colour.h>
#include <wx/event.h>
#include <wx/gdicmn.h>
#include <vector>
#include <string>
#include <wx/dialog.h>
#include <wx/panel.h>
#include <wx/busycursor.h>
#include <wx/arrstr.h>
#include <wx/image.h>
#include <wx/anybutton.h>
#include <wx/scrolwin.h>
#include <wx/sizer.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <wx/spinctrl.h>
#include "libslic3r/ObjColorUtils.hpp"
#include "libslic3r/Model.hpp"
using namespace Slic3r;
using namespace Slic3r::GUI;
int objcolor_scale(const int val) { return val * Slic3r::GUI::wxGetApp().em_unit() / 10; }
int OBJCOLOR_ITEM_WIDTH() { return objcolor_scale(30); }
static const wxColour g_text_color = wxColour(107, 107, 107, 255);
static const wxColour g_undefined_color_in_obj = wxColour(0, 255, 0, 255);
const int HEADER_BORDER = 5;
const int CONTENT_BORDER = 3;
const int PANEL_WIDTH = 400;
const int COLOR_LABEL_WIDTH = 180;
const int IMAGE_SIZE_WIDTH = 300;
#define MIN_OBJCOLOR_DIALOG_WIDTH FromDIP(400)
#define FIX_SCROLL_HEIGTH FromDIP(400)
#define BTN_SIZE wxSize(FromDIP(58), FromDIP(24))
#define BTN_GAP FromDIP(15)
#define FIX_SCROLL_IMAGE_WIDTH FromDIP(270)
static void update_ui(wxWindow* window)
{
Slic3r::GUI::wxGetApp().UpdateDarkUI(window);
}
static const char g_min_cluster_color = 1;
static const char g_max_color = (int) EnforcerBlockerType::ExtruderMax;
wxBoxSizer* ObjColorDialog::create_btn_sizer(long flags,bool exist_error)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
if (!exist_error) {
btn_sizer->AddSpacer(FromDIP(25));
auto *tips = new HyperLink(this, _L("Wiki Guide")); // ORCA
tips->SetURL("https://www.orcaslicer.com/wiki/import_export#obj");
btn_sizer->Add(tips, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
btn_sizer->AddStretchSpacer();
auto dlg_btns = new DialogButtons(this, {"OK", "Cancel"});
m_button_list[wxOK] = dlg_btns->GetOK();
m_button_list[wxCANCEL] = dlg_btns->GetCANCEL();
btn_sizer->Add(dlg_btns, 0, wxEXPAND);
return btn_sizer;
}
void ObjColorDialog::on_dpi_changed(const wxRect &suggested_rect)
{
m_panel_ObjColor->msw_rescale();
this->Refresh();
}
void ObjColorDialog::update_layout() {
m_main_sizer->Layout();
SetSizerAndFit(m_main_sizer);
}
bool ObjColorDialog::Show(bool show) {
if (m_panel_ObjColor->do_show(show)) {
return DPIDialog::Show(true);
} else {
return DPIDialog::Show(false);
}
};
ObjColorDialog::ObjColorDialog(wxWindow *parent, Slic3r::ObjDialogInOut &in_out, const std::vector<std::string> &extruder_colours, const bool semm)
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe),
wxID_ANY,
_(L("OBJ file import color")),
wxDefaultPosition,
wxDefaultSize,
wxDEFAULT_DIALOG_STYLE /* | wxRESIZE_BORDER*/)
{
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1));
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
this->SetBackgroundColour(*wxWHITE);
this->SetMinSize(wxSize(MIN_OBJCOLOR_DIALOG_WIDTH, -1));
m_main_sizer = new wxBoxSizer(wxVERTICAL);
m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
// set min sizer width according to extruders count
auto sizer_width = (int) (2.8 * OBJCOLOR_ITEM_WIDTH());
sizer_width = sizer_width > MIN_OBJCOLOR_DIALOG_WIDTH ? sizer_width : MIN_OBJCOLOR_DIALOG_WIDTH;
m_main_sizer->SetMinSize(wxSize(sizer_width, -1));
bool some_face_no_color = false;
if (!in_out.deal_vertex_color) {
auto temp0 = in_out.input_colors.size();
auto temp1 = in_out.model->objects[0]->volumes[0]->mesh_ptr()->facets_count();
some_face_no_color = temp0 < temp1;
}
bool ok = in_out.lost_material_name.empty() && !some_face_no_color;
if (ok) {
m_panel_ObjColor = new ObjColorPanel(this, in_out, extruder_colours, semm);
m_panel_ObjColor->set_layout_callback([this]() { update_layout(); });
m_main_sizer->Add(m_panel_ObjColor, 1, wxEXPAND | wxALL, 0);
}
else {
wxBoxSizer * error_mtl_sizer = new wxBoxSizer(wxVERTICAL);
wxStaticText *error_mtl_title = new wxStaticText(this, wxID_ANY, _L("Some faces don't have color defined."));
if (!in_out.lost_material_name.empty()) {
error_mtl_title->SetLabel(_L("MTL file exist error, could not find the material:") + " " + in_out.lost_material_name + ".");
}
error_mtl_title->SetFont(Label::Head_12);
error_mtl_sizer->Add(error_mtl_title, 0, wxALIGN_LEFT | wxBOTTOM | wxTOP, FromDIP(5));
wxStaticText *tip_title = new wxStaticText(this, wxID_ANY, _L("Please check OBJ or MTL file."));
tip_title->SetFont(Label::Head_12);
error_mtl_sizer->Add(tip_title, 0, wxALIGN_LEFT | wxBOTTOM | wxTOP, FromDIP(5));
m_main_sizer->Add(error_mtl_sizer, 1, wxEXPAND | wxLEFT, FromDIP(25));
}
m_buttons_sizer = create_btn_sizer(wxOK | wxCANCEL, !ok);
if (!ok) {
m_button_list[wxCANCEL]->Hide();
m_button_list[wxOK]->Enable(true);
// ORCA no need to set colors again
} else {
m_button_list[wxOK]->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent &e) {
if (m_panel_ObjColor->is_ok() == m_button_list[wxOK]->IsEnabled()) { return; }
m_button_list[wxOK]->Enable(m_panel_ObjColor->is_ok());
// ORCA no need to set colors again
}));
}
m_main_sizer->Add(m_buttons_sizer, 0, wxBOTTOM | wxTOP | wxRIGHT | wxEXPAND, BTN_GAP);
SetSizer(m_main_sizer);
m_main_sizer->SetSizeHints(this);
if (this->FindWindowById(wxID_OK, this)) {
this->FindWindowById(wxID_OK, this)->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {// if OK button is clicked..
if (!m_panel_ObjColor) {
EndModal(wxCANCEL);
return;
}
m_panel_ObjColor->clear_instance_and_revert_offset();
m_panel_ObjColor->send_new_filament_to_ui();
EndModal(wxID_OK);
}, wxID_OK);
}
if (this->FindWindowById(wxID_CANCEL, this)) {
update_ui(static_cast<wxButton*>(this->FindWindowById(wxID_CANCEL, this)));
this->FindWindowById(wxID_CANCEL, this)->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
if (m_panel_ObjColor) {
m_panel_ObjColor->cancel_paint_color();
}
EndModal(wxCANCEL);
});
}
this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &e) {
if (m_panel_ObjColor) {
m_panel_ObjColor->cancel_paint_color();
}
EndModal(wxCANCEL);
});
wxGetApp().UpdateDlgDarkUI(this);
CenterOnParent();
}
// This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers)
ObjColorPanel::ObjColorPanel(wxWindow *parent, Slic3r::ObjDialogInOut &in_out, const std::vector<std::string> &extruder_colours, const bool semm)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize /*,wxBORDER_RAISED*/)
, m_obj_in_out(in_out)
, m_input_colors(in_out.input_colors)
, m_filament_ids(in_out.filament_ids)
, m_first_extruder_id(in_out.first_extruder_id)
, m_semm(semm)
{
if (in_out.input_colors.size() == 0) { return; }
for (const std::string& color : extruder_colours) {
m_colours.push_back(wxColor(color));
}
//deal input_colors
m_input_colors_size = in_out.input_colors.size();
for (size_t i = 0; i < in_out.input_colors.size(); i++) {
if (color_is_equal(in_out.input_colors[i], UNDEFINE_COLOR)) { // not define color range:0~1
in_out.input_colors[i] = convert_to_rgba(m_colours[0]);
}
}
if (in_out.is_single_color && in_out.input_colors.size() >= 1) {
m_cluster_colors_from_algo.emplace_back(in_out.input_colors[0]);
m_cluster_colours.emplace_back(convert_to_wxColour(in_out.input_colors[0]));
m_cluster_labels_from_algo.reserve(m_input_colors_size);
for (size_t i = 0; i < m_input_colors_size; i++) {
m_cluster_labels_from_algo.emplace_back(0);
}
m_cluster_map_filaments.resize(m_cluster_colors_from_algo.size());
m_new_add_colors.resize(m_cluster_map_filaments.size());
m_color_num_recommend = m_color_cluster_num_by_algo = m_cluster_colors_from_algo.size();
} else {//cluster deal
deal_algo(-1);
}
//end first cluster
//draw ui
auto sizer_width = FromDIP(300);
// Create two switched panels with their own sizers
m_sizer_simple = new wxBoxSizer(wxVERTICAL);
m_page_simple = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
m_page_simple->SetSizer(m_sizer_simple);
m_page_simple->SetBackgroundColour(*wxWHITE);
update_ui(m_page_simple);
// BBS
m_sizer_simple->AddSpacer(FromDIP(10));
// BBS: for tunning flush volumes
{
//color cluster results
wxBoxSizer * specify_cluster_sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText *specify_color_cluster_title = new wxStaticText(m_page_simple, wxID_ANY, _L("Specify number of colors:"));
specify_color_cluster_title->SetFont(Label::Head_14);
specify_cluster_sizer->Add(specify_color_cluster_title, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
m_color_cluster_num_by_user_ebox = new SpinInput(m_page_simple, "", wxEmptyString, wxDefaultPosition, wxSize(FromDIP(60), -1), wxTE_PROCESS_ENTER);
m_color_cluster_num_by_user_ebox->SetValue(std::to_string(m_color_cluster_num_by_algo).c_str());
m_color_cluster_num_by_user_ebox->SetToolTip(_L("Enter or click the adjustment button to modify number again"));
{//event
auto on_apply_color_cluster_text_modify = [this](wxEvent &e) {
int number = m_color_cluster_num_by_user_ebox->GetValue();
if (number > m_color_num_recommend || number < g_min_cluster_color) {
number = number < g_min_cluster_color ? g_min_cluster_color : m_color_num_recommend;
auto str = wxString::Format(("%d"), number);
m_color_cluster_num_by_user_ebox->SetValue(str);
// m_color_cluster_num_by_user_ebox->SetInsertionPointEnd();
}
if (m_last_cluster_num != number) {
wxBusyCursor busy;
deal_algo(number, true);
Layout();
Refresh();
Update();
m_last_cluster_num = number;
}
};
m_color_cluster_num_by_user_ebox->Bind(wxEVT_TEXT_ENTER, on_apply_color_cluster_text_modify);
m_color_cluster_num_by_user_ebox->Bind(wxEVT_SPINCTRL, on_apply_color_cluster_text_modify);
m_color_cluster_num_by_user_ebox->Bind(wxEVT_CHAR, [](wxKeyEvent &e) {
int keycode = e.GetKeyCode();
wxString input_char = wxString::Format("%c", keycode);
long value;
if (!input_char.ToLong(&value))
return;
e.Skip();
});
}
specify_cluster_sizer->AddSpacer(FromDIP(2));
specify_cluster_sizer->Add(m_color_cluster_num_by_user_ebox, 0, wxALIGN_CENTER | wxALL, 0);
specify_cluster_sizer->AddSpacer(FromDIP(15));
wxStaticText *recommend_color_cluster_title = new wxStaticText(m_page_simple, wxID_ANY, "(" + std::to_string(m_color_num_recommend) + " " + _L("Recommended ") + ")");
specify_cluster_sizer->Add(recommend_color_cluster_title, 0, wxALIGN_CENTER | wxALL, 0);
m_sizer_simple->Add(specify_cluster_sizer, 0, wxEXPAND | wxLEFT, FromDIP(20));
{//add combox
auto icon_sizer = new wxBoxSizer(wxHORIZONTAL);
auto plater = wxGetApp().plater();
{
auto mo = m_obj_in_out.model->objects[0];
mo->add_instance();
auto mv = mo->volumes[0];
m_thumbnail_offset = Slic3r::Vec3d::Zero();
auto box = mo->bounding_box_exact();
if (box.min.x() < 0 || box.min.y() < 0 || box.min.z() < 0) {
m_thumbnail_offset = Slic3r::Vec3d(box.min.x() < 0 ? -box.min.x() : 0, box.min.y() < 0 ? -box.min.y() : 0, box.min.z() < 0 ? -box.min.z() : 0);
mv->translate(m_thumbnail_offset);
}
}
wxStaticText *combox_title = new wxStaticText(m_page_simple, wxID_ANY, _L("view"));
auto cur_combox = new ComboBox(m_page_simple, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(100), -1), 0, NULL, wxCB_READONLY);
wxArrayString choices = get_all_camera_view_type();
for (size_t i = 0; i < choices.size(); i++) { cur_combox->Append(choices[i]); }
cur_combox->SetSelection(0);
cur_combox->Bind(wxEVT_COMBOBOX, [this](auto &e) {
set_view_angle_type(e.GetSelection());
Layout();
Fit();
});
// add image
wxImage image(IMAGE_SIZE_WIDTH, IMAGE_SIZE_WIDTH);
image.InitAlpha();
for (unsigned int r = 0; r < IMAGE_SIZE_WIDTH; ++r) {
for (unsigned int c = 0; c < IMAGE_SIZE_WIDTH; ++c) {
image.SetRGB((int) c, (int) r, 0, 255, 0);
image.SetAlpha((int) c, (int) r, 255);
}
}
m_image_button = new wxButton(m_page_simple, wxID_ANY, {}, wxDefaultPosition, wxSize(FromDIP(IMAGE_SIZE_WIDTH), FromDIP(IMAGE_SIZE_WIDTH)),
wxBORDER_NONE | wxBU_AUTODRAW);
m_image_button->SetBitmap(image);
m_image_button->SetCanFocus(false);
#ifdef __WXGTK__
RemoveButtonBorder(m_image_button);
#endif
icon_sizer->Add(m_image_button, 0, wxEXPAND | wxALL,
FromDIP(0)); // wxEXPAND | wxALL
cur_combox->Raise();//for mac
m_sizer_simple->Add(icon_sizer, FromDIP(0), wxALIGN_CENTER | wxALL, FromDIP(0));
auto view_sizer = new wxBoxSizer(wxHORIZONTAL);
view_sizer->Add(combox_title, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
view_sizer->Add(cur_combox , 0, wxALIGN_CENTER | wxALL, FromDIP(5));
m_sizer_simple->Add(view_sizer, 0, wxALIGN_RIGHT | wxRIGHT, FromDIP(20));
}
wxBoxSizer * current_filaments_title_sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText *current_filaments_title = new wxStaticText(m_page_simple, wxID_ANY, _L("Current filament colors"));
current_filaments_title->SetFont(Label::Head_14);
current_filaments_title_sizer->Add(current_filaments_title, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
m_sizer_simple->Add(current_filaments_title_sizer, 0, wxEXPAND | wxLEFT, FromDIP(20));
int current_filament_row = 1;
int current_filament_col = 1;
const int one_row_max = 16;
if (m_colours.size() > 1) {
current_filament_row = (m_colours.size() - 1) / one_row_max + 1;
if (current_filament_row >= 2) {
current_filament_col = one_row_max;
} else {
current_filament_col = m_colours.size();
}
}
m_sizer_current_filaments = new wxBoxSizer(wxHORIZONTAL);
wxGridSizer *current_filaments_sizer = new wxGridSizer(current_filament_row, current_filament_col, FromDIP(5), FromDIP(7)); //(int rows, int cols, int vgap, int hgap );
for (size_t i = 0; i < m_colours.size(); i++) {
auto extruder_icon_sizer = create_extruder_icon_and_rgba_sizer(m_page_simple, i, m_colours[i]);
current_filaments_sizer->Add(extruder_icon_sizer, 0, wxALIGN_LEFT);
}
m_sizer_current_filaments->AddSpacer(FromDIP(1));
m_sizer_current_filaments->Add(current_filaments_sizer, 0, wxEXPAND);
m_sizer_current_filaments->AddStretchSpacer();
m_sizer_simple->Add(m_sizer_current_filaments, 0, wxEXPAND | wxLEFT, FromDIP(25));
//colors table title
wxBoxSizer * matching_title_sizer = new wxBoxSizer(wxHORIZONTAL);
matching_title_sizer->AddSpacer(FromDIP(25));
wxStaticText *matching_title = new wxStaticText(m_page_simple, wxID_ANY, _L("Matching"));
matching_title->SetFont(Label::Head_14);
matching_title_sizer->Add(matching_title, 0, wxEXPAND , 0);
m_sizer_simple->Add(matching_title_sizer, 0, wxEXPAND | wxTOP, FromDIP(15));// wxTop has FromDIP(10) margin
//new color table
m_scrolledWindow = new wxScrolledWindow(m_page_simple, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
m_scrolledWindow->SetScrollRate(0, FromDIP(20));
m_scrolledWindow->EnableScrolling(false, true);
m_scrolledWindow->ShowScrollbars(wxScrollbarVisibility::wxSHOW_SB_NEVER, wxScrollbarVisibility::wxSHOW_SB_DEFAULT);
draw_new_table();
m_sizer_simple->Add(m_scrolledWindow, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(15));
//buttons
wxBoxSizer *quick_set_sizer = new wxBoxSizer(wxHORIZONTAL);
quick_set_sizer->AddSpacer(FromDIP(25));
wxStaticText *quick_set_title = new wxStaticText(m_page_simple, wxID_ANY, _L("Quick set"));
quick_set_title->SetFont(Label::Head_12);
quick_set_sizer->Add(quick_set_title, 0, wxALIGN_CENTER | wxALL, 0);
quick_set_sizer->AddSpacer(FromDIP(10));
auto calc_approximate_match_btn_sizer = create_approximate_match_btn_sizer(m_page_simple);
auto calc_add_btn_sizer = create_add_btn_sizer(m_page_simple);
auto calc_reset_btn_sizer = create_reset_btn_sizer(m_page_simple);
quick_set_sizer->Add(calc_add_btn_sizer, 0, wxALIGN_CENTER | wxALL, 0);
quick_set_sizer->AddSpacer(FromDIP(10));
quick_set_sizer->Add(calc_approximate_match_btn_sizer, 0, wxALIGN_CENTER | wxALL, 0);
quick_set_sizer->AddSpacer(FromDIP(10));
quick_set_sizer->Add(calc_reset_btn_sizer, 0, wxALIGN_CENTER | wxALL, 0);
quick_set_sizer->AddSpacer(FromDIP(10));
m_sizer_simple->Add(quick_set_sizer, 0, wxEXPAND | wxTOP, FromDIP(10));
wxBoxSizer *warning_sizer = new wxBoxSizer(wxHORIZONTAL);
m_warning_text = new wxStaticText(m_page_simple, wxID_ANY, "");
m_warning_text->SetForegroundColour(wxColour(107, 107, 107, 100));
warning_sizer->Add(m_warning_text, 0, wxALIGN_CENTER | wxALL, 0);
m_sizer_simple->Add(warning_sizer, 0, wxEXPAND | wxLEFT, FromDIP(25));
m_sizer_simple->AddSpacer(15);
}
deal_default_strategy();
deal_thumbnail();
//page_simple//page_advanced
m_sizer = new wxBoxSizer(wxVERTICAL);
m_sizer->Add(m_page_simple, 0, wxEXPAND, 0);
m_sizer->SetSizeHints(this);
SetSizer(m_sizer);
this->Layout();
}
ObjColorPanel::~ObjColorPanel() {
}
void ObjColorPanel::msw_rescale()
{
for (unsigned int i = 0; i < m_extruder_icon_list.size(); ++i) {
auto bitmap = *get_extruder_color_icon(m_colours[i].GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(i + 1), FromDIP(16), FromDIP(16));
m_extruder_icon_list[i]->SetBitmap(bitmap);
}
/* for (unsigned int i = 0; i < m_color_cluster_icon_list.size(); ++i) {
auto bitmap = *get_extruder_color_icon(m_cluster_colours[i].GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(i + 1), FromDIP(16), FromDIP(16));
m_color_cluster_icon_list[i]->SetBitmap(bitmap);
}*/
}
bool ObjColorPanel::is_ok() {
for (auto item : m_result_icon_list) {
if (item->bitmap_combox->IsShown()) {
auto selection = item->bitmap_combox->GetSelection();
if (selection < 1) {
return false;
}
}
}
return true;
}
void ObjColorPanel::send_new_filament_to_ui()
{
update_new_add_final_colors();
if (m_is_add_filament) {
for (auto c : m_new_add_final_colors) {
/*auto evt = new ColorEvent(EVT_ADD_CUSTOM_FILAMENT, c);
wxQueueEvent(wxGetApp().plater(), evt);*/
wxGetApp().sidebar().add_custom_filament(c);
}
}
}
void ObjColorPanel::cancel_paint_color() {
m_filament_ids.clear();
auto mo = m_obj_in_out.model->objects[0];
mo->config.set("extruder", 1);
clear_instance_and_revert_offset();
auto mv = mo->volumes[0];
mv->mmu_segmentation_facets.reset();
mv->config.set("extruder", 1);
m_first_extruder_id = 1;
}
void ObjColorPanel::update_filament_ids()
{
//deal m_filament_ids
m_filament_ids.clear();
m_filament_ids.reserve(m_input_colors_size);
for (size_t i = 0; i < m_input_colors_size; i++) {
auto label = m_cluster_labels_from_algo[i];
if (m_cluster_map_filaments[label] > 0) {
m_filament_ids.emplace_back(m_cluster_map_filaments[label]);
} else {
m_filament_ids.emplace_back(1);//min filament_id is 1
}
}
m_first_extruder_id = m_cluster_map_filaments[0];
}
void ObjColorPanel::set_layout_callback(LayoutChanggeCallback callback) {
m_layout_callback = callback;
}
void ObjColorPanel::do_layout_callback() {
if (m_layout_callback) {
m_layout_callback();
}
}
wxBoxSizer *ObjColorPanel::create_approximate_match_btn_sizer(wxWindow *parent)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
m_quick_approximate_match_btn = new Button(parent, _L("Color match"));
m_quick_approximate_match_btn->SetToolTip(_L("Approximate color matching."));
m_quick_approximate_match_btn->SetStyle(ButtonStyle::Regular, ButtonType::Window);
m_quick_approximate_match_btn->SetFocus();
btn_sizer->Add(m_quick_approximate_match_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 0);
m_quick_approximate_match_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
deal_approximate_match_btn();
deal_thumbnail();
});
return btn_sizer;
}
wxBoxSizer *ObjColorPanel::create_add_btn_sizer(wxWindow *parent)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
m_quick_add_btn = new Button(parent, _L("Append"));
m_quick_add_btn->SetToolTip(_L("Append to existing filaments"));
auto cur_btn = m_quick_add_btn;
cur_btn->SetStyle(ButtonStyle::Regular, ButtonType::Window);
cur_btn->SetFocus();
btn_sizer->Add(cur_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 0);
cur_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
deal_add_btn();
deal_thumbnail();
});
m_quick_add_btn->Enable(m_semm); // only SEMM printer can add more colors
return btn_sizer;
}
wxBoxSizer *ObjColorPanel::create_reset_btn_sizer(wxWindow *parent)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
m_quick_reset_btn = new Button(parent, _L("Reset"));
m_quick_reset_btn->SetToolTip(_L("Reset mapped extruders."));
auto cur_btn = m_quick_reset_btn;
cur_btn->SetStyle(ButtonStyle::Regular, ButtonType::Window);
cur_btn->SetFocus();
btn_sizer->Add(cur_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 0);
cur_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
deal_reset_btn();
deal_thumbnail();
});
return btn_sizer;
}
wxBoxSizer *ObjColorPanel::create_extruder_icon_and_rgba_sizer(wxWindow *parent, int id, const wxColour &color)
{
auto icon_sizer = new wxBoxSizer(wxHORIZONTAL);
wxButton *icon = new wxButton(parent, wxID_ANY, {}, wxDefaultPosition, FromDIP(wxSize(20,20)), wxBORDER_NONE | wxBU_AUTODRAW);
icon->SetBitmap(*get_extruder_color_icon(color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(id + 1), FromDIP(20), FromDIP(20)));
icon->SetCanFocus(false);
#ifdef __WXGTK__
RemoveButtonBorder(icon);
#endif
m_extruder_icon_list.emplace_back(icon);
icon_sizer->Add(icon, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0); // wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM
//icon_sizer->AddSpacer(FromDIP(5));
return icon_sizer;
}
std::string ObjColorPanel::get_color_str(const wxColour &color) {
std::string str = ("R:" + std::to_string(color.Red()) +
std::string(" G:") + std::to_string(color.Green()) +
std::string(" B:") + std::to_string(color.Blue()) +
std::string(" A:") + std::to_string(color.Alpha()));
return str;
}
ComboBox *ObjColorPanel::CreateEditorCtrl(wxWindow *parent, int id) // wxRect labelRect,, const wxVariant &value
{
std::vector<wxBitmap *> icons = get_extruder_color_icons();
const double em = Slic3r::GUI::wxGetApp().em_unit();
bool thin_icon = false;
const int icon_width = lround((thin_icon ? 2 : 4.4) * em);
const int icon_height = lround(2 * em);
m_combox_icon_width = icon_width;
m_combox_icon_height = icon_height;
icons.insert(icons.begin(), get_extruder_color_icon(g_undefined_color_in_obj.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(-1), icon_width, icon_height));
if (icons.empty())
return nullptr;
::ComboBox *c_editor = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr,
wxCB_READONLY | CB_NO_DROP_ICON | CB_NO_TEXT);
c_editor->SetMinSize(wxSize(icon_width + FromDIP(8), -1)); // match size with bitmap
c_editor->SetMaxSize(wxSize(icon_width + FromDIP(8), -1)); // match size with bitmap
c_editor->GetDropDown().SetUseContentWidth(false);
for (size_t i = 0; i < icons.size(); i++) {
c_editor->Append(wxString::Format("%d", i), *icons[i]);
if (i == 0) {
c_editor->SetItemTooltip(i, g_undefined_color_in_obj.GetAsString(wxC2S_HTML_SYNTAX));
} else {
c_editor->SetItemTooltip(i, m_colours[i-1].GetAsString(wxC2S_HTML_SYNTAX));
}
}
c_editor->SetSelection(0);
c_editor->SetName(wxString::Format("%d", id));
c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
auto *com_box = static_cast<ComboBox *>(evt.GetEventObject());
int i = atoi(com_box->GetName().c_str());
if (i < m_cluster_map_filaments.size()) {
m_cluster_map_filaments[i] = com_box->GetSelection();
m_new_add_colors[i] = com_box->GetItemTooltip(com_box->GetSelection());
deal_thumbnail();
}
evt.StopPropagation();
});
return c_editor;
}
void ObjColorPanel::deal_approximate_match_btn()
{
m_warning_text->SetLabelText("");
if (m_result_icon_list.size() == 0) { return; }
auto map_count = m_result_icon_list[0]->bitmap_combox->GetCount() -1;
if (map_count < 1) { return; }
for (size_t i = 0; i < m_cluster_colours.size(); i++) {
auto c = m_cluster_colours[i];
std::vector<ColorDistValue> color_dists;
color_dists.resize(map_count);
for (size_t j = 0; j < map_count; j++) {
auto tip_color = m_result_icon_list[0]->bitmap_combox->GetItemTooltip(j+1);
wxColour candidate_c(tip_color);
color_dists[j].distance = calc_color_distance(c, candidate_c);
color_dists[j].id = j + 1;
}
std::sort(color_dists.begin(), color_dists.end(), [](ColorDistValue &a, ColorDistValue& b) {
return a.distance < b.distance;
});
auto new_index= color_dists[0].id;
m_result_icon_list[i]->bitmap_combox->SetSelection(new_index);
m_new_add_colors[i] = m_result_icon_list[i]->bitmap_combox->GetItemTooltip(new_index);
m_cluster_map_filaments[i] = new_index;
}
}
void ObjColorPanel::show_sizer(wxSizer *sizer, bool show)
{
wxSizerItemList items = sizer->GetChildren();
for (wxSizerItemList::iterator it = items.begin(); it != items.end(); ++it) {
wxSizerItem *item = *it;
if (wxWindow *window = item->GetWindow()) {
window->Show(show);
}
if (wxSizer *son_sizer = item->GetSizer()) {
show_sizer(son_sizer, show);
}
}
}
void ObjColorPanel::draw_new_table()
{
auto cluster_count = m_cluster_colours.size();
auto col = 3;
auto row = cluster_count / col;
auto remainder = cluster_count % col;
if (remainder > 0) {
row++;
}
int old_row = 0;
bool first_draw = false;
if (m_new_grid_sizer) {
old_row = m_new_grid_sizer->GetRows();
m_new_grid_sizer->SetRows(row);
}
else {
first_draw = true;
m_new_grid_sizer = new wxGridSizer(row, 1, 1, 3); //(int rows, int cols, int vgap, int hgap );
m_scrolledWindow->SetSizer(m_new_grid_sizer);
}
if (!first_draw) {
for (size_t i = old_row; i < row; i++) { show_sizer(m_row_sizer_list[i], true); }
for (size_t i = row; i < old_row; i++) { show_sizer(m_row_sizer_list[i], false); }
for (size_t i = 0; i < cluster_count; i++) { show_sizer(m_row_col_boxsizer_list[i], true); }
for (size_t i = cluster_count; i < m_color_num_recommend; i++) { show_sizer(m_row_col_boxsizer_list[i], false); }
for (size_t ii = 0; ii < row; ii++) {
for (size_t j = 0; j < col; j++) {
auto id = ii * col + j;
if (id >= cluster_count) {
break;
}
auto color = m_cluster_colours[id];
m_color_cluster_icon_list[id]->SetBitmap(*get_extruder_color_icon(color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), "", FromDIP(16), FromDIP(16)));
}
}
}
else {//first draw
for (size_t ii = 0; ii < row; ii++) {
wxPanel *row_panel = new wxPanel(m_scrolledWindow);
//row_panel->SetBackgroundColour(ii % 2 == 0 ? *wxWHITE : wxColour(238, 238, 238));
auto row_sizer = new wxGridSizer(1, col, 1, 1);
row_panel->SetSizer(row_sizer);
row_panel->SetMinSize(wxSize(FromDIP(PANEL_WIDTH), -1));
row_panel->SetMaxSize(wxSize(FromDIP(PANEL_WIDTH), -1));
for (size_t j = 0; j < col; j++) {
auto id = ii * col + j;
if (id >= cluster_count) {
break;
}
auto cluster_color_icon_sizer = create_color_icon_map_rgba_sizer(row_panel, id, m_cluster_colours[id]);
m_row_col_boxsizer_list.emplace_back(cluster_color_icon_sizer);
row_sizer->Add(cluster_color_icon_sizer, 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL, 0);
}
m_row_sizer_list.emplace_back(row_sizer);
m_new_grid_sizer->Add(row_panel, 0, wxALIGN_LEFT | wxALL, FromDIP(HEADER_BORDER));
}
}
m_new_grid_sizer->Layout();
auto height = m_new_grid_sizer->GetMinSize().GetY();
if (row <= 4 || height < FromDIP(185)) {
m_scrolledWindow->SetMinSize(wxSize(-1, height));
} else {
m_scrolledWindow->SetMinSize(wxSize(-1, FromDIP(185)));
m_scrolledWindow->SetMaxSize(wxSize(-1, FromDIP(185)));
}
m_scrolledWindow->FitInside();
if (!first_draw) {
do_layout_callback();
}
}
void ObjColorPanel::update_new_add_final_colors()
{
m_new_add_final_colors = m_new_add_colors;
if (!m_cluster_map_filaments.empty()) {
m_max_filament_index = *std::max_element(m_cluster_map_filaments.begin(), m_cluster_map_filaments.end());
} else {
m_max_filament_index = 0;
}
if (m_max_filament_index <= m_colours.size()) { // Fix 20240904
m_new_add_final_colors.clear();
}
else {
m_new_add_final_colors.resize(m_max_filament_index - m_colours.size());
for (int ii = m_colours.size() ; ii < m_max_filament_index; ii++) {
for (int j = 0; j < m_cluster_map_filaments.size(); j++) {
if (m_cluster_map_filaments[j] == (ii+ 1) && j < m_new_add_colors.size()) {
auto index = ii - m_colours.size();
if (index < m_new_add_final_colors.size()) {
m_new_add_final_colors[index] = m_new_add_colors[j];
}
}
}
}
}
if (m_new_add_final_colors.size() > 0) {
m_is_add_filament = true;
}
}
void ObjColorPanel::deal_algo(char cluster_number, bool redraw_ui)
{
if (m_last_cluster_number == cluster_number) {
return;
}
wxBusyCursor cursor;
m_last_cluster_number = cluster_number;
obj_color_deal_algo(m_input_colors, m_cluster_colors_from_algo, m_cluster_labels_from_algo, cluster_number,g_max_color);
m_cluster_colours.clear();
m_cluster_colours.reserve(m_cluster_colors_from_algo.size());
for (size_t i = 0; i < m_cluster_colors_from_algo.size(); i++) {
m_cluster_colours.emplace_back(convert_to_wxColour(m_cluster_colors_from_algo[i]));
}
if (m_cluster_colours.size() == 0) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",m_cluster_colours.size() = 0\n";
return;
}
m_cluster_map_filaments.resize(m_cluster_colors_from_algo.size());
m_new_add_colors.resize(m_cluster_map_filaments.size());
m_color_cluster_num_by_algo = m_cluster_colors_from_algo.size();
if (cluster_number == -1) {
m_color_num_recommend = m_color_cluster_num_by_algo;
}
//redraw ui
if (redraw_ui) {
deal_reset_btn();
draw_new_table();
deal_default_strategy();
deal_thumbnail();
}
}
void ObjColorPanel::deal_default_strategy()
{
bool is_exceed = deal_add_btn();
if (!is_exceed) {
deal_approximate_match_btn();
}
m_warning_text->SetLabelText(_L("Note") + ": " + _L("The color has been selected, you can choose OK \n to continue or manually adjust it."));
}
void ObjColorPanel::deal_thumbnail() {
update_filament_ids();
// generate model volume
if (m_obj_in_out.deal_vertex_color) {
if (m_obj_in_out.filament_ids.size() > 0) {
m_deal_thumbnail_flag = Model::obj_import_vertex_color_deal(m_obj_in_out.filament_ids, m_obj_in_out.first_extruder_id, m_obj_in_out.model);
}
} else {
if (m_obj_in_out.filament_ids.size() > 0) {
m_deal_thumbnail_flag = Model::obj_import_face_color_deal(m_obj_in_out.filament_ids, m_obj_in_out.first_extruder_id, m_obj_in_out.model);
}
}
generate_thumbnail();
}
void ObjColorPanel::generate_thumbnail()
{
if (m_deal_thumbnail_flag && m_obj_in_out.model->objects.size() == 1) {
std::vector<Slic3r::ColorRGBA> colors = GUI::wxGetApp().plater()->get_extruders_colors();
for (size_t i = 0; i < m_new_add_colors.size(); i++) {
Slic3r::ColorRGBA temp_color;
temp_color[0] = m_new_add_colors[i].Red() / 255.f;
temp_color[1] = m_new_add_colors[i].Green() / 255.f;
temp_color[2] = m_new_add_colors[i].Blue() / 255.f;
temp_color[3] = m_new_add_colors[i].Alpha() / 255.f;
colors.emplace_back(temp_color);
}
auto mo = m_obj_in_out.model->objects[0];
wxGetApp().plater()->update_obj_preview_thumbnail(mo, 0, 0, colors, (int) m_camera_view_angle_type);
// get thumbnail image
PartPlate *plate = wxGetApp().plater()->get_partplate_list().get_plate(0);
auto & data = plate->obj_preview_thumbnail_data;
if (data.is_valid()) {
wxImage image(data.width, data.height);
image.InitAlpha();
for (unsigned int r = 0; r < data.height; ++r) {
unsigned int rr = (data.height - 1 - r) * data.width;
for (unsigned int c = 0; c < data.width; ++c) {
unsigned char *px = (unsigned char *) data.pixels.data() + 4 * (rr + c);
image.SetRGB((int) c, (int) r, px[0], px[1], px[2]);
image.SetAlpha((int) c, (int) r, px[3]);
}
}
image = image.Rescale(FromDIP(IMAGE_SIZE_WIDTH), FromDIP(IMAGE_SIZE_WIDTH));
m_image_button->SetBitmap(image);
}
}
else {
#ifdef _WIN32
__debugbreak();
#endif
}
}
void ObjColorPanel::set_view_angle_type(int value)
{
if (value >= 0) {
m_camera_view_angle_type = (Slic3r::GUI::Camera::ViewAngleType) value;
generate_thumbnail();
}
}
void ObjColorPanel::clear_instance_and_revert_offset()
{
auto mo = m_obj_in_out.model->objects[0];
mo->clear_instances();
auto mv = mo->volumes[0];
auto box = mo->bounding_box_exact();
if (!m_thumbnail_offset.isApprox(Slic3r::Vec3d::Zero())) {
mv->translate(-m_thumbnail_offset);
}
}
bool ObjColorPanel::do_show(bool show) {
if (show) {
return true;
} else {
return false;
}
}
bool ObjColorPanel::deal_add_btn()
{
if (!m_semm) { return false; }// only SEMM printer can add more colors
if (m_colours.size() > g_max_color) { return false; }
deal_reset_btn();
std::vector<wxBitmap *> new_icons;
auto new_color_size = m_cluster_colors_from_algo.size();
new_icons.reserve(new_color_size);
m_new_add_colors.clear();
m_new_add_colors.reserve(new_color_size);
int new_index = m_colours.size() + 1;
bool is_exceed = false;
for (size_t i = 0; i < new_color_size; i++) {
if (m_colours.size() + new_icons.size() >= g_max_color) {
is_exceed = true;
break;
}
wxColour cur_color = convert_to_wxColour(m_cluster_colors_from_algo[i]);
m_new_add_colors.emplace_back(cur_color);
new_icons.emplace_back(get_extruder_color_icon(cur_color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(),
std::to_string(new_index), m_combox_icon_width, m_combox_icon_height));
new_index++;
}
new_index = m_colours.size() + 1;
for (size_t i = 0; i < m_result_icon_list.size(); i++) {
auto item = m_result_icon_list[i];
for (size_t k = 0; k < new_icons.size(); k++) {
item->bitmap_combox->Append(wxString::Format("%d", item->bitmap_combox->GetCount()), *new_icons[k]);
item->bitmap_combox->SetItemTooltip(item->bitmap_combox->GetCount() -1,m_new_add_colors[k].GetAsString(wxC2S_HTML_SYNTAX));
}
item->bitmap_combox->SetSelection(new_index);
m_cluster_map_filaments[i] = new_index;
new_index++;
}
if (is_exceed) {
deal_approximate_match_btn();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",Waring:The count of newly added and \n current extruders exceeds 32.";
return true;
}
return false;
}
void ObjColorPanel::deal_reset_btn()
{
for (auto item : m_result_icon_list) {
// delete redundant bitmap
while (item->bitmap_combox->GetCount() > m_colours.size()+ 1) {
item->bitmap_combox->DeleteOneItem(item->bitmap_combox->GetCount() - 1);
}
item->bitmap_combox->SetSelection(0);
}
for (int i = 0; i < m_new_add_colors.size(); i++) {
m_new_add_colors[i] = g_undefined_color_in_obj;
}
m_warning_text->SetLabelText("");
}
wxBoxSizer *ObjColorPanel::create_color_icon_map_rgba_sizer(wxWindow *parent, int id, const wxColour &color)
{
auto icon_sizer = new wxBoxSizer(wxHORIZONTAL);
//icon_sizer->AddSpacer(FromDIP(40));
wxButton *icon = new wxButton(parent, wxID_ANY, {}, wxDefaultPosition, FromDIP(wxSize(20,20)), wxBORDER_NONE | wxBU_AUTODRAW);
icon->SetBitmap(*get_extruder_color_icon(color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), "", FromDIP(20), FromDIP(20)));
icon->SetCanFocus(false);
#ifdef __WXGTK__
RemoveButtonBorder(icon);
#endif
m_color_cluster_icon_list.emplace_back(icon);
icon_sizer->Add(icon, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0); // wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM
icon_sizer->AddSpacer(FromDIP(10));
wxStaticText *map_text = new wxStaticText(parent, wxID_ANY, _L(u8"—> "));
map_text->SetFont(Label::Head_12);
icon_sizer->Add(map_text, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
for (size_t i = m_result_icon_list.size(); i < id + 1; i++) {
m_result_icon_list.emplace_back(new ButtonState());
}
m_result_icon_list[id]->bitmap_combox = CreateEditorCtrl(parent, id);
icon_sizer->Add(m_result_icon_list[id]->bitmap_combox, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
return icon_sizer;
}
-136
View File
@@ -1,136 +0,0 @@
#ifndef _OBJ_COLOR_DIALOG_H_
#define _OBJ_COLOR_DIALOG_H_
#include "GUI_Utils.hpp"
#include "Camera.hpp"
#include "GuiColor.hpp"
#include "libslic3r/Format/OBJ.hpp"
#include <wx/panel.h>
#include <vector>
#include <string>
#include <functional>
#include <wx/colour.h>
#include <wx/scrolwin.h>
#include "libslic3r/Color.hpp"
#include "libslic3r/Point.hpp"
#include <unordered_map>
#include <wx/sizer.h>
#include <wx/spinctrl.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include <wx/msgdlg.h>
#include "Widgets/SpinInput.hpp"
class Button;
class Label;
class ComboBox;
class ObjColorPanel : public wxPanel
{
public:
// BBS
ObjColorPanel(wxWindow *parent, Slic3r::ObjDialogInOut &in_out, const std::vector<std::string> &extruder_colours, bool semm);
~ObjColorPanel();
void msw_rescale();
bool is_ok();
void send_new_filament_to_ui();
void cancel_paint_color();
void update_filament_ids();
struct ButtonState
{
ComboBox* bitmap_combox{nullptr};
bool is_map{false};//int id{0};
};
typedef std::function<void()> LayoutChanggeCallback;
void set_layout_callback(LayoutChanggeCallback);
void do_layout_callback();
bool do_show(bool show);
void clear_instance_and_revert_offset();
private:
wxBoxSizer *create_approximate_match_btn_sizer(wxWindow *parent);
wxBoxSizer *create_add_btn_sizer(wxWindow *parent);
wxBoxSizer *create_reset_btn_sizer(wxWindow *parent);
wxBoxSizer *create_extruder_icon_and_rgba_sizer(wxWindow *parent, int id, const wxColour& color);
std::string get_color_str(const wxColour &color);
wxBoxSizer *create_color_icon_map_rgba_sizer(wxWindow *parent, int id, const wxColour &color);//for display map
ComboBox* CreateEditorCtrl(wxWindow *parent,int id);
void draw_new_table();
void update_new_add_final_colors();
void show_sizer(wxSizer *sizer, bool show);
void deal_approximate_match_btn();
bool deal_add_btn();
void deal_reset_btn();
void deal_algo(char cluster_number,bool redraw_ui =false);
void deal_default_strategy();
void deal_thumbnail();
void generate_thumbnail();
void set_view_angle_type(int);
private:
//view ui
Slic3r::ObjDialogInOut & m_obj_in_out;
Slic3r::GUI::Camera::ViewAngleType m_camera_view_angle_type{Slic3r::GUI::Camera::ViewAngleType::Iso};
wxPanel * m_page_simple = nullptr;
wxBoxSizer * m_sizer = nullptr;
wxBoxSizer * m_sizer_simple = nullptr;
wxBoxSizer * m_sizer_current_filaments = nullptr;
SpinInput * m_color_cluster_num_by_user_ebox{nullptr};
wxStaticText * m_warning_text{nullptr};
wxGridSizer * m_new_grid_sizer{nullptr};
wxScrolledWindow * m_scrolledWindow{nullptr};
Button * m_quick_approximate_match_btn{nullptr};
Button * m_quick_add_btn{nullptr};
Button * m_quick_reset_btn{nullptr};
std::vector<wxButton*> m_extruder_icon_list;
std::vector<wxButton*> m_color_cluster_icon_list;//need modeify
std::vector<wxStaticText*> m_color_cluster_text_list;//need modeify
std::vector<wxGridSizer*> m_row_sizer_list; // control show or not
std::vector<wxBoxSizer *> m_row_col_boxsizer_list;
std::vector<ButtonState*> m_result_icon_list;
int m_last_cluster_num{-1};
int m_combox_icon_width;
int m_combox_icon_height;
wxButton * m_image_button = nullptr;
LayoutChanggeCallback m_layout_callback;
//data
char m_last_cluster_number{-2};
std::vector<Slic3r::RGBA>& m_input_colors;
int m_color_num_recommend{0};
int m_color_cluster_num_by_algo{0};
int m_input_colors_size{0};
bool m_semm; // only SEMM printer can add more colors
std::vector<wxColour> m_colours;//from project and show right
std::vector<int> m_cluster_map_filaments;//show middle
int m_max_filament_index = 0;
std::vector<wxColour> m_cluster_colours;//from_algo and show left
bool m_deal_thumbnail_flag{false};
std::vector<wxColour> m_new_add_colors;
std::vector<wxColour> m_new_add_final_colors;
//algo result
std::vector<Slic3r::RGBA> m_cluster_colors_from_algo;
std::vector<int> m_cluster_labels_from_algo;
//result
bool m_is_add_filament{false};
unsigned char& m_first_extruder_id;
std::vector<unsigned char> &m_filament_ids;
Slic3r::Vec3d m_thumbnail_offset;
};
class ObjColorDialog : public Slic3r::GUI::DPIDialog
{
public:
ObjColorDialog(wxWindow *parent, Slic3r::ObjDialogInOut &in_out, const std::vector<std::string> &extruder_colours, bool semm);
wxBoxSizer *create_btn_sizer(long flags, bool exist_error);
void on_dpi_changed(const wxRect &suggested_rect) override;
void update_layout();
bool Show(bool show) override;
private:
ObjColorPanel* m_panel_ObjColor = nullptr;
wxBoxSizer * m_main_sizer = nullptr;
wxBoxSizer * m_buttons_sizer = nullptr;
std::unordered_map<int, Button *> m_button_list;
};
#endif // _WIPE_TOWER_DIALOG_H_
+2 -21
View File
@@ -273,7 +273,6 @@
#include "ColorDecomposeSupport.hpp"
#include "FilamentBitmapUtils.hpp"
#include "libslic3r/FilamentMixer.hpp"
#include "ObjColorDialog.hpp"
#include "libslic3r/CustomGCode.hpp"
#include "libslic3r/Platform.hpp"
@@ -9775,16 +9774,6 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
bool is_xxx;
Semver file_version;
//ObjImportColorFn obj_color_fun=nullptr;
auto obj_color_fun = [&path](ObjDialogInOut &in_out) {
if (!boost::iends_with(path.string(), ".obj")) { return; }
const std::vector<std::string> extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config();
ObjColorDialog color_dlg(nullptr, in_out, extruder_colours, Sidebar::should_show_SEMM_buttons());
if (color_dlg.ShowModal() != wxID_OK) {
in_out.filament_ids.clear();
}
};
if (boost::iends_with(path.string(), ".stp") ||
boost::iends_with(path.string(), ".step")) {
double linear = string_to_double_decimal_point(wxGetApp().app_config->get("linear_deflection"));
@@ -9851,7 +9840,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
cont = dlg.Update(progress_percent, msg);
cancel = !cont;
},
nullptr, 0, obj_color_fun);
nullptr, 0);
}
if (designer_model_id.empty() && boost::algorithm::iends_with(path.string(), ".stl")) {
@@ -12143,14 +12132,6 @@ void Plater::priv::reload_from_disk()
// load one file at a time
for (size_t i = 0; i < input_paths.size(); ++i) {
const auto& path = input_paths[i].string();
auto obj_color_fun = [&path](ObjDialogInOut &in_out) {
if (!boost::iends_with(path, ".obj")) { return; }
const std::vector<std::string> extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config();
ObjColorDialog color_dlg(nullptr, in_out, extruder_colours, Sidebar::should_show_SEMM_buttons());
if (color_dlg.ShowModal() != wxID_OK) {
in_out.filament_ids.clear();
}
};
wxBusyCursor wait;
wxBusyInfo info(_L("Reload from:") + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
@@ -12170,7 +12151,7 @@ void Plater::priv::reload_from_disk()
bool is_split = wxGetApp().app_config->get_bool("is_split_compound");
new_model = Model::read_from_step(path, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, nullptr, nullptr, nullptr, linear, angle, is_split);
}else {
new_model = Model::read_from_file(path, nullptr, nullptr, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, &plate_data, &project_presets, nullptr, nullptr, nullptr, nullptr, nullptr, 0, obj_color_fun);
new_model = Model::read_from_file(path, nullptr, nullptr, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, &plate_data, &project_presets, nullptr, nullptr, nullptr, nullptr, nullptr, 0);
}
@@ -239,12 +239,15 @@ void NotificationManager::SlicingProgressNotification::render(GLCanvas3D& canvas
const float gcodeviewer_height = wxGetApp().plater()->get_preview_canvas3D()->get_gcode_viewer().get_legend_height();
//const float dailytips_panel_height = std::min(380.0f * scale, std::max(90.0f, (cnv_size.get_height() - gcodeviewer_height - progress_panel_height - dailytips_child_window_padding.y - initial_y - m_line_height * 4)));
const float dailytips_panel_height = 125.0f * scale;
const ImVec2 dailytips_size = ImVec2(dailytips_panel_width, dailytips_panel_height);
//Orca: Update the child's effective height before positioning this parent window.
m_dailytips_panel->set_size(dailytips_size);
float right_gap = right_margin + (move_from_overlay ? overlay_width + m_line_height * 5 : 0);
m_window_pos = ImVec2((float)cnv_size.get_width() - right_gap - m_window_width, (float)cnv_size.get_height() - m_top_y);
imgui.set_next_window_pos(m_window_pos.x, m_window_pos.y, ImGuiCond_Always, 0.0f, 0.0f);
m_window_height = progress_panel_height + m_dailytips_panel->get_size().y + progress_child_window_padding.y + dailytips_child_window_padding.y + bottom_padding.y;
m_top_y = initial_y + m_window_height;
m_window_pos = ImVec2((float)cnv_size.get_width() - right_gap - m_window_width, (float)cnv_size.get_height() - m_top_y);
imgui.set_next_window_pos(m_window_pos.x, m_window_pos.y, ImGuiCond_Always, 0.0f, 0.0f);
ImGui::SetNextWindowSizeConstraints(ImVec2(m_window_width, m_window_height), ImVec2(m_window_width, m_window_height));
// name of window indentifies window - has to be unique string
@@ -307,9 +310,7 @@ void NotificationManager::SlicingProgressNotification::render(GLCanvas3D& canvas
child_name = "##DailyTipsPanel" + std::to_string(parent_window->ID);
ImVec2 dailytips_pos = ImGui::GetCursorScreenPos() + dailytips_child_window_padding;
ImVec2 dailytips_size = ImVec2(dailytips_panel_width, dailytips_panel_height);
m_dailytips_panel->set_position(dailytips_pos);
m_dailytips_panel->set_size(dailytips_size);
m_dailytips_panel->set_fade_opacity(m_current_fade_opacity);
ImGui::SetNextWindowPos(dailytips_pos);
if (ImGui::BeginChild(child_name.c_str(), ImVec2(dailytips_panel_width, dailytips_panel_height), false, child_window_flags)) {
@@ -504,4 +505,4 @@ void NotificationManager::SlicingProgressNotification::render_progress_close_but
}
}
}}
}}
@@ -566,6 +566,12 @@ struct SlicedCage
using Walls = std::vector<std::vector<ProcessedPoint>>;
bool has_curled_lines(const PrintObject &object)
{
const auto layers = object.layers();
return std::any_of(layers.begin(), layers.end(), [](const Layer *layer) { return !layer->curled_lines.empty(); });
}
// Estimates every wall of `layer` against whatever layer `estimator` was last prepared with before it.
Walls estimate_walls(ExtrusionQualityEstimator &estimator, const PrintObject *object, const Layer &layer)
{
@@ -707,6 +713,53 @@ TEST_CASE("Precomputed overhang data has the curled-line tree exactly when a reg
CHECK((precomputed.front().lower_curled_lines != nullptr) == slowdown);
}
TEST_CASE("Curled walls are estimated only when overhang speed and the slowdown for curled perimeters are both on", "[ExtrusionProcessor]")
{
const auto [overhang_speed, slowdown, estimated] = GENERATE(table<bool, bool, bool>({
{true, true, true},
{true, false, false},
{false, true, false},
}));
DynamicPrintConfig config = caged_overhang_config("classic");
config.set_deserialize_strict({{"enable_overhang_speed", overhang_speed ? "1" : "0"},
{"slowdown_for_curled_perimeters", slowdown ? "1" : "0"}});
const SlicedCage cage(config);
CHECK(has_curled_lines(*cage.object) == estimated);
}
TEST_CASE("Curled walls are estimated when overhang speed and the slowdown for curled perimeters are on in different objects", "[ExtrusionProcessor]")
{
const std::vector<std::vector<ConfigBase::SetDeserializeItem>> overrides{
{{"enable_overhang_speed", "1"}, {"slowdown_for_curled_perimeters", "0"}},
{{"enable_overhang_speed", "0"}, {"slowdown_for_curled_perimeters", "1"}},
};
Print print;
Model model;
init_print(std::vector<TriangleMesh>{caged_overhang_mesh(), caged_overhang_mesh()}, print, model, caged_overhang_config("classic"),
&overrides);
print.process();
REQUIRE(print.objects().size() == 2);
for (const PrintObject *object : print.objects())
CHECK(has_curled_lines(*object));
}
TEST_CASE("Curled walls from an earlier slice are dropped once overhang speed is off", "[ExtrusionProcessor]")
{
DynamicPrintConfig config = caged_overhang_config("classic");
config.set_deserialize_strict("slowdown_for_curled_perimeters", "1");
SlicedCage cage(config);
const Layer *first_layer = cage.print.objects().front()->layers().front();
REQUIRE(has_curled_lines(*cage.print.objects().front()));
config.set_deserialize_strict("enable_overhang_speed", "0");
cage.print.apply(cage.model, config);
cage.print.process();
REQUIRE(cage.print.objects().front()->layers().front() == first_layer);
CHECK_FALSE(has_curled_lines(*cage.print.objects().front()));
}
TEST_CASE("Caged external overhangs are slowed when printed by object or through the pressure equalizer", "[ExtrusionProcessor]")
{
const auto [key, value] = GENERATE(table<const char *, const char *>({