Merge branch 'master' into ys_detach_btn

This commit is contained in:
bubnikv 2020-03-06 14:40:28 +01:00
commit 3834ab7ede
44 changed files with 793 additions and 759 deletions

View file

@ -16,8 +16,8 @@
#include "Point.hpp" #include "Point.hpp"
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/format.hpp> #include <boost/format/format_fwd.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree_fwd.hpp>
#include <cereal/access.hpp> #include <cereal/access.hpp>
#include <cereal/types/base_class.hpp> #include <cereal/types/base_class.hpp>

View file

@ -1,4 +1,5 @@
#include "Extruder.hpp" #include "Extruder.hpp"
#include "PrintConfig.hpp"
namespace Slic3r { namespace Slic3r {

View file

@ -3,10 +3,11 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "Point.hpp" #include "Point.hpp"
#include "PrintConfig.hpp"
namespace Slic3r { namespace Slic3r {
class GCodeConfig;
class Extruder class Extruder
{ {
public: public:

View file

@ -40,7 +40,7 @@ public:
~ExtrusionEntityCollection() { clear(); } ~ExtrusionEntityCollection() { clear(); }
explicit operator ExtrusionPaths() const; explicit operator ExtrusionPaths() const;
bool is_collection() const { return true; } bool is_collection() const override { return true; }
ExtrusionRole role() const override { ExtrusionRole role() const override {
ExtrusionRole out = erNone; ExtrusionRole out = erNone;
for (const ExtrusionEntity *ee : entities) { for (const ExtrusionEntity *ee : entities) {
@ -49,7 +49,7 @@ public:
} }
return out; return out;
} }
bool can_reverse() const { return !this->no_sort; } bool can_reverse() const override { return !this->no_sort; }
bool empty() const { return this->entities.empty(); } bool empty() const { return this->entities.empty(); }
void clear(); void clear();
void swap (ExtrusionEntityCollection &c); void swap (ExtrusionEntityCollection &c);
@ -83,9 +83,9 @@ public:
static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed); static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed);
ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const
{ return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); } { return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); }
void reverse(); void reverse() override;
const Point& first_point() const { return this->entities.front()->first_point(); } const Point& first_point() const override { return this->entities.front()->first_point(); }
const Point& last_point() const { return this->entities.back()->last_point(); } const Point& last_point() const override { return this->entities.back()->last_point(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
@ -102,11 +102,11 @@ public:
/// You should be iterating over flatten().entities if you are interested in the underlying ExtrusionEntities (and don't care about hierarchy). /// You should be iterating over flatten().entities if you are interested in the underlying ExtrusionEntities (and don't care about hierarchy).
/// \param preserve_ordering Flag to method that will flatten if and only if the underlying collection is sortable when True (default: False). /// \param preserve_ordering Flag to method that will flatten if and only if the underlying collection is sortable when True (default: False).
ExtrusionEntityCollection flatten(bool preserve_ordering = false) const; ExtrusionEntityCollection flatten(bool preserve_ordering = false) const;
double min_mm3_per_mm() const; double min_mm3_per_mm() const override;
double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; } double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
// Following methods shall never be called on an ExtrusionEntityCollection. // Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const { Polyline as_polyline() const override {
throw std::runtime_error("Calling as_polyline() on a ExtrusionEntityCollection"); throw std::runtime_error("Calling as_polyline() on a ExtrusionEntityCollection");
return Polyline(); return Polyline();
}; };

View file

@ -632,8 +632,11 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
// Negative support_contact_z is not taken into account, it can result in false positives in cases // Negative support_contact_z is not taken into account, it can result in false positives in cases
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752) // where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
// Only check this layer in case it has some extrusions.
bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions());
if (layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON)
throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" + throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" +
_(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " + _(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " +
std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is " std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is "

View file

@ -212,10 +212,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors) if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
something_nonoverriddable = false; something_nonoverriddable = false;
for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
if (!layer_tools.wiping_extrusions().is_overriddable_and_mark(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) { if (!layer_tools.wiping_extrusions().is_overriddable_and_mark(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region))
something_nonoverriddable = true; something_nonoverriddable = true;
break;
}
} }
if (something_nonoverriddable) if (something_nonoverriddable)
@ -237,7 +235,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
has_infill = true; has_infill = true;
if (m_print_config_ptr) { if (m_print_config_ptr) {
if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable_and_mark(*fill, *m_print_config_ptr, object, region)) if (! layer_tools.wiping_extrusions().is_overriddable_and_mark(*fill, *m_print_config_ptr, object, region))
something_nonoverriddable = true; something_nonoverriddable = true;
} }
} }

View file

@ -30,14 +30,6 @@ public:
// When allocating extruder overrides of an object's ExtrusionEntity, overrides for maximum 3 copies are allocated in place. // When allocating extruder overrides of an object's ExtrusionEntity, overrides for maximum 3 copies are allocated in place.
typedef boost::container::small_vector<int32_t, 3> ExtruderPerCopy; typedef boost::container::small_vector<int32_t, 3> ExtruderPerCopy;
class ExtruderOverrides
{
public:
ExtruderOverrides(const ExtruderPerCopy *overrides, const int correct_extruder_id) : m_overrides(overrides) {}
private:
const ExtruderPerCopy *m_overrides;
};
// This is called from GCode::process_layer - see implementation for further comments: // This is called from GCode::process_layer - see implementation for further comments:
const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies); const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies);

View file

@ -39,7 +39,7 @@ public:
// last point == first point for polygons // last point == first point for polygons
const Point& last_point() const override { return this->points.front(); } const Point& last_point() const override { return this->points.front(); }
virtual Lines lines() const; Lines lines() const override;
Polyline split_at_vertex(const Point &point) const; Polyline split_at_vertex(const Point &point) const;
// Split a closed polygon into an open polyline, with the split point duplicated at both ends. // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
Polyline split_at_index(int index) const; Polyline split_at_index(int index) const;

View file

@ -65,7 +65,7 @@ public:
const Point& last_point() const override { return this->points.back(); } const Point& last_point() const override { return this->points.back(); }
const Point& leftmost_point() const; const Point& leftmost_point() const;
virtual Lines lines() const; Lines lines() const override;
void clip_end(double distance); void clip_end(double distance);
void clip_start(double distance); void clip_start(double distance);
void extend_end(double distance); void extend_end(double distance);

View file

@ -349,7 +349,7 @@ public:
Print() = default; Print() = default;
virtual ~Print() { this->clear(); } virtual ~Print() { this->clear(); }
PrinterTechnology technology() const noexcept { return ptFFF; } PrinterTechnology technology() const noexcept override { return ptFFF; }
// Methods, which change the state of Print / PrintObject / PrintRegion. // Methods, which change the state of Print / PrintObject / PrintRegion.
// The following methods are synchronized with process() and export_gcode(), // The following methods are synchronized with process() and export_gcode(),

View file

@ -36,7 +36,9 @@ void AboutDialogLogo::onRepaint(wxEvent &event)
// CopyrightsDialog // CopyrightsDialog
// ----------------------------------------- // -----------------------------------------
CopyrightsDialog::CopyrightsDialog() CopyrightsDialog::CopyrightsDialog()
: DPIDialog(NULL, wxID_ANY, wxString::Format("%s - %s", SLIC3R_APP_NAME, _(L("Portions copyright"))), : DPIDialog(NULL, wxID_ANY, from_u8((boost::format("%1% - %2%")
% SLIC3R_APP_NAME
% _utf8(L("Portions copyright"))).str()),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{ {
this->SetFont(wxGetApp().normal_font()); this->SetFont(wxGetApp().normal_font());
@ -196,7 +198,7 @@ void CopyrightsDialog::onCloseDialog(wxEvent &)
} }
AboutDialog::AboutDialog() AboutDialog::AboutDialog()
: DPIDialog(NULL, wxID_ANY, wxString::Format(_(L("About %s")), SLIC3R_APP_NAME), wxDefaultPosition, : DPIDialog(NULL, wxID_ANY, from_u8((boost::format(_utf8(L("About %s"))) % SLIC3R_APP_NAME).str()), wxDefaultPosition,
wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) wxDefaultSize, /*wxCAPTION*/wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{ {
SetFont(wxGetApp().normal_font()); SetFont(wxGetApp().normal_font());
@ -253,32 +255,33 @@ AboutDialog::AboutDialog()
int size[] = {fs,fs,fs,fs,fs,fs,fs}; int size[] = {fs,fs,fs,fs,fs,fs,fs};
m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
m_html->SetBorders(2); m_html->SetBorders(2);
const wxString copyright_str = _(L("Copyright")); const std::string copyright_str = _utf8(L("Copyright"));
// TRN "Slic3r _is licensed under the_ License" // TRN "Slic3r _is licensed under the_ License"
const wxString is_lecensed_str = _(L("is licensed under the")); const std::string is_lecensed_str = _utf8(L("is licensed under the"));
const wxString license_str = _(L("GNU Affero General Public License, version 3")); const std::string license_str = _utf8(L("GNU Affero General Public License, version 3"));
const wxString based_on_str = _(L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community.")); const std::string based_on_str = _utf8(L("PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap community."));
const wxString contributors_str = _(L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others.")); const std::string contributors_str = _utf8(L("Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others."));
const auto text = wxString::Format( const auto text = from_u8(
(boost::format(
"<html>" "<html>"
"<body bgcolor= %s link= %s>" "<body bgcolor= %1% link= %2%>"
"<font color=%s>" "<font color=%3%>"
"%s &copy; 2016-2019 Prusa Research. <br />" "%4% &copy; 2016-2019 Prusa Research. <br />"
"%s &copy; 2011-2018 Alessandro Ranellucci. <br />" "%5% &copy; 2011-2018 Alessandro Ranellucci. <br />"
"<a href=\"http://slic3r.org/\">Slic3r</a> %s " "<a href=\"http://slic3r.org/\">Slic3r</a> %6% "
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%s</a>." "<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">%7%</a>."
"<br /><br />" "<br /><br />"
"%s" "%8%"
"<br /><br />" "<br /><br />"
"%s" "%9%"
"</font>" "</font>"
"</body>" "</body>"
"</html>", bgr_clr_str, text_clr_str, text_clr_str, "</html>") % bgr_clr_str % text_clr_str % text_clr_str
copyright_str, copyright_str, % copyright_str % copyright_str
is_lecensed_str, % is_lecensed_str
license_str, % license_str
based_on_str, % based_on_str
contributors_str); % contributors_str).str());
m_html->SetPage(text); m_html->SetPage(text);
vsizer->Add(m_html, 1, wxEXPAND | wxBOTTOM, 10); vsizer->Add(m_html, 1, wxEXPAND | wxBOTTOM, 10);
m_html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this); m_html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this);

View file

@ -295,79 +295,15 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe
it->second["zoom_speed"] = std::to_string(zoom_speed); it->second["zoom_speed"] = std::to_string(zoom_speed);
} }
bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed) std::vector<std::string> AppConfig::get_mouse_device_names() const
{ {
std::string key = std::string("mouse_device:") + name; static constexpr char *prefix = "mouse_device:";
auto it = m_storage.find(key); static constexpr size_t prefix_len = 13; // strlen(prefix); reports error C2131: expression did not evaluate to a constant on VS2019
if (it == m_storage.end()) std::vector<std::string> out;
return false; for (const std::pair<std::string, std::map<std::string, std::string>>& key_value_pair : m_storage)
if (boost::starts_with(key_value_pair.first, "mouse_device:") && key_value_pair.first.size() > prefix_len)
auto it_val = it->second.find("translation_speed"); out.emplace_back(key_value_pair.first.substr(prefix_len));
if (it_val == it->second.end()) return out;
return false;
speed = ::atof(it_val->second.c_str());
return true;
}
bool AppConfig::get_mouse_device_translation_deadzone(const std::string& name, double& deadzone)
{
std::string key = std::string("mouse_device:") + name;
auto it = m_storage.find(key);
if (it == m_storage.end())
return false;
auto it_val = it->second.find("translation_deadzone");
if (it_val == it->second.end())
return false;
deadzone = ::atof(it_val->second.c_str());
return true;
}
bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& speed)
{
std::string key = std::string("mouse_device:") + name;
auto it = m_storage.find(key);
if (it == m_storage.end())
return false;
auto it_val = it->second.find("rotation_speed");
if (it_val == it->second.end())
return false;
speed = (float)::atof(it_val->second.c_str());
return true;
}
bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone)
{
std::string key = std::string("mouse_device:") + name;
auto it = m_storage.find(key);
if (it == m_storage.end())
return false;
auto it_val = it->second.find("rotation_deadzone");
if (it_val == it->second.end())
return false;
deadzone = (float)::atof(it_val->second.c_str());
return true;
}
bool AppConfig::get_mouse_device_zoom_speed(const std::string& name, double& speed)
{
std::string key = std::string("mouse_device:") + name;
auto it = m_storage.find(key);
if (it == m_storage.end())
return false;
auto it_val = it->second.find("zoom_speed");
if (it_val == it->second.end())
return false;
speed = (float)::atof(it_val->second.c_str());
return true;
} }
void AppConfig::update_config_dir(const std::string &dir) void AppConfig::update_config_dir(const std::string &dir)

View file

@ -134,16 +134,36 @@ public:
void set_recent_projects(const std::vector<std::string>& recent_projects); void set_recent_projects(const std::vector<std::string>& recent_projects);
void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed); void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed);
bool get_mouse_device_translation_speed(const std::string& name, double& speed); std::vector<std::string> get_mouse_device_names() const;
bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone); bool get_mouse_device_translation_speed(const std::string& name, double& speed) const
bool get_mouse_device_rotation_speed(const std::string& name, float& speed); { return get_3dmouse_device_numeric_value(name, "translation_speed", speed); }
bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone); bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone) const
bool get_mouse_device_zoom_speed(const std::string& name, double& speed); { return get_3dmouse_device_numeric_value(name, "translation_deadzone", deadzone); }
bool get_mouse_device_rotation_speed(const std::string& name, float& speed) const
{ return get_3dmouse_device_numeric_value(name, "rotation_speed", speed); }
bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone) const
{ return get_3dmouse_device_numeric_value(name, "rotation_deadzone", deadzone); }
bool get_mouse_device_zoom_speed(const std::string& name, double& speed) const
{ return get_3dmouse_device_numeric_value(name, "zoom_speed", speed); }
static const std::string SECTION_FILAMENTS; static const std::string SECTION_FILAMENTS;
static const std::string SECTION_MATERIALS; static const std::string SECTION_MATERIALS;
private: private:
template<typename T>
bool get_3dmouse_device_numeric_value(const std::string &device_name, const char *parameter_name, T &out) const
{
std::string key = std::string("mouse_device:") + device_name;
auto it = m_storage.find(key);
if (it == m_storage.end())
return false;
auto it_val = it->second.find(parameter_name);
if (it_val == it->second.end())
return false;
out = T(::atof(it_val->second.c_str()));
return true;
}
// Map of section, name -> value // Map of section, name -> value
std::map<std::string, std::map<std::string, std::string>> m_storage; std::map<std::string, std::map<std::string, std::string>> m_storage;
// Map of enabled vendors / models / variants // Map of enabled vendors / models / variants

