Merge branch 'master' into fs_QuadricEdgeCollapse

This commit is contained in:
Filip Sykala 2021-08-23 11:33:14 +02:00
commit 89819c1c22
57 changed files with 797 additions and 415 deletions

View file

@ -306,7 +306,7 @@ void GLVolume::SinkingContours::update()
int object_idx = m_parent.object_idx();
Model& model = GUI::wxGetApp().plater()->model();
if (0 <= object_idx && object_idx < model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) {
if (0 <= object_idx && object_idx < (int)model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) {
const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box();
if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) {
m_old_box = box;

View file

@ -12,6 +12,46 @@
namespace Slic3r {
namespace GUI {
void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWindow* parent, wxColourPickerCtrl** sys_colour, wxColourPickerCtrl** mod_colour)
{
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 5, 5);
sizer->Add(grid_sizer, 0, wxEXPAND);
ScalableBitmap bmp_delete = ScalableBitmap(parent, "cross");
ScalableBitmap bmp_delete_focus = ScalableBitmap(parent, "cross_focus");
auto add_color = [grid_sizer, parent](wxColourPickerCtrl** color_picker, const wxColour& color, const wxColour& def_color, wxString label_text) {
//
auto sys_label = new wxStaticText(parent, wxID_ANY, label_text);
sys_label->SetForegroundColour(color);
*color_picker = new wxColourPickerCtrl(parent, wxID_ANY, color);
wxGetApp().UpdateDarkUI((*color_picker)->GetPickerCtrl(), true);
(*color_picker)->Bind(wxEVT_COLOURPICKER_CHANGED, [color_picker, sys_label](wxCommandEvent&) {
sys_label->SetForegroundColour((*color_picker)->GetColour());
sys_label->Refresh();
});
auto btn = new ScalableButton(parent, wxID_ANY, "undo");
btn->SetToolTip(_L("Revert color to default"));
btn->Bind(wxEVT_BUTTON, [sys_label, color_picker, def_color](wxEvent& event) {
(*color_picker)->SetColour(def_color);
sys_label->SetForegroundColour(def_color);
sys_label->Refresh();
});
parent->Bind(wxEVT_UPDATE_UI, [color_picker, def_color](wxUpdateUIEvent& evt) {
evt.Enable((*color_picker)->GetColour() != def_color);
}, btn->GetId());
grid_sizer->Add(*color_picker, 0, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(btn, 0, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(sys_label, 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
};
add_color(sys_colour, wxGetApp().get_label_clr_sys(), wxGetApp().get_label_default_clr_system(), _L("Value is the same as the system value"));
add_color(mod_colour, wxGetApp().get_label_clr_modified(),wxGetApp().get_label_default_clr_modified(), _L("Value was changed and is not equal to the system value or the last saved preset"));
}
ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector<Entry> &entries) :
wxDialog(parent, wxID_ANY, _(L("Buttons And Text Colors Description")), wxDefaultPosition, wxDefaultSize),
m_entries(entries)
@ -35,50 +75,23 @@ ButtonsDescription::ButtonsDescription(wxWindow* parent, const std::vector<Entry
}
// Text color description
auto sys_label = new wxStaticText(this, wxID_ANY, _(L("Value is the same as the system value")));
sys_label->SetForegroundColour(wxGetApp().get_label_clr_sys());
auto sys_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_sys());
wxGetApp().UpdateDarkUI(sys_colour->GetPickerCtrl(), true);
sys_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([sys_colour, sys_label](wxCommandEvent e)
{
sys_label->SetForegroundColour(sys_colour->GetColour());
sys_label->Refresh();
}));
size_t t= 0;
while (t < 3) {
grid_sizer->Add(new wxStaticText(this, wxID_ANY, ""), -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
++t;
}
grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(sys_colour, -1, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(sys_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
auto mod_label = new wxStaticText(this, wxID_ANY, _(L("Value was changed and is not equal to the system value or the last saved preset")));
mod_label->SetForegroundColour(wxGetApp().get_label_clr_modified());
auto mod_colour = new wxColourPickerCtrl(this, wxID_ANY, wxGetApp().get_label_clr_modified());
wxGetApp().UpdateDarkUI(mod_colour->GetPickerCtrl(), true);
mod_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([mod_colour, mod_label](wxCommandEvent e)
{
mod_label->SetForegroundColour(mod_colour->GetColour());
mod_label->Refresh();
}));
grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(mod_colour, -1, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(mod_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
FillSizerWithTextColorDescriptions(sizer, this, &sys_colour, &mod_colour);
main_sizer->Add(sizer, 0, wxEXPAND | wxALL, 20);
auto buttons = CreateStdDialogButtonSizer(wxOK|wxCANCEL);
main_sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
wxGetApp().UpdateDlgDarkUI(this, true);
wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
btn->Bind(wxEVT_BUTTON, [sys_colour, mod_colour, this](wxCommandEvent&) {
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
wxGetApp().set_label_clr_sys(sys_colour->GetColour());
wxGetApp().set_label_clr_modified(mod_colour->GetColour());
EndModal(wxID_OK);
});
wxGetApp().UpdateDarkUI(btn);
wxGetApp().UpdateDarkUI(static_cast<wxButton*>(FindWindowById(wxID_CANCEL, this)));
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
}

View file

@ -5,12 +5,15 @@
#include <vector>
class ScalableBitmap;
class wxColourPickerCtrl;
namespace Slic3r {
namespace GUI {
class ButtonsDescription : public wxDialog
{
wxColourPickerCtrl* sys_colour{ nullptr };
wxColourPickerCtrl* mod_colour{ nullptr };
public:
struct Entry {
Entry(ScalableBitmap *bitmap, const std::string &symbol, const std::string &explanation) : bitmap(bitmap), symbol(symbol), explanation(explanation) {}
@ -23,6 +26,8 @@ public:
ButtonsDescription(wxWindow* parent, const std::vector<Entry> &entries);
~ButtonsDescription() {}
static void FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWindow* parent, wxColourPickerCtrl** sys_colour, wxColourPickerCtrl** mod_colour);
private:
std::vector<Entry> m_entries;
};

View file

@ -268,7 +268,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("gap_fill_speed", have_perimeters);
for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" })
toggle_field(el, has_top_solid_infill);
toggle_field(el, has_top_solid_infill || (has_spiral_vase && has_bottom_solid_infill));
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
for (auto el : { "perimeter_acceleration", "infill_acceleration",

View file

@ -494,15 +494,7 @@ PageWelcome::PageWelcome(ConfigWizard *parent)
{
welcome_text->Hide();
cbox_reset->Hide();
#ifdef __linux__
if (!DesktopIntegrationDialog::is_integrated())
cbox_integrate->Show(true);
else
cbox_integrate->Hide();
#else
cbox_integrate->Hide();
#endif
cbox_integrate->Hide();
}
void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason)
@ -510,7 +502,7 @@ void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason)
const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY;
welcome_text->Show(data_empty);
cbox_reset->Show(!data_empty);
#ifdef __linux__
#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION)
if (!DesktopIntegrationDialog::is_integrated())
cbox_integrate->Show(true);
else
@ -2453,6 +2445,34 @@ bool ConfigWizard::priv::check_and_install_missing_materials(Technology technolo
return true;
}
static std::set<std::string> get_new_added_presets(const std::map<std::string, std::string>& old_data, const std::map<std::string, std::string>& new_data)
{
auto get_aliases = [](const std::map<std::string, std::string>& data) {
std::set<std::string> old_aliases;
for (auto item : data) {
const std::string& name = item.first;
size_t pos = name.find("@");
old_aliases.emplace(pos == std::string::npos ? name : name.substr(0, pos-1));
}
return old_aliases;
};
std::set<std::string> old_aliases = get_aliases(old_data);
std::set<std::string> new_aliases = get_aliases(new_data);
std::set<std::string> diff;
std::set_difference(new_aliases.begin(), new_aliases.end(), old_aliases.begin(), old_aliases.end(), std::inserter(diff, diff.begin()));
return diff;
}
static std::string get_first_added_preset(const std::map<std::string, std::string>& old_data, const std::map<std::string, std::string>& new_data)
{
std::set<std::string> diff = get_new_added_presets(old_data, new_data);
if (diff.empty())
return std::string();
return *diff.begin();
}
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
{
const auto enabled_vendors = appconfig_new.vendors();
@ -2525,13 +2545,61 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
preset_bundle->reset(true);
}
std::string preferred_model;
std::string preferred_variant;
const auto enabled_vendors_old = app_config->vendors();
auto get_preferred_printer_model = [enabled_vendors, enabled_vendors_old](const std::string& bundle_name, const Bundle& bundle, std::string& variant) {
const auto config = enabled_vendors.find(bundle_name);
if (config == enabled_vendors.end())
return std::string();
for (const auto& model : bundle.vendor_profile->models) {
if (const auto model_it = config->second.find(model.id);
model_it != config->second.end() && model_it->second.size() > 0) {
variant = *model_it->second.begin();
const auto config_old = enabled_vendors_old.find(bundle_name);
if (config_old == enabled_vendors_old.end())
return model.id;
const auto model_it_old = config_old->second.find(model.id);
if (model_it_old == config_old->second.end())
return model.id;
else if (model_it_old->second != model_it->second) {
for (const auto& var : model_it->second)
if (model_it_old->second.find(var) == model_it_old->second.end()) {
variant = var;
return model.id;
}
}
}
}
if (!variant.empty())
variant.clear();
return std::string();
};
// Prusa printers are considered first, then 3rd party.
if (preferred_model = get_preferred_printer_model("PrusaResearch", bundles.prusa_bundle(), preferred_variant);
preferred_model.empty()) {
for (const auto& bundle : bundles) {
if (bundle.second.is_prusa_bundle) { continue; }
if (preferred_model = get_preferred_printer_model(bundle.first, bundle.second, preferred_variant);
!preferred_model.empty())
break;
}
}
std::string first_added_filament, first_added_sla_material;
auto apply_section = [this, app_config](const std::string& section_name, std::string& first_added_preset) {
if (appconfig_new.has_section(section_name)) {
// get first of new added preset names
const std::map<std::string, std::string>& old_presets = app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map<std::string, std::string>();
first_added_preset = get_first_added_preset(old_presets, appconfig_new.get_section(section_name));
app_config->set_section(section_name, appconfig_new.get_section(section_name));
}
};
apply_section(AppConfig::SECTION_FILAMENTS, first_added_filament);
apply_section(AppConfig::SECTION_MATERIALS, first_added_sla_material);
app_config->set_vendors(appconfig_new);
if (appconfig_new.has_section(AppConfig::SECTION_FILAMENTS)) {
app_config->set_section(AppConfig::SECTION_FILAMENTS, appconfig_new.get_section(AppConfig::SECTION_FILAMENTS));
}
if (appconfig_new.has_section(AppConfig::SECTION_MATERIALS)) {
app_config->set_section(AppConfig::SECTION_MATERIALS, appconfig_new.get_section(AppConfig::SECTION_MATERIALS));
}
app_config->set("version_check", page_update->version_check ? "1" : "0");
app_config->set("preset_update", page_update->preset_update ? "1" : "0");
app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0");
@ -2556,44 +2624,8 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
page_mode->serialize_mode(app_config);
std::string preferred_model;
// Figure out the default pre-selected printer based on the selections in the pickers.
// The default is the first selected printer model (one with at least 1 variant selected).
// The default is only applied by load_presets() if the user doesn't have a (visible) printer
// selected already.
// Prusa printers are considered first, then 3rd party.
const auto config_prusa = enabled_vendors.find("PrusaResearch");
if (config_prusa != enabled_vendors.end()) {
for (const auto &model : bundles.prusa_bundle().vendor_profile->models) {
const auto model_it = config_prusa->second.find(model.id);
if (model_it != config_prusa->second.end() && model_it->second.size() > 0) {
preferred_model = model.id;
break;
}
}
}
if (preferred_model.empty()) {
for (const auto &bundle : bundles) {
if (bundle.second.is_prusa_bundle) { continue; }
const auto config = enabled_vendors.find(bundle.first);
if (config == enabled_vendors.end()) { continue; }
for (const auto &model : bundle.second.vendor_profile->models) {
const auto model_it = config->second.find(model.id);
if (model_it != config->second.end() && model_it->second.size() > 0) {
preferred_model = model.id;
break;
}
}
}
}
// Reloading the configs after some modifications were done to PrusaSlicer.ini.
// Just perform the substitutions silently, as the substitutions were already presented to the user on application start-up
// and the Wizard shall not create any new values that would require substitution.
// Throw on substitutions in system profiles, as the system profiles provided over the air should be compatible with this PrusaSlicer version.
preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem, preferred_model);
preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem,
{preferred_model, preferred_variant, first_added_filament, first_added_sla_material});
if (page_custom->custom_wanted()) {
page_firmware->apply_custom_config(*custom_config);

View file

@ -1,15 +1,19 @@
#ifdef __linux__
#include "DesktopIntegrationDialog.hpp"
#include "GUI_App.hpp"
#include "GUI.hpp"
#include "format.hpp"
#include "I18N.hpp"
#include "NotificationManager.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Platform.hpp"
#include "libslic3r/Config.hpp"
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <wx/filename.h>
#include <wx/stattext.h>
@ -17,9 +21,9 @@
namespace Slic3r {
namespace GUI {
namespace integrate_desktop_internal{
namespace {
// Disects path strings stored in system variable divided by ':' and adds into vector
static void resolve_path_from_var(const std::string& var, std::vector<std::string>& paths)
void resolve_path_from_var(const std::string& var, std::vector<std::string>& paths)
{
wxString wxdirs;
if (! wxGetEnv(boost::nowide::widen(var), &wxdirs) || wxdirs.empty() )
@ -34,7 +38,7 @@ static void resolve_path_from_var(const std::string& var, std::vector<std::strin
paths.push_back(dirs);
}
// Return true if directory in path p+dir_name exists
static bool contains_path_dir(const std::string& p, const std::string& dir_name)
bool contains_path_dir(const std::string& p, const std::string& dir_name)
{
if (p.empty() || dir_name.empty())
return false;
@ -47,7 +51,7 @@ static bool contains_path_dir(const std::string& p, const std::string& dir_name)
return false;
}
// Creates directory in path if not exists yet
static void create_dir(const boost::filesystem::path& path)
void create_dir(const boost::filesystem::path& path)
{
if (boost::filesystem::exists(path))
return;
@ -58,7 +62,7 @@ static void create_dir(const boost::filesystem::path& path)
BOOST_LOG_TRIVIAL(error)<< "create directory failed: " << ec.message();
}
// Starts at basic_path (excluded) and creates all directories in dir_path
static void create_path(const std::string& basic_path, const std::string& dir_path)
void create_path(const std::string& basic_path, const std::string& dir_path)
{
if (basic_path.empty() || dir_path.empty())
return;
@ -76,7 +80,7 @@ static void create_path(const std::string& basic_path, const std::string& dir_pa
create_dir(path);
}
// Calls our internal copy_file function to copy file at icon_path to dest_path
static bool copy_icon(const std::string& icon_path, const std::string& dest_path)
bool copy_icon(const std::string& icon_path, const std::string& dest_path)
{
BOOST_LOG_TRIVIAL(debug) <<"icon from "<< icon_path;
BOOST_LOG_TRIVIAL(debug) <<"icon to "<< dest_path;
@ -90,8 +94,8 @@ static bool copy_icon(const std::string& icon_path, const std::string& dest_path
return true;
}
// Creates new file filled with data.
static bool create_desktop_file(const std::string& path, const std::string& data)
{
bool create_desktop_file(const std::string& path, const std::string& data)
{
BOOST_LOG_TRIVIAL(debug) <<".desktop to "<< path;
std::ofstream output(path);
output << data;
@ -109,10 +113,6 @@ static bool create_desktop_file(const std::string& path, const std::string& data
// methods that actually do / undo desktop integration. Static to be accesible from anywhere.
bool DesktopIntegrationDialog::is_integrated()
{
const char *appimage_env = std::getenv("APPIMAGE");
if (!appimage_env)
return false;
const AppConfig *app_config = wxGetApp().app_config;
std::string path(app_config->get("desktop_integration_app_path"));
BOOST_LOG_TRIVIAL(debug) << "Desktop integration desktop file path: " << path;
@ -126,10 +126,6 @@ bool DesktopIntegrationDialog::is_integrated()
}
bool DesktopIntegrationDialog::integration_possible()
{
const char *appimage_env = std::getenv("APPIMAGE");
if (!appimage_env)
return false;
return true;
}
void DesktopIntegrationDialog::perform_desktop_integration()
@ -138,19 +134,31 @@ void DesktopIntegrationDialog::perform_desktop_integration()
// Path to appimage
const char *appimage_env = std::getenv("APPIMAGE");
std::string appimage_path;
std::string excutable_path;
if (appimage_env) {
try {
appimage_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string();
excutable_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string();
} catch (std::exception &) {
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - boost::filesystem::canonical did not return appimage path.";
show_error(nullptr, _L("Performing desktop integration failed - boost::filesystem::canonical did not return appimage path."));
return;
}
} else {
// not appimage - not performing
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - not Appimage executable.";
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail);
return;
// not appimage - find executable
excutable_path = boost::dll::program_location().string();
//excutable_path = wxStandardPaths::Get().GetExecutablePath().string();
BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << excutable_path;
if (excutable_path.empty())
{
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - no executable found.";
show_error(nullptr, _L("Performing desktop integration failed - Could not find executable."));
return;
}
}
// Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path'
boost::replace_all(excutable_path, "'", "'\\''");
// Find directories icons and applications
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
@ -158,8 +166,8 @@ void DesktopIntegrationDialog::perform_desktop_integration()
// The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
// If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
std::vector<std::string>target_candidates;
integrate_desktop_internal::resolve_path_from_var("XDG_DATA_HOME", target_candidates);
integrate_desktop_internal::resolve_path_from_var("XDG_DATA_DIRS", target_candidates);
resolve_path_from_var("XDG_DATA_HOME", target_candidates);
resolve_path_from_var("XDG_DATA_DIRS", target_candidates);
AppConfig *app_config = wxGetApp().app_config;
// suffix string to create different desktop file for alpha, beta.
@ -186,7 +194,6 @@ void DesktopIntegrationDialog::perform_desktop_integration()
icon_theme_dirs = "/hicolor/96x96/apps";
}
std::string target_dir_icons;
std::string target_dir_desktop;
@ -194,24 +201,24 @@ void DesktopIntegrationDialog::perform_desktop_integration()
// iterate thru target_candidates to find icons folder
for (size_t i = 0; i < target_candidates.size(); ++i) {
// Copy icon PrusaSlicer.png from resources_dir()/icons to target_dir_icons/icons/
if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "icons")) {
if (contains_path_dir(target_candidates[i], "icons")) {
target_dir_icons = target_candidates[i];
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir());
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
if (integrate_desktop_internal::copy_icon(icon_path, dest_path))
if (copy_icon(icon_path, dest_path))
break; // success
else
target_dir_icons.clear(); // copying failed
// if all failed - try creating default home folder
if (i == target_candidates.size() - 1) {
// create $HOME/.local/share
integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs);
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/icons" + icon_theme_dirs);
// copy icon
target_dir_icons = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer.png",resources_dir());
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
if (!integrate_desktop_internal::contains_path_dir(target_dir_icons, "icons")
|| !integrate_desktop_internal::copy_icon(icon_path, dest_path)) {
if (!contains_path_dir(target_dir_icons, "icons")
|| !copy_icon(icon_path, dest_path)) {
// every attempt failed - icon wont be present
target_dir_icons.clear();
}
@ -228,7 +235,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
// iterate thru target_candidates to find applications folder
for (size_t i = 0; i < target_candidates.size(); ++i)
{
if (integrate_desktop_internal::contains_path_dir(target_candidates[i], "applications")) {
if (contains_path_dir(target_candidates[i], "applications")) {
target_dir_desktop = target_candidates[i];
// Write slicer desktop file
std::string desktop_file = GUI::format(
@ -236,33 +243,33 @@ void DesktopIntegrationDialog::perform_desktop_integration()
"Name=PrusaSlicer%1%\n"
"GenericName=3D Printing Software\n"
"Icon=PrusaSlicer%2%\n"
"Exec=%3% %%F\n"
"Exec=\'%3%\' %%F\n"
"Terminal=false\n"
"Type=Application\n"
"MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n"
"Categories=Graphics;3DGraphics;Engineering;\n"
"Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n"
"StartupNotify=false\n"
"StartupWMClass=prusa-slicer", name_suffix, version_suffix, appimage_path);
"StartupWMClass=prusa-slicer", name_suffix, version_suffix, excutable_path);
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
if (integrate_desktop_internal::create_desktop_file(path, desktop_file)){
if (create_desktop_file(path, desktop_file)){
BOOST_LOG_TRIVIAL(debug) << "PrusaSlicer.desktop file installation success.";
break;
} else {
// write failed - try another path
BOOST_LOG_TRIVIAL(error) << "PrusaSlicer.desktop file installation failed.";
BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicer.desktop file installation failed. failed path: " << target_candidates[i];
target_dir_desktop.clear();
}
// if all failed - try creating default home folder
if (i == target_candidates.size() - 1) {
// create $HOME/.local/share
integrate_desktop_internal::create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
// create desktop file
target_dir_desktop = GUI::format("%1%/.local/share",wxFileName::GetHomeDir());
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
if (integrate_desktop_internal::contains_path_dir(target_dir_desktop, "applications")) {
if (!integrate_desktop_internal::create_desktop_file(path, desktop_file)) {
if (contains_path_dir(target_dir_desktop, "applications")) {
if (!create_desktop_file(path, desktop_file)) {
// Desktop file not written - end desktop integration
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create desktop file";
return;
@ -278,7 +285,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
if(target_dir_desktop.empty()) {
// Desktop file not written - end desktop integration
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not find applications directory";
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail);
show_error(nullptr, _L("Performing desktop integration failed - could not find applications directory."));
return;
}
// save path to desktop file
@ -290,7 +297,7 @@ void DesktopIntegrationDialog::perform_desktop_integration()
{
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
if (integrate_desktop_internal::copy_icon(icon_path, dest_path))
if (copy_icon(icon_path, dest_path))
// save path to icon
app_config->set("desktop_integration_icon_viewer_path", dest_path);
else
@ -303,32 +310,26 @@ void DesktopIntegrationDialog::perform_desktop_integration()
"Name=Prusa Gcode Viewer%1%\n"
"GenericName=3D Printing Software\n"
"Icon=PrusaSlicer-gcodeviewer%2%\n"
"Exec=%3% --gcodeviwer %%F\n"
"Exec=\'%3%\' --gcodeviwer %%F\n"
"Terminal=false\n"
"Type=Application\n"
"MimeType=text/x.gcode;\n"
"Categories=Graphics;3DGraphics;\n"
"Keywords=3D;Printing;Slicer;\n"
"StartupNotify=false", name_suffix, version_suffix, appimage_path);
"StartupNotify=false", name_suffix, version_suffix, excutable_path);
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
if (integrate_desktop_internal::create_desktop_file(desktop_path, desktop_file))
if (create_desktop_file(desktop_path, desktop_file))
// save path to desktop file
app_config->set("desktop_integration_app_viewer_path", desktop_path);
else {
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could create gcode viewer desktop file";
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationFail);
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
}
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
}
void DesktopIntegrationDialog::undo_desktop_intgration()
{
const char *appimage_env = std::getenv("APPIMAGE");
if (!appimage_env) {
BOOST_LOG_TRIVIAL(error) << "Undo desktop integration failed - not Appimage executable.";
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationFail);
return;
}
const AppConfig *app_config = wxGetApp().app_config;
// slicer .desktop
std::string path = std::string(app_config->get("desktop_integration_app_path"));

View file

@ -2028,7 +2028,7 @@ void Control::show_cog_icon_context_menu()
append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "",
[this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu);
if (m_mode != MultiExtruder && m_draw_mode == dmRegular)
if (GUI::wxGetApp().is_editor() && m_mode != MultiExtruder && m_draw_mode == dmRegular)
append_menu_item(&menu, wxID_ANY, _L("Set auto color changes"), "",
[this](wxCommandEvent&) { auto_color_change(); }, "", &menu);

View file

@ -169,6 +169,8 @@ void GLModel::reset()
void GLModel::render() const
{
GLShaderProgram* shader = wxGetApp().get_current_shader();
for (const RenderData& data : m_render_data) {
if (data.vbo_id == 0 || data.ibo_id == 0)
continue;
@ -190,7 +192,6 @@ void GLModel::render() const
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
GLShaderProgram* shader = wxGetApp().get_current_shader();
if (shader != nullptr)
shader->set_uniform("uniform_color", data.color);
else

View file

@ -1195,7 +1195,7 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height;
float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height;
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } });
}
}

View file

@ -1030,17 +1030,22 @@ bool GUI_App::dark_mode()
#endif
}
const wxColour GUI_App::get_label_default_clr_system()
{
return dark_mode() ? wxColour(115, 220, 103) : wxColour(26, 132, 57);
}
const wxColour GUI_App::get_label_default_clr_modified()
{
return dark_mode() ? wxColour(253, 111, 40) : wxColour(252, 77, 1);
}
void GUI_App::init_label_colours()
{
m_color_label_modified = get_label_default_clr_modified();
m_color_label_sys = get_label_default_clr_system();
bool is_dark_mode = dark_mode();
if (is_dark_mode) {
m_color_label_modified = wxColour(253, 111, 40);
m_color_label_sys = wxColour(115, 220, 103);
}
else {
m_color_label_modified = wxColour(252, 77, 1);
m_color_label_sys = wxColour(26, 132, 57);
}
#ifdef _WIN32
m_color_label_default = is_dark_mode ? wxColour(250, 250, 250): wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
m_color_highlight_label_default = is_dark_mode ? wxColour(230, 230, 230): wxSystemSettings::GetColour(/*wxSYS_COLOUR_HIGHLIGHTTEXT*/wxSYS_COLOUR_WINDOWTEXT);
@ -1801,10 +1806,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
local_menu->Append(config_id_base + ConfigMenuSnapshots, _L("&Configuration Snapshots") + dots, _L("Inspect / activate configuration snapshots"));
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _L("Take Configuration &Snapshot"), _L("Capture a configuration snapshot"));
local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for updates"), _L("Check for configuration updates"));
#ifdef __linux__
if (DesktopIntegrationDialog::integration_possible())
local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration"));
#endif
#if defined(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION)
//if (DesktopIntegrationDialog::integration_possible())
local_menu->Append(config_id_base + ConfigMenuDesktopIntegration, _L("Desktop Integration"), _L("Desktop Integration"));
#endif //(__linux__) && defined(SLIC3R_DESKTOP_INTEGRATION)
local_menu->AppendSeparator();
}
local_menu->Append(config_id_base + ConfigMenuPreferences, _L("&Preferences") + dots +

View file

@ -179,6 +179,8 @@ public:
static unsigned get_colour_approx_luma(const wxColour &colour);
static bool dark_mode();
const wxColour get_label_default_clr_system();
const wxColour get_label_default_clr_modified();
void init_label_colours();
void update_label_colours_from_appconfig();
void update_label_colours();

View file

@ -3404,6 +3404,18 @@ void ObjectList::update_selections_on_canvas()
std::vector<unsigned int> idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx);
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
}
else if (type == itInfo) {
// When selecting an info item, select one instance of the
// respective object - a gizmo may want to be opened.
int inst_idx = selection.get_instance_idx();
int scene_obj_idx = selection.get_object_idx();
mode = Selection::Instance;
// select first instance, unless an instance of the object is already selected
if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx)
inst_idx = 0;
std::vector<unsigned int> idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx);
volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end());
}
else
{
mode = Selection::Instance;
@ -3418,7 +3430,7 @@ void ObjectList::update_selections_on_canvas()
if (sel_cnt == 1) {
wxDataViewItem item = GetSelection();
if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer | itInfo))
if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer))
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode);
else
add_to_selection(item, selection, instance_idx, mode);

View file

@ -1019,7 +1019,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { internal_left_uv, internal_top_uv }, { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv } });
GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } });
break;
}
zoomed_top_y -= zoomed_stride_y;

View file

@ -3,6 +3,7 @@
#include "format.hpp"
#include "I18N.hpp"
#include "GUI_ObjectList.hpp"
#include "GLCanvas3D.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Config.hpp"
@ -305,7 +306,10 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
m_loaded_hints.emplace_back(hint_data);
}
else if (dict["hypertext_type"] == "gallery") {
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } };
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() {
// Deselect all objects, otherwise gallery wont show.
wxGetApp().plater()->canvas3D()->deselect_all();
wxGetApp().obj_list()->load_shape_object_from_gallery(); } };
m_loaded_hints.emplace_back(hint_data);
}
} else {

View file

@ -35,7 +35,9 @@ protected:
void prepare() override;
void on_exception(const std::exception_ptr &) override;
void process() override;
public:
ArrangeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
: PlaterJob{std::move(pri), plater}
@ -46,8 +48,6 @@ public:
return int(m_selected.size() + m_unprintable.size());
}
void process() override;
void finalize() override;
};

