Port STEP import dialog from BambuStudio (#9102)

![image](https://github.com/user-attachments/assets/db27ee5d-c158-4729-a648-a33f1222a479)


Thanks BambuLab!

Fix #8820
This commit is contained in:
SoftFever
2025-04-05 21:20:51 +08:00
committed by GitHub
19 changed files with 817 additions and 74 deletions

View File

@@ -38,7 +38,7 @@ Points CameraUtils::project(const Camera & camera,
return result;
}
Point CameraUtils::project(const Camera &camera, const Vec3d &point)
Slic3r::Point CameraUtils::project(const Camera &camera, const Vec3d &point)
{
// IMPROVE: do it faster when you need it (inspire in project multi point)
return project(camera, std::vector{point}).front();

View File

@@ -25,7 +25,7 @@ public:
/// <returns>projected points by camera into coordinate of camera.
/// x(from left to right), y(from top to bottom)</returns>
static Points project(const Camera& camera, const std::vector<Vec3d> &points);
static Point project(const Camera& camera, const Vec3d &point);
static Slic3r::Point project(const Camera& camera, const Vec3d &point);
/// <summary>
/// Create hull around GLVolume in 2d space of camera

View File

@@ -174,6 +174,8 @@ protected:
void find_single();
};
wxDECLARE_EVENT(wxEVT_THREAD_DONE, wxCommandEvent);
} // namespace GUI
} // namespace Slic3r

View File