View file

@ -1,5 +1,6 @@
#include "BackgroundSlicingProcess.hpp" #include "BackgroundSlicingProcess.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "GUI.hpp"
#include <wx/app.h> #include <wx/app.h>
#include <wx/panel.h> #include <wx/panel.h>
@ -219,9 +220,9 @@ void BackgroundSlicingProcess::thread_proc()
// Canceled, this is all right. // Canceled, this is all right.
assert(m_print->canceled()); assert(m_print->canceled());
} catch (const std::bad_alloc& ex) { } catch (const std::bad_alloc& ex) {
wxString errmsg = wxString::Format(_(L("%s has encountered an error. It was likely caused by running out of memory. " wxString errmsg = GUI::from_u8((boost::format(_utf8(L("%s has encountered an error. It was likely caused by running out of memory. "
"If you are sure you have enough RAM on your system, this may also be a bug and we would " "If you are sure you have enough RAM on your system, this may also be a bug and we would "
"be glad if you reported it.")), SLIC3R_APP_NAME); "be glad if you reported it."))) % SLIC3R_APP_NAME).str());
error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what()); error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what());
} catch (std::exception &ex) { } catch (std::exception &ex) {
error = ex.what(); error = ex.what();

View file

@ -215,14 +215,14 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
void BonjourDialog::on_timer(wxTimerEvent &) void BonjourDialog::on_timer(wxTimerEvent &)
{ {
const auto search_str = _(L("Searching for devices")); const auto search_str = _utf8(L("Searching for devices"));
if (timer_state > 0) { if (timer_state > 0) {
const std::string dots(timer_state, '.'); const std::string dots(timer_state, '.');
label->SetLabel(wxString::Format("%s %s", search_str, dots)); label->SetLabel(GUI::from_u8((boost::format("%1% %2%") % search_str % dots).str()));
timer_state = (timer_state) % 3 + 1; timer_state = (timer_state) % 3 + 1;
} else { } else {
label->SetLabel(wxString::Format("%s: %s", search_str, _(L("Finished"))+".")); label->SetLabel(GUI::from_u8((boost::format("%1%: %2%") % search_str % (_utf8(L("Finished"))+".")).str()));
timer->Stop(); timer->Stop();
} }
} }

View file

@ -323,6 +323,7 @@ void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, b
Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target; Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ()); auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ());
m_view_rotation *= rot_z * Eigen::AngleAxisd(delta_zenit_rad, rot_z.inverse() * get_dir_right()); m_view_rotation *= rot_z * Eigen::AngleAxisd(delta_zenit_rad, rot_z.inverse() * get_dir_right());
m_view_rotation.normalize();
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.)); m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
} }
@ -334,6 +335,7 @@ void Camera::rotate_local_around_target(const Vec3d& rotation_rad)
Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target; Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
Vec3d axis = m_view_rotation.conjugate() * rotation_rad.normalized(); Vec3d axis = m_view_rotation.conjugate() * rotation_rad.normalized();
m_view_rotation *= Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)); m_view_rotation *= Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis));
m_view_rotation.normalize();
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.)); m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
update_zenit(); update_zenit();
} }
@ -559,6 +561,7 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up
// Initialize the rotation quaternion from the rotation submatrix of of m_view_matrix. // Initialize the rotation quaternion from the rotation submatrix of of m_view_matrix.
m_view_rotation = Eigen::Quaterniond(m_view_matrix.matrix().template block<3, 3>(0, 0)); m_view_rotation = Eigen::Quaterniond(m_view_matrix.matrix().template block<3, 3>(0, 0));
m_view_rotation.normalize();
update_zenit(); update_zenit();
} }
@ -571,6 +574,7 @@ void Camera::set_default_orientation()
double sin_theta = ::sin(theta_rad); double sin_theta = ::sin(theta_rad);
Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad)); Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
m_view_rotation = Eigen::AngleAxisd(theta_rad, Vec3d::UnitX()) * Eigen::AngleAxisd(phi_rad, Vec3d::UnitZ()); m_view_rotation = Eigen::AngleAxisd(theta_rad, Vec3d::UnitX()) * Eigen::AngleAxisd(phi_rad, Vec3d::UnitZ());
m_view_rotation.normalize();
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- camera_pos), m_view_rotation, Vec3d(1., 1., 1.)); m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- camera_pos), m_view_rotation, Vec3d(1., 1., 1.));
} }

View file

@ -72,7 +72,7 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
} }
if (! compatible) { if (! compatible) {
text += "<p align=\"right\">" + wxString::Format(_(L("Incompatible with this %s")), SLIC3R_APP_NAME) + "</p>"; text += "<p align=\"right\">" + from_u8((boost::format(_utf8(L("Incompatible with this %s"))) % SLIC3R_APP_NAME).str()) + "</p>";
} }
else if (! snapshot_active) else if (! snapshot_active)
text += "<p align=\"right\"><a href=\"" + snapshot.id + "\">" + _(L("Activate")) + "</a></p>"; text += "<p align=\"right\"><a href=\"" + snapshot.id + "\">" + _(L("Activate")) + "</a></p>";

View file

@ -215,7 +215,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
const auto &variant = model.variants[i]; const auto &variant = model.variants[i];
const auto label = model.technology == ptFFF const auto label = model.technology == ptFFF
? wxString::Format("%s %s %s", variant.name, _(L("mm")), _(L("nozzle"))) ? from_u8((boost::format("%1% %2% %3%") % variant.name % _utf8(L("mm")) % _utf8(L("nozzle"))).str())
: from_u8(model.name); : from_u8(model.name);
if (i == 1) { if (i == 1) {
@ -422,17 +422,17 @@ void ConfigWizardPage::append_spacer(int space)
// Wizard pages // Wizard pages
PageWelcome::PageWelcome(ConfigWizard *parent) PageWelcome::PageWelcome(ConfigWizard *parent)
: ConfigWizardPage(parent, wxString::Format( : ConfigWizardPage(parent, from_u8((boost::format(
#ifdef __APPLE__ #ifdef __APPLE__
_(L("Welcome to the %s Configuration Assistant")) _utf8(L("Welcome to the %s Configuration Assistant"))
#else #else
_(L("Welcome to the %s Configuration Wizard")) _utf8(L("Welcome to the %s Configuration Wizard"))
#endif #endif
, SLIC3R_APP_NAME), _(L("Welcome"))) ) % SLIC3R_APP_NAME).str()), _(L("Welcome")))
, welcome_text(append_text(wxString::Format( , welcome_text(append_text(from_u8((boost::format(
_(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), _utf8(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")))
SLIC3R_APP_NAME, % SLIC3R_APP_NAME
_(ConfigWizard::name())) % _utf8(ConfigWizard::name())).str())
)) ))
, cbox_reset(append( , cbox_reset(append(
new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))) new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)")))
@ -478,7 +478,7 @@ PagePrinters::PagePrinters(ConfigWizard *parent,
continue; continue;
} }
const auto picker_title = family.empty() ? wxString() : wxString::Format(_(L("%s Family")), family); const auto picker_title = family.empty() ? wxString() : from_u8((boost::format(_utf8(L("%s Family"))) % family).str());
auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, *appconfig, filter); auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, *appconfig, filter);
picker->Bind(EVT_PRINTER_PICK, [this, appconfig](const PrinterPickerEvent &evt) { picker->Bind(EVT_PRINTER_PICK, [this, appconfig](const PrinterPickerEvent &evt) {

View file

@ -530,7 +530,7 @@ wxString Control::get_label(int tick) const
const wxString str = m_values.empty() ? const wxString str = m_values.empty() ?
wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) :
wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None);
return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); return from_u8((boost::format("%1%\n(%2%)") % str % (m_values.empty() ? value : value+1)).str());
} }
void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, bool right_side/*=true*/) const
@ -952,8 +952,8 @@ wxString Control::get_tooltip(int tick/*=-1*/)
return _(L("Discard all custom changes")); return _(L("Discard all custom changes"));
if (m_focus == fiCogIcon) if (m_focus == fiCogIcon)
return m_mode == t_mode::MultiAsSingle ? return m_mode == t_mode::MultiAsSingle ?
wxString::Format(_(L("Jump to height %s or " GUI::from_u8((boost::format(_utf8(L("Jump to height %s or "
"Set extruder sequence for the entire print")), " (Shift + G)\n") : "Set extruder sequence for the entire print"))) % " (Shift + G)\n").str()) :
_(L("Jump to height")) + " (Shift + G)"; _(L("Jump to height")) + " (Shift + G)";
if (m_focus == fiColorBand) if (m_focus == fiColorBand)
return m_mode != t_mode::SingleExtruder ? "" : return m_mode != t_mode::SingleExtruder ? "" :
@ -1914,8 +1914,8 @@ bool Control::check_ticks_changed_event(const std::string& gcode)
{ {
wxString message = m_mode == t_mode::SingleExtruder ? ( wxString message = m_mode == t_mode::SingleExtruder ? (
_(L("The last color change data was saved for a multi extruder printing.")) + "\n\n" + _(L("The last color change data was saved for a multi extruder printing.")) + "\n\n" +
_(L("Select YES if you want to delete all saved tool changes, \n\t" _(L("Select YES if you want to delete all saved tool changes, \n"
"NO if you want all tool changes switch to color changes, \n\t" "NO if you want all tool changes switch to color changes, \n"
"or CANCEL to leave it unchanged.")) + "\n\n\t" + "or CANCEL to leave it unchanged.")) + "\n\n\t" +
_(L("Do you want to delete all saved tool changes?")) _(L("Do you want to delete all saved tool changes?"))
) : ( // t_mode::MultiExtruder ) : ( // t_mode::MultiExtruder

View file

@ -172,7 +172,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
if (label.Last() == '\n') label.RemoveLast(); if (label.Last() == '\n') label.RemoveLast();
while (label.Last() == ' ') label.RemoveLast(); while (label.Last() == ' ') label.RemoveLast();
if (label.Last() == ':') label.RemoveLast(); if (label.Last() == ':') label.RemoveLast();
show_error(m_parent, wxString::Format(_(L("%s doesn't support percentage")), label)); show_error(m_parent, from_u8((boost::format(_utf8(L("%s doesn't support percentage"))) % label).str()));
set_value(double_to_string(m_opt.min), true); set_value(double_to_string(m_opt.min), true);
m_value = double(m_opt.min); m_value = double(m_opt.min);
break; break;
@ -237,12 +237,12 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm"; const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm";
const wxString stVal = double_to_string(val, 2); const wxString stVal = double_to_string(val, 2);
const wxString msg_text = wxString::Format(_(L("Do you mean %s%% instead of %s %s?\n" const wxString msg_text = from_u8((boost::format(_utf8(L("Do you mean %s%% instead of %s %s?\n"
"Select YES if you want to change this value to %s%%, \n" "Select YES if you want to change this value to %s%%, \n"
"or NO if you are sure that %s %s is a correct value.")), stVal, stVal, sidetext, stVal, stVal, sidetext); "or NO if you are sure that %s %s is a correct value."))) % stVal % stVal % sidetext % stVal % stVal % sidetext).str());
wxMessageDialog dialog(m_parent, msg_text, _(L("Parameter validation")) + ": " + m_opt_id , wxICON_WARNING | wxYES | wxNO); wxMessageDialog dialog(m_parent, msg_text, _(L("Parameter validation")) + ": " + m_opt_id , wxICON_WARNING | wxYES | wxNO);
if (dialog.ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
set_value(wxString::Format("%s%%", stVal), false/*true*/); set_value(from_u8((boost::format("%s%%") % stVal).str()), false/*true*/);
str += "%%"; str += "%%";
} }
else else

View file

@ -284,7 +284,7 @@ public:
TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~TextCtrl() {} ~TextCtrl() {}
void BUILD(); void BUILD() override;
bool value_was_changed(); bool value_was_changed();
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER // Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
void propagate_value(); void propagate_value();
@ -303,9 +303,9 @@ public:
void msw_rescale(bool rescale_sidetext = false) override; void msw_rescale(bool rescale_sidetext = false) override;
virtual void enable(); void enable() override;
virtual void disable(); void disable() override;
virtual wxWindow* getWindow() { return window; } wxWindow* getWindow() override { return window; }
}; };
class CheckBox : public Field { class CheckBox : public Field {

View file

@ -3082,10 +3082,11 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
{ {
// try to filter out events coming from mouse 3d #ifdef WIN32
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); // Try to filter out spurious mouse wheel events comming from 3D mouse.
if (controller.process_mouse_wheel()) if (wxGetApp().plater()->get_mouse3d_controller().process_mouse_wheel())
return; return;
#endif
if (!m_initialized) if (!m_initialized)
return; return;
@ -3465,7 +3466,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (m_hover_volume_idxs.empty() && m_mouse.is_start_position_3D_defined()) if (m_hover_volume_idxs.empty() && m_mouse.is_start_position_3D_defined())
{ {
const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.); const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.);
if (wxGetApp().plater()->get_mouse3d_controller().is_running() || (wxGetApp().app_config->get("use_free_camera") == "1")) if (wxGetApp().plater()->get_mouse3d_controller().connected() || (wxGetApp().app_config->get("use_free_camera") == "1"))
// Virtual track ball (similar to the 3DConnexion mouse). // Virtual track ball (similar to the 3DConnexion mouse).
m_camera.rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.)); m_camera.rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.));
else else

View file

@ -302,9 +302,9 @@ void GLCanvas3DManager::init_gl()
if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) { if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) {
// Complain about the OpenGL version. // Complain about the OpenGL version.
wxString message = wxString::Format( wxString message = from_u8((boost::format(
_(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" _utf8(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n"
"while OpenGL version %s, render %s, vendor %s was detected.")), wxString(s_gl_info.get_version()), wxString(s_gl_info.get_renderer()), wxString(s_gl_info.get_vendor())); "while OpenGL version %s, render %s, vendor %s was detected."))) % s_gl_info.get_version() % s_gl_info.get_renderer() % s_gl_info.get_vendor()).str());
message += "\n"; message += "\n";
message += _(L("You may need to update your graphics card driver.")); message += _(L("You may need to update your graphics card driver."));
#ifdef _WIN32 #ifdef _WIN32

View file

@ -1,7 +1,8 @@
#ifndef slic3r_GUI_hpp_ #ifndef slic3r_GUI_hpp_
#define slic3r_GUI_hpp_ #define slic3r_GUI_hpp_
#include <boost/filesystem/path.hpp> namespace boost { class any; }
namespace boost::filesystem { class path; }
#include <wx/string.h> #include <wx/string.h>