View file

@ -24,6 +24,7 @@ class FillBedJob : public PlaterJob
protected:
void prepare() override;
void process() override;
public:
FillBedJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
@ -35,8 +36,6 @@ public:
return m_status_range;
}
void process() override;
void finalize() override;
};

View file

@ -49,11 +49,20 @@ protected:
// Launched just before start(), a job can use it to prepare internals
virtual void prepare() {}
// The method where the actual work of the job should be defined.
virtual void process() = 0;
// Launched when the job is finished. It refreshes the 3Dscene by def.
virtual void finalize() { m_finalized = true; }
virtual void on_exception(const std::exception_ptr &) {}
// Exceptions occuring in process() are redirected from the worker thread
// into the main (UI) thread. This method is called from the main thread and
// can be overriden to handle these exceptions.
virtual void on_exception(const std::exception_ptr &eptr)
{
if (eptr) std::rethrow_exception(eptr);
}
public:
Job(std::shared_ptr<ProgressIndicator> pri);
@ -65,8 +74,6 @@ public:
Job &operator=(const Job &) = delete;
Job &operator=(Job &&) = delete;
virtual void process() = 0;
void start();
// To wait for the running job and join the threads. False is

View file

@ -27,9 +27,9 @@ class RotoptimizeJob : public PlaterJob
"structures.\nNote that this method will try to find the best surface of the object "
"for touching the print bed if no elevation is set.")},
// Just a min area bounding box that is done for all methods anyway.
{L("Smallest bounding box (Z axis only)"),
nullptr,
L("Rotate the object only in Z axis to have the smallest bounding box.")}};
{L("Lowest Z height"),
sla::find_min_z_height_rotation,
L("Rotate the model to have the lowest z height for faster print time.")}};
size_t m_method_id = 0;
float m_accuracy = 0.75;
@ -48,14 +48,14 @@ class RotoptimizeJob : public PlaterJob
protected:
void prepare() override;
void process() override;
public:
RotoptimizeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
: PlaterJob{std::move(pri), plater}
{}
void process() override;
void finalize() override;
static constexpr size_t get_methods_count() { return std::size(Methods); }