@@ -151,6 +151,7 @@
#include "DailyTips.hpp"
#include "CreatePresetsDialog.hpp"
#include "FileArchiveDialog.hpp"
#include "StepMeshDialog.hpp"
using boost::optional;
namespace fs = boost::filesystem;
@@ -4155,32 +4156,25 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
filament_ids.clear();
}
};
model = Slic3r::Model::read_from_file(
path.string(), nullptr, nullptr, strategy, &plate_data, &project_presets, &is_xxx, &file_version, nullptr,
[this, &dlg, real_filename, &progress_percent, &file_percent, INPUT_FILES_RATIO, total_files, i, &designer_model_id, &designer_country_code](int current, int total, bool &cancel, std::string &mode_id, std::string &code)
{
designer_model_id = mode_id;
designer_country_code = code;
bool cont = true;
float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * 100.0f * ((float)current / (float)total) / (float)total_files;
BOOST_LOG_TRIVIAL(trace) << "load_stl_file: percent(float)=" << percent_float << ", curr = " << current << ", total = " << total;
progress_percent = (int)percent_float;
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
cont = dlg.Update(progress_percent, msg);
cancel = !cont;
},
[this, &dlg, real_filename, &progress_percent, &file_percent, step_percent, INPUT_FILES_RATIO, total_files, i](int load_stage, int current, int total, bool &cancel)
{
bool cont = true;
float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * ((float)step_percent[load_stage] + (float)current * (float)(step_percent[load_stage + 1] - step_percent[load_stage]) / (float)total) / (float)total_files;
BOOST_LOG_TRIVIAL(trace) << "load_step_file: percent(float)=" << percent_float << ", stage = " << load_stage << ", curr = " << current << ", total = " << total;
progress_percent = (int)percent_float;
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
cont = dlg.Update(progress_percent, msg);
cancel = !cont;
},
[](int isUtf8StepFile) {
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_defletion"));
if (linear <= 0) linear = 0.003;
double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion"));
if (angle <= 0) angle = 0.5;
bool split_compound = wxGetApp().app_config->get_bool("is_split_compound");
model = Slic3r::Model:: read_from_step(path.string(), strategy,
[this, &dlg, real_filename, &progress_percent, &file_percent, step_percent, INPUT_FILES_RATIO, total_files, i](int load_stage, int current, int total, bool &cancel)
{
bool cont = true;
float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * ((float)step_percent[load_stage] + (float)current * (float)(step_percent[load_stage + 1] - step_percent[load_stage]) / (float)total) / (float)total_files;
BOOST_LOG_TRIVIAL(trace) << "load_step_file: percent(float)=" << percent_float << ", stage = " << load_stage << ", curr = " << current << ", total = " << total;
progress_percent = (int)percent_float;
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
cont = dlg.Update(progress_percent, msg);
cancel = !cont;
},
[](int isUtf8StepFile) {
if (!isUtf8StepFile) {
const auto no_warn = wxGetApp().app_config->get_bool("step_not_utf8_no_warn");
if (!no_warn) {
@@ -4194,8 +4188,42 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
}
},
nullptr, 0, obj_color_fun);
[this, &path, &is_user_cancel, &linear, &angle, &split_compound](Slic3r::Step& file, double& linear_value, double& angle_value, bool& is_split)-> int {
if (wxGetApp().app_config->get_bool("enable_step_mesh_setting")) {
StepMeshDialog mesh_dlg(nullptr, file, linear, angle);
if (mesh_dlg.ShowModal() == wxID_OK) {
linear_value = mesh_dlg.get_linear_defletion();
angle_value = mesh_dlg.get_angle_defletion();
is_split = mesh_dlg.get_split_compound_value();
return 1;
}
}else {
linear_value = linear;
angle_value = angle;
is_split = split_compound;
return 1;
}
is_user_cancel = true;
return -1;
}, linear, angle, split_compound);
}else {
model = Slic3r::Model:: read_from_file(
path.string(), nullptr, nullptr, strategy, &plate_data, &project_presets, &is_xxx, &file_version, nullptr,
[this, &dlg, real_filename, &progress_percent, &file_percent, INPUT_FILES_RATIO, total_files, i, &designer_model_id, &designer_country_code](int current, int total, bool &cancel, std::string &mode_id, std::string &code)
{
designer_model_id = mode_id;
designer_country_code = code;
bool cont = true;
float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * 100.0f * ((float)current / (float)total) / (float)total_files;
BOOST_LOG_TRIVIAL(trace) << "load_stl_file: percent(float)=" << percent_float << ", curr = " << current << ", total = " << total;
progress_percent = (int)percent_float;
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
cont = dlg.Update(progress_percent, msg);
cancel = !cont;
},
nullptr, 0, obj_color_fun);
}
if (designer_model_id.empty() && boost::algorithm::iends_with(path.string(), ".stl")) {
read_binary_stl(path.string(), designer_model_id, designer_country_code);
@@ -5937,8 +5965,17 @@ void Plater::priv::reload_from_disk()
std::vector<Preset*> project_presets;
// BBS: backup
new_model = Model::read_from_file(path, nullptr, nullptr, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, &plate_data, &project_presets, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, 0, obj_color_fun);
if (boost::iends_with(path, ".stp") ||
boost::iends_with(path, ".step")) {
double linear = string_to_double_decimal_point(wxGetApp().app_config->get("linear_defletion"));
double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion"));
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);
}
for (ModelObject* model_object : new_model.objects)
{
model_object->center_around_origin();

View File

@@ -1225,6 +1225,7 @@ wxWindow* PreferencesDialog::create_general_page()
auto item_calc_mode = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time the color changed."), page, _L("If enabled, auto-calculate every time the color changed."), 50, "auto_calculate");
auto item_calc_in_long_retract = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time when the filament is changed."), page, _L("If enabled, auto-calculate every time when filament is changed"), 50, "auto_calculate_when_filament_change");
auto item_remember_printer_config = create_item_checkbox(_L("Remember printer configuration"), page, _L("If enabled, Orca will remember and switch filament/process configuration for each printer automatically."), 50, "remember_printer_config");
auto item_step_mesh_setting = create_item_checkbox(_L("Show the step mesh parameter setting dialog."), page, _L("If enabled,a parameter settings dialog will appear during STEP file import."), 50, "enable_step_mesh_setting");
auto item_multi_machine = create_item_checkbox(_L("Multi-device Management(Take effect after restarting Orca)."), page, _L("With this option enabled, you can send a task to multiple devices at the same time and manage multiple devices."), 50, "enable_multi_machine");
auto item_auto_arrange = create_item_checkbox(_L("Auto arrange plate after cloning"), page, _L("Auto arrange plate after object cloning"), 50, "auto_arrange");
auto title_presets = create_item_title(_L("Presets"), page, _L("Presets"));
@@ -1305,6 +1306,7 @@ wxWindow* PreferencesDialog::create_general_page()
sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3));
sizer_page->Add(item_calc_in_long_retract, 0, wxTOP, FromDIP(3));
sizer_page->Add(item_multi_machine, 0, wxTOP, FromDIP(3));
sizer_page->Add(item_step_mesh_setting, 0, wxTOP, FromDIP(3));
sizer_page->Add(item_auto_arrange, 0, wxTOP, FromDIP(3));
sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20));
sizer_page->Add(item_calc_mode, 0, wxTOP, FromDIP(3));