View file

@ -798,7 +798,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt)); wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt));
const auto config_wizard_name = _(ConfigWizard::name(true)); const auto config_wizard_name = _(ConfigWizard::name(true));
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name); const auto config_wizard_tooltip = from_u8((boost::format(_utf8(L("Run %s"))) % config_wizard_name).str());
// Cmd+, is standard on OS X - what about other operating systems? // Cmd+, is standard on OS X - what about other operating systems?
local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("&Configuration Snapshots")) + dots, _(L("Inspect / activate configuration snapshots"))); local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("&Configuration Snapshots")) + dots, _(L("Inspect / activate configuration snapshots")));

View file

@ -354,7 +354,7 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
for (const auto& error : error_msg) for (const auto& error : error_msg)
if (error.second > 0) if (error.second > 0)
tooltip += wxString::Format("\t%d %s\n", error.second, _(error.first)); tooltip += from_u8((boost::format("\t%1% %2%\n") % error.second % _utf8(error.first)).str());
if (is_windows10()) if (is_windows10())
tooltip += _(L("Right button click the icon to fix STL through Netfabb")); tooltip += _(L("Right button click the icon to fix STL through Netfabb"));
@ -1180,7 +1180,7 @@ const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxStrin
for (auto& it : bundle_quick) for (auto& it : bundle_quick)
{ {
if ( bundle_name == wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)) ) if ( bundle_name == from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str()) )
return it.second; return it.second;
} }
#endif #endif
@ -1528,13 +1528,13 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
#if 0 #if 0
for (auto& it : m_freq_settings_fff) for (auto& it : m_freq_settings_fff)
{ {
settings_id = menu->FindItem(wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first))); settings_id = menu->FindItem(from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str()));
if (settings_id != wxNOT_FOUND) if (settings_id != wxNOT_FOUND)
menu->Destroy(settings_id); menu->Destroy(settings_id);
} }
for (auto& it : m_freq_settings_sla) for (auto& it : m_freq_settings_sla)
{ {
settings_id = menu->FindItem(wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first))); settings_id = menu->FindItem(from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str()));
if (settings_id != wxNOT_FOUND) if (settings_id != wxNOT_FOUND)
menu->Destroy(settings_id); menu->Destroy(settings_id);
} }
@ -1847,7 +1847,7 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu, const bool is_obje
if (improper_category(it.first, extruders_cnt)) if (improper_category(it.first, extruders_cnt))
continue; continue;
append_menu_item(menu, wxID_ANY, wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)), "", append_menu_item(menu, wxID_ANY, from_u8((boost::format(_utf8(L("Quick Add Settings (%s)"))) % _(it.first)).str()), "",
[menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); }, [menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); },
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu, CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu,
[this]() { return true; }, wxGetApp().plater()); [this]() { return true; }, wxGetApp().plater());
@ -4091,9 +4091,9 @@ void ObjectList::toggle_printable_state(wxDataViewItem item)
// get object's printable and change it // get object's printable and change it
const bool printable = !m_objects_model->IsPrintable(item); const bool printable = !m_objects_model->IsPrintable(item);
const wxString snapshot_text = wxString::Format("%s %s", const wxString snapshot_text = from_u8((boost::format("%1% %2%")
printable ? _(L("Set Printable")) : _(L("Set Unprintable")), % (printable ? _(L("Set Printable")) : _(L("Set Unprintable")))
object->name); % object->name).str());
take_snapshot(snapshot_text); take_snapshot(snapshot_text);
// set printable value for all instances in object // set printable value for all instances in object

View file

