Added STEP loading dialog (SPE-2723)

This commit is contained in:
YuSanka 2025-03-19 17:22:23 +01:00 committed by Lukas Matena
parent 8d4f86b84a
commit 5b0a81fca4
13 changed files with 429 additions and 16 deletions

View File

@ -88,6 +88,7 @@ src/slic3r/GUI/Jobs/SLAImportDialog.hpp
src/slic3r/GUI/Jobs/SLAImportJob.cpp src/slic3r/GUI/Jobs/SLAImportJob.cpp
src/slic3r/GUI/KBShortcutsDialog.cpp src/slic3r/GUI/KBShortcutsDialog.cpp
src/slic3r/GUI/BulkExportDialog.cpp src/slic3r/GUI/BulkExportDialog.cpp
src/slic3r/GUI/LoadStepDialog.cpp
src/slic3r/GUI/LoginDialog.cpp src/slic3r/GUI/LoginDialog.cpp
src/slic3r/GUI/MainFrame.cpp src/slic3r/GUI/MainFrame.cpp
src/slic3r/GUI/Mouse3DController.cpp src/slic3r/GUI/Mouse3DController.cpp

View File

@ -235,6 +235,15 @@ void AppConfig::set_defaults()
set("sys_menu_enabled", "1"); set("sys_menu_enabled", "1");
#endif // _WIN32 #endif // _WIN32
if (get("show_step_import_parameters").empty())
set("show_step_import_parameters", "1");
if (get("linear_precision").empty())
set("linear_precision", "0.005");
if (get("angle_precision").empty())
set("angle_precision", "1.");
// Remove legacy window positions/sizes // Remove legacy window positions/sizes
erase("", "main_frame_maximized"); erase("", "main_frame_maximized");
erase("", "main_frame_pos"); erase("", "main_frame_pos");

View File