View file

@ -10,18 +10,16 @@ class SLAImportJob : public PlaterJob {
std::unique_ptr<priv> p;
protected:
void prepare() override;
void process() override;
void finalize() override;
public:
SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater);
~SLAImportJob();
void process() override;
void reset();
protected:
void prepare() override;
void finalize() override;
};
}} // namespace Slic3r::GUI

View file

@ -4,6 +4,7 @@
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/TriangleMeshSlicer.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/Model.hpp"
#include "slic3r/GUI/Camera.hpp"
@ -225,7 +226,7 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
// Also, remove anything below the bed (sinking objects).
for (i=0; i<hits.size(); ++i) {
Vec3d transformed_hit = trafo * hits[i].position();
if (transformed_hit.z() >= 0. &&
if (transformed_hit.z() >= SINKING_Z_THRESHOLD &&
(! clipping_plane || ! clipping_plane->is_point_clipped(transformed_hit)))
break;
}

View file

@ -47,7 +47,7 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat
_u8L("You have just added a G-code for color change, but its value is empty.\n"
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
_u8L("This model doesn't allow to automatically add the color changes") },
_u8L("No color change event was added to the print. The print does not look like a sign.") },
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Desktop integration was successful.") },
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10,

View file

@ -393,7 +393,7 @@ private:
{
public:
ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); }
ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { }
virtual void set_percentage(float percent) { m_percentage = percent; }
protected:
virtual void init() override;
@ -436,6 +436,7 @@ private:
, m_file_size(filesize)
{
m_has_cancel_button = true;
set_percentage(percentage);
}
static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; }
void set_percentage(float percent) override;

View file

@ -1643,7 +1643,7 @@ struct Plater::priv
BoundingBox scaled_bed_shape_bb() const;
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool force_center_on_bed = false);
wxString get_export_file(GUI::FileType file_type);
@ -2334,7 +2334,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
else {
model = Slic3r::Model::read_from_file(path.string(), nullptr, nullptr, only_if(load_config, Model::LoadAttribute::CheckVersion));
for (auto obj : model.objects)
if (obj->name.empty())
if (obj->name.empty() ||
obj->name.find_first_of("/") != std::string::npos) // When file is imported from Fusion360 the path containes "/" instead of "\\" (see https://github.com/prusa3d/PrusaSlicer/issues/6803)
// But read_from_file doesn't support that direction separator and as a result object name containes full path
obj->name = fs::path(obj->input_file).filename().string();
}
} catch (const ConfigurationError &e) {
@ -2360,25 +2362,23 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// Convert even if the object is big.
convert_from_imperial_units(model, false);
else if (model.looks_like_saved_in_meters()) {
//wxMessageDialog msg_dlg(q, format_wxstr(_L_PLURAL(
MessageDialog msg_dlg(q, format_wxstr(_L_PLURAL(
"The object in file %s looks like saved in meters.\n"
"Should I consider it as a saved in meters and convert it?",
"Some objects in file %s look like saved in meters.\n"
"Should I consider them as a saved in meters and convert them?", model.objects.size()), from_path(filename)) + "\n",
_L("The object appears to be saved in meters"), wxICON_WARNING | wxYES | wxNO);
"The dimensions of the object from file %s seem to be defined in meters.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
"The dimensions of some objects from file %s seem to be defined in meters.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
if (msg_dlg.ShowModal() == wxID_YES)
//FIXME up-scale only the small parts?
model.convert_from_meters(true);
}
else if (model.looks_like_imperial_units()) {
//wxMessageDialog msg_dlg(q, format_wxstr(_L_PLURAL(
MessageDialog msg_dlg(q, format_wxstr(_L_PLURAL(
"The object in file %s looks like saved in inches.\n"
"Should I consider it as a saved in inches and convert it?",
"Some objects in file %s look like saved in inches.\n"
"Should I consider them as a saved in inches and convert them?", model.objects.size()), from_path(filename)) + "\n",
_L("The object appears to be saved in inches"), wxICON_WARNING | wxYES | wxNO);
"The dimensions of the object from file %s seem to be defined in inches.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
"The dimensions of some objects from file %s seem to be defined in inches.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
if (msg_dlg.ShowModal() == wxID_YES)
//FIXME up-scale only the small parts?
convert_from_imperial_units(model, true);
@ -2426,7 +2426,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
if (one_by_one) {
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
auto loaded_idxs = load_model_objects(model.objects, is_project_file, !is_project_file);
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
} else {
// This must be an .stl or .obj file, which may contain a maximum of one volume.
@ -2481,7 +2481,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// #define AUTOPLACEMENT_ON_LOAD
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z)
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z, bool force_center_on_bed)
{
const BoundingBoxf bed_shape = bed_shape_bb();
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
@ -2541,6 +2541,9 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
object->ensure_on_bed(allow_negative_z);
}
if (force_center_on_bed)
model.center_instances_around_point(bed_shape.center());
#ifdef AUTOPLACEMENT_ON_LOAD
// FIXME distance should be a config value /////////////////////////////////
auto min_obj_distance = static_cast<coord_t>(6/SCALING_FACTOR);
@ -5902,6 +5905,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
p->sidebar->update_searcher();
p->sidebar->show_sliced_info_sizer(false);
p->reset_gcode_toolpaths();
p->view3D->get_canvas3d()->reset_sequential_print_clearance();
}
else if (opt_key == "bed_shape" || opt_key == "bed_custom_texture" || opt_key == "bed_custom_model") {
bed_shape_changed = true;

View file

@ -7,6 +7,7 @@
#include "libslic3r/AppConfig.hpp"
#include <wx/notebook.h>
#include "Notebook.hpp"
#include "ButtonsDescription.hpp"
namespace Slic3r {
namespace GUI {
@ -395,7 +396,8 @@ void PreferencesDialog::build(size_t selected_tab)
auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
this->Bind(wxEVT_BUTTON, &PreferencesDialog::accept, this, wxID_OK);
wxGetApp().UpdateDlgDarkUI(this, true);
for (int id : {wxID_OK, wxID_CANCEL})
wxGetApp().UpdateDarkUI(static_cast<wxButton*>(FindWindowById(id, this)));
sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM | wxTOP, 10);
@ -638,32 +640,7 @@ void PreferencesDialog::create_settings_text_color_widget()
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
wxSizer* sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(2, 5, 5);
sizer->Add(grid_sizer, 0, wxEXPAND);
auto sys_label = new wxStaticText(parent, wxID_ANY, _L("Value is the same as the system value"));
sys_label->SetForegroundColour(wxGetApp().get_label_clr_sys());
m_sys_colour = new wxColourPickerCtrl(parent, wxID_ANY, wxGetApp().get_label_clr_sys());
wxGetApp().UpdateDarkUI(m_sys_colour->GetPickerCtrl(), true);
m_sys_colour->Bind(wxEVT_COLOURPICKER_CHANGED, [this, sys_label](wxCommandEvent&) {
sys_label->SetForegroundColour(m_sys_colour->GetColour());
sys_label->Refresh();
});
grid_sizer->Add(m_sys_colour, 0, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(sys_label, 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
auto mod_label = new wxStaticText(parent, wxID_ANY, _L("Value was changed and is not equal to the system value or the last saved preset"));
mod_label->SetForegroundColour(wxGetApp().get_label_clr_modified());
m_mod_colour = new wxColourPickerCtrl(parent, wxID_ANY, wxGetApp().get_label_clr_modified());
wxGetApp().UpdateDarkUI(m_mod_colour->GetPickerCtrl(), true);
m_mod_colour->Bind(wxEVT_COLOURPICKER_CHANGED, [this, mod_label](wxCommandEvent&) {
mod_label->SetForegroundColour(m_mod_colour->GetColour());
mod_label->Refresh();
});
grid_sizer->Add(m_mod_colour, 0, wxALIGN_CENTRE_VERTICAL);
grid_sizer->Add(mod_label, 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
ButtonsDescription::FillSizerWithTextColorDescriptions(sizer, parent, &m_sys_colour, &m_mod_colour);
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
}

View file

@ -634,6 +634,12 @@ PlaterPresetComboBox::~PlaterPresetComboBox()
edit_btn->Destroy();
}
static void run_wizard(ConfigWizard::StartPage sp)
{
if (wxGetApp().check_and_save_current_preset_changes())
wxGetApp().run_wizard(ConfigWizard::RR_USER, sp);
}
void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt)
{
auto selected_item = evt.GetSelection();
@ -653,7 +659,7 @@ void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt)
case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break;
default: break;
}
wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); });
wxTheApp->CallAfter([sp]() { run_wizard(sp); });
}
return;
}
@ -685,7 +691,7 @@ void PlaterPresetComboBox::show_add_menu()
append_menu_item(menu, wxID_ANY, _L("Add/Remove presets"), "",
[](wxCommandEvent&) {
wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); });
wxTheApp->CallAfter([]() { run_wizard(ConfigWizard::SP_PRINTERS); });
}, "edit_uni", menu, []() { return true; }, wxGetApp().plater());
append_menu_item(menu, wxID_ANY, _L("Add physical printer"), "",
@ -715,7 +721,7 @@ void PlaterPresetComboBox::show_edit_menu()
else
append_menu_item(menu, wxID_ANY, _L("Add/Remove presets"), "",
[](wxCommandEvent&) {
wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS); });
wxTheApp->CallAfter([]() { run_wizard(ConfigWizard::SP_PRINTERS); });
}, "edit_uni", menu, []() { return true; }, wxGetApp().plater());
append_menu_item(menu, wxID_ANY, _L("Add physical printer"), "",
@ -918,7 +924,7 @@ void TabPresetComboBox::OnSelect(wxCommandEvent &evt)
this->SetSelection(m_last_selected);
if (marker == LABEL_ITEM_WIZARD_PRINTERS)
wxTheApp->CallAfter([this]() {
wxGetApp().run_wizard(ConfigWizard::RR_USER, ConfigWizard::SP_PRINTERS);
run_wizard(ConfigWizard::SP_PRINTERS);
// update combobox if its parent is a PhysicalPrinterDialog
PhysicalPrinterDialog* parent = dynamic_cast<PhysicalPrinterDialog*>(this->GetParent());

View file

@ -1518,11 +1518,11 @@ void TabPrint::build()
optgroup->append_single_option_line("support_material_auto", category_path + "auto-generated-supports");
optgroup->append_single_option_line("support_material_threshold", category_path + "overhang-threshold");
optgroup->append_single_option_line("support_material_enforce_layers", category_path + "enforce-support-for-the-first");
optgroup->append_single_option_line("raft_first_layer_density", category_path + "raft-first-layer-density");
optgroup->append_single_option_line("raft_first_layer_expansion", category_path + "raft-first-layer-expansion");
optgroup = page->new_optgroup(L("Raft"));
optgroup->append_single_option_line("raft_layers", category_path + "raft-layers");
optgroup->append_single_option_line("raft_first_layer_density", category_path + "raft-first-layer-density");
optgroup->append_single_option_line("raft_first_layer_expansion", category_path + "raft-first-layer-expansion");
optgroup->append_single_option_line("raft_contact_distance");
optgroup->append_single_option_line("raft_expansion");
@ -1747,14 +1747,14 @@ bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode)
std::vector<std::string> tags;
bool invalid = GCodeProcessor::contains_reserved_tags(gcode, 5, tags);
if (invalid) {
wxString reports = _L_PLURAL("The following line", "The following lines", tags.size());
reports += ":\n";
for (const std::string& keyword : tags) {
reports += ";" + keyword + "\n";
}
reports += _L("contain reserved keywords.") + "\n";
reports += _L("Please remove them, as they may cause problems in g-code visualization and printing time estimation.");
std::string lines = ":\n";
for (const std::string& keyword : tags)
lines += ";" + keyword + "\n";
wxString reports = format_wxstr(
_L_PLURAL("The following line %s contains reserved keywords.\nPlease remove it, as it may cause problems in G-code visualization and printing time estimation.",
"The following lines %s contain reserved keywords.\nPlease remove them, as they may cause problems in G-code visualization and printing time estimation.",
tags.size()),
lines);
//wxMessageDialog dialog(wxGetApp().mainframe, reports, _L("Found reserved keywords in") + " " + _(title), wxICON_WARNING | wxOK);
MessageDialog dialog(wxGetApp().mainframe, reports, _L("Found reserved keywords in") + " " + _(title), wxICON_WARNING | wxOK);
dialog.ShowModal();