@ -98,7 +98,7 @@ bool ObjectSettings::update_settings_list()
btn->SetBitmapHover(m_bmp_delete_focus.bmp()); btn->SetBitmapHover(m_bmp_delete_focus.bmp());
btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) { btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) {
wxGetApp().plater()->take_snapshot(wxString::Format(_(L("Delete Option %s")), opt_key)); wxGetApp().plater()->take_snapshot(from_u8((boost::format(_utf8(L("Delete Option %s"))) % opt_key).str()));
config->erase(opt_key); config->erase(opt_key);
wxGetApp().obj_list()->changed_object(); wxGetApp().obj_list()->changed_object();
wxTheApp->CallAfter([this]() { wxTheApp->CallAfter([this]() {
@ -149,7 +149,7 @@ bool ObjectSettings::update_settings_list()
optgroup->get_field(opt)->m_on_change = [optgroup](const std::string& opt_id, const boost::any& value) { optgroup->get_field(opt)->m_on_change = [optgroup](const std::string& opt_id, const boost::any& value) {
// first of all take a snapshot and then change value in configuration // first of all take a snapshot and then change value in configuration
wxGetApp().plater()->take_snapshot(wxString::Format(_(L("Change Option %s")), opt_id)); wxGetApp().plater()->take_snapshot(from_u8((boost::format(_utf8(L("Change Option %s"))) % opt_id).str()));
optgroup->on_change_OG(opt_id, value); optgroup->on_change_OG(opt_id, value);
}; };

View file

@ -1,7 +1,8 @@
#ifndef slic3r_LambdaObjectDialog_hpp_ #ifndef slic3r_LambdaObjectDialog_hpp_
#define slic3r_LambdaObjectDialog_hpp_ #define slic3r_LambdaObjectDialog_hpp_
#include "GUI.hpp" #include <vector>
#include <memory>
#include <wx/dialog.h> #include <wx/dialog.h>
#include <wx/sizer.h> #include <wx/sizer.h>

View file

@ -109,7 +109,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
return; return;
} }
if(m_plater) m_plater->stop_jobs(); if (m_plater)
m_plater->stop_jobs();
// Weird things happen as the Paint messages are floating around the windows being destructed. // Weird things happen as the Paint messages are floating around the windows being destructed.
// Avoid the Paint messages by hiding the main window. // Avoid the Paint messages by hiding the main window.
@ -117,6 +118,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
// In addition, there were some crashes due to the Paint events sent to already destructed windows. // In addition, there were some crashes due to the Paint events sent to already destructed windows.
this->Show(false); this->Show(false);
// Stop the background thread (Windows and Linux).
// Disconnect from a 3DConnextion driver (OSX).
m_plater->get_mouse3d_controller().shutdown();
// Store the device parameter database back to appconfig.
m_plater->get_mouse3d_controller().save_config(*wxGetApp().app_config);
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback, // Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
// but in rare cases it may not have been called yet. // but in rare cases it may not have been called yet.
wxGetApp().app_config->save(); wxGetApp().app_config->save();
@ -827,7 +834,7 @@ void MainFrame::quick_slice(const int qs)
} }
else if (qs & qsSaveAs) { else if (qs & qsSaveAs) {
// The following line may die if the output_filename_format template substitution fails. // The following line may die if the output_filename_format template substitution fails.
wxFileDialog dlg(this, wxString::Format(_(L("Save %s file as:")) , qs & qsExportSVG ? _(L("SVG")) : _(L("G-code")) ), wxFileDialog dlg(this, from_u8((boost::format(_utf8(L("Save %s file as:"))) % ((qs & qsExportSVG) ? _(L("SVG")) : _(L("G-code")))).str()),
wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file),
qs & qsExportSVG ? file_wildcards(FT_SVG) : file_wildcards(FT_GCODE), qs & qsExportSVG ? file_wildcards(FT_SVG) : file_wildcards(FT_GCODE),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT); wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@ -850,7 +857,7 @@ void MainFrame::quick_slice(const int qs)
// show processbar dialog // show processbar dialog
m_progress_dialog = new wxProgressDialog(_(L("Slicing")) + dots, m_progress_dialog = new wxProgressDialog(_(L("Slicing")) + dots,
// TRN "Processing input_file_basename" // TRN "Processing input_file_basename"
wxString::Format(_(L("Processing %s")), input_file_basename + dots), from_u8((boost::format(_utf8(L("Processing %s"))) % (input_file_basename + dots)).str()),
100, this, 4); 100, this, 4);
m_progress_dialog->Pulse(); m_progress_dialog->Pulse();
{ {
@ -1029,7 +1036,7 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
wxGetApp().load_current_presets(); wxGetApp().load_current_presets();
const auto message = wxString::Format(_(L("%d presets successfully imported.")), presets_imported); const auto message = wxString::Format(_(L("%d presets successfully imported.")), presets_imported);
Slic3r::GUI::show_info(this, message, "Info"); Slic3r::GUI::show_info(this, message, wxString("Info"));
} }
// Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset. // Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.

View file

@ -54,204 +54,162 @@ static const std::vector<int> _3DCONNEXION_DEVICES =
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
const double Mouse3DController::State::DefaultTranslationScale = 2.5;
const double Mouse3DController::State::MaxTranslationDeadzone = 0.2;
const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone;
const float Mouse3DController::State::DefaultRotationScale = 1.0f;
const float Mouse3DController::State::MaxRotationDeadzone = 0.2f;
const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone;
const double Mouse3DController::State::DefaultZoomScale = 0.1;
Mouse3DController::State::State()
: m_buttons_enabled(false)
, m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone)
, m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone)
, m_zoom_params(DefaultZoomScale, 0.0)
, m_mouse_wheel_counter(0)
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
, m_translation_queue_max_size(0) template<typename T>
, m_rotation_queue_max_size(0) void update_maximum(std::atomic<T>& maximum_value, T const& value) noexcept
, m_buttons_queue_max_size(0) {
T prev_value = maximum_value;
while (prev_value < value && ! maximum_value.compare_exchange_weak(prev_value, value)) ;
}
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
{
}
void Mouse3DController::State::append_translation(const Vec3d& translation) void Mouse3DController::State::append_translation(const Vec3d& translation, size_t input_queue_max_size)
{ {
while (m_translation.queue.size() >= m_translation.max_size) tbb::mutex::scoped_lock lock(m_input_queue_mutex);
{ while (m_input_queue.size() >= input_queue_max_size)
m_translation.queue.pop(); m_input_queue.pop_front();
} m_input_queue.emplace_back(QueueItem::translation(translation));
m_translation.queue.push(translation);
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
m_translation_queue_max_size = std::max(m_translation_queue_max_size, m_translation.queue.size()); update_maximum(input_queue_max_size_achieved, m_input_queue.size());
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
} }
void Mouse3DController::State::append_rotation(const Vec3f& rotation) void Mouse3DController::State::append_rotation(const Vec3f& rotation, size_t input_queue_max_size)
{ {
while (m_rotation.queue.size() >= m_rotation.max_size) tbb::mutex::scoped_lock lock(m_input_queue_mutex);
{ while (m_input_queue.size() >= input_queue_max_size)
m_rotation.queue.pop(); m_input_queue.pop_front();
} m_input_queue.emplace_back(QueueItem::rotation(rotation.cast<double>()));
m_rotation.queue.push(rotation); #ifdef WIN32
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT if (rotation.x() != 0.0f)
m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, m_rotation.queue.size());
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (rotation(0) != 0.0f)
++ m_mouse_wheel_counter; ++ m_mouse_wheel_counter;
} #endif // WIN32
void Mouse3DController::State::append_button(unsigned int id)
{
if (!m_buttons_enabled)
return;
m_buttons.push(id);
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size()); update_maximum(input_queue_max_size_achieved, m_input_queue.size());
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
} }
void Mouse3DController::State::append_button(unsigned int id, size_t /* input_queue_max_size */)
{
tbb::mutex::scoped_lock lock(m_input_queue_mutex);
m_input_queue.emplace_back(QueueItem::buttons(id));
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
update_maximum(input_queue_max_size_achieved, m_input_queue.size());
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
}
#ifdef WIN32
// Filter out mouse scroll events produced by the 3DConnexion driver.
bool Mouse3DController::State::process_mouse_wheel() bool Mouse3DController::State::process_mouse_wheel()
{ {
if (m_mouse_wheel_counter.load() == 0) tbb::mutex::scoped_lock lock(m_input_queue_mutex);
if (m_mouse_wheel_counter == 0)
// No 3DConnexion rotation has been captured since the last mouse scroll event.
return false; return false;
else if (!m_rotation.queue.empty()) if (std::find_if(m_input_queue.begin(), m_input_queue.end(), [](const QueueItem &item){ return item.is_rotation(); }) != m_input_queue.end()) {
{ // There is a rotation stored in the queue. Suppress one mouse scroll event.
-- m_mouse_wheel_counter; -- m_mouse_wheel_counter;
return true; return true;
} }
m_mouse_wheel_counter = 0;
m_mouse_wheel_counter.store(0);
return true; return true;
} }
#endif // WIN32
void Mouse3DController::State::set_queues_max_size(size_t size) bool Mouse3DController::State::apply(const Mouse3DController::Params &params, Camera& camera)
{
if (size > 0)
{
m_translation.max_size = size;
m_rotation.max_size = size;
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
m_translation_queue_max_size = 0;
m_rotation_queue_max_size = 0;
m_buttons_queue_max_size = 0;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
}
}
bool Mouse3DController::State::apply(Camera& camera)
{ {
if (! wxGetApp().IsActive()) if (! wxGetApp().IsActive())
return false; return false;
bool ret = false; std::deque<QueueItem> input_queue;
if (has_translation())
{ {
const Vec3d& translation = m_translation.queue.front(); // Atomically move m_input_queue to input_queue.
tbb::mutex::scoped_lock lock(m_input_queue_mutex);
input_queue = std::move(m_input_queue);
m_input_queue.clear();
}
for (const QueueItem &input_queue_item : input_queue) {
if (input_queue_item.is_translation()) {
const Vec3d& translation = input_queue_item.vector;
double zoom_factor = camera.min_zoom() / camera.get_zoom(); double zoom_factor = camera.min_zoom() / camera.get_zoom();
camera.set_target(camera.get_target() + zoom_factor * m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(2) * camera.get_dir_up())); camera.set_target(camera.get_target() + zoom_factor * params.translation.scale * (translation.x() * camera.get_dir_right() + translation.z() * camera.get_dir_up()));
if (translation(1) != 0.0) if (translation.y() != 0.0)
camera.update_zoom(m_zoom_params.scale * translation(1) / std::abs(translation(1))); camera.update_zoom(params.zoom.scale * translation.y() / std::abs(translation.y()));
m_translation.queue.pop(); } else if (input_queue_item.is_rotation()) {
ret = true; Vec3d rot = params.rotation.scale * input_queue_item.vector * (PI / 180.);
}
if (has_rotation())
{
Vec3d rot = (m_rotation_params.scale * m_rotation.queue.front()).cast<double>() * (PI / 180.);
camera.rotate_local_around_target(Vec3d(rot.x(), - rot.z(), rot.y())); camera.rotate_local_around_target(Vec3d(rot.x(), - rot.z(), rot.y()));
m_rotation.queue.pop(); break;
ret = true; } else {
assert(input_queue_item.is_buttons());
switch (input_queue_item.type_or_buttons) {
case 0: camera.update_zoom(1.0); break;
case 1: camera.update_zoom(-1.0); break;
default: break;
}
}
} }
if (m_buttons_enabled && has_button()) return ! input_queue.empty();
}
// Load the device parameter database from appconfig. To be called on application startup.
void Mouse3DController::load_config(const AppConfig &appconfig)
{ {
unsigned int button = m_buttons.front(); // We do not synchronize m_params_by_device with the background thread explicitely
switch (button) // as there should be a full memory barrier executed once the background thread is started.
m_params_by_device.clear();
for (const std::string &device_name : appconfig.get_mouse_device_names()) {
double translation_speed = 4.0;
float rotation_speed = 4.0;
double translation_deadzone = Params::DefaultTranslationDeadzone;
float rotation_deadzone = Params::DefaultRotationDeadzone;
double zoom_speed = 2.0;
appconfig.get_mouse_device_translation_speed(device_name, translation_speed);
appconfig.get_mouse_device_translation_deadzone(device_name, translation_deadzone);
appconfig.get_mouse_device_rotation_speed(device_name, rotation_speed);
appconfig.get_mouse_device_rotation_deadzone(device_name, rotation_deadzone);
appconfig.get_mouse_device_zoom_speed(device_name, zoom_speed);
// clamp to valid values
Params params;
params.translation.scale = Params::DefaultTranslationScale * std::clamp(translation_speed, 0.1, 10.0);
params.translation.deadzone = std::clamp(translation_deadzone, 0.0, Params::MaxTranslationDeadzone);
params.rotation.scale = Params::DefaultRotationScale * std::clamp(rotation_speed, 0.1f, 10.0f);
params.rotation.deadzone = std::clamp(rotation_deadzone, 0.0f, Params::MaxRotationDeadzone);
params.zoom.scale = Params::DefaultZoomScale * std::clamp(zoom_speed, 0.1, 10.0);
m_params_by_device[device_name] = std::move(params);
}
}
// Store the device parameter database back to appconfig. To be called on application closeup.
void Mouse3DController::save_config(AppConfig &appconfig) const
{ {
case 0: { camera.update_zoom(1.0); break; } // We do not synchronize m_params_by_device with the background thread explicitely
case 1: { camera.update_zoom(-1.0); break; } // as there should be a full memory barrier executed once the background thread is stopped.
default: { break; } for (const std::pair<std::string, Params> &key_value_pair : m_params_by_device) {
const std::string &device_name = key_value_pair.first;
const Params &params = key_value_pair.second;
// Store current device parameters into the config
appconfig.set_mouse_device(device_name, params.translation.scale / Params::DefaultTranslationScale, params.translation.deadzone,
params.rotation.scale / Params::DefaultRotationScale, params.rotation.deadzone, params.zoom.scale / Params::DefaultZoomScale);
} }
m_buttons.pop();
ret = true;
}
return ret;
}
Mouse3DController::Mouse3DController()
: m_initialized(false)
, m_device(nullptr)
, m_device_str("")
, m_running(false)
, m_show_settings_dialog(false)
, m_mac_mouse_connected(false)
, m_settings_dialog_closed_by_user(false)
#if __APPLE__
,m_handler_mac(new Mouse3DHandlerMac(this))
#endif //__APPLE__
{
m_last_time = std::chrono::high_resolution_clock::now();
}
void Mouse3DController::init()
{
if (m_initialized)
return;
// Initialize the hidapi library
int res = hid_init();
if (res != 0)
{
BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library";
return;
}
m_initialized = true;
}
void Mouse3DController::shutdown()
{
if (!m_initialized)
return;
stop();
disconnect_device();
// Finalize the hidapi library
hid_exit();
m_initialized = false;
} }
bool Mouse3DController::apply(Camera& camera) bool Mouse3DController::apply(Camera& camera)
{ {
if (!m_initialized)
return false;
// check if the user unplugged the device // check if the user unplugged the device
if (!is_running() && is_device_connected()) if (! m_connected) {
{
disconnect_device();
// hides the settings dialog if the user un-plug the device // hides the settings dialog if the user un-plug the device
m_show_settings_dialog = false; m_show_settings_dialog = false;
m_settings_dialog_closed_by_user = false; m_settings_dialog_closed_by_user = false;
} }
return m_state.apply(m_params, camera);
// check if the user plugged the device
if (connect_device())
start();
return is_device_connected() ? m_state.apply(camera) : false;
} }
void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
{ {
if (!is_running() || !m_show_settings_dialog) if (! m_show_settings_dialog || ! m_connected)
return; return;
// when the user clicks on [X] or [Close] button we need to trigger // when the user clicks on [X] or [Close] button we need to trigger
@ -264,6 +222,13 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
return; return;
} }
Params params_copy;
bool params_changed = false;
{
tbb::mutex::scoped_lock lock(m_params_ui_mutex);
params_copy = m_params_ui;
}
Size cnv_size = canvas.get_canvas_size(); Size cnv_size = canvas.get_canvas_size();
ImGuiWrapper& imgui = *wxGetApp().imgui(); ImGuiWrapper& imgui = *wxGetApp().imgui();
@ -296,30 +261,40 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
imgui.text(_(L("Speed:"))); imgui.text(_(L("Speed:")));
ImGui::PopStyleColor(); ImGui::PopStyleColor();
float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; float translation_scale = (float)params_copy.translation.scale / Params::DefaultTranslationScale;
if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.1f, 10.0f, "%.1f")) if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.1f, 10.0f, "%.1f")) {
m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale); params_copy.translation.scale = Params::DefaultTranslationScale * (double)translation_scale;
params_changed = true;
}
float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale; float rotation_scale = params_copy.rotation.scale / Params::DefaultRotationScale;
if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.1f, 10.0f, "%.1f")) if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.1f, 10.0f, "%.1f")) {
m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale); params_copy.rotation.scale = Params::DefaultRotationScale * rotation_scale;
params_changed = true;
}
float zoom_scale = m_state.get_zoom_scale() / State::DefaultZoomScale; float zoom_scale = params_copy.zoom.scale / Params::DefaultZoomScale;
if (imgui.slider_float(_(L("Zoom")), &zoom_scale, 0.1f, 10.0f, "%.1f")) if (imgui.slider_float(_(L("Zoom")), &zoom_scale, 0.1f, 10.0f, "%.1f")) {
m_state.set_zoom_scale(State::DefaultZoomScale * zoom_scale); params_copy.zoom.scale = Params::DefaultZoomScale * zoom_scale;
params_changed = true;
}
ImGui::Separator(); ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color); ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Deadzone:"))); imgui.text(_(L("Deadzone:")));
ImGui::PopStyleColor(); ImGui::PopStyleColor();
float translation_deadzone = (float)m_state.get_translation_deadzone(); float translation_deadzone = (float)params_copy.translation.deadzone;
if (imgui.slider_float(_(L("Translation")) + "/" + _(L("Zoom")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) if (imgui.slider_float(_(L("Translation")) + "/" + _(L("Zoom")), &translation_deadzone, 0.0f, (float)Params::MaxTranslationDeadzone, "%.2f")) {
m_state.set_translation_deadzone((double)translation_deadzone); params_copy.translation.deadzone = (double)translation_deadzone;
params_changed = true;
}
float rotation_deadzone = m_state.get_rotation_deadzone(); float rotation_deadzone = params_copy.rotation.deadzone;
if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, Params::MaxRotationDeadzone, "%.2f")) {
m_state.set_rotation_deadzone(rotation_deadzone); params_copy.rotation.deadzone = rotation_deadzone;
params_changed = true;
}
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
ImGui::Separator(); ImGui::Separator();
@ -328,8 +303,8 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
imgui.text("DEBUG:"); imgui.text("DEBUG:");
imgui.text("Vectors:"); imgui.text("Vectors:");
ImGui::PopStyleColor(); ImGui::PopStyleColor();
Vec3f translation = m_state.get_translation().cast<float>(); Vec3f translation = m_state.get_first_vector_of_type(State::QueueItem::TranslationType).cast<float>();
Vec3f rotation = m_state.get_rotation(); Vec3f rotation = m_state.get_first_vector_of_type(State::QueueItem::RotationType).cast<float>();
ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
@ -337,19 +312,16 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
imgui.text("Queue size:"); imgui.text("Queue size:");
ImGui::PopStyleColor(); ImGui::PopStyleColor();
int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() }; int input_queue_size_current[2] = { int(m_state.input_queue_size_current()), int(m_state.input_queue_max_size_achieved) };
int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() }; ImGui::InputInt2("Current##4", input_queue_size_current, ImGuiInputTextFlags_ReadOnly);
int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() };
ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly); int input_queue_size_param = int(params_copy.input_queue_max_size);
ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); if (ImGui::InputInt("Max size", &input_queue_size_param, 1, 1, ImGuiInputTextFlags_ReadOnly))
ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly);
int queue_size = (int)m_state.get_queues_max_size();
if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly))
{ {
if (queue_size > 0) if (input_queue_size_param > 0) {
m_state.set_queues_max_size(queue_size); params_copy.input_queue_max_size = input_queue_size_param;
params_changed = true;
}
} }
ImGui::Separator(); ImGui::Separator();
@ -377,23 +349,165 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
} }
imgui.end(); imgui.end();
if (params_changed) {
// Synchronize front end parameters to back end.
tbb::mutex::scoped_lock lock(m_params_ui_mutex);
auto pthis = const_cast<Mouse3DController*>(this);
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (params_copy.input_queue_max_size != params_copy.input_queue_max_size)
// Reset the statistics counter.
m_state.input_queue_max_size_achieved = 0;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
pthis->m_params_ui = params_copy;
pthis->m_params_ui_changed = true;
}
}
#if __APPLE__
void Mouse3DController::connected(std::string device_name)
{
m_device_str = device_name;
// Copy the parameters for m_device_str into the current parameters.
if (auto it_params = m_params_by_device.find(m_device_str); it_params != m_params_by_device.end()) {
tbb::mutex::scoped_lock lock(m_params_ui_mutex);
m_params = m_params_ui = it_params->second;
}
m_connected = true;
}
void Mouse3DController::disconnected()
{
// Copy the current parameters for m_device_str into the parameter database.
assert(! m_device_str.empty());
if (! m_device_str.empty()) {
tbb::mutex::scoped_lock lock(m_params_ui_mutex);
m_params_by_device[m_device_str] = m_params_ui;
}
m_device_str.clear();
m_connected = false;
}
bool Mouse3DController::handle_input(const DataPacketAxis& packet)
{
if (! wxGetApp().IsActive())
return false;
{
// Synchronize parameters between the UI thread and the background thread.
//FIXME is this necessary on OSX? Are these notifications triggered from the main thread or from a worker thread?
tbb::mutex::scoped_lock lock(m_params_ui_mutex);
if (m_params_ui_changed) {
m_params = m_params_ui;
m_params_ui_changed = false;
}
}
bool updated = false;
// translation
double deadzone = m_params.translation.deadzone;
Vec3d translation(std::abs(packet[0]) > deadzone ? -packet[0] : 0.0,
std::abs(packet[1]) > deadzone ? packet[1] : 0.0,
std::abs(packet[2]) > deadzone ? packet[2] : 0.0);
if (! translation.isApprox(Vec3d::Zero())) {
m_state.append_translation(translation, m_params.input_queue_max_size);
updated = true;
}
// rotation
deadzone = m_params.rotation.deadzone;
Vec3f rotation(std::abs(packet[3]) > deadzone ? (float)packet[3] : 0.0,
std::abs(packet[4]) > deadzone ? (float)packet[4] : 0.0,
std::abs(packet[5]) > deadzone ? (float)packet[5] : 0.0);
if (! rotation.isApprox(Vec3f::Zero())) {
m_state.append_rotation(rotation, m_params.input_queue_max_size);
updated = true;
}
if (updated) {
wxGetApp().plater()->set_current_canvas_as_dirty();
// ask for an idle event to update 3D scene
wxWakeUpIdle();
}
return updated;
}
#else //__APPLE__
// Initialize the application.
void Mouse3DController::init()
{
assert(! m_thread.joinable());
if (! m_thread.joinable()) {
m_stop = false;
m_thread = std::thread(&Mouse3DController::run, this);
}
}
// Closing the application.
void Mouse3DController::shutdown()
{
if (m_thread.joinable()) {
// Stop the worker thread, if running.
{
// Notify the worker thread to cancel wait on detection polling.
std::unique_lock<std::mutex> lock(m_stop_condition_mutex);
m_stop = true;
m_stop_condition.notify_all();
}
// Wait for the worker thread to stop.
m_thread.join();
}
}
// Main routine of the worker thread.
void Mouse3DController::run()
{
// Initialize the hidapi library
int res = hid_init();
if (res != 0) {
// Give up.
BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library";
return;
}
for (;;) {
{
tbb::mutex::scoped_lock lock(m_params_ui_mutex);
if (m_stop)
break;
if (m_params_ui_changed) {
m_params = m_params_ui;
m_params_ui_changed = false;
}
}
if (m_device == nullptr)
// Polls the HID devices, blocks for maximum 2 seconds.
m_connected = this->connect_device();
else
// Waits for 3DConnexion mouse input for maximum 100ms, then repeats.
this->collect_input();
}
this->disconnect_device();
// Finalize the hidapi library
hid_exit();
} }
bool Mouse3DController::connect_device() bool Mouse3DController::connect_device()
{ {
#ifdef __APPLE__ if (m_stop)
return false;
#endif//__APPLE__
static const long long DETECTION_TIME_MS = 2000; // two seconds
if (is_device_connected())
return false; return false;
// check time since last detection took place {
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_last_time).count() < DETECTION_TIME_MS) // Wait for 2 seconds, but cancellable by m_stop.
return false; std::unique_lock<std::mutex> lock(m_stop_condition_mutex);
m_stop_condition.wait_for(lock, std::chrono::seconds(2), [this]{ return this->m_stop; });
}
m_last_time = std::chrono::high_resolution_clock::now(); if (m_stop)
return false;
// Enumerates devices // Enumerates devices
hid_device_info* devices = hid_enumerate(0, 0); hid_device_info* devices = hid_enumerate(0, 0);
@ -623,23 +737,11 @@ bool Mouse3DController::connect_device()
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << "Opened device." << std::endl; std::cout << "Opened device." << std::endl;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
// get device parameters from the config, if present // Copy the parameters for m_device_str into the current parameters.
double translation_speed = 4.0; if (auto it_params = m_params_by_device.find(m_device_str); it_params != m_params_by_device.end()) {
float rotation_speed = 4.0; tbb::mutex::scoped_lock lock(m_params_ui_mutex);
double translation_deadzone = State::DefaultTranslationDeadzone; m_params = m_params_ui = it_params->second;
float rotation_deadzone = State::DefaultRotationDeadzone; }
double zoom_speed = 2.0;
wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed);
wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone);
wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed);
wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone);
wxGetApp().app_config->get_mouse_device_zoom_speed(m_device_str, zoom_speed);
// clamp to valid values
m_state.set_translation_scale(State::DefaultTranslationScale * std::clamp(translation_speed, 0.1, 10.0));
m_state.set_translation_deadzone(std::clamp(translation_deadzone, 0.0, State::MaxTranslationDeadzone));
m_state.set_rotation_scale(State::DefaultRotationScale * std::clamp(rotation_speed, 0.1f, 10.0f));
m_state.set_rotation_deadzone(std::clamp(rotation_deadzone, 0.0f, State::MaxRotationDeadzone));
m_state.set_zoom_scale(State::DefaultZoomScale * std::clamp(zoom_speed, 0.1, 10.0));
} }
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
else else
@ -657,138 +759,85 @@ bool Mouse3DController::connect_device()
void Mouse3DController::disconnect_device() void Mouse3DController::disconnect_device()
{ {
if (!is_device_connected()) if (m_device) {
return;
// Stop the secondary thread, if running
if (m_thread.joinable())
m_thread.join();
// Store current device parameters into the config
wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(),
m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone(), m_state.get_zoom_scale() / State::DefaultZoomScale);
wxGetApp().app_config->save();
// Close the 3Dconnexion device
hid_close(m_device); hid_close(m_device);
m_device = nullptr; m_device = nullptr;
BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str; BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str;
// Copy the current parameters for m_device_str into the parameter database.
m_device_str = "";
}
void Mouse3DController::start()
{ {
if (!is_device_connected() || m_running) tbb::mutex::scoped_lock lock(m_params_ui_mutex);
return; m_params_by_device[m_device_str] = m_params_ui;
}
m_thread = std::thread(&Mouse3DController::run, this); m_device_str.clear();
m_connected = false;
}
} }
void Mouse3DController::run()
{
m_running = true;
while (m_running)
{
collect_input();
}
}
void Mouse3DController::collect_input() void Mouse3DController::collect_input()
{ {
DataPacketRaw packet = { 0 }; DataPacketRaw packet = { 0 };
// Read packet, block maximum 100 ms. That means when closing the application, closing the application will be delayed by 100 ms.
int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100); int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100);
if (res < 0) if (res < 0) {
{ // An error occourred (device detached from pc ?). Close the 3Dconnexion device.
// An error occourred (device detached from pc ?) this->disconnect_device();
stop(); } else
return; this->handle_input(packet, res, m_params, m_state);
}
handle_input(packet, res);
} }
void Mouse3DController::handle_input_axis(const DataPacketAxis& packet) // Unpack raw 3DConnexion HID packet of a wired 3D mouse into m_state. Called by the worker thread.
bool Mouse3DController::handle_input(const DataPacketRaw& packet, const int packet_lenght, const Params &params, State &state_in_out)
{ {
if (! wxGetApp().IsActive()) if (! wxGetApp().IsActive())
return; return false;
bool appended = false;
//translation
double deadzone = m_state.get_translation_deadzone();
Vec3d translation(std::abs(packet[0]) > deadzone ? -packet[0] : 0.0,
std::abs(packet[1]) > deadzone ? packet[1] : 0.0,
std::abs(packet[2]) > deadzone ? packet[2] : 0.0);
if (!translation.isApprox(Vec3d::Zero()))
{
m_state.append_translation(translation);
appended = true;
}
//rotation
deadzone = m_state.get_rotation_deadzone();
Vec3f rotation(std::abs(packet[3]) > deadzone ? (float)packet[3] : 0.0,
std::abs(packet[4]) > deadzone ? (float)packet[4] : 0.0,
std::abs(packet[5]) > deadzone ? (float)packet[5] : 0.0);
if (!rotation.isApprox(Vec3f::Zero()))
{
m_state.append_rotation(rotation);
appended = true;
}
if (appended)
{
wxGetApp().plater()->set_current_canvas_as_dirty();
// ask for an idle event to update 3D scene
wxWakeUpIdle();
}
}
void Mouse3DController::handle_input(const DataPacketRaw& packet, const int packet_lenght)
{
if (!wxGetApp().IsActive())
return;
int res = packet_lenght; int res = packet_lenght;
bool updated = false; bool updated = false;
if (res == 7) if (res == 7)
updated = handle_packet(packet); updated = handle_packet(packet, params, state_in_out);
else if (res == 13) else if (res == 13)
updated = handle_wireless_packet(packet); updated = handle_wireless_packet(packet, params, state_in_out);
else if ((res == 3) && (packet[0] == 3)) else if ((res == 3) && (packet[0] == 3))
// On Mac button packets can be 3 bytes long // On Mac button packets can be 3 bytes long
updated = handle_packet(packet); updated = handle_packet(packet, params, state_in_out);
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
else if (res > 0) else if (res > 0)
std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl; std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (updated) #if 1
{ if (updated) {
wxGetApp().plater()->set_current_canvas_as_dirty(); wxGetApp().plater()->set_current_canvas_as_dirty();
// ask for an idle event to update 3D scene // ask for an idle event to update 3D scene
wxWakeUpIdle(); wxWakeUpIdle();
} }
#endif
return updated;
} }
bool Mouse3DController::handle_packet(const DataPacketRaw& packet) // Unpack raw 3DConnexion HID packet of a wired 3D mouse into m_state. Called by handle_input() from the worker thread.
bool Mouse3DController::handle_packet(const DataPacketRaw& packet, const Params &params, State &state_in_out)
{ {
switch (packet[0]) switch (packet[0])
{ {
case 1: // Translation case 1: // Translation
{ {
if (handle_packet_translation(packet)) if (handle_packet_translation(packet, params, state_in_out))
return true; return true;
break; break;
} }
case 2: // Rotation case 2: // Rotation
{ {
if (handle_packet_rotation(packet, 1)) if (handle_packet_rotation(packet, 1, params, state_in_out))
return true; return true;
break; break;
} }
case 3: // Button case 3: // Button
{ {
if (handle_packet_button(packet, packet.size() - 1)) if (params.buttons_enabled && handle_packet_button(packet, packet.size() - 1, params, state_in_out))
return true; return true;
break; break;
@ -796,14 +845,14 @@ bool Mouse3DController::handle_packet(const DataPacketRaw& packet)
case 23: // Battery charge case 23: // Battery charge
{ {
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; std::cout << "3DConnexion - battery level: " << (int)packet[1] << " percent" << std::endl;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
break; break;
} }
default: default:
{ {
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; std::cout << "3DConnexion - Got unknown data packet of code: " << (int)packet[0] << std::endl;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
break; break;
} }
@ -812,14 +861,15 @@ bool Mouse3DController::handle_packet(const DataPacketRaw& packet)
return false; return false;
} }
bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet) // Unpack raw 3DConnexion HID packet of a wireless 3D mouse into m_state. Called by handle_input() from the worker thread.
bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet, const Params &params, State &state_in_out)
{ {
switch (packet[0]) switch (packet[0])
{ {
case 1: // Translation + Rotation case 1: // Translation + Rotation
{ {
bool updated = handle_packet_translation(packet); bool updated = handle_packet_translation(packet, params, state_in_out);
updated |= handle_packet_rotation(packet, 7); updated |= handle_packet_rotation(packet, 7, params, state_in_out);
if (updated) if (updated)
return true; return true;
@ -828,7 +878,7 @@ bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet)
} }
case 3: // Button case 3: // Button
{ {
if (handle_packet_button(packet, 12)) if (params.buttons_enabled && handle_packet_button(packet, 12, params, state_in_out))
return true; return true;
break; break;
@ -836,14 +886,14 @@ bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet)
case 23: // Battery charge case 23: // Battery charge
{ {
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl; std::cout << "3DConnexion - battery level: " << (int)packet[1] << " percent" << std::endl;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
break; break;
} }
default: default:
{ {
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl; std::cout << "3DConnexion - Got unknown data packet of code: " << (int)packet[0] << std::endl;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
break; break;
} }
@ -852,46 +902,52 @@ bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet)
return false; return false;
} }
double convert_input(unsigned char first, unsigned char second, double deadzone) // Convert a signed 16bit word from a 3DConnexion mouse HID packet into a double coordinate, apply a dead zone.
static double convert_input(int coord_byte_low, int coord_byte_high, double deadzone)
{ {
short value = first | second << 8; int value = coord_byte_low | (coord_byte_high << 8);
if (value >= 32768)
value = value - 65536;
double ret = (double)value / 350.0; double ret = (double)value / 350.0;
return (std::abs(ret) > deadzone) ? ret : 0.0; return (std::abs(ret) > deadzone) ? ret : 0.0;
} }
bool Mouse3DController::handle_packet_translation(const DataPacketRaw& packet) // Unpack raw 3DConnexion HID packet, decode state of translation axes into state_in_out. Called by handle_input() from the worker thread.
bool Mouse3DController::handle_packet_translation(const DataPacketRaw& packet, const Params &params, State &state_in_out)
{ {
double deadzone = m_state.get_translation_deadzone(); double deadzone = params.translation.deadzone;
Vec3d translation(-convert_input(packet[1], packet[2], deadzone), Vec3d translation(-convert_input(packet[1], packet[2], deadzone),
convert_input(packet[3], packet[4], deadzone), convert_input(packet[3], packet[4], deadzone),
convert_input(packet[5], packet[6], deadzone)); convert_input(packet[5], packet[6], deadzone));
if (!translation.isApprox(Vec3d::Zero())) if (!translation.isApprox(Vec3d::Zero()))
{ {
m_state.append_translation(translation); state_in_out.append_translation(translation, params.input_queue_max_size);
return true; return true;
} }
return false; return false;
} }
bool Mouse3DController::handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte) // Unpack raw 3DConnexion HID packet, decode state of rotation axes into state_in_out. Called by the handle_input() from worker thread.
bool Mouse3DController::handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte, const Params &params, State &state_in_out)
{ {
double deadzone = (double)m_state.get_rotation_deadzone(); double deadzone = (double)params.rotation.deadzone;
Vec3f rotation((float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone), Vec3f rotation((float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone),
(float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone), (float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone),
(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone)); (float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone));
if (!rotation.isApprox(Vec3f::Zero())) if (!rotation.isApprox(Vec3f::Zero()))
{ {
m_state.append_rotation(rotation); state_in_out.append_rotation(rotation, params.input_queue_max_size);
return true; return true;
} }
return false; return false;
} }
bool Mouse3DController::handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size) // Unpack raw 3DConnexion HID packet, decode button state into state_in_out. Called by handle_input() from the worker thread.
bool Mouse3DController::handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size, const Params &params, State &state_in_out)
{ {
unsigned int data = 0; unsigned int data = 0;
for (unsigned int i = 1; i < packet_size; ++i) for (unsigned int i = 1; i < packet_size; ++i)
@ -904,7 +960,7 @@ bool Mouse3DController::handle_packet_button(const DataPacketRaw& packet, unsign
{ {
if (data_bits.test(i)) if (data_bits.test(i))
{ {
m_state.append_button((unsigned int)i); state_in_out.append_button((unsigned int)i, params.input_queue_max_size);
return true; return true;
} }
} }
@ -912,5 +968,7 @@ bool Mouse3DController::handle_packet_button(const DataPacketRaw& packet, unsign
return false; return false;
} }
#endif //__APPLE__
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View file

@ -10,67 +10,81 @@
#include <queue> #include <queue>
#include <thread> #include <thread>
#include <mutex>
#include <vector> #include <vector>
#include <chrono> #include <chrono>
#include <condition_variable>
#include <tbb/mutex.h>
namespace Slic3r { namespace Slic3r {
namespace GUI {
#if __APPLE__ class AppConfig;
class Mouse3DHandlerMac;
#endif//__APPLE__ namespace GUI {
struct Camera; struct Camera;
class GLCanvas3D; class GLCanvas3D;
class Mouse3DController class Mouse3DController
{ {
class State // Parameters, which are configured by the ImGUI dialog when pressing Ctrl+M.
// The UI thread modifies a copy of the parameters and indicates to the background thread that there was a change
// to copy the parameters.
struct Params
{ {
public: static constexpr double DefaultTranslationScale = 2.5;
static const double DefaultTranslationScale; static constexpr double MaxTranslationDeadzone = 0.2;
static const double MaxTranslationDeadzone; static constexpr double DefaultTranslationDeadzone = 0.5 * MaxTranslationDeadzone;
static const double DefaultTranslationDeadzone; static constexpr float DefaultRotationScale = 1.0f;
static const float DefaultRotationScale; static constexpr float MaxRotationDeadzone = 0.2f;
static const float MaxRotationDeadzone; static constexpr float DefaultRotationDeadzone = 0.5f * MaxRotationDeadzone;
static const float DefaultRotationDeadzone; static constexpr double DefaultZoomScale = 0.1;
static const double DefaultZoomScale;
private:
template <typename Number> template <typename Number>
struct CustomParameters struct CustomParameters
{ {
Number scale; Number scale;
Number deadzone; Number deadzone;
CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {}
}; };
template <class T> CustomParameters<double> translation { DefaultTranslationScale, DefaultTranslationDeadzone };
struct InputQueue CustomParameters<float> rotation { DefaultRotationScale, DefaultRotationDeadzone };
{ CustomParameters<double> zoom { DefaultZoomScale, 0.0 };
size_t max_size; // Do not process button presses from 3DConnexion device, let the user map the 3DConnexion keys in 3DConnexion driver.
std::queue<T> queue; bool buttons_enabled { false };
// The default value of 15 for max_size seems to work fine on all platforms
// The default value of 5 for max_size seems to work fine on all platforms
// The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1 // The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1
// and playing with the imgui dialog which shows by pressing CTRL+M // and playing with the imgui dialog which shows by pressing CTRL+M
InputQueue() : max_size(5) {} size_t input_queue_max_size { 15 };
}; };
InputQueue<Vec3d> m_translation; // Queue of the 3DConnexion input events (translations, rotations, button presses).
InputQueue<Vec3f> m_rotation; class State
std::queue<unsigned int> m_buttons; {
public:
struct QueueItem {
static QueueItem translation(const Vec3d &translation) { QueueItem out; out.vector = translation; out.type_or_buttons = TranslationType; return out; }
static QueueItem rotation(const Vec3d &rotation) { QueueItem out; out.vector = rotation; out.type_or_buttons = RotationType; return out; }
static QueueItem buttons(unsigned int buttons) { QueueItem out; out.type_or_buttons = buttons; return out; }
bool m_buttons_enabled; bool is_translation() const { return this->type_or_buttons == TranslationType; }
bool is_rotation() const { return this->type_or_buttons == RotationType; }
bool is_buttons() const { return ! this->is_translation() && ! this->is_rotation(); }
CustomParameters<double> m_translation_params; Vec3d vector;
CustomParameters<float> m_rotation_params; unsigned int type_or_buttons;
CustomParameters<double> m_zoom_params;
static constexpr unsigned int TranslationType = std::numeric_limits<unsigned int>::max();
static constexpr unsigned int RotationType = TranslationType - 1;
};
private:
// m_input_queue is accessed by the background thread and by the UI thread. Access to m_input_queue
// is guarded with m_input_queue_mutex.
std::deque<QueueItem> m_input_queue;
mutable tbb::mutex m_input_queue_mutex;
#ifdef WIN32
// When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected. // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected.
// We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere // We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere
// by triggering unwanted zoom in/out of the scene // by triggering unwanted zoom in/out of the scene
@ -78,131 +92,136 @@ class Mouse3DController
// Mouse3DController::collect_input() through the call to the append_rotation() method // Mouse3DController::collect_input() through the call to the append_rotation() method
// GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method // GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method
// GLCanvas3D::on_idle() through the call to the apply() method // GLCanvas3D::on_idle() through the call to the apply() method
std::atomic<unsigned int> m_mouse_wheel_counter; unsigned int m_mouse_wheel_counter { 0 };
#endif /* WIN32 */
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
size_t m_translation_queue_max_size;
size_t m_rotation_queue_max_size;
size_t m_buttons_queue_max_size;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
public: public:
State(); // Called by the background thread or by by Mouse3DHandlerMac.mm when a new event is received from 3DConnexion device.
void append_translation(const Vec3d& translation, size_t input_queue_max_size);
void append_translation(const Vec3d& translation); void append_rotation(const Vec3f& rotation, size_t input_queue_max_size);
void append_rotation(const Vec3f& rotation); void append_button(unsigned int id, size_t input_queue_max_size);
void append_button(unsigned int id);
bool has_translation() const { return !m_translation.queue.empty(); }
bool has_rotation() const { return !m_rotation.queue.empty(); }
bool has_button() const { return !m_buttons.empty(); }
#ifdef WIN32
// Called by GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
// to filter out spurious mouse scroll events produced by the 3DConnexion driver on Windows.
bool process_mouse_wheel(); bool process_mouse_wheel();
#endif /* WIN32 */
double get_translation_scale() const { return m_translation_params.scale; }
void set_translation_scale(double scale) { m_translation_params.scale = scale; }
float get_rotation_scale() const { return m_rotation_params.scale; }
void set_rotation_scale(float scale) { m_rotation_params.scale = scale; }
double get_zoom_scale() const { return m_zoom_params.scale; }
void set_zoom_scale(double scale) { m_zoom_params.scale = scale; }
double get_translation_deadzone() const { return m_translation_params.deadzone; }
void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; }
float get_rotation_deadzone() const { return m_rotation_params.deadzone; }
void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; }
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); } Vec3d get_first_vector_of_type(unsigned int type) const {
Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); } tbb::mutex::scoped_lock lock(m_input_queue_mutex);
unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; } auto it = std::find_if(m_input_queue.begin(), m_input_queue.end(), [type](const QueueItem& item) { return item.type_or_buttons == type; });
return (it == m_input_queue.end()) ? Vec3d::Zero() : it->vector;
unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); } }
unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); } size_t input_queue_size_current() const {
unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); } tbb::mutex::scoped_lock lock(m_input_queue_mutex);
return m_input_queue.size();
size_t get_translation_queue_max_size() const { return m_translation_queue_max_size; } }
size_t get_rotation_queue_max_size() const { return m_rotation_queue_max_size; } std::atomic<size_t> input_queue_max_size_achieved;
size_t get_buttons_queue_max_size() const { return m_buttons_queue_max_size; }
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
size_t get_queues_max_size() const { return m_translation.max_size; } // Apply the 3DConnexion events stored in the input queue, reset the input queue.
void set_queues_max_size(size_t size); // Returns true if any change to the camera took place.
bool apply(const Params &params, Camera& camera);
// return true if any change to the camera took place
bool apply(Camera& camera);
}; };
bool m_initialized; // Background thread works with this copy.
Params m_params;
// UI thread will read / write this copy.
Params m_params_ui;
bool m_params_ui_changed { false };
mutable tbb::mutex m_params_ui_mutex;
// This is a database of parametes of all 3DConnexion devices ever connected.
// This database is loaded from AppConfig on application start and it is stored to AppConfig on application exit.
// We need to do that as the AppConfig is not thread safe and we need read the parameters on device connect / disconnect,
// which is now done by a background thread.
std::map<std::string, Params> m_params_by_device;
mutable State m_state; mutable State m_state;
std::thread m_thread; std::atomic<bool> m_connected;
hid_device* m_device;
std::string m_device_str; std::string m_device_str;
bool m_running;
bool m_mac_mouse_connected; #if ! __APPLE__
mutable bool m_show_settings_dialog; // Worker thread for enumerating devices, connecting, reading data from the device and closing the device.
// set to true when ther user closes the dialog by clicking on [X] or [Close] buttons std::thread m_thread;
mutable bool m_settings_dialog_closed_by_user; hid_device* m_device { nullptr };
std::chrono::time_point<std::chrono::high_resolution_clock> m_last_time; // Using m_stop_condition_mutex to synchronize m_stop.
bool m_stop { false };
// Mutex and condition variable for sleeping during the detection of 3DConnexion devices by polling while allowing
// cancellation before the end of the polling interval.
std::mutex m_stop_condition_mutex;
std::condition_variable m_stop_condition;
#endif
// Is the ImGUI dialog shown? Accessed from UI thread only.
mutable bool m_show_settings_dialog { false };
// Set to true when ther user closes the dialog by clicking on [X] or [Close] buttons. Accessed from UI thread only.
mutable bool m_settings_dialog_closed_by_user { false };
public: public:
Mouse3DController(); // Load the device parameter database from appconfig. To be called on application startup.
void load_config(const AppConfig &appconfig);
// Store the device parameter database back to appconfig. To be called on application closeup.
void save_config(AppConfig &appconfig) const;
// Start the background thread to detect and connect to a HID device (Windows and Linux).
// Connect to a 3DConnextion driver (OSX).
// Call load_config() before init().
void init(); void init();
// Stop the background thread (Windows and Linux).
// Disconnect from a 3DConnextion driver (OSX).
// Call save_config() after shutdown().
void shutdown(); void shutdown();
bool is_device_connected() const { return m_device != nullptr || m_mac_mouse_connected; } bool connected() const { return m_connected; }
bool is_running() const { return m_running || m_mac_mouse_connected; }
void set_mac_mouse_connected(bool b){m_mac_mouse_connected = b;}; #if __APPLE__
// Interfacing with the Objective C code (MouseHandlerMac.mm)
void connected(std::string device_name);
void disconnected();
typedef std::array<double, 6> DataPacketAxis;
// Unpack a 3DConnexion packet provided by the 3DConnexion driver into m_state. Called by Mouse3DHandlerMac.mm
bool handle_input(const DataPacketAxis& packet);
#endif // __APPLE__
#ifdef WIN32
// On Windows, the 3DConnexion driver sends out mouse wheel rotation events to an active application
// if the application does not register at the driver. This is a workaround to ignore these superfluous
// mouse wheel events.
bool process_mouse_wheel() { return m_state.process_mouse_wheel(); } bool process_mouse_wheel() { return m_state.process_mouse_wheel(); }
#endif // WIN32
// Apply the received 3DConnexion mouse events to the camera. Called from the UI rendering thread.
bool apply(Camera& camera); bool apply(Camera& camera);
bool is_settings_dialog_shown() const { return m_show_settings_dialog; } bool is_settings_dialog_shown() const { return m_show_settings_dialog; }
void show_settings_dialog(bool show) { m_show_settings_dialog = show && is_running(); } void show_settings_dialog(bool show) { m_show_settings_dialog = show && this->connected(); }
void render_settings_dialog(GLCanvas3D& canvas) const; void render_settings_dialog(GLCanvas3D& canvas) const;
typedef std::array<double, 6> DataPacketAxis; #if ! __APPLE__
void handle_input_axis(const DataPacketAxis& packet);
private: private:
bool connect_device(); bool connect_device();
void disconnect_device(); void disconnect_device();
void start();
void stop() { m_running = false; }
typedef std::array<unsigned char, 13> DataPacketRaw;
// secondary thread methods // secondary thread methods
void run(); void run();
void collect_input(); void collect_input();
void handle_input(const DataPacketRaw& packet, const int packet_lenght);
bool handle_packet(const DataPacketRaw& packet);
bool handle_wireless_packet(const DataPacketRaw& packet);
bool handle_packet_translation(const DataPacketRaw& packet);
bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte);
bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size);
#if __APPLE__ typedef std::array<unsigned char, 13> DataPacketRaw;
Mouse3DHandlerMac* m_handler_mac;
#endif//__APPLE__ // Unpack raw 3DConnexion HID packet of a wired 3D mouse into m_state. Called by the worker thread.
static bool handle_input(const DataPacketRaw& packet, const int packet_lenght, const Params &params, State &state_in_out);
// The following is called by handle_input() from the worker thread.
static bool handle_packet(const DataPacketRaw& packet, const Params &params, State &state_in_out);
static bool handle_wireless_packet(const DataPacketRaw& packet, const Params &params, State &state_in_out);
static bool handle_packet_translation(const DataPacketRaw& packet, const Params &params, State &state_in_out);
static bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte, const Params &params, State &state_in_out);
static bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size, const Params &params, State &state_in_out);
#endif /* __APPLE__ */
}; };
#if __APPLE__
class Mouse3DHandlerMac{
public:
Mouse3DHandlerMac(Mouse3DController* controller);
~Mouse3DHandlerMac();
bool available();
};
#endif//__APPLE__
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r
#endif // slic3r_Mouse3DController_hpp_ #endif // slic3r_Mouse3DController_hpp_

View file

@ -1,4 +1,3 @@
#include "Mouse3DController.hpp" #include "Mouse3DController.hpp"
#include <stdint.h> #include <stdint.h>
@ -10,7 +9,7 @@
#include <cstdio> #include <cstdio>
static Slic3r::GUI::Mouse3DController* mouse_3d_controller = NULL; static Slic3r::GUI::Mouse3DController* mouse_3d_controller = nullptr;
static uint16_t clientID = 0; static uint16_t clientID = 0;
@ -65,7 +64,7 @@ typedef int16_t (*ConnexionClientControl_ptr)(uint16_t clientID,
int32_t param, int32_t param,
int32_t *result); int32_t *result);
#define DECLARE_FUNC(name) name##_ptr name = NULL #define DECLARE_FUNC(name) name##_ptr name = nullptr
DECLARE_FUNC(SetConnexionHandlers); DECLARE_FUNC(SetConnexionHandlers);
DECLARE_FUNC(InstallConnexionHandlers); DECLARE_FUNC(InstallConnexionHandlers);
@ -79,15 +78,12 @@ static void *load_func(void *module, const char *func_name)
{ {
void *func = dlsym(module, func_name); void *func = dlsym(module, func_name);
//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (func) { if (func) {
BOOST_LOG_TRIVIAL(info) << func_name <<" loaded"; BOOST_LOG_TRIVIAL(info) << func_name <<" loaded";
} }
else { else {
//printf("<!> %s\n", dlerror());
BOOST_LOG_TRIVIAL(error) <<"loading 3dx drivers dlsym error: "<< dlerror(); BOOST_LOG_TRIVIAL(error) <<"loading 3dx drivers dlsym error: "<< dlerror();
} }
//#endif
return func; return func;
} }
@ -98,9 +94,8 @@ static void *module; // handle to the whole driver
static bool load_driver_functions() static bool load_driver_functions()
{ {
if (driver_loaded) { if (driver_loaded)
return true; return true;
}
module = dlopen("/Library/Frameworks/3DconnexionClient.framework/3DconnexionClient", module = dlopen("/Library/Frameworks/3DconnexionClient.framework/3DconnexionClient",
RTLD_LAZY | RTLD_LOCAL); RTLD_LAZY | RTLD_LOCAL);
@ -109,15 +104,14 @@ static bool load_driver_functions()
BOOST_LOG_TRIVIAL(info) << "loading 3dx drivers"; BOOST_LOG_TRIVIAL(info) << "loading 3dx drivers";
LOAD_FUNC(SetConnexionHandlers); LOAD_FUNC(SetConnexionHandlers);
if (SetConnexionHandlers != NULL) { if (SetConnexionHandlers != nullptr) {
driver_loaded = true; driver_loaded = true;
has_new_driver = true; has_new_driver = true;
} }
else { else {
BOOST_LOG_TRIVIAL(info) << "installing 3dx drivers"; BOOST_LOG_TRIVIAL(info) << "installing 3dx drivers";
LOAD_FUNC(InstallConnexionHandlers); LOAD_FUNC(InstallConnexionHandlers);
driver_loaded = (InstallConnexionHandlers != nullptr);
driver_loaded = (InstallConnexionHandlers != NULL);
} }
if (driver_loaded) { if (driver_loaded) {
@ -130,15 +124,9 @@ static bool load_driver_functions()
} }
else { else {
BOOST_LOG_TRIVIAL(error) << "3dx drivers module loading error: "<< dlerror() ; BOOST_LOG_TRIVIAL(error) << "3dx drivers module loading error: "<< dlerror() ;
#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("<!> %s\n", dlerror());
#endif
} }
#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("loaded: %s\n", driver_loaded ? "YES" : "NO"); BOOST_LOG_TRIVIAL(info) << "3dx drivers loaded: " << (driver_loaded ? (has_new_driver ? "YES, new" : "YES, old") : "NO");
printf("new: %s\n", has_new_driver ? "YES" : "NO");
#endif
BOOST_LOG_TRIVIAL(info) << "3dx drivers loaded: "<< driver_loaded ? "YES" : "NO" ;
return driver_loaded; return driver_loaded;
} }
@ -149,29 +137,22 @@ static void unload_driver()
static void DeviceAdded(uint32_t unused) static void DeviceAdded(uint32_t unused)
{ {
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout<<"3D device added"<<std::endl;
#endif
BOOST_LOG_TRIVIAL(info)<<"3dx device added"; BOOST_LOG_TRIVIAL(info)<<"3dx device added";
// determine exactly which device is plugged in // determine exactly which device is plugged in
int32_t result; int32_t result;
ConnexionClientControl(clientID, kConnexionCtlGetDeviceID, 0, &result); ConnexionClientControl(clientID, kConnexionCtlGetDeviceID, 0, &result);
int16_t vendorID = result >> 16; int vendorID = result >> 16;
int16_t productID = result & 0xffff; int productID = result & 0xffff;
//TODO: verify device //TODO: verify device
char buf[64];
sprintf(buf, "VID%04X,PID%04X", vendorID, productID);
mouse_3d_controller->set_mac_mouse_connected(true); mouse_3d_controller->connected(buf);
} }
static void DeviceRemoved(uint32_t unused) static void DeviceRemoved(uint32_t unused)
{ {
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("3d device removed\n");
#endif
BOOST_LOG_TRIVIAL(info) << "3dx device removed\n"; BOOST_LOG_TRIVIAL(info) << "3dx device removed\n";
mouse_3d_controller->set_mac_mouse_connected(true); mouse_3d_controller->disconnected();
} }
static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg) static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg)
@ -181,17 +162,12 @@ static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg)
if (s->client == clientID) { if (s->client == clientID) {
switch (s->command) { switch (s->command) {
case kConnexionCmdHandleAxis: { case kConnexionCmdHandleAxis: {
/* // The axis field is an array of 6 signed 16-bit integers corresponding to the 6 device axes. Data is ordered as Tx, Tz, Ty, Rx, Rz, Ry. The values reported are scaled by the driver according to the speed slider settings on the 3Dconnexion preference panel. At maximum speed, the range is - 1024 to 1024. Typical range that you should optimize your application for should be -500 to 500.
The axis field is an array of 6 signed 16-bit integers corresponding to the 6 device axes. Data is ordered as Tx, Tz, Ty, Rx, Rz, Ry. The values reported are scaled by the driver according to the speed slider settings on the 3Dconnexion preference panel. At maximum speed, the range is - 1024 to 1024. Typical range that you should optimize your application for should be -500 to 500.
*/
// Actually we are getting values way over 1024. Max is probably 2048 now. // Actually we are getting values way over 1024. Max is probably 2048 now.
std::array<double, 6> packet; Slic3r::GUI::Mouse3DController::DataPacketAxis packet;
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; ++ i)
packet[i] = (double)s->axis[i]/350.0;//wanted to divide by 500 but 350 is used at raw input so i used same value. packet[i] = (double)s->axis[i]/350.0;//wanted to divide by 500 but 350 is used at raw input so i used same value.
} mouse_3d_controller->handle_input(packet);
mouse_3d_controller->handle_input_axis(packet);
break; break;
} }
case kConnexionCmdHandleButtons: case kConnexionCmdHandleButtons:
@ -203,41 +179,35 @@ static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg)
} }
} }
} }
} }
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
Mouse3DHandlerMac::Mouse3DHandlerMac(Mouse3DController* controller)
// Initialize the application.
void Mouse3DController::init()
{ {
BOOST_LOG_TRIVIAL(info) << "3dx mac handler starts"; BOOST_LOG_TRIVIAL(info) << "3dx mac handler starts";
if (load_driver_functions()) { if (load_driver_functions()) {
mouse_3d_controller = controller; mouse_3d_controller = this;
uint16_t error; uint16_t error = has_new_driver ?
if (has_new_driver) { SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, false) :
error = SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, false); InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved);
}
else {
error = InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved);
}
if (error) {
return;
}
if (! error) {
// Registration is done either by 4letter constant (CFBundleSignature - obsolete // Registration is done either by 4letter constant (CFBundleSignature - obsolete
//and we dont have that) or Executable name in pascal string(first byte is string lenght). //and we dont have that) or Executable name in pascal string(first byte is string lenght).
//If no packets are recieved the name might be different - check cmake. If debugging try commenting //If no packets are recieved the name might be different - check cmake. If debugging try commenting
// set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") // set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
clientID = RegisterConnexionClient( clientID = RegisterConnexionClient(
0, "\013PrusaSlicer", kConnexionClientModeTakeOver, kConnexionMaskAxis); 0, "\013PrusaSlicer", kConnexionClientModeTakeOver, kConnexionMaskAxis);
BOOST_LOG_TRIVIAL(info) << "3dx mac handler registered"; BOOST_LOG_TRIVIAL(info) << "3dx mac handler registered";
} }
} }
}
Mouse3DHandlerMac::~Mouse3DHandlerMac() void Mouse3DController::shutdown()
{ {
if (driver_loaded) { if (driver_loaded) {
UnregisterConnexionClient(clientID); UnregisterConnexionClient(clientID);
@ -247,9 +217,4 @@ Mouse3DHandlerMac::~Mouse3DHandlerMac()
mouse_3d_controller = nullptr; mouse_3d_controller = nullptr;
} }
bool Mouse3DHandlerMac::available()
{
return driver_loaded;
}
}}//namespace Slic3r::GUI }}//namespace Slic3r::GUI

View file

@ -81,7 +81,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1)); html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1));
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK); // wxSYS_COLOUR_WINDOW wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
const int font_size = font.GetPointSize()-1; const int font_size = font.GetPointSize()-1;

View file

@ -1196,7 +1196,7 @@ void Sidebar::update_sliced_info_sizer()
wxString new_label = _(L("Used Material (ml)")) + ":"; wxString new_label = _(L("Used Material (ml)")) + ":";
const bool is_supports = ps.support_used_material > 0.0; const bool is_supports = ps.support_used_material > 0.0;
if (is_supports) if (is_supports)
new_label += wxString::Format("\n - %s\n - %s", _(L("object(s)")), _(L("supports and pad"))); new_label += from_u8((boost::format("\n - %s\n - %s") % _utf8(L("object(s)")) % _utf8(L("supports and pad"))).str());
wxString info_text = is_supports ? wxString info_text = is_supports ?
wxString::Format("%.2f \n%.2f \n%.2f", (ps.objects_used_material + ps.support_used_material) / 1000, wxString::Format("%.2f \n%.2f \n%.2f", (ps.objects_used_material + ps.support_used_material) / 1000,
@ -1233,7 +1233,7 @@ void Sidebar::update_sliced_info_sizer()
wxString new_label = _(L("Used Filament (m)")); wxString new_label = _(L("Used Filament (m)"));
if (is_wipe_tower) if (is_wipe_tower)
new_label += wxString::Format(" :\n - %s\n - %s", _(L("objects")), _(L("wipe tower"))); new_label += from_u8((boost::format(":\n - %1%\n - %2%") % _utf8(L("objects")) % _utf8(L("wipe tower"))).str());
wxString info_text = is_wipe_tower ? wxString info_text = is_wipe_tower ?
wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / 1000, wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / 1000,
@ -1247,7 +1247,7 @@ void Sidebar::update_sliced_info_sizer()
new_label = _(L("Cost")); new_label = _(L("Cost"));
if (is_wipe_tower) if (is_wipe_tower)
new_label += wxString::Format(" :\n - %s\n - %s", _(L("objects")), _(L("wipe tower"))); new_label += from_u8((boost::format(":\n - %1%\n - %2%") % _utf8(L("objects")) % _utf8(L("wipe tower"))).str());
info_text = ps.total_cost == 0.0 ? "N/A" : info_text = ps.total_cost == 0.0 ? "N/A" :
is_wipe_tower ? is_wipe_tower ?
@ -1276,25 +1276,25 @@ void Sidebar::update_sliced_info_sizer()
for (int i = (int)times.size() - 1; i >= 0; --i) for (int i = (int)times.size() - 1; i >= 0; --i)
{ {
if (i == 0 || times[i - 1].first == cgtPausePrint) if (i == 0 || times[i - 1].first == cgtPausePrint)
new_label += wxString::Format("\n - %s%d", str_color + " ", color_change_count); new_label += from_u8((boost::format("\n - %1%%2%") % (std::string(str_color.ToUTF8()) + " ") % color_change_count).str());
else if (times[i - 1].first == cgtColorChange) else if (times[i - 1].first == cgtColorChange)
new_label += wxString::Format("\n - %s%d", str_color + " ", color_change_count--); new_label += from_u8((boost::format("\n - %1%%2%") % (std::string(str_color.ToUTF8()) + " ") % color_change_count--).str());
if (i != (int)times.size() - 1 && times[i].first == cgtPausePrint) if (i != (int)times.size() - 1 && times[i].first == cgtPausePrint)
new_label += wxString::Format(" -> %s", str_pause); new_label += from_u8((boost::format(" -> %1%") % std::string(str_pause.ToUTF8())).str());
info_text += wxString::Format("\n%s", times[i].second); info_text += from_u8((boost::format("\n%1%") % times[i].second).str());
} }
}; };
if (ps.estimated_normal_print_time != "N/A") { if (ps.estimated_normal_print_time != "N/A") {
new_label += wxString::Format("\n - %s", _(L("normal mode"))); new_label += from_u8((boost::format("\n - %1%") % _utf8(L("normal mode"))).str());
info_text += wxString::Format("\n%s", ps.estimated_normal_print_time); info_text += from_u8((boost::format("\n%1%") % ps.estimated_normal_print_time).str());
fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text); fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text);
} }
if (ps.estimated_silent_print_time != "N/A") { if (ps.estimated_silent_print_time != "N/A") {
new_label += wxString::Format("\n - %s", _(L("stealth mode"))); new_label += from_u8((boost::format("\n - %1%") % _utf8(L("stealth mode"))).str());
info_text += wxString::Format("\n%s", ps.estimated_silent_print_time); info_text += from_u8((boost::format("\n%1%") % ps.estimated_silent_print_time).str());
fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text); fill_labels(ps.estimated_silent_custom_gcode_print_times, new_label, info_text);
} }
p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
@ -2150,6 +2150,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// updates camera type from .ini file // updates camera type from .ini file
camera.set_type(get_config("use_perspective_camera")); camera.set_type(get_config("use_perspective_camera"));
// Load the 3DConnexion device database.
mouse3d_controller.load_config(*wxGetApp().app_config);
// Start the background thread to detect and connect to a HID device (Windows and Linux).
// Connect to a 3DConnextion driver (OSX).
mouse3d_controller.init(); mouse3d_controller.init();
// Initialize the Undo / Redo stack with a first snapshot. // Initialize the Undo / Redo stack with a first snapshot.
@ -2161,8 +2165,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
Plater::priv::~priv() Plater::priv::~priv()
{ {
mouse3d_controller.shutdown();
if (config != nullptr) if (config != nullptr)
delete config; delete config;
} }
@ -2285,7 +2287,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
for (size_t i = 0; i < input_files.size(); i++) { for (size_t i = 0; i < input_files.size(); i++) {
const auto &path = input_files[i]; const auto &path = input_files[i];
const auto filename = path.filename(); const auto filename = path.filename();
const auto dlg_info = wxString::Format(_(L("Processing input file %s")), from_path(filename)) + "\n"; const auto dlg_info = from_u8((boost::format(_utf8(L("Processing input file %s"))) % from_path(filename)).str()) + "\n";
dlg.Update(100 * i / input_files.size(), dlg_info); dlg.Update(100 * i / input_files.size(), dlg_info);
const bool type_3mf = std::regex_match(path.string(), pattern_3mf); const bool type_3mf = std::regex_match(path.string(), pattern_3mf);
@ -2392,8 +2394,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
for (auto obj : model.objects) for (auto obj : model.objects)
if ( obj->volumes.size()>1 ) { if ( obj->volumes.size()>1 ) {
Slic3r::GUI::show_error(nullptr, Slic3r::GUI::show_error(nullptr,
wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")), from_u8((boost::format(_utf8(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")))
from_path(filename))); % from_path(filename)).str()));
return obj_idxs; return obj_idxs;
} }
} }
@ -4987,7 +4989,7 @@ void Plater::export_stl(bool extended, bool selection_only)
} }
Slic3r::store_stl(path_u8.c_str(), &mesh, true); Slic3r::store_stl(path_u8.c_str(), &mesh, true);
p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path)); p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("STL file exported to %s"))) % path).str()));
} }
void Plater::export_amf() void Plater::export_amf()
@ -5004,10 +5006,10 @@ void Plater::export_amf()
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1"; bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) { if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
// Success // Success
p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("AMF file exported to %s"))) % path).str()));
} else { } else {
// Failure // Failure
p->statusbar()->set_status_text(wxString::Format(_(L("Error exporting AMF file %s")), path)); p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("Error exporting AMF file %s"))) % path).str()));
} }
} }
@ -5040,12 +5042,12 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) { if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
// Success // Success
p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("3MF file exported to %s"))) % path).str()));
p->set_project_filename(path); p->set_project_filename(path);
} }
else { else {
// Failure // Failure
p->statusbar()->set_status_text(wxString::Format(_(L("Error exporting 3MF file %s")), path)); p->statusbar()->set_status_text(from_u8((boost::format(_utf8(L("Error exporting 3MF file %s"))) % path).str()));
} }
} }