@ -37,7 +37,7 @@ bool is_project_file(const std::string& input_file)
} }
// Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well. // Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well.
static Model read_model_from_file(const std::string& input_file, LoadAttributes options) static Model read_model_from_file(const std::string& input_file, LoadAttributes options, const std::optional<std::pair<double, double>>& step_deflections = std::nullopt)
{ {
Model model; Model model;
@ -49,8 +49,9 @@ static Model read_model_from_file(const std::string& input_file, LoadAttributes
result = load_stl(input_file.c_str(), &model); result = load_stl(input_file.c_str(), &model);
else if (boost::algorithm::iends_with(input_file, ".obj")) else if (boost::algorithm::iends_with(input_file, ".obj"))
result = load_obj(input_file.c_str(), &model); result = load_obj(input_file.c_str(), &model);
else if (boost::algorithm::iends_with(input_file, ".step") || boost::algorithm::iends_with(input_file, ".stp")) else if (boost::algorithm::iends_with(input_file, ".step") || boost::algorithm::iends_with(input_file, ".stp")) {
result = load_step(input_file.c_str(), &model); result = load_step(input_file.c_str(), &model, step_deflections);
}
else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml")) else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
//? result = load_amf(input_file.c_str(), &temp_config, &temp_config_substitutions_context, &model, options & LoadAttribute::CheckVersion); //? result = load_amf(input_file.c_str(), &temp_config, &temp_config_substitutions_context, &model, options & LoadAttribute::CheckVersion);
//? LoadAttribute::CheckVersion is needed here, when we loading just a geometry //? LoadAttribute::CheckVersion is needed here, when we loading just a geometry
@ -216,9 +217,10 @@ static int removed_objects_with_zero_volume(Model& model)
Model load_model(const std::string& input_file, Model load_model(const std::string& input_file,
LoadAttributes options/* = LoadAttribute::AddDefaultInstances*/, LoadAttributes options/* = LoadAttribute::AddDefaultInstances*/,
LoadStats* stats/*= nullptr*/) LoadStats* stats/*= nullptr*/,
std::optional<std::pair<double, double>> step_deflections/* = std::nullopt*/)
{ {
Model model = read_model_from_file(input_file, options); Model model = read_model_from_file(input_file, options, step_deflections);
for (auto obj : model.objects) for (auto obj : model.objects)
if (obj->name.empty()) if (obj->name.empty())

View File

@ -14,6 +14,8 @@
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include "enum_bitmask.hpp" #include "enum_bitmask.hpp"
#include <utility>
#include <optional>
namespace Slic3r { namespace Slic3r {
@ -46,7 +48,8 @@ namespace FileReader
// Exceptions don't catched inside // Exceptions don't catched inside
Model load_model(const std::string& input_file, Model load_model(const std::string& input_file,
LoadAttributes options = LoadAttribute::AddDefaultInstances, LoadAttributes options = LoadAttribute::AddDefaultInstances,
LoadStats* statistics = nullptr); LoadStats* statistics = nullptr,
std::optional<std::pair<double, double>> step_deflections = std::nullopt);
// Load model, config and config substitutions from input file and fill statistics if it's required. // Load model, config and config substitutions from input file and fill statistics if it's required.
// Exceptions don't catched inside // Exceptions don't catched inside

View File

@ -27,9 +27,12 @@
namespace Slic3r { namespace Slic3r {
#if __APPLE__ #if __APPLE__
extern "C" bool load_step_internal(const char *path, OCCTResult* res); extern "C" bool load_step_internal(const char *path, OCCTResult* res, std::optional<std::pair<double, double>> deflections /*= std::nullopt*/);
#endif #endif
// Inside deflections pair:
// * first value is linear deflection
// * second value is angle deflection
LoadStepFn get_load_step_fn() LoadStepFn get_load_step_fn()
{ {
static LoadStepFn load_step_fn = nullptr; static LoadStepFn load_step_fn = nullptr;
@ -80,7 +83,7 @@ LoadStepFn get_load_step_fn()
return load_step_fn; return load_step_fn;
} }
bool load_step(const char *path, Model *model /*BBS:, ImportStepProgressFn proFn*/) bool load_step(const char *path, Model *model /*BBS:, ImportStepProgressFn proFn*/, std::optional<std::pair<double, double>> deflections)
{ {
OCCTResult occt_object; OCCTResult occt_object;
@ -89,7 +92,7 @@ bool load_step(const char *path, Model *model /*BBS:, ImportStepProgressFn proFn
if (!load_step_fn) if (!load_step_fn)
return false; return false;
load_step_fn(path, &occt_object); load_step_fn(path, &occt_object, deflections);
assert(! occt_object.volumes.empty()); assert(! occt_object.volumes.empty());

View File

@ -9,6 +9,9 @@
#ifndef slic3r_Format_STEP_hpp_ #ifndef slic3r_Format_STEP_hpp_
#define slic3r_Format_STEP_hpp_ #define slic3r_Format_STEP_hpp_
#include <utility>
#include <optional>
namespace Slic3r { namespace Slic3r {
class Model; class Model;
@ -16,7 +19,10 @@ class Model;
//typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn; //typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn;
// Load a step file into a provided model. // Load a step file into a provided model.
extern bool load_step(const char *path_str, Model *model /*LMBBS:, ImportStepProgressFn proFn = nullptr*/); // Inside deflections pair:
// * first value is linear deflection
// * second value is angle deflection
extern bool load_step(const char *path_str, Model *model /*LMBBS:, ImportStepProgressFn proFn = nullptr*/, std::optional<std::pair<double, double>> deflections = std::nullopt);
}; // namespace Slic3r }; // namespace Slic3r

View File

@ -79,7 +79,7 @@ static void getNamedSolids(const TopLoc_Location& location, const Handle(XCAFDoc
} }
} }
extern "C" OCCTWRAPPER_EXPORT bool load_step_internal(const char *path, OCCTResult* res /*BBS:, ImportStepProgressFn proFn*/) extern "C" OCCTWRAPPER_EXPORT bool load_step_internal(const char *path, OCCTResult* res /*BBS:, ImportStepProgressFn proFn*/, std::optional<std::pair<double, double>> deflections /*= std::nullopt*/)
{ {
try { try {
//bool cb_cancel = false; //bool cb_cancel = false;
@ -129,7 +129,9 @@ try {
res->object_name = obj_name; res->object_name = obj_name;
for (const NamedSolid &namedSolid : namedSolids) { for (const NamedSolid &namedSolid : namedSolids) {
BRepMesh_IncrementalMesh mesh(namedSolid.solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true); BRepMesh_IncrementalMesh mesh(namedSolid.solid,
deflections.has_value() ? deflections.value().first : STEP_TRANS_CHORD_ERROR, false,
deflections.has_value() ? deflections.value().second : STEP_TRANS_ANGLE_RES, true);
res->volumes.emplace_back(); res->volumes.emplace_back();
std::vector<Vec3f> vertices; std::vector<Vec3f> vertices;

View File

@ -5,6 +5,8 @@
#include <array> #include <array>
#include <string> #include <string>
#include <vector> #include <vector>
#include <utility>
#include <optional>
struct stl_facet; struct stl_facet;
@ -21,7 +23,7 @@ struct OCCTResult {
std::vector<OCCTVolume> volumes; std::vector<OCCTVolume> volumes;
}; };
using LoadStepFn = bool (*)(const char *path, OCCTResult* occt_result); using LoadStepFn = bool (*)(const char *path, OCCTResult* occt_result, std::optional<std::pair<double, double>> deflections);
}; // namespace Slic3r }; // namespace Slic3r

View File

@ -143,6 +143,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Sidebar.hpp GUI/Sidebar.hpp
GUI/Plater.cpp GUI/Plater.cpp
GUI/Plater.hpp GUI/Plater.hpp
GUI/LoadStepDialog.cpp
GUI/LoadStepDialog.hpp
GUI/PresetComboBoxes.hpp GUI/PresetComboBoxes.hpp
GUI/PresetComboBoxes.cpp GUI/PresetComboBoxes.cpp
GUI/BitmapComboBox.hpp GUI/BitmapComboBox.hpp

View File

@ -0,0 +1,247 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral
///|/ Copyright (c) 2022 Michael Kirsch
///|/ Copyright (c) 2021 Boleslaw Ciesielski
///|/ Copyright (c) 2019 John Drake @foxox
///|/
///|/ ported from lib/Slic3r/GUI/Plater.pm:
///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros
///|/ Copyright (c) 2018 Martin Loidl @LoidlM
///|/ Copyright (c) 2017 Matthias Gazzari @qtux
///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens
///|/ Copyright (c) 2015 Daren Schwenke
///|/ Copyright (c) 2014 Mark Hindess
///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake
///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen
///|/ Copyright (c) 2012 Sam Wong
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "LoadStepDialog.hpp"
#include <wx/window.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/radiobut.h>
#include <wx/slider.h>
#include <vector>
#include <utility>
#include "GUI_App.hpp"
#include "format.hpp"
#include "MsgDialog.hpp"
#include "Widgets/CheckBox.hpp"
namespace Slic3r::GUI {
static std::vector<std::pair<std::string, PrecisionParams>> default_step_import_params = {
{"Low" , {0.1, 1. }},
{"Medium" , {0.05, 0.5 }},
{"High" , {0.001, 0.03}},
};
LoadStepDialog::LoadStepDialog(wxWindow* parent, const std::string& filename, double linear_precision, double angle_precision, bool multiple_loading)
: DPIDialog(parent, wxID_ANY, format_wxstr(_L("STEP import quality (%1%)"), filename), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
m_params({ linear_precision, angle_precision })
{
#ifdef _WIN32
wxGetApp().UpdateDarkUI(this);
#else
//SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif
const wxFont& font = wxGetApp().normal_font();
SetFont(font);
// Call your custom function manually after constructing base
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); // Get the sizer
add_params(main_sizer);
main_sizer->Add(new StaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, em_unit());
wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
m_remember_chb = new ::CheckBox(this, _L("Remember my choice"));
bottom_sizer->Add(m_remember_chb, 0, wxEXPAND | wxRIGHT, 5);
bottom_sizer->AddStretchSpacer();
auto buttons_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
if (multiple_loading) {
auto apply_btn = new wxButton(this, wxID_APPLY, "Apply to all");
apply_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) {
m_apply_to_all = true;
EndModal(wxID_OK);
});
buttons_sizer->Insert(0, apply_btn, 0, wxRIGHT, 5);
}
bottom_sizer->Add(buttons_sizer, 0, wxEXPAND | wxLEFT, 5);
main_sizer->Add(bottom_sizer, 0, wxEXPAND | wxALL, 10);
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
m_custom_sizer->ShowItems(!m_default);
// Update DarkUi just for buttons
wxGetApp().UpdateDlgDarkUI(this, true);
}
void LoadStepDialog::add_params(wxSizer* sizer)
{
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(new wxStaticText(this, wxID_ANY, _L("Select requested quality of the mesh after import: ")));
// add radio buttons for selection default parameters
for (const auto& [name, params] : default_step_import_params) {
wxRadioButton* radio_def = new wxRadioButton(this, wxID_ANY, format_wxstr("%1%", _L(name)));
radio_def->Bind(wxEVT_RADIOBUTTON, [params_copy = params, this](wxEvent&) {
m_params.linear = params_copy.linear;
m_params.angle = params_copy.angle;
m_custom_sizer->ShowItems(false);
});
bool is_selected = m_params.linear == params.linear && params.angle == params.angle;
radio_def->SetValue(is_selected);
m_default |= is_selected;
main_sizer->Add(radio_def, 0, wxLEFT | wxTOP, em_unit());
}
// add radio buttons for set custom parameters
wxRadioButton* radio_custom = new wxRadioButton(this, wxID_ANY, _L("Custom"));
radio_custom->Bind(wxEVT_RADIOBUTTON, [&, this](wxEvent&) {
m_custom_sizer->ShowItems(true);
#ifdef __linux__
this->Fit();
#endif // __linux__
m_params.linear = string_to_double_decimal_point(m_linear_precision_val->GetValue().ToStdString());
m_params.angle = string_to_double_decimal_point(m_angle_precision_val->GetValue().ToStdString());
});
main_sizer->Add(radio_custom, 0, wxLEFT | wxTOP, em_unit());
radio_custom->SetValue(!m_default);
long slyder_style = wxSL_HORIZONTAL | wxSL_TICKS;
long text_ctrl_style = wxTE_PROCESS_ENTER;
#ifdef _WIN32
text_ctrl_style |= wxBORDER_SIMPLE;
#endif
const wxSize def_slider_size = wxSize(15 * em_unit(), wxDefaultCoord);
const wxSize def_editor_size = wxSize(5 * em_unit(), wxDefaultCoord);
const int hgap = 5;
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(4, em_unit(), hgap);
grid_sizer->SetFlexibleDirection(wxBOTH);
grid_sizer->AddGrowableCol(1, 1);
grid_sizer->AddGrowableRow(0, 1);
grid_sizer->AddGrowableRow(1, 1);
wxBoxSizer* labels_sizer = new wxBoxSizer(wxHORIZONTAL);
{
const wxString left_text = _L("Lower quality");
const int left_text_gap = std::max(GetTextExtent(_L("Linear precision")).x, GetTextExtent(_L("Angle precision")).x) + 4 * hgap - GetTextExtent(left_text).x * 0.5;
const wxString right_text = _L("Higher quality");
const int right_text_gap = GetTextExtent(_L("mm")).x + def_editor_size.x + 4 * hgap - GetTextExtent(right_text).x * 0.5;
labels_sizer->Add(new wxStaticText(this, wxID_ANY, left_text), 0, wxLEFT, left_text_gap);
labels_sizer->Add(new wxStaticText(this, wxID_ANY, wxEmptyString), 1, wxEXPAND);
labels_sizer->Add(new wxStaticText(this, wxID_ANY, right_text), 0, wxRIGHT, right_text_gap);
}
auto high_vals = std::find_if(default_step_import_params.begin(), default_step_import_params.end(),
[](const std::pair<std::string, PrecisionParams>& val) { return val.first == "High"; });
auto low_vals = std::find_if(default_step_import_params.begin(), default_step_import_params.end(),
[](const std::pair<std::string, PrecisionParams>& val) { return val.first == "Low"; });
assert(high_vals != default_step_import_params.end() && low_vals != default_step_import_params.end());
m_linear_precision_sl.init(high_vals->second.linear, low_vals->second.linear, 0.001);
m_angle_precision_sl.init(high_vals->second.angle, low_vals->second.angle, 0.01);
auto process_value_change = [](double& precision, wxTextCtrl* text_ctrl, wxSlider* slider, const SliderHelper& sl_helper) -> void {
wxString str_val = text_ctrl->GetValue();
double val = string_to_double_decimal_point(str_val.ToStdString());
precision = sl_helper.adjust_to_region(val);
slider->SetValue(sl_helper.get_pos(precision));
if (wxString str_precision = format_wxstr("%1%", precision); str_precision != str_val)
text_ctrl->SetValue(str_precision);
};
// Add "Linear precision"
m_linear_precision_slider = new wxSlider(this, wxID_ANY, m_linear_precision_sl.get_pos(m_params.linear), m_linear_precision_sl.beg_sl_pos, m_linear_precision_sl.end_sl_pos, wxDefaultPosition, def_slider_size, slyder_style);
m_linear_precision_slider->SetTickFreq(10);
m_linear_precision_slider->Bind(wxEVT_SLIDER, [this](wxCommandEvent e) {
m_params.linear = m_linear_precision_sl.get_value(m_linear_precision_slider->GetValue());
m_linear_precision_val->SetValue(format_wxstr("%1%", m_params.linear));
});
m_linear_precision_val = new wxTextCtrl(this, wxID_ANY, format_wxstr("%1%", m_linear_precision_sl.adjust_to_region(m_params.linear)), wxDefaultPosition, def_editor_size, text_ctrl_style);
m_linear_precision_val->SetToolTip(format_wxstr("Set value from the range [%1%; %2%] with %3% step",
m_linear_precision_sl.min_val, m_linear_precision_sl.max_val, m_linear_precision_sl.val_step));
m_linear_precision_val->Bind(wxEVT_TEXT_ENTER, [process_value_change, this](wxCommandEvent& e) {
process_value_change(m_params.linear, m_linear_precision_val, m_linear_precision_slider, m_linear_precision_sl);
});
m_linear_precision_val->Bind(wxEVT_KILL_FOCUS, [process_value_change, this](wxFocusEvent& e) {
process_value_change(m_params.linear, m_linear_precision_val, m_linear_precision_slider, m_linear_precision_sl);
e.Skip();
});
grid_sizer->Add(new wxStaticText(this, wxID_ANY, _L("Linear precision") + ": "), 0, wxALIGN_CENTER_VERTICAL);
grid_sizer->Add(m_linear_precision_slider, 1, wxEXPAND);
grid_sizer->Add(m_linear_precision_val, 0, wxALIGN_CENTER_VERTICAL);
grid_sizer->Add(new wxStaticText(this, wxID_ANY, _L("mm")), 0, wxALIGN_CENTER_VERTICAL);
// Add "Angle precision"
m_angle_precision_slider = new wxSlider(this, wxID_ANY, m_angle_precision_sl.get_pos(m_params.angle), m_angle_precision_sl.beg_sl_pos, m_angle_precision_sl.end_sl_pos, wxDefaultPosition, def_slider_size, slyder_style);
m_angle_precision_slider->SetTickFreq(10);
m_angle_precision_slider->Bind(wxEVT_SLIDER, [this](wxCommandEvent e) {
m_params.angle = m_angle_precision_sl.get_value(m_angle_precision_slider->GetValue());
m_angle_precision_val->SetValue(format_wxstr("%1%", m_params.angle));
});
m_angle_precision_val = new wxTextCtrl(this, wxID_ANY, format_wxstr("%1%", m_angle_precision_sl.adjust_to_region(m_params.angle)), wxDefaultPosition, def_editor_size, text_ctrl_style);
m_angle_precision_val->SetToolTip(format_wxstr("Set value from the range [%1%; %2%] with %3% step",
m_angle_precision_sl.min_val, m_angle_precision_sl.max_val, m_angle_precision_sl.val_step));
m_angle_precision_val->Bind(wxEVT_TEXT_ENTER, [process_value_change, this](wxCommandEvent& e) {
process_value_change(m_params.angle, m_angle_precision_val, m_angle_precision_slider, m_angle_precision_sl);
});
m_angle_precision_val->Bind(wxEVT_KILL_FOCUS, [process_value_change, this](wxFocusEvent& e) {
process_value_change(m_params.angle, m_angle_precision_val, m_angle_precision_slider, m_angle_precision_sl);
e.Skip();
});
grid_sizer->Add(new wxStaticText(this, wxID_ANY, _L("Angle precision") + ": "), 0, wxALIGN_CENTER_VERTICAL);
grid_sizer->Add(m_angle_precision_slider, 1, wxEXPAND);
grid_sizer->Add(m_angle_precision_val, 0, wxALIGN_CENTER_VERTICAL);
grid_sizer->Add(new wxStaticText(this, wxID_ANY, _L("°")), 0, wxALIGN_CENTER_VERTICAL);
m_custom_sizer = new wxBoxSizer(wxVERTICAL);
m_custom_sizer->Add(labels_sizer, 0, wxEXPAND | wxBOTTOM | wxTOP, em_unit());
m_custom_sizer->Add(grid_sizer, 1, wxEXPAND);
main_sizer->Add(m_custom_sizer, 1, wxEXPAND | wxLEFT, 3 * em_unit());
sizer->Add(main_sizer, 1, wxEXPAND | wxALL, em_unit());
}
bool LoadStepDialog::IsCheckBoxChecked()
{
return m_remember_chb && m_remember_chb->GetValue();
}
bool LoadStepDialog::IsApplyToAllClicked()
{
return m_apply_to_all;
}
} // namespace Slic3r::GUI

View File

@ -0,0 +1,99 @@
///|/ Copyright (c) Prusa Research 2018 - 2025 Oleksandra Iushchenko @YuSanka
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_LoadStepDialog_hpp_
#define slic3r_LoadStepDialog_hpp_
#include <string>
#include <wx/dialog.h>
#include "GUI_Utils.hpp"
class wxBoxSizer;
class wxTextCtrl;
class wxSlider;
class CheckBox;
namespace Slic3r::GUI {
struct PrecisionParams
{
double linear;
double angle;
};
struct SliderHelper
{
double min_val;
double max_val;
double val_step;
int beg_sl_pos;
int end_sl_pos;
void init(double min, double max, double step, int beg_pos = 1) {
assert(val_step != 0.);
min_val = min;
max_val = max;
val_step = step;
beg_sl_pos = beg_pos;
end_sl_pos = beg_sl_pos + int(double(max_val - min_val) / val_step);
}
double get_value(int pos) const {
return max_val - val_step * (pos - beg_sl_pos);
}
int get_pos(double value) const {
return beg_sl_pos + int((max_val - value) / val_step);
}
double adjust_to_region(double value) const {
return std::max(std::min(value, max_val), min_val);
}
};
class LoadStepDialog : public DPIDialog
{
public:
LoadStepDialog(wxWindow* parent, const std::string& filename, double linear_precision, double angle_precision, bool multiple_loading);
~LoadStepDialog() = default;
bool IsCheckBoxChecked();
bool IsApplyToAllClicked();
double get_linear_precision() { return m_params.linear; }
double get_angle_precision() { return m_params.angle; }
protected:
void on_dpi_changed(const wxRect& suggested_rect) override {}
void on_sys_color_changed() override {};
private:
void add_params(wxSizer* sizer);
private:
PrecisionParams m_params;
::CheckBox* m_remember_chb { nullptr };
wxTextCtrl* m_linear_precision_val { nullptr };
wxTextCtrl* m_angle_precision_val { nullptr };
wxSlider* m_linear_precision_slider { nullptr };
wxSlider* m_angle_precision_slider { nullptr };
wxBoxSizer* m_custom_sizer { nullptr };
bool m_default { false };
bool m_apply_to_all { false };
SliderHelper m_linear_precision_sl;
SliderHelper m_angle_precision_sl;
};
} // namespace Slic3r::GUI
#endif

View File

@ -30,6 +30,7 @@
#include <string> #include <string>
#include <regex> #include <regex>
#include <future> #include <future>
#include <utility>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@ -138,6 +139,7 @@
#include "ConfigWizardWebViewPage.hpp" #include "ConfigWizardWebViewPage.hpp"
#include "PresetArchiveDatabase.hpp" #include "PresetArchiveDatabase.hpp"
#include "BulkExportDialog.hpp" #include "BulkExportDialog.hpp"
#include "LoadStepDialog.hpp"
#include "libslic3r/ArrangeHelper.hpp" #include "libslic3r/ArrangeHelper.hpp"
@ -325,6 +327,7 @@ struct Plater::priv
static const std::regex pattern_prusa; static const std::regex pattern_prusa;
static const std::regex pattern_zip; static const std::regex pattern_zip;
static const std::regex pattern_printRequest; static const std::regex pattern_printRequest;
static const std::regex pattern_step;
priv(Plater *q, MainFrame *main_frame); priv(Plater *q, MainFrame *main_frame);
~priv(); ~priv();
@ -615,11 +618,13 @@ private:
bool show_warning_dialog { false }; bool show_warning_dialog { false };
}; };
// FIXME: Some of the regex patterns are wrong (missing [.] before file extension).
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|3mf)", std::regex::icase); const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|3mf)", std::regex::icase);
const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase); const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase);
const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase); const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase);
const std::regex Plater::priv::pattern_zip(".*zip", std::regex::icase); const std::regex Plater::priv::pattern_zip(".*zip", std::regex::icase);
const std::regex Plater::priv::pattern_printRequest(".*printRequest", std::regex::icase); const std::regex Plater::priv::pattern_printRequest(".*printRequest", std::regex::icase);
const std::regex Plater::priv::pattern_step(".*[.](step|stp)", std::regex::icase);
Plater::priv::priv(Plater* q, MainFrame* main_frame) Plater::priv::priv(Plater* q, MainFrame* main_frame)
: q(q) : q(q)
@ -1288,6 +1293,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
int answer_convert_from_meters = wxOK_DEFAULT; int answer_convert_from_meters = wxOK_DEFAULT;
int answer_convert_from_imperial_units = wxOK_DEFAULT; int answer_convert_from_imperial_units = wxOK_DEFAULT;
int answer_consider_as_multi_part_objects = wxOK_DEFAULT; int answer_consider_as_multi_part_objects = wxOK_DEFAULT;
bool apply_step_import_parameters_to_all { false };
bool in_temp = false; bool in_temp = false;
const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data(); const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data();
@ -1303,7 +1309,26 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
const auto &path = input_files[i]; const auto &path = input_files[i];
#endif // _WIN32 #endif // _WIN32
in_temp = (path.parent_path() == temp_path); in_temp = (path.parent_path() == temp_path);
const auto filename = path.filename(); const boost::filesystem::path filename = path.filename();
const bool type_step = std::regex_match(path.string(), pattern_step);
if (type_step && !apply_step_import_parameters_to_all &&
wxGetApp().app_config->get_bool("show_step_import_parameters")) {
double linear_precision = string_to_double_decimal_point(wxGetApp().app_config->get("linear_precision"));
double angle_precision = string_to_double_decimal_point(wxGetApp().app_config->get("angle_precision"));
LoadStepDialog dlg(q, filename.string(), linear_precision, angle_precision, (input_files_size - i) > 1);
if (dlg.ShowModal() == wxID_OK) {
wxGetApp().app_config->set("linear_precision", float_to_string_decimal_point(dlg.get_linear_precision()));
wxGetApp().app_config->set("angle_precision", float_to_string_decimal_point(dlg.get_angle_precision()));
if (dlg.IsCheckBoxChecked())
wxGetApp().app_config->set("show_step_import_parameters", "0");
apply_step_import_parameters_to_all = dlg.IsApplyToAllClicked();
} else
continue;
}
if (progress_dlg) { if (progress_dlg) {
progress_dlg->Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename)); progress_dlg->Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename));
progress_dlg->Fit(); progress_dlg->Fit();
@ -1352,6 +1377,13 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
model = FileReader::load_model_with_config(path.string(), &config_loaded, &config_substitutions, prusaslicer_generator_version, FileReader::LoadAttribute::CheckVersion, &load_stats); model = FileReader::load_model_with_config(path.string(), &config_loaded, &config_substitutions, prusaslicer_generator_version, FileReader::LoadAttribute::CheckVersion, &load_stats);
} }
else if (load_model) { else if (load_model) {
if (type_step) {
double linear_precision = string_to_double_decimal_point(wxGetApp().app_config->get("linear_precision"));
double angle_precision = string_to_double_decimal_point(wxGetApp().app_config->get("angle_precision"));
model = FileReader::load_model(path.string(), FileReader::LoadAttributes{}, &load_stats,
std::make_pair(linear_precision, angle_precision));
}
else
model = FileReader::load_model(path.string(), FileReader::LoadAttributes{}, &load_stats); model = FileReader::load_model(path.string(), FileReader::LoadAttributes{}, &load_stats);
} }
} catch (const ConfigurationError &e) { } catch (const ConfigurationError &e) {

View File

@ -135,7 +135,7 @@ void PreferencesDialog::show(const std::string& highlight_opt_key /*= std::strin
downloader->set_path_name(app_config->get("url_downloader_dest")); downloader->set_path_name(app_config->get("url_downloader_dest"));
downloader->allow(!app_config->has("downloader_url_registered") || app_config->get_bool("downloader_url_registered")); downloader->allow(!app_config->has("downloader_url_registered") || app_config->get_bool("downloader_url_registered"));
for (const std::string opt_key : {"suppress_hyperlinks", "downloader_url_registered", "show_login_button"}) for (const std::string opt_key : {"suppress_hyperlinks", "downloader_url_registered", "show_login_button", "show_step_import_parameters"})
m_optgroup_other->set_value(opt_key, app_config->get_bool(opt_key)); m_optgroup_other->set_value(opt_key, app_config->get_bool(opt_key));
// by default "Log in" button is visible // by default "Log in" button is visible
if (!app_config->has("show_login_button")) if (!app_config->has("show_login_button"))
@ -629,6 +629,11 @@ void PreferencesDialog::build()
// "If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks."), // "If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks."),
app_config->get_bool("suppress_hyperlinks")); app_config->get_bool("suppress_hyperlinks"));
append_bool_option(m_optgroup_other, "show_step_import_parameters",
L("Show STEP file import parameters"),
L("If enabled, PrusaSlicer will show dialog with quality selection when importing STEP file."),
app_config->get_bool("show_step_import_parameters"));
append_bool_option(m_optgroup_other, "show_login_button", append_bool_option(m_optgroup_other, "show_login_button",
L("Show \"Log in\" button in application top bar"), L("Show \"Log in\" button in application top bar"),
L("If enabled, PrusaSlicer will show up \"Log in\" button in application top bar."), L("If enabled, PrusaSlicer will show up \"Log in\" button in application top bar."),