View File

@@ -0,0 +1,386 @@
#include "StepMeshDialog.hpp"
#include <thread>
#include <wx/event.h>
#include <wx/sizer.h>
#include <wx/slider.h>
#include <wx/dcmemory.h>
#include "GUI_App.hpp"
#include "I18N.hpp"
#include "MainFrame.hpp"
#include "Widgets/Button.hpp"
#include "Widgets/TextInput.hpp"
#include <chrono>
using namespace Slic3r;
using namespace Slic3r::GUI;
static int _scale(const int val) { return val * Slic3r::GUI::wxGetApp().em_unit() / 10; }
static int _ITEM_WIDTH() { return _scale(30); }
#define MIN_DIALOG_WIDTH FromDIP(400)
#define SLIDER_WIDTH FromDIP(200)
#define SLIDER_HEIGHT FromDIP(25)
#define TEXT_CTRL_WIDTH FromDIP(70)
#define BUTTON_SIZE wxSize(FromDIP(58), FromDIP(24))
#define BUTTON_BORDER FromDIP(int(400 - 58 * 2) / 8)
#define SLIDER_SCALE(val) ((val) / 0.001)
#define SLIDER_UNSCALE(val) ((val) * 0.001)
#define SLIDER_SCALE_10(val) ((val) / 0.01)
#define SLIDER_UNSCALE_10(val) ((val) * 0.01)
#define LEFT_RIGHT_PADING FromDIP(20)
#define FONT_COLOR wxColour("#6B6B6B")
wxDEFINE_EVENT(wxEVT_THREAD_DONE, wxCommandEvent);
class CenteredStaticText : public wxStaticText
{
public:
CenteredStaticText(wxWindow* parent, wxWindowID id, const wxString& label, const wxPoint& position, const wxSize& size = wxDefaultSize, long style = 0)
: wxStaticText(parent, id, label, position, size, style) {
CenterOnPosition(position);
}
void CenterOnPosition(const wxPoint& position) {
int textWidth, textHeight;
GetTextExtent(GetLabel(), &textWidth, &textHeight);
int x = position.x - textWidth / 2;
int y = position.y - textHeight / 2;
SetPosition(wxPoint(x, y));
}
};
void StepMeshDialog::on_dpi_changed(const wxRect& suggested_rect) {
};
bool StepMeshDialog:: validate_number_range(const wxString& value, double min, double max) {
double num = 0.0;
if (value.IsEmpty()) {
return false;
}
try {
if (!value.ToDouble(&num)) {
return false;
}
} catch (...) {
return false;
}
return (num >= min && num <= max);
}
StepMeshDialog::StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double linear_init, double angle_init)
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe),
wxID_ANY,
_(L("Step file import parameters")),
wxDefaultPosition,
wxDefaultSize,
wxDEFAULT_DIALOG_STYLE /* | wxRESIZE_BORDER*/), m_file(file)
{
m_linear_last = wxString::Format("%.3f", linear_init);
m_angle_last = wxString::Format("%.2f", angle_init);
Bind(wxEVT_THREAD_DONE, &StepMeshDialog::on_task_done, this);
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico")
% Slic3r::resources_dir()).str();
SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
SetBackgroundColour(*wxWHITE);
wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
bSizer->SetMinSize(wxSize(MIN_DIALOG_WIDTH, -1));
auto image_bitmap = create_scaled_bitmap("step_mesh_info", this, FromDIP(120));
// wxPanel* overlay_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, image_bitmap.GetSize(), wxTAB_TRAVERSAL);
// overlay_panel->SetBackgroundStyle(wxBG_STYLE_PAINT);
// wxStaticBitmap *image = new wxStaticBitmap(overlay_panel, wxID_ANY, image_bitmap, wxDefaultPosition, overlay_panel->GetSize(), 0);
// CenteredStaticText* text_1 = new CenteredStaticText (overlay_panel, wxID_ANY, _L("Smooth"),
// wxPoint(overlay_panel->GetSize().GetWidth() / 6,
// overlay_panel->GetSize().GetHeight() / 2));
// CenteredStaticText* text_2 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Rough"),
// wxPoint(overlay_panel->GetSize().GetWidth() * 5 / 6,
// overlay_panel->GetSize().GetHeight() / 2));
// CenteredStaticText* text_3 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Reduce Linear"),
// wxPoint(overlay_panel->GetSize().GetWidth() / 2,
// overlay_panel->GetSize().GetHeight() * 1.3 / 3));
// CenteredStaticText* text_4 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Reduce Angle"),
// wxPoint(overlay_panel->GetSize().GetWidth() / 2,
// overlay_panel->GetSize().GetHeight() * 2 / 3));
// CenteredStaticText* text_5 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("More faces"),
// wxPoint(overlay_panel->GetSize().GetWidth() / 6,
// overlay_panel->GetSize().GetHeight() * 2.8 / 3));
// CenteredStaticText* text_6 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Fewer faces"),
// wxPoint(overlay_panel->GetSize().GetWidth() * 5 / 6,
// overlay_panel->GetSize().GetHeight() * 2.8 / 3));
// bSizer->Add(overlay_panel, 0, wxALIGN_CENTER | wxALL, 10);
wxBoxSizer* tips_sizer = new wxBoxSizer(wxVERTICAL);
wxStaticText* info = new wxStaticText(this, wxID_ANY, _L("Smaller linear and angular deflections result in higher-quality transformations but increase the processing time."));
info->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
wxStaticText *tips = new wxStaticText(this, wxID_ANY, _L("View Wiki for more information"));
wxFont font(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false);
font.SetUnderlined(true);
tips->SetForegroundColour(StateColor::darkModeColorFor(wxColour(0, 151, 137)));
tips->SetFont(font);
tips->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) {
wxLaunchDefaultBrowser("https://wiki.bambulab.com/en/software/bambu-studio/step");
});
info->Wrap(FromDIP(400));
tips_sizer->Add(info, 0, wxALIGN_LEFT);
tips_sizer->Add(tips, 0, wxALIGN_LEFT);
bSizer->Add(tips_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, LEFT_RIGHT_PADING);
wxBoxSizer* linear_sizer = new wxBoxSizer(wxHORIZONTAL);
//linear_sizer->SetMinSize(wxSize(MIN_DIALOG_WIDTH, -1));
wxStaticText* linear_title = new wxStaticText(this,
wxID_ANY, _L("Linear Deflection") + ": ");
linear_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
linear_sizer->Add(linear_title, 0, wxALIGN_LEFT);
linear_sizer->AddStretchSpacer(1);
wxSlider* linear_slider = new wxSlider(this, wxID_ANY,
SLIDER_SCALE(get_linear_defletion()),
1, 100, wxDefaultPosition,
wxSize(SLIDER_WIDTH, SLIDER_HEIGHT),
wxSL_HORIZONTAL);
linear_sizer->Add(linear_slider, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5));
auto linear_input = new ::TextInput(this, m_linear_last, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(TEXT_CTRL_WIDTH, -1), wxTE_CENTER);
linear_input->GetTextCtrl()->SetFont(Label::Body_12);
linear_input->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
linear_sizer->Add(linear_input, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5));
linear_input->Bind(wxEVT_KILL_FOCUS, ([this, linear_input](wxFocusEvent& e) {
wxString value = linear_input->GetTextCtrl()->GetValue();
if (validate_number_range(value, 0.001, 0.1)) {
m_linear_last = value;
update_mesh_number_text();
} else {
MessageDialog msg_dlg(nullptr, _L("Please input a valid value (0.001 < linear deflection < 0.1)"), wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
linear_input->GetTextCtrl()->SetValue(m_linear_last);
}
e.Skip();
}));
// textctrl bind slider
linear_input->Bind(wxEVT_TEXT, ([this, linear_slider, linear_input](wxCommandEvent& e) {
double slider_value_long;
int slider_value;
wxString value = linear_input->GetTextCtrl()->GetValue();
if (value.ToDouble(&slider_value_long)) {
slider_value = SLIDER_SCALE(slider_value_long);
if (slider_value >= linear_slider->GetMin() && slider_value <= linear_slider->GetMax()) {
linear_slider->SetValue(slider_value);
}
}
}));
linear_slider->Bind(wxEVT_SLIDER, ([this, linear_slider, linear_input](wxCommandEvent& e) {
double slider_value = SLIDER_UNSCALE(linear_slider->GetValue());
linear_input->GetTextCtrl()->SetValue(wxString::Format("%.3f", slider_value));
m_linear_last = wxString::Format("%.3f", slider_value);
}));
linear_slider->Bind(wxEVT_LEFT_UP, ([this](wxMouseEvent& e) {
update_mesh_number_text();
e.Skip();
}));
bSizer->Add(linear_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, LEFT_RIGHT_PADING);
wxBoxSizer* angle_sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText* angle_title = new wxStaticText(this,
wxID_ANY, _L("Angle Deflection") + ": ");
angle_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
angle_sizer->Add(angle_title, 0, wxALIGN_LEFT);
angle_sizer->AddStretchSpacer(1);
wxSlider* angle_slider = new wxSlider(this, wxID_ANY,
SLIDER_SCALE_10(get_angle_defletion()),
1, 100, wxDefaultPosition,
wxSize(SLIDER_WIDTH, SLIDER_HEIGHT),
wxSL_HORIZONTAL);
angle_sizer->Add(angle_slider, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5));
auto angle_input = new ::TextInput(this, m_angle_last, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(TEXT_CTRL_WIDTH, -1), wxTE_CENTER);
angle_input->GetTextCtrl()->SetFont(Label::Body_12);
angle_input->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
angle_sizer->Add(angle_input, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5));
angle_input->Bind(wxEVT_KILL_FOCUS, ([this, angle_input](wxFocusEvent& e) {
wxString value = angle_input->GetTextCtrl()->GetValue();
if (validate_number_range(value, 0.01, 1)) {
m_angle_last = value;
update_mesh_number_text();
} else {
MessageDialog msg_dlg(nullptr, _L("Please input a valid value (0.01 < angle deflection < 1.0)"), wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
angle_input->GetTextCtrl()->SetValue(m_angle_last);
}
e.Skip();
}));
// textctrl bind slider
angle_input->Bind(wxEVT_TEXT, ([this, angle_slider, angle_input](wxCommandEvent& e) {
double slider_value_long;
int slider_value;
wxString value = angle_input->GetTextCtrl()->GetValue();
if (value.ToDouble(&slider_value_long)) {
slider_value = SLIDER_SCALE_10(slider_value_long);
if (slider_value >= angle_slider->GetMin() && slider_value <= angle_slider->GetMax()) {
angle_slider->SetValue(slider_value);
}
}
}));
angle_slider->Bind(wxEVT_SLIDER, ([this, angle_slider, angle_input](wxCommandEvent& e) {
double slider_value = SLIDER_UNSCALE_10(angle_slider->GetValue());
angle_input->GetTextCtrl()->SetValue(wxString::Format("%.2f", slider_value));
m_angle_last = wxString::Format("%.2f", slider_value);
}));
angle_slider->Bind(wxEVT_LEFT_UP, ([this](wxMouseEvent& e) {
update_mesh_number_text();
e.Skip();
}));
bSizer->Add(angle_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING);
wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL);
m_split_compound_checkbox = new wxCheckBox(this, wxID_ANY, _L("Split compound and compsolid into multiple objects"), wxDefaultPosition, wxDefaultSize, 0);
m_split_compound_checkbox->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
m_split_compound_checkbox->SetValue(wxGetApp().app_config->get_bool("is_split_compound"));
check_sizer->Add(m_split_compound_checkbox, 0, wxALIGN_LEFT);
bSizer->Add(check_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING);
wxBoxSizer* mesh_face_number_sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText *mesh_face_number_title = new wxStaticText(this, wxID_ANY, _L("Number of triangular facets") + ": ");
mesh_face_number_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
mesh_face_number_text = new wxStaticText(this, wxID_ANY, _L("0"));
mesh_face_number_text->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
mesh_face_number_text->SetMinSize(wxSize(FromDIP(150), -1));
mesh_face_number_sizer->Add(mesh_face_number_title, 0, wxALIGN_LEFT);
mesh_face_number_sizer->Add(mesh_face_number_text, 0, wxALIGN_LEFT);
bSizer->Add(mesh_face_number_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING);
wxBoxSizer* bSizer_button = new wxBoxSizer(wxHORIZONTAL);
bSizer_button->SetMinSize(wxSize(FromDIP(100), -1));
m_checkbox = new wxCheckBox(this, wxID_ANY, _L("Don't show again"), wxDefaultPosition, wxDefaultSize, 0);
m_checkbox->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
bSizer_button->Add(m_checkbox, 0, wxALIGN_LEFT);
bSizer_button->AddStretchSpacer(1);
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(AMS_CONTROL_BRAND_COLOUR, StateColor::Normal));
m_button_ok = new Button(this, _L("OK"));
m_button_ok->SetBackgroundColor(btn_bg_green);
m_button_ok->SetBorderColor(*wxWHITE);
m_button_ok->SetTextColor(wxColour(0xFFFFFE));
m_button_ok->SetFont(Label::Body_12);
m_button_ok->SetSize(BUTTON_SIZE);
m_button_ok->SetMinSize(BUTTON_SIZE);
m_button_ok->SetCornerRadius(FromDIP(12));
bSizer_button->Add(m_button_ok, 0, wxALIGN_RIGHT, BUTTON_BORDER);
m_button_ok->Bind(wxEVT_LEFT_DOWN, [this, angle_input, linear_input](wxMouseEvent& e) {
stop_task();
if (validate_number_range(angle_input->GetTextCtrl()->GetValue(), 0.01, 1) &&
validate_number_range(linear_input->GetTextCtrl()->GetValue(), 0.001, 0.1)) {
if (m_checkbox->IsChecked()) {
wxGetApp().app_config->set_bool("enable_step_mesh_setting", false);
}
wxGetApp().app_config->set_bool("is_split_compound", m_split_compound_checkbox->GetValue());
wxGetApp().app_config->set("linear_defletion", float_to_string_decimal_point(get_linear_defletion(), 3));
wxGetApp().app_config->set("angle_defletion", float_to_string_decimal_point(get_angle_defletion(), 2));
EndModal(wxID_OK);
}
SetFocusIgnoringChildren();
});
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed), std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
m_button_cancel = new Button(this, _L("Cancel"));
m_button_cancel->SetBackgroundColor(btn_bg_white);
m_button_cancel->SetBorderColor(wxColour(38, 46, 48));
m_button_cancel->SetFont(Label::Body_12);
m_button_cancel->SetSize(BUTTON_SIZE);
m_button_cancel->SetMinSize(BUTTON_SIZE);
m_button_cancel->SetCornerRadius(FromDIP(12));
bSizer_button->Add(m_button_cancel, 0, wxALIGN_RIGHT | wxLEFT, BUTTON_BORDER);
m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) {
stop_task();
EndModal(wxID_CANCEL);
});
bSizer->Add(bSizer_button, 1, wxEXPAND | wxALL, LEFT_RIGHT_PADING);
this->SetSizer(bSizer);
update_mesh_number_text();
this->Layout();
bSizer->Fit(this);
this->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {
SetFocusIgnoringChildren();
});
mesh_face_number_text->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {
SetFocusIgnoringChildren();
});
this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) {
stop_task();
EndModal(wxID_CANCEL);
});
wxGetApp().UpdateDlgDarkUI(this);
}
StepMeshDialog::~StepMeshDialog()
{
stop_task();
}
void StepMeshDialog::on_task_done(wxCommandEvent& event)
{
wxString text = event.GetString();
mesh_face_number_text->SetLabel(text);
if(m_task) {
if (m_task->joinable()) {
m_task->join();
delete m_task;
m_task = nullptr;
}
}
}
void StepMeshDialog::stop_task()
{
if(m_task) {
m_file.m_stop_mesh.store(true);
if (m_task->joinable()) {
m_task->join();
delete m_task;
m_task = nullptr;
}
m_file.m_stop_mesh.store(false);
}
}
void StepMeshDialog::update_mesh_number_text()
{
if ((m_last_linear == get_linear_defletion()) && (m_last_angle == get_angle_defletion()) && (m_mesh_number != 0))
return;
wxString newText = wxString::Format(_L("Calculating, please wait..."));
mesh_face_number_text->SetLabel(newText);
stop_task();
if (!m_task) {
m_task = new boost::thread(Slic3r::create_thread([this]() -> void {
m_mesh_number = m_file.get_triangle_num(get_linear_defletion(), get_angle_defletion());
if (m_mesh_number != 0) {
wxString number_text = wxString::Format("%d", m_mesh_number);
wxCommandEvent event(wxEVT_THREAD_DONE);
event.SetString(number_text);
wxPostEvent(this, event);
m_last_linear = get_linear_defletion();
m_last_angle = get_angle_defletion();
}
}));
}
}