View file

@ -269,7 +269,7 @@ void PrintHostQueueDialog::on_error(Event &evt)
set_state(evt.job_id, ST_ERROR); set_state(evt.job_id, ST_ERROR);
auto errormsg = wxString::Format("%s\n%s", _(L("Error uploading to print host:")), evt.error); auto errormsg = from_u8((boost::format("%1%\n%2%") % _utf8(L("Error uploading to print host:")) % std::string(evt.error.ToUTF8())).str());
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later

View file

@ -3,6 +3,7 @@
#include "GLCanvas3D.hpp" #include "GLCanvas3D.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "GUI.hpp"
#include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectManipulation.hpp"
#include "GUI_ObjectList.hpp" #include "GUI_ObjectList.hpp"
#include "Gizmos/GLGizmoBase.hpp" #include "Gizmos/GLGizmoBase.hpp"
@ -1470,9 +1471,10 @@ void Selection::toggle_instance_printable_state()
ModelInstance* instance = model_object->instances[instance_idx]; ModelInstance* instance = model_object->instances[instance_idx];
const bool printable = !instance->printable; const bool printable = !instance->printable;
wxString snapshot_text = model_object->instances.size() == 1 ? wxString::Format("%s %s", wxString snapshot_text = model_object->instances.size() == 1 ? from_u8((boost::format("%1% %2%")
printable ? _(L("Set Printable")) : _(L("Set Unprintable")), model_object->name) : % (printable ? _utf8(L("Set Printable")) : _utf8(L("Set Unprintable")))
printable ? _(L("Set Printable Instance")) : _(L("Set Unprintable Instance")); % model_object->name).str()) :
(printable ? _(L("Set Printable Instance")) : _(L("Set Unprintable Instance")));
wxGetApp().plater()->take_snapshot(snapshot_text); wxGetApp().plater()->take_snapshot(snapshot_text);
instance->printable = printable; instance->printable = printable;

View file

@ -130,7 +130,7 @@ void Tab::create_preset_tab()
add_scaled_button(panel, &m_btn_hide_incompatible_presets, m_bmp_hide_incompatible_presets.name()); add_scaled_button(panel, &m_btn_hide_incompatible_presets, m_bmp_hide_incompatible_presets.name());
// TRN "Save current Settings" // TRN "Save current Settings"
m_btn_save_preset->SetToolTip(wxString::Format(_(L("Save current %s")),m_title)); m_btn_save_preset->SetToolTip(from_u8((boost::format(_utf8(L("Save current %s"))) % m_title).str()));
m_btn_delete_preset->SetToolTip(_(L("Delete this preset"))); m_btn_delete_preset->SetToolTip(_(L("Delete this preset")));
m_btn_delete_preset->Disable(); m_btn_delete_preset->Disable();
@ -1032,8 +1032,7 @@ void Tab::update_preset_description_line()
} else if (parent == nullptr) { } else if (parent == nullptr) {
description_line = _(L("Current preset is inherited from the default preset.")); description_line = _(L("Current preset is inherited from the default preset."));
} else { } else {
description_line = wxString::Format( description_line = _(L("Current preset is inherited from")) + ":\n\t" + parent->name;
_(L("Current preset is inherited from:\n\t%s")), GUI::from_u8(parent->name));
} }
if (preset.is_default || preset.is_system) if (preset.is_default || preset.is_system)
@ -1750,8 +1749,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config)); std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
if (! host) { if (! host) {
const auto text = wxString::Format("%s", const wxString text = _(L("Could not get a valid Printer Host reference"));
_(L("Could not get a valid Printer Host reference")));
show_error(this, text); show_error(this, text);
return; return;
} }
@ -1777,7 +1775,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
option.opt.width = Field::def_width_wider(); option.opt.width = Field::def_width_wider();
optgroup->append_single_option_line(option); optgroup->append_single_option_line(option);
const auto ca_file_hint = _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")); const auto ca_file_hint = _utf8(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."));
if (Http::ca_file_supported()) { if (Http::ca_file_supported()) {
option = optgroup->get_option("printhost_cafile"); option = optgroup->get_option("printhost_cafile");
@ -1819,15 +1817,19 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
Line line { "", "" }; Line line { "", "" };
line.full_width = 1; line.full_width = 1;
line.widget = [this, ca_file_hint] (wxWindow* parent) { line.widget = [ca_file_hint] (wxWindow* parent) {
auto txt = new wxStaticText(parent, wxID_ANY, wxString::Format("%s\n\n\t%s", std::string info = _utf8(L("HTTPS CA File")) + ":\n\t" +
wxString::Format(_(L("HTTPS CA File:\n\ (boost::format(_utf8(L("On this system, %s uses HTTPS certificates from the system Certificate Store or Keychain."))) % SLIC3R_APP_NAME).str() +
"\n\t" + _utf8(L("To use a custom CA file, please import your CA file into Certificate Store / Keychain."));
auto txt = new wxStaticText(parent, wxID_ANY, from_u8((boost::format("%1%\n\n\t%2%") % info % ca_file_hint).str()));
/* % (boost::format(_utf8(L("HTTPS CA File:\n\
\tOn this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.\n\ \tOn this system, %s uses HTTPS certificates from the system Certificate Store or Keychain.\n\
\tTo use a custom CA file, please import your CA file into Certificate Store / Keychain.")), SLIC3R_APP_NAME), \tTo use a custom CA file, please import your CA file into Certificate Store / Keychain."))) % SLIC3R_APP_NAME).str()
ca_file_hint)); % std::string(ca_file_hint.ToUTF8())).str()));
txt->SetFont(Slic3r::GUI::wxGetApp().normal_font()); */ txt->SetFont(Slic3r::GUI::wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(txt); sizer->Add(txt, 1, wxEXPAND);
return sizer; return sizer;
}; };
@ -2921,8 +2923,8 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr
std::string type_name = presets->name(); std::string type_name = presets->name();
wxString tab = " "; wxString tab = " ";
wxString name = old_preset.is_default ? wxString name = old_preset.is_default ?
wxString::Format(_(L("Default preset (%s)")), _(type_name)) : from_u8((boost::format(_utf8(L("Default preset (%s)"))) % _utf8(type_name)).str()) :
wxString::Format(_(L("Preset (%s)")), _(type_name)) + "\n" + tab + old_preset.name; from_u8((boost::format(_utf8(L("Preset (%s)"))) % _utf8(type_name)).str()) + "\n" + tab + old_preset.name;
// Collect descriptions of the dirty options. // Collect descriptions of the dirty options.
wxString changes; wxString changes;
@ -3190,7 +3192,7 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
{ {
deps.checkbox = new wxCheckBox(parent, wxID_ANY, _(L("All"))); deps.checkbox = new wxCheckBox(parent, wxID_ANY, _(L("All")));
deps.checkbox->SetFont(Slic3r::GUI::wxGetApp().normal_font()); deps.checkbox->SetFont(Slic3r::GUI::wxGetApp().normal_font());
add_scaled_button(parent, &deps.btn, "printer_white", wxString::Format(" %s %s", _(L("Set")), dots), wxBU_LEFT | wxBU_EXACTFIT); add_scaled_button(parent, &deps.btn, "printer_white", from_u8((boost::format(" %s %s") % _utf8(L("Set")) % std::string(dots.ToUTF8())).str()), wxBU_LEFT | wxBU_EXACTFIT);
deps.btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); deps.btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
@ -3312,8 +3314,8 @@ void Tab::fill_icon_descriptions()
m_icon_descriptions.emplace_back(&m_bmp_white_bullet, L("WHITE BULLET"), m_icon_descriptions.emplace_back(&m_bmp_white_bullet, L("WHITE BULLET"),
// TRN Description for "WHITE BULLET" // TRN Description for "WHITE BULLET"
L("for the left button: \tindicates a non-system (or non-default) preset,\n" L("for the left button: indicates a non-system (or non-default) preset,\n"
"for the right button: \tindicates that the settings hasn't been modified.")); "for the right button: indicates that the settings hasn't been modified."));
m_icon_descriptions.emplace_back(&m_bmp_value_revert, L("BACK ARROW"), m_icon_descriptions.emplace_back(&m_bmp_value_revert, L("BACK ARROW"),
// TRN Description for "BACK ARROW" // TRN Description for "BACK ARROW"
@ -3464,7 +3466,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
void SavePresetWindow::build(const wxString& title, const std::string& default_name, std::vector<std::string> &values) void SavePresetWindow::build(const wxString& title, const std::string& default_name, std::vector<std::string> &values)
{ {
// TRN Preset // TRN Preset
auto text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L("Save %s as:")), title), auto text = new wxStaticText(this, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % title).str()),
wxDefaultPosition, wxDefaultSize); wxDefaultPosition, wxDefaultSize);
m_combo = new wxComboBox(this, wxID_ANY, from_u8(default_name), m_combo = new wxComboBox(this, wxID_ANY, from_u8(default_name),
wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER); wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER);

View file

@ -265,8 +265,8 @@ MsgDataIncompatible::~MsgDataIncompatible() {}
MsgDataLegacy::MsgDataLegacy() : MsgDataLegacy::MsgDataLegacy() :
MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update"))) MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update")))
{ {
auto *text = new wxStaticText(this, wxID_ANY, wxString::Format( auto *text = new wxStaticText(this, wxID_ANY, from_u8((boost::format(
_(L( _utf8(L(
"%s now uses an updated configuration structure.\n\n" "%s now uses an updated configuration structure.\n\n"
"So called 'System presets' have been introduced, which hold the built-in default settings for various " "So called 'System presets' have been introduced, which hold the built-in default settings for various "
@ -276,8 +276,9 @@ MsgDataLegacy::MsgDataLegacy() :
"Please proceed with the %s that follows to set up the new presets " "Please proceed with the %s that follows to set up the new presets "
"and to choose whether to enable automatic preset updates." "and to choose whether to enable automatic preset updates."
)), )))
SLIC3R_APP_NAME, _(ConfigWizard::name()) % SLIC3R_APP_NAME
% _utf8(ConfigWizard::name())).str()
)); ));
text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
content_sizer->Add(text); content_sizer->Add(text);

View file

@ -603,7 +603,9 @@ void apply_extruder_selector(wxBitmapComboBox** ctrl,
++i; ++i;
} }
(*ctrl)->Append(use_full_item_name ? wxString::Format("%s %d", str, i) : wxString::Format("%d", i), *bmp); (*ctrl)->Append(use_full_item_name
? Slic3r::GUI::from_u8((boost::format("%1% %2%") % str % i).str())
: wxString::Format("%d", i), *bmp);
++i; ++i;
} }
(*ctrl)->SetSelection(0); (*ctrl)->SetSelection(0);
@ -700,8 +702,9 @@ ModeButton::ModeButton( wxWindow* parent,
void ModeButton::Init(const wxString &mode) void ModeButton::Init(const wxString &mode)
{ {
m_tt_focused = wxString::Format(_(L("Switch to the %s mode")), mode); std::string mode_str = std::string(mode.ToUTF8());
m_tt_selected = wxString::Format(_(L("Current mode is %s")), mode); m_tt_focused = Slic3r::GUI::from_u8((boost::format(_utf8(L("Switch to the %s mode"))) % mode_str).str());
m_tt_selected = Slic3r::GUI::from_u8((boost::format(_utf8(L("Current mode is %s"))) % mode_str).str());
SetBitmapMargins(3, 0); SetBitmapMargins(3, 0);

View file

@ -13,6 +13,7 @@
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "Http.hpp" #include "Http.hpp"
@ -65,7 +66,7 @@ bool AstroBox::test(wxString &msg) const
const auto text = ptree.get_optional<std::string>("text"); const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text); res = validate_version_text(text);
if (! res) { if (! res) {
msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "AstroBox"); msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "AstroBox")).str());
} }
} }
catch (const std::exception &) { catch (const std::exception &) {
@ -85,8 +86,10 @@ wxString AstroBox::get_test_ok_msg () const
wxString AstroBox::get_test_failed_msg (wxString &msg) const wxString AstroBox::get_test_failed_msg (wxString &msg) const
{ {
return wxString::Format("%s: %s\n\n%s", return GUI::from_u8((boost::format("%s: %s\n\n%s")
_(L("Could not connect to AstroBox")), msg, _(L("Note: AstroBox version at least 1.1.0 is required."))); % _utf8(L("Could not connect to AstroBox"))
% std::string(msg.ToUTF8())
% _utf8(L("Note: AstroBox version at least 1.1.0 is required."))).str());
} }
bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const

View file

@ -51,7 +51,9 @@ wxString Duet::get_test_ok_msg () const
wxString Duet::get_test_failed_msg (wxString &msg) const wxString Duet::get_test_failed_msg (wxString &msg) const
{ {
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg); return GUI::from_u8((boost::format("%s: %s")
% _utf8(L("Could not connect to Duet"))
% std::string(msg.ToUTF8())).str());
} }
bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const

View file

@ -70,7 +70,10 @@ wxString FlashAir::get_test_ok_msg () const
wxString FlashAir::get_test_failed_msg (wxString &msg) const wxString FlashAir::get_test_failed_msg (wxString &msg) const
{ {
return wxString::Format("%s: %s", _(L("Could not connect to FlashAir")), msg, _(L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required."))); return GUI::from_u8((boost::format("%s: %s")
% _utf8(L("Could not connect to FlashAir"))
% std::string(msg.ToUTF8())
% _utf8(L("Note: FlashAir with firmware 2.00.02 or newer and activated upload function is required."))).str());
} }
bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const

View file

@ -13,6 +13,7 @@
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "Http.hpp" #include "Http.hpp"
@ -65,7 +66,7 @@ bool OctoPrint::test(wxString &msg) const
const auto text = ptree.get_optional<std::string>("text"); const auto text = ptree.get_optional<std::string>("text");
res = validate_version_text(text); res = validate_version_text(text);
if (! res) { if (! res) {
msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "OctoPrint"); msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "OctoPrint")).str());
} }
} }
catch (const std::exception &) { catch (const std::exception &) {
@ -85,8 +86,10 @@ wxString OctoPrint::get_test_ok_msg () const
wxString OctoPrint::get_test_failed_msg (wxString &msg) const wxString OctoPrint::get_test_failed_msg (wxString &msg) const
{ {
return wxString::Format("%s: %s\n\n%s", return GUI::from_u8((boost::format("%s: %s\n\n%s")
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); % _utf8(L("Could not connect to OctoPrint"))
% std::string(msg.ToUTF8())
% _utf8(L("Note: OctoPrint version at least 1.1.0 is required."))).str());
} }
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
@ -178,7 +181,9 @@ wxString SL1Host::get_test_ok_msg () const
wxString SL1Host::get_test_failed_msg (wxString &msg) const wxString SL1Host::get_test_failed_msg (wxString &msg) const
{ {
return wxString::Format("%s: %s", _(L("Could not connect to Prusa SLA")), msg); return GUI::from_u8((boost::format("%s: %s")
% _utf8(L("Could not connect to Prusa SLA"))
% std::string(msg.ToUTF8())).str());
} }
bool SL1Host::validate_version_text(const boost::optional<std::string> &version_text) const bool SL1Host::validate_version_text(const boost::optional<std::string> &version_text) const

View file

@ -702,15 +702,15 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
const auto max_slic3r = incompat.version.max_slic3r_version; const auto max_slic3r = incompat.version.max_slic3r_version;
wxString restrictions; wxString restrictions;
if (min_slic3r != Semver::zero() && max_slic3r != Semver::inf()) { if (min_slic3r != Semver::zero() && max_slic3r != Semver::inf()) {
restrictions = wxString::Format(_(L("requires min. %s and max. %s")), restrictions = GUI::from_u8((boost::format(_utf8(L("requires min. %s and max. %s")))
min_slic3r.to_string(), % min_slic3r.to_string()
max_slic3r.to_string() % max_slic3r.to_string()).str()
); );
} else if (min_slic3r != Semver::zero()) { } else if (min_slic3r != Semver::zero()) {
restrictions = wxString::Format(_(L("requires min. %s")), min_slic3r.to_string()); restrictions = GUI::from_u8((boost::format(_utf8(L("requires min. %s"))) % min_slic3r.to_string()).str());
BOOST_LOG_TRIVIAL(debug) << "Bundle is not downgrade, user will now have to do whole wizard. This should not happen."; BOOST_LOG_TRIVIAL(debug) << "Bundle is not downgrade, user will now have to do whole wizard. This should not happen.";
} else { } else {
restrictions = wxString::Format(_(L("requires max. %s")), max_slic3r.to_string()); restrictions = GUI::from_u8((boost::format(_utf8(L("requires max. %s"))) % max_slic3r.to_string()).str());
} }
incompats_map.emplace(std::make_pair(incompat.vendor, std::move(restrictions))); incompats_map.emplace(std::make_pair(incompat.vendor, std::move(restrictions)));