View File

@@ -0,0 +1,55 @@
#ifndef _STEP_MESH_DIALOG_H_
#define _STEP_MESH_DIALOG_H_
#include <thread>
#include "GUI_App.hpp"
#include "GUI_Utils.hpp"
#include "libslic3r/Format/STEP.hpp"
#include "Widgets/Button.hpp"
class Button;
class StepMeshDialog : public Slic3r::GUI::DPIDialog
{
public:
StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double linear_init, double angle_init);
~StepMeshDialog() override;
void on_dpi_changed(const wxRect& suggested_rect) override;
inline double get_linear_defletion() {
double value;
if (m_linear_last.ToDouble(&value)) {
return value;
}else {
return m_last_linear;
}
}
inline double get_angle_defletion() {
double value;
if (m_angle_last.ToDouble(&value)) {
return value;
} else {
return m_last_angle;
}
}
inline bool get_split_compound_value() {
return m_split_compound_checkbox->GetValue();
}
private:
Slic3r::Step& m_file;
Button* m_button_ok = nullptr;
Button* m_button_cancel = nullptr;
wxCheckBox* m_checkbox = nullptr;
wxCheckBox* m_split_compound_checkbox = nullptr;
wxString m_linear_last;
wxString m_angle_last;
wxStaticText* mesh_face_number_text;
double m_last_linear = 0.003;
double m_last_angle = 0.5;
unsigned int m_mesh_number = 0;
boost::thread* m_task {nullptr};
bool validate_number_range(const wxString& value, double min, double max);
void update_mesh_number_text();
void on_task_done(wxCommandEvent& event);
void stop_task();
};
#endif // _STEP_MESH_DIALOG